restructure
This commit is contained in:
parent
1223958c1f
commit
556b064e1d
@ -1,55 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace dom {
|
|
||||||
|
|
||||||
enum class Elements {
|
|
||||||
Box,
|
|
||||||
Text,
|
|
||||||
};
|
|
||||||
|
|
||||||
class Element {
|
|
||||||
public:
|
|
||||||
Element() = default;
|
|
||||||
Element(const Element&) = default;
|
|
||||||
Element(Element&&) = delete;
|
|
||||||
auto operator=(const Element&) -> Element& = default;
|
|
||||||
auto operator=(Element&&) -> Element& = default;
|
|
||||||
virtual ~Element() = default;
|
|
||||||
|
|
||||||
[[nodiscard]] virtual auto element_type() const noexcept -> Elements = 0;
|
|
||||||
[[nodiscard]] virtual auto parent() noexcept -> std::shared_ptr<Element>
|
|
||||||
= 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Box final : public Element {
|
|
||||||
public:
|
|
||||||
Box() = default;
|
|
||||||
[[nodiscard]] auto element_type() const noexcept -> Elements override
|
|
||||||
{
|
|
||||||
return Elements::Text;
|
|
||||||
};
|
|
||||||
auto add_child() noexcept { }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<std::shared_ptr<Element>> children;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Text final : public Element {
|
|
||||||
public:
|
|
||||||
Text(std::string value)
|
|
||||||
: value { std::move(value) }
|
|
||||||
{ }
|
|
||||||
[[nodiscard]] auto element_type() const noexcept -> Elements override
|
|
||||||
{
|
|
||||||
return Elements::Text;
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string value;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
class Graphics {
|
|
||||||
public:
|
|
||||||
Graphics(const Graphics&) = default;
|
|
||||||
Graphics(Graphics&&) = default;
|
|
||||||
auto operator=(const Graphics&) -> Graphics& = default;
|
|
||||||
auto operator=(Graphics&&) -> Graphics& = default;
|
|
||||||
virtual ~Graphics() = default;
|
|
||||||
|
|
||||||
private:
|
|
||||||
};
|
|
@ -1,14 +0,0 @@
|
|||||||
#include "lexer.hpp"
|
|
||||||
#include "utils/all.hpp"
|
|
||||||
#include <cctype>
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
namespace markup {
|
|
||||||
|
|
||||||
auto constexpr Lexer::next() noexcept -> Result<Token, void>
|
|
||||||
{
|
|
||||||
if (done())
|
|
||||||
return token(TokenTypes::Eof, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,88 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "utils/all.hpp"
|
|
||||||
#include <optional>
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
namespace markup {
|
|
||||||
|
|
||||||
enum class TokenTypes {
|
|
||||||
Eof,
|
|
||||||
Whitespace,
|
|
||||||
MultilineComment,
|
|
||||||
SinglelineComment,
|
|
||||||
|
|
||||||
Name,
|
|
||||||
Int,
|
|
||||||
Float,
|
|
||||||
String,
|
|
||||||
Id, // Example = `#my_id`
|
|
||||||
Class, // Example = `.my_class`
|
|
||||||
|
|
||||||
True,
|
|
||||||
False,
|
|
||||||
Null,
|
|
||||||
|
|
||||||
LBrace,
|
|
||||||
RBrace,
|
|
||||||
Comma,
|
|
||||||
Equal,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Token {
|
|
||||||
TokenTypes type;
|
|
||||||
size_t index, length;
|
|
||||||
int line, column;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Lexer final {
|
|
||||||
public:
|
|
||||||
Lexer(std::string_view text)
|
|
||||||
: text { text }
|
|
||||||
{ }
|
|
||||||
auto constexpr next() noexcept -> Result<Token, void>;
|
|
||||||
auto peek() noexcept -> Result<Token, void>
|
|
||||||
{
|
|
||||||
if (last_token)
|
|
||||||
return Result<Token, void>::create_ok(*last_token);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
auto constexpr make_number() noexcept -> Result<Token, void>;
|
|
||||||
auto constexpr make_id() noexcept -> Result<Token, void>;
|
|
||||||
[[nodiscard]] auto constexpr inline token(
|
|
||||||
TokenTypes type, size_t begin) noexcept -> Token
|
|
||||||
{
|
|
||||||
auto token = Token { type, begin, index - begin, line, column };
|
|
||||||
last_token = token;
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
[[nodiscard]] auto constexpr inline done() const noexcept -> bool
|
|
||||||
{
|
|
||||||
return index >= text.size();
|
|
||||||
}
|
|
||||||
[[nodiscard]] auto constexpr inline current() const noexcept -> char
|
|
||||||
{
|
|
||||||
return text.at(index);
|
|
||||||
}
|
|
||||||
auto constexpr inline step() noexcept -> void
|
|
||||||
{
|
|
||||||
if (done())
|
|
||||||
return;
|
|
||||||
index++;
|
|
||||||
column++;
|
|
||||||
if (!done() and text.at(index) == '\n') {
|
|
||||||
column = 1;
|
|
||||||
line++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string_view text;
|
|
||||||
size_t index = 0;
|
|
||||||
int line = 1;
|
|
||||||
int column = 1;
|
|
||||||
std::optional<Token> last_token;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
|
|
||||||
browser_sources += files(
|
|
||||||
'parser.cpp',
|
|
||||||
'lexer.cpp',
|
|
||||||
)
|
|
@ -1,3 +0,0 @@
|
|||||||
#include "parser.hpp"
|
|
||||||
|
|
||||||
namespace languages { }
|
|
@ -1,7 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
namespace markup {
|
|
||||||
|
|
||||||
class Parser final { };
|
|
||||||
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
|
|
||||||
subdir('markup')
|
|
||||||
subdir('scriptlang')
|
|
||||||
|
|
||||||
browser_sources += files(
|
|
||||||
'main.cpp',
|
|
||||||
)
|
|
@ -1,254 +0,0 @@
|
|||||||
#include "lexer.hpp"
|
|
||||||
#include <cctype>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
namespace scriptlang {
|
|
||||||
|
|
||||||
auto Lexer::make_token() noexcept -> Result<Token, Error>
|
|
||||||
{
|
|
||||||
if (done())
|
|
||||||
return token(Tokens::Eof, index, current_location());
|
|
||||||
if (std::isspace(current()) != 0)
|
|
||||||
return skip_whitespace();
|
|
||||||
if (std::isdigit(current()) != 0)
|
|
||||||
return make_number();
|
|
||||||
if (std::isalpha(current()) != 0 or current() == '_')
|
|
||||||
return make_id();
|
|
||||||
if (current() == '"')
|
|
||||||
return make_string();
|
|
||||||
return make_static();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Lexer::skip_whitespace() noexcept -> Result<Token, Error>
|
|
||||||
{
|
|
||||||
while (!done() and std::isspace(current()) != 0)
|
|
||||||
step();
|
|
||||||
return make_token();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Lexer::make_number() noexcept -> Result<Token, Error>
|
|
||||||
{
|
|
||||||
auto begin = index;
|
|
||||||
auto span_from = current_location();
|
|
||||||
while (!done() and std::isdigit(current()) != 0)
|
|
||||||
step();
|
|
||||||
if (current() == '.') {
|
|
||||||
step();
|
|
||||||
while (!done() and std::isdigit(current()) != 0)
|
|
||||||
step();
|
|
||||||
return token(Tokens::Float, begin, span_from);
|
|
||||||
}
|
|
||||||
return token(Tokens::Int, begin, span_from);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Lexer::make_id() noexcept -> Result<Token, Error>
|
|
||||||
{
|
|
||||||
auto begin = index;
|
|
||||||
auto span_from = current_location();
|
|
||||||
while (!done()
|
|
||||||
and (std::isalpha(current()) != 0 or std::isdigit(current()) != 0
|
|
||||||
or current() == '_'))
|
|
||||||
step();
|
|
||||||
return token(id_or_keyword_type(text.substr(begin, index - begin)), begin,
|
|
||||||
span_from);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Lexer::id_or_keyword_type(std::string_view substring) noexcept -> Tokens
|
|
||||||
{
|
|
||||||
if (substring.compare("if") == 0)
|
|
||||||
return Tokens::If;
|
|
||||||
if (substring.compare("else") == 0)
|
|
||||||
return Tokens::Else;
|
|
||||||
if (substring.compare("for") == 0)
|
|
||||||
return Tokens::For;
|
|
||||||
if (substring.compare("loop") == 0)
|
|
||||||
return Tokens::Loop;
|
|
||||||
if (substring.compare("while") == 0)
|
|
||||||
return Tokens::While;
|
|
||||||
if (substring.compare("break") == 0)
|
|
||||||
return Tokens::Break;
|
|
||||||
if (substring.compare("continue") == 0)
|
|
||||||
return Tokens::Continue;
|
|
||||||
if (substring.compare("fn") == 0)
|
|
||||||
return Tokens::Fn;
|
|
||||||
if (substring.compare("return") == 0)
|
|
||||||
return Tokens::Return;
|
|
||||||
if (substring.compare("false") == 0)
|
|
||||||
return Tokens::False;
|
|
||||||
if (substring.compare("true") == 0)
|
|
||||||
return Tokens::True;
|
|
||||||
if (substring.compare("and") == 0)
|
|
||||||
return Tokens::And;
|
|
||||||
if (substring.compare("or") == 0)
|
|
||||||
return Tokens::Or;
|
|
||||||
if (substring.compare("xor") == 0)
|
|
||||||
return Tokens::Xor;
|
|
||||||
return Tokens::Id;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Lexer::make_string() noexcept -> Result<Token, Error>
|
|
||||||
{
|
|
||||||
auto begin = index;
|
|
||||||
auto span_from = current_location();
|
|
||||||
step();
|
|
||||||
auto escaped = false;
|
|
||||||
while (!done() and (current() != '"' or escaped)) {
|
|
||||||
escaped = escaped ? false : current() == '\\';
|
|
||||||
step();
|
|
||||||
}
|
|
||||||
if (current() != '"')
|
|
||||||
return Error {
|
|
||||||
{ span_from, { line, column } },
|
|
||||||
"unterminated string",
|
|
||||||
};
|
|
||||||
step();
|
|
||||||
return token(Tokens::String, begin, span_from);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Lexer::make_static() noexcept -> Result<Token, Error>
|
|
||||||
{
|
|
||||||
auto begin = index;
|
|
||||||
auto span_from = current_location();
|
|
||||||
auto type = static_token_type();
|
|
||||||
if (!type)
|
|
||||||
return type.transform<Token>();
|
|
||||||
return token(*type, begin, span_from);
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
|
||||||
auto Lexer::static_token_type() noexcept -> Result<Tokens, Error>
|
|
||||||
{
|
|
||||||
using TT = Tokens;
|
|
||||||
auto stepped = [&](Tokens v) {
|
|
||||||
step();
|
|
||||||
return v;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (current() == '(')
|
|
||||||
return stepped(TT::LParen);
|
|
||||||
if (current() == ')')
|
|
||||||
return stepped(TT::RParen);
|
|
||||||
if (current() == '{')
|
|
||||||
return stepped(TT::LBrace);
|
|
||||||
if (current() == '}')
|
|
||||||
return stepped(TT::RBrace);
|
|
||||||
if (current() == '[')
|
|
||||||
return stepped(TT::LBracket);
|
|
||||||
if (current() == ']')
|
|
||||||
return stepped(TT::RBracket);
|
|
||||||
if (current() == '.')
|
|
||||||
return stepped(TT::Dot);
|
|
||||||
if (current() == ',')
|
|
||||||
return stepped(TT::Comma);
|
|
||||||
if (current() == ':')
|
|
||||||
return stepped(TT::Colon);
|
|
||||||
if (current() == ';')
|
|
||||||
return stepped(TT::Semicolon);
|
|
||||||
if (current() == ']')
|
|
||||||
return stepped(TT::RBracket);
|
|
||||||
if (current() == '+') {
|
|
||||||
step();
|
|
||||||
if (current() == '+')
|
|
||||||
return stepped(TT::DoublePlus);
|
|
||||||
if (current() == '=')
|
|
||||||
return stepped(TT::PlusEqual);
|
|
||||||
return TT::Plus;
|
|
||||||
}
|
|
||||||
if (current() == '-') {
|
|
||||||
step();
|
|
||||||
if (current() == '>')
|
|
||||||
return stepped(TT::ThinArrow);
|
|
||||||
if (current() == '-')
|
|
||||||
return stepped(TT::DoubleMinus);
|
|
||||||
if (current() == '=')
|
|
||||||
return stepped(TT::MinusEqual);
|
|
||||||
return TT::Minus;
|
|
||||||
}
|
|
||||||
if (current() == '*') {
|
|
||||||
step();
|
|
||||||
if (current() == '=')
|
|
||||||
return TT::AsteriskEqual;
|
|
||||||
return TT::Asterisk;
|
|
||||||
}
|
|
||||||
if (current() == '/') {
|
|
||||||
step();
|
|
||||||
if (current() == '*')
|
|
||||||
return skip_multiline_comment();
|
|
||||||
if (current() == '/')
|
|
||||||
return skip_singleline_comment();
|
|
||||||
if (current() == '=')
|
|
||||||
return TT::SlashEqual;
|
|
||||||
return TT::Slash;
|
|
||||||
}
|
|
||||||
if (current() == '%') {
|
|
||||||
step();
|
|
||||||
if (current() == '=')
|
|
||||||
return TT::PercentEqual;
|
|
||||||
return TT::Percent;
|
|
||||||
}
|
|
||||||
if (current() == '^') {
|
|
||||||
step();
|
|
||||||
if (current() == '=')
|
|
||||||
return TT::PowerEqual;
|
|
||||||
return TT::Power;
|
|
||||||
}
|
|
||||||
if (current() == '=') {
|
|
||||||
step();
|
|
||||||
if (current() == '>')
|
|
||||||
return stepped(TT::FatArrow);
|
|
||||||
if (current() == '=')
|
|
||||||
return stepped(TT::DoubleEqual);
|
|
||||||
return TT::Equal;
|
|
||||||
}
|
|
||||||
if (current() == '!') {
|
|
||||||
step();
|
|
||||||
if (current() == '=')
|
|
||||||
return stepped(TT::ExclamationEqual);
|
|
||||||
return TT::Exclamation;
|
|
||||||
}
|
|
||||||
if (current() == '<') {
|
|
||||||
step();
|
|
||||||
if (current() == '=')
|
|
||||||
return stepped(TT::LessEqual);
|
|
||||||
return TT::Less;
|
|
||||||
}
|
|
||||||
if (current() == '>') {
|
|
||||||
step();
|
|
||||||
if (current() == '=')
|
|
||||||
return stepped(TT::GreaterEqual);
|
|
||||||
return TT::Greater;
|
|
||||||
}
|
|
||||||
return Error {
|
|
||||||
{ { line, column - 1 }, { line, column } },
|
|
||||||
"unexpected character",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Lexer::skip_multiline_comment() noexcept -> Result<Tokens, Error>
|
|
||||||
{
|
|
||||||
step();
|
|
||||||
auto last = current();
|
|
||||||
step();
|
|
||||||
while (!done() and last != '*' and current() != '/')
|
|
||||||
step();
|
|
||||||
if (last != '*' or current() != '/')
|
|
||||||
return Error {
|
|
||||||
{ { line, column - 1 }, { line, column } },
|
|
||||||
"unterminated multiline comment",
|
|
||||||
};
|
|
||||||
step();
|
|
||||||
return Tokens::MultilineComment;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Lexer::skip_singleline_comment() noexcept -> Result<Tokens, Error>
|
|
||||||
{
|
|
||||||
step();
|
|
||||||
while (!done() and current() != '\n')
|
|
||||||
step();
|
|
||||||
if (current() == '\n')
|
|
||||||
step();
|
|
||||||
return Tokens::SinglelineComment;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,174 +0,0 @@
|
|||||||
#include "utils/all.hpp"
|
|
||||||
#include <optional>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
namespace scriptlang {
|
|
||||||
|
|
||||||
enum class Tokens {
|
|
||||||
Eof,
|
|
||||||
|
|
||||||
MultilineComment,
|
|
||||||
SinglelineComment,
|
|
||||||
|
|
||||||
Id,
|
|
||||||
Int,
|
|
||||||
Float,
|
|
||||||
String,
|
|
||||||
|
|
||||||
If,
|
|
||||||
Else,
|
|
||||||
For,
|
|
||||||
Loop,
|
|
||||||
While,
|
|
||||||
Break,
|
|
||||||
Continue,
|
|
||||||
Fn,
|
|
||||||
Return,
|
|
||||||
False,
|
|
||||||
True,
|
|
||||||
And,
|
|
||||||
Or,
|
|
||||||
Xor,
|
|
||||||
|
|
||||||
LParen,
|
|
||||||
RParen,
|
|
||||||
LBrace,
|
|
||||||
RBrace,
|
|
||||||
LBracket,
|
|
||||||
RBracket,
|
|
||||||
Dot,
|
|
||||||
Comma,
|
|
||||||
Colon,
|
|
||||||
Semicolon,
|
|
||||||
|
|
||||||
Plus,
|
|
||||||
DoublePlus,
|
|
||||||
PlusEqual,
|
|
||||||
|
|
||||||
Minus,
|
|
||||||
ThinArrow,
|
|
||||||
DoubleMinus,
|
|
||||||
MinusEqual,
|
|
||||||
|
|
||||||
Asterisk,
|
|
||||||
AsteriskEqual,
|
|
||||||
|
|
||||||
Slash,
|
|
||||||
SlashEqual,
|
|
||||||
|
|
||||||
Percent,
|
|
||||||
PercentEqual,
|
|
||||||
|
|
||||||
Power,
|
|
||||||
PowerEqual,
|
|
||||||
|
|
||||||
Equal,
|
|
||||||
FatArrow,
|
|
||||||
DoubleEqual,
|
|
||||||
|
|
||||||
Exclamation,
|
|
||||||
ExclamationEqual,
|
|
||||||
|
|
||||||
Less,
|
|
||||||
LessEqual,
|
|
||||||
|
|
||||||
Greater,
|
|
||||||
GreaterEqual,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Location {
|
|
||||||
int line, column;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Span {
|
|
||||||
Location from, to;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Token {
|
|
||||||
Tokens type;
|
|
||||||
size_t index, length;
|
|
||||||
Span span;
|
|
||||||
|
|
||||||
[[nodiscard]] static auto token_span(
|
|
||||||
const Token& from, const Token& to) noexcept -> Span
|
|
||||||
{
|
|
||||||
return { from.span.from, to.span.to };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Error {
|
|
||||||
Span span;
|
|
||||||
std::string message;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Lexer {
|
|
||||||
public:
|
|
||||||
Lexer(std::string_view text)
|
|
||||||
: text { text }
|
|
||||||
{ }
|
|
||||||
auto next() noexcept -> Result<Token, Error> { return make_token(); }
|
|
||||||
auto peek() noexcept -> Result<Token, Error>
|
|
||||||
{
|
|
||||||
if (!last_token)
|
|
||||||
return Error { { { 0, 0 }, { 0, 0 } }, "no token yet" };
|
|
||||||
return Result<Token, Error>::create_ok(*last_token);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
auto make_token() noexcept -> Result<Token, Error>;
|
|
||||||
auto skip_whitespace() noexcept -> Result<Token, Error>;
|
|
||||||
auto make_number() noexcept -> Result<Token, Error>;
|
|
||||||
auto make_id() noexcept -> Result<Token, Error>;
|
|
||||||
auto id_or_keyword_type(std::string_view substring) noexcept -> Tokens;
|
|
||||||
auto make_string() noexcept -> Result<Token, Error>;
|
|
||||||
auto make_static() noexcept -> Result<Token, Error>;
|
|
||||||
auto static_token_type() noexcept -> Result<Tokens, Error>;
|
|
||||||
auto skip_multiline_comment() noexcept -> Result<Tokens, Error>;
|
|
||||||
auto skip_singleline_comment() noexcept -> Result<Tokens, Error>;
|
|
||||||
|
|
||||||
[[nodiscard]] auto constexpr inline current_location() const noexcept
|
|
||||||
-> Location
|
|
||||||
{
|
|
||||||
return { line, column };
|
|
||||||
}
|
|
||||||
[[nodiscard]] auto constexpr inline token(
|
|
||||||
Tokens type, size_t begin, Location span_from) noexcept -> Token
|
|
||||||
{
|
|
||||||
auto token = Token {
|
|
||||||
type,
|
|
||||||
begin,
|
|
||||||
index - begin,
|
|
||||||
{ span_from, { line, column } },
|
|
||||||
};
|
|
||||||
last_token = token;
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
[[nodiscard]] auto constexpr inline done() const noexcept -> bool
|
|
||||||
{
|
|
||||||
return index >= text.size();
|
|
||||||
}
|
|
||||||
[[nodiscard]] auto constexpr inline current() const noexcept -> char
|
|
||||||
{
|
|
||||||
return text.at(index);
|
|
||||||
}
|
|
||||||
auto constexpr inline step() noexcept -> void
|
|
||||||
{
|
|
||||||
if (done())
|
|
||||||
return;
|
|
||||||
index++;
|
|
||||||
column++;
|
|
||||||
if (!done() and text.at(index) == '\n') {
|
|
||||||
column = 1;
|
|
||||||
line++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string_view text;
|
|
||||||
size_t index = 0;
|
|
||||||
int line = 1;
|
|
||||||
int column = 1;
|
|
||||||
std::optional<Token> last_token;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
|
|
||||||
browser_sources += files(
|
|
||||||
'lexer.cpp',
|
|
||||||
'parser.cpp',
|
|
||||||
)
|
|
@ -1,203 +0,0 @@
|
|||||||
#include "parser.hpp"
|
|
||||||
#include "utils/result.hpp"
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace scriptlang {
|
|
||||||
|
|
||||||
auto Parser::parse_expression(bool strictly_values) noexcept
|
|
||||||
-> Result<std::unique_ptr<Expression>, Error>
|
|
||||||
{
|
|
||||||
if (strictly_values)
|
|
||||||
return parse_array(true);
|
|
||||||
return Error { { { 0, 0 }, { 0, 0 } }, "not implemented" };
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Parser::parse_array(bool strictly_values) noexcept
|
|
||||||
-> Result<std::unique_ptr<Expression>, Error>
|
|
||||||
{
|
|
||||||
auto values = std::vector<std::unique_ptr<Expression>> {};
|
|
||||||
auto first_bracket = *lexer.peek();
|
|
||||||
if (first_bracket.type == Tokens::LBracket) {
|
|
||||||
(void)lexer.next();
|
|
||||||
auto value = parse_expression(strictly_values);
|
|
||||||
if (!value)
|
|
||||||
return value;
|
|
||||||
values.emplace_back(std::move(*value));
|
|
||||||
while (lexer.peek()->type == Tokens::Comma) {
|
|
||||||
(void)lexer.next();
|
|
||||||
if (lexer.peek()->type == Tokens::LBracket)
|
|
||||||
break;
|
|
||||||
auto value2 = parse_expression(strictly_values);
|
|
||||||
values.emplace_back(std::move(*value2));
|
|
||||||
}
|
|
||||||
auto last_bracket = *lexer.peek();
|
|
||||||
if (last_bracket.type != Tokens::RBracket)
|
|
||||||
return Error {
|
|
||||||
last_bracket.span,
|
|
||||||
"unterminated array",
|
|
||||||
};
|
|
||||||
(void)lexer.next().unwrap();
|
|
||||||
return {
|
|
||||||
std::make_unique<Array>(
|
|
||||||
Token::token_span(first_bracket, last_bracket),
|
|
||||||
std::move(values)),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return parse_struct(strictly_values);
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
|
||||||
auto Parser::parse_struct(bool strictly_values) noexcept
|
|
||||||
-> Result<std::unique_ptr<Expression>, Error>
|
|
||||||
{
|
|
||||||
auto values = std::map<std::string, std::unique_ptr<Expression>> {};
|
|
||||||
auto first_brace = *lexer.peek();
|
|
||||||
if (first_brace.type == Tokens::LBrace) {
|
|
||||||
auto name = *lexer.next();
|
|
||||||
if (name.type != Tokens::LBrace) {
|
|
||||||
if (name.type != Tokens::Id)
|
|
||||||
return Error {
|
|
||||||
name.span,
|
|
||||||
"unexpected token, expected Id or String",
|
|
||||||
};
|
|
||||||
if (lexer.next()->type != Tokens::Colon)
|
|
||||||
return Error {
|
|
||||||
lexer.peek()->span,
|
|
||||||
"unexpected token, expected ':'",
|
|
||||||
};
|
|
||||||
if (auto result = lexer.next(); !result)
|
|
||||||
return { std::move(result.unwrap_error()) };
|
|
||||||
auto value = parse_expression(strictly_values);
|
|
||||||
if (!value)
|
|
||||||
return value.transform<std::unique_ptr<Expression>>();
|
|
||||||
if (values.find(token_text(name)) != values.end())
|
|
||||||
return Error {
|
|
||||||
name.span,
|
|
||||||
"multiple definitions of struct field",
|
|
||||||
};
|
|
||||||
values.insert_or_assign(token_text(name), std::move(*value));
|
|
||||||
while (lexer.peek()->type == Tokens::Comma) {
|
|
||||||
auto name2 = *lexer.next();
|
|
||||||
if (name2.type == Tokens::RBrace)
|
|
||||||
break;
|
|
||||||
if (name2.type != Tokens::Id)
|
|
||||||
return Error {
|
|
||||||
name2.span,
|
|
||||||
"unexpected token, expected Id",
|
|
||||||
};
|
|
||||||
if (lexer.next()->type != Tokens::Colon)
|
|
||||||
return Error {
|
|
||||||
lexer.peek()->span,
|
|
||||||
"unexpected token, expected ':'",
|
|
||||||
};
|
|
||||||
(void)lexer.next();
|
|
||||||
auto value2 = parse_expression(strictly_values);
|
|
||||||
if (!value2)
|
|
||||||
return value2.transform<std::unique_ptr<Expression>>();
|
|
||||||
if (values.find(token_text(name2)) != values.end())
|
|
||||||
return Error {
|
|
||||||
name2.span,
|
|
||||||
"multiple definitions of struct field",
|
|
||||||
};
|
|
||||||
values.insert_or_assign(token_text(name2), std::move(*value2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto last_brace = *lexer.peek();
|
|
||||||
if (last_brace.type != Tokens::RBrace)
|
|
||||||
return Error {
|
|
||||||
last_brace.span,
|
|
||||||
fmt::format("unterminated struct, expected '}}', got {}",
|
|
||||||
last_brace.type),
|
|
||||||
};
|
|
||||||
(void)lexer.next().unwrap();
|
|
||||||
return {
|
|
||||||
std::make_unique<Struct>(
|
|
||||||
Token::token_span(first_brace, last_brace), std::move(values)),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return parse_atom();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Parser::parse_atom() noexcept -> Result<std::unique_ptr<Expression>, Error>
|
|
||||||
{
|
|
||||||
auto token = *lexer.peek();
|
|
||||||
switch (token.type) {
|
|
||||||
case Tokens::Id: {
|
|
||||||
auto node = std::make_unique<Id>(Token::token_span(token, token),
|
|
||||||
token_text(token.index, token.length));
|
|
||||||
(void)lexer.next().unwrap();
|
|
||||||
return { std::move(node) };
|
|
||||||
}
|
|
||||||
case Tokens::Int: {
|
|
||||||
auto node = std::make_unique<Int>(Token::token_span(token, token),
|
|
||||||
std::atol(token_text(token.index, token.length).c_str()));
|
|
||||||
(void)lexer.next().unwrap();
|
|
||||||
return { std::move(node) };
|
|
||||||
}
|
|
||||||
case Tokens::Float: {
|
|
||||||
auto node = std::make_unique<Float>(Token::token_span(token, token),
|
|
||||||
std::atof(token_text(token.index, token.length).c_str()));
|
|
||||||
(void)lexer.next().unwrap();
|
|
||||||
return { std::move(node) };
|
|
||||||
}
|
|
||||||
case Tokens::False: {
|
|
||||||
auto node = std::make_unique<Bool>(
|
|
||||||
Token::token_span(token, token), false);
|
|
||||||
(void)lexer.next().unwrap();
|
|
||||||
return { std::move(node) };
|
|
||||||
}
|
|
||||||
case Tokens::True: {
|
|
||||||
auto node
|
|
||||||
= std::make_unique<Bool>(Token::token_span(token, token), true);
|
|
||||||
(void)lexer.next().unwrap();
|
|
||||||
return { std::move(node) };
|
|
||||||
}
|
|
||||||
case Tokens::String: {
|
|
||||||
auto node
|
|
||||||
= std::make_unique<String>(Token::token_span(token, token),
|
|
||||||
*parse_string_value(token_text(token.index, token.length)));
|
|
||||||
(void)lexer.next().unwrap();
|
|
||||||
return { std::move(node) };
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return Error { token.span, "unexpected token, expected value" };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] auto Parser::parse_string_value(std::string_view literal) noexcept
|
|
||||||
-> Result<std::string, utils::result::Error<std::string>>
|
|
||||||
{
|
|
||||||
if (literal.size() < 2)
|
|
||||||
return utils::result::Error<std::string> { "malformed string" };
|
|
||||||
auto value = std::string {};
|
|
||||||
auto escaped = false;
|
|
||||||
for (const auto c : literal.substr(1, literal.size() - 2)) {
|
|
||||||
if (escaped) {
|
|
||||||
value.push_back([&] {
|
|
||||||
switch (c) {
|
|
||||||
case 'n':
|
|
||||||
return '\n';
|
|
||||||
case 'r':
|
|
||||||
return '\r';
|
|
||||||
case 't':
|
|
||||||
return '\t';
|
|
||||||
case 'v':
|
|
||||||
return '\n';
|
|
||||||
default:
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
}());
|
|
||||||
escaped = false;
|
|
||||||
} else if (c == '\\') {
|
|
||||||
escaped = true;
|
|
||||||
} else {
|
|
||||||
value.push_back(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,258 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "lexer.hpp"
|
|
||||||
#include "utils/all.hpp"
|
|
||||||
#include "utils/result.hpp"
|
|
||||||
#include <fmt/core.h>
|
|
||||||
#include <map>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace scriptlang {
|
|
||||||
|
|
||||||
enum class Expressions {
|
|
||||||
Binary,
|
|
||||||
Negate,
|
|
||||||
Not,
|
|
||||||
Index,
|
|
||||||
Access,
|
|
||||||
Call,
|
|
||||||
Operator,
|
|
||||||
|
|
||||||
Array,
|
|
||||||
Struct,
|
|
||||||
Id,
|
|
||||||
Int,
|
|
||||||
Float,
|
|
||||||
Bool,
|
|
||||||
String,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Expression {
|
|
||||||
public:
|
|
||||||
Expression() = default;
|
|
||||||
Expression(const Expression&) = delete;
|
|
||||||
Expression(Expression&&) = delete;
|
|
||||||
auto operator=(const Expression&) -> Expression& = delete;
|
|
||||||
auto operator=(Expression&&) -> Expression& = delete;
|
|
||||||
virtual ~Expression() = default;
|
|
||||||
[[nodiscard]] virtual auto expression_type() const noexcept -> Expressions
|
|
||||||
= 0;
|
|
||||||
[[nodiscard]] virtual auto span() const noexcept -> Span = 0;
|
|
||||||
[[nodiscard]] virtual auto to_string() const noexcept -> std::string = 0;
|
|
||||||
|
|
||||||
private:
|
|
||||||
};
|
|
||||||
|
|
||||||
class Array final : public Expression {
|
|
||||||
public:
|
|
||||||
Array(Span span, std::vector<std::unique_ptr<Expression>> values)
|
|
||||||
: m_span { span }
|
|
||||||
, m_values { std::move(values) }
|
|
||||||
{ }
|
|
||||||
[[nodiscard]] auto expression_type() const noexcept -> Expressions override
|
|
||||||
{
|
|
||||||
return Expressions::Array;
|
|
||||||
}
|
|
||||||
[[nodiscard]] auto span() const noexcept -> Span override { return m_span; }
|
|
||||||
[[nodiscard]] auto values() const noexcept -> auto& { return m_values; }
|
|
||||||
[[nodiscard]] auto to_string() const noexcept -> std::string override
|
|
||||||
{
|
|
||||||
auto values_strings = std::string {};
|
|
||||||
auto first = true;
|
|
||||||
for (const auto& value : m_values) {
|
|
||||||
if (!first)
|
|
||||||
values_strings.append(", ");
|
|
||||||
first = false;
|
|
||||||
values_strings.append(value->to_string());
|
|
||||||
}
|
|
||||||
return fmt::format("Array {{ [ {} ] }}", values_strings);
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
Span m_span;
|
|
||||||
std::vector<std::unique_ptr<Expression>> m_values;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Struct final : public Expression {
|
|
||||||
public:
|
|
||||||
Struct(Span span, std::map<std::string, std::unique_ptr<Expression>> values)
|
|
||||||
: m_span { span }
|
|
||||||
, m_values { std::move(values) }
|
|
||||||
{ }
|
|
||||||
[[nodiscard]] auto expression_type() const noexcept -> Expressions override
|
|
||||||
{
|
|
||||||
return Expressions::Struct;
|
|
||||||
}
|
|
||||||
[[nodiscard]] auto span() const noexcept -> Span override { return m_span; }
|
|
||||||
[[nodiscard]] auto values() const noexcept -> auto& { return m_values; }
|
|
||||||
[[nodiscard]] auto to_string() const noexcept -> std::string override
|
|
||||||
{
|
|
||||||
auto values_strings = std::string {};
|
|
||||||
auto first = true;
|
|
||||||
for (const auto& [name, value] : m_values) {
|
|
||||||
if (!first)
|
|
||||||
values_strings.append(", ");
|
|
||||||
first = false;
|
|
||||||
values_strings.append(value->to_string());
|
|
||||||
}
|
|
||||||
return fmt::format("Struct {{ [ {} ] }}", values_strings);
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
Span m_span;
|
|
||||||
std::map<std::string, std::unique_ptr<Expression>> m_values;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Id final : public Expression {
|
|
||||||
public:
|
|
||||||
Id(Span span, std::string value)
|
|
||||||
: m_span { span }
|
|
||||||
, m_value { std::move(value) }
|
|
||||||
{ }
|
|
||||||
[[nodiscard]] auto expression_type() const noexcept -> Expressions override
|
|
||||||
{
|
|
||||||
return Expressions::Id;
|
|
||||||
}
|
|
||||||
[[nodiscard]] auto span() const noexcept -> Span override { return m_span; }
|
|
||||||
[[nodiscard]] auto value() const noexcept { return m_value; }
|
|
||||||
[[nodiscard]] auto to_string() const noexcept -> std::string override
|
|
||||||
{
|
|
||||||
return fmt::format("Id {{ {} }}", m_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Span m_span;
|
|
||||||
std::string m_value;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Int final : public Expression {
|
|
||||||
public:
|
|
||||||
Int(Span span, int64_t value)
|
|
||||||
: m_span { span }
|
|
||||||
, m_value { value }
|
|
||||||
{ }
|
|
||||||
[[nodiscard]] auto expression_type() const noexcept -> Expressions override
|
|
||||||
{
|
|
||||||
return Expressions::Int;
|
|
||||||
}
|
|
||||||
[[nodiscard]] auto span() const noexcept -> Span override { return m_span; }
|
|
||||||
[[nodiscard]] auto value() const noexcept { return m_value; }
|
|
||||||
[[nodiscard]] auto to_string() const noexcept -> std::string override
|
|
||||||
{
|
|
||||||
return fmt::format("Int {{ {} }}", m_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Span m_span;
|
|
||||||
int64_t m_value;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Float final : public Expression {
|
|
||||||
public:
|
|
||||||
Float(Span span, double value)
|
|
||||||
: m_span { span }
|
|
||||||
, m_value { value }
|
|
||||||
{ }
|
|
||||||
[[nodiscard]] auto expression_type() const noexcept -> Expressions override
|
|
||||||
{
|
|
||||||
return Expressions::Float;
|
|
||||||
}
|
|
||||||
[[nodiscard]] auto value() const noexcept { return m_value; }
|
|
||||||
[[nodiscard]] auto span() const noexcept -> Span override { return m_span; }
|
|
||||||
[[nodiscard]] auto to_string() const noexcept -> std::string override
|
|
||||||
{
|
|
||||||
return fmt::format("Float {{ {} }}", m_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Span m_span;
|
|
||||||
double m_value;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Bool final : public Expression {
|
|
||||||
public:
|
|
||||||
Bool(Span span, bool value)
|
|
||||||
: m_span { span }
|
|
||||||
, m_value { value }
|
|
||||||
{ }
|
|
||||||
[[nodiscard]] auto expression_type() const noexcept -> Expressions override
|
|
||||||
{
|
|
||||||
return Expressions::Bool;
|
|
||||||
}
|
|
||||||
[[nodiscard]] auto value() const noexcept { return m_value; }
|
|
||||||
[[nodiscard]] auto span() const noexcept -> Span override { return m_span; }
|
|
||||||
[[nodiscard]] auto to_string() const noexcept -> std::string override
|
|
||||||
{
|
|
||||||
return fmt::format("Bool {{ {} }}", m_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Span m_span;
|
|
||||||
bool m_value;
|
|
||||||
};
|
|
||||||
|
|
||||||
class String final : public Expression {
|
|
||||||
public:
|
|
||||||
String(Span span, std::string value)
|
|
||||||
: m_span { span }
|
|
||||||
, m_value { std::move(value) }
|
|
||||||
{ }
|
|
||||||
[[nodiscard]] auto expression_type() const noexcept -> Expressions override
|
|
||||||
{
|
|
||||||
return Expressions::String;
|
|
||||||
}
|
|
||||||
[[nodiscard]] auto value() const noexcept -> const std::string&
|
|
||||||
{
|
|
||||||
return m_value;
|
|
||||||
}
|
|
||||||
[[nodiscard]] auto span() const noexcept -> Span override { return m_span; }
|
|
||||||
[[nodiscard]] auto to_string() const noexcept -> std::string override
|
|
||||||
{
|
|
||||||
return fmt::format("String {{ \"{}\" }}", m_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Span m_span;
|
|
||||||
std::string m_value;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Parser final {
|
|
||||||
public:
|
|
||||||
Parser(std::string_view text)
|
|
||||||
: text { text }
|
|
||||||
, lexer(text)
|
|
||||||
{
|
|
||||||
[[maybe_unused]] auto _ = lexer.next();
|
|
||||||
}
|
|
||||||
auto parse_expression(bool strictly_values) noexcept
|
|
||||||
-> Result<std::unique_ptr<Expression>, Error>;
|
|
||||||
auto parse_array(bool strictly_values) noexcept
|
|
||||||
-> Result<std::unique_ptr<Expression>, Error>;
|
|
||||||
auto parse_struct(bool strictly_values) noexcept
|
|
||||||
-> Result<std::unique_ptr<Expression>, Error>;
|
|
||||||
auto parse_atom() noexcept -> Result<std::unique_ptr<Expression>, Error>;
|
|
||||||
|
|
||||||
private:
|
|
||||||
[[nodiscard]] static auto parse_string_value(
|
|
||||||
std::string_view literal) noexcept
|
|
||||||
-> Result<std::string, utils::result::Error<std::string>>;
|
|
||||||
[[nodiscard]] auto token_text(size_t index, size_t length) const noexcept
|
|
||||||
-> std::string
|
|
||||||
{
|
|
||||||
return std::string { text.substr(index, length) };
|
|
||||||
}
|
|
||||||
[[nodiscard]] auto token_text(const Token& token) const noexcept
|
|
||||||
-> std::string
|
|
||||||
{
|
|
||||||
return std::string { text.substr(token.index, token.length) };
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string_view text;
|
|
||||||
Lexer lexer;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
29
meson.build
29
meson.build
@ -16,38 +16,19 @@ add_project_arguments(
|
|||||||
language: 'cpp',
|
language: 'cpp',
|
||||||
)
|
)
|
||||||
|
|
||||||
common_sources = []
|
sources = []
|
||||||
subdir('utils')
|
|
||||||
|
|
||||||
browser_sources = common_sources
|
|
||||||
subdir('browser')
|
subdir('browser')
|
||||||
|
|
||||||
executable(
|
executable(
|
||||||
'web-browser',
|
'web-browser',
|
||||||
browser_sources,
|
sources,
|
||||||
win_subsystem: 'console',
|
win_subsystem: 'console',
|
||||||
include_directories: [
|
include_directories: [],
|
||||||
include_directories('.'),
|
override_options: [
|
||||||
include_directories('browser'),
|
'werror=true'
|
||||||
],
|
],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
fmt_dep,
|
fmt_dep,
|
||||||
sdl2_dep,
|
sdl2_dep,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
server_sources = common_sources
|
|
||||||
subdir('server')
|
|
||||||
|
|
||||||
executable(
|
|
||||||
'web-server',
|
|
||||||
server_sources,
|
|
||||||
win_subsystem: 'console',
|
|
||||||
include_directories: [
|
|
||||||
include_directories('.'),
|
|
||||||
include_directories('server'),
|
|
||||||
],
|
|
||||||
dependencies: [
|
|
||||||
fmt_dep,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
#include <fmt/core.h>
|
|
||||||
|
|
||||||
int main() { fmt::print("server: hello world!\n"); }
|
|
@ -1,5 +0,0 @@
|
|||||||
|
|
||||||
server_sources += files(
|
|
||||||
'main.cpp',
|
|
||||||
)
|
|
||||||
|
|
4
src/meson.build
Normal file
4
src/meson.build
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
sources = files(
|
||||||
|
'main.cpp',
|
||||||
|
)
|
@ -1,2 +0,0 @@
|
|||||||
|
|
||||||
common_sources = files()
|
|
Loading…
Reference in New Issue
Block a user