Compare commits
4 Commits
224468446d
...
db485af7f6
| Author | SHA1 | Date | |
|---|---|---|---|
|
db485af7f6
|
|||
|
854343a5a3
|
|||
|
3faafe371f
|
|||
|
4604f5186e
|
+1
-1
@@ -13,4 +13,4 @@ include(GoogleTest)
|
||||
file(GLOB_RECURSE FURC_TESTS "test/**.cpp")
|
||||
add_executable(furc_tests ${FURC_TESTS})
|
||||
target_link_libraries(furc_tests PRIVATE libfurc GTest::gtest_main)
|
||||
gtest_discover_tests(furc_tests)
|
||||
# gtest_discover_tests(furc_tests)
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
#ifndef FURC_FRONT_AST_HPP
|
||||
#define FURC_FRONT_AST_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace furc {
|
||||
|
||||
struct ast_type {
|
||||
enum type_e {
|
||||
Void = 0,
|
||||
S8,
|
||||
U8,
|
||||
S16,
|
||||
U16,
|
||||
S32,
|
||||
U32,
|
||||
S64,
|
||||
U64,
|
||||
} type = Void;
|
||||
};
|
||||
|
||||
class ast_node {
|
||||
public:
|
||||
enum category_e {
|
||||
Statement,
|
||||
Declaration,
|
||||
Expression,
|
||||
Literal,
|
||||
};
|
||||
public:
|
||||
ast_node() = default;
|
||||
virtual ~ast_node() = default;
|
||||
|
||||
ast_node(ast_node&&) noexcept = default;
|
||||
ast_node& operator=(ast_node&&) noexcept = default;
|
||||
|
||||
ast_node(const ast_node&) = delete;
|
||||
ast_node& operator=(const ast_node&) = delete;
|
||||
public:
|
||||
virtual category_e category() const = 0;
|
||||
};
|
||||
|
||||
using ast_node_cat = ast_node::category_e;
|
||||
|
||||
class stmt_node : public ast_node {
|
||||
public:
|
||||
enum stmt_type_e {
|
||||
Declaration = 0,
|
||||
Expression,
|
||||
|
||||
Compound,
|
||||
};
|
||||
public:
|
||||
category_e category() const override { return ast_node_cat::Statement; }
|
||||
|
||||
virtual stmt_type_e stmt_type() const = 0;
|
||||
};
|
||||
|
||||
struct comp_stmt_node final : public stmt_node {
|
||||
stmt_type_e stmt_type() const override { return Compound; }
|
||||
|
||||
std::vector<stmt_node*> stmts;
|
||||
};
|
||||
|
||||
class decl_node : public stmt_node {
|
||||
public:
|
||||
enum decl_type_e {
|
||||
Variable,
|
||||
Function
|
||||
};
|
||||
public:
|
||||
category_e category() const override { return ast_node_cat::Declaration; }
|
||||
|
||||
stmt_type_e stmt_type() const override { return Declaration; }
|
||||
|
||||
virtual decl_type_e decl_type() const = 0;
|
||||
};
|
||||
|
||||
class expr_node;
|
||||
|
||||
struct var_decl_node final : public decl_node {
|
||||
decl_type_e decl_type() const override { return Variable; }
|
||||
|
||||
std::string name;
|
||||
ast_type type;
|
||||
expr_node* init = nullptr;
|
||||
};
|
||||
|
||||
struct func_decl_node final : public decl_node {
|
||||
decl_type_e decl_type() const override { return Function; }
|
||||
|
||||
std::string name;
|
||||
ast_type type;
|
||||
std::vector<var_decl_node> params;
|
||||
std::optional<comp_stmt_node> body;
|
||||
};
|
||||
|
||||
class expr_node : public stmt_node {
|
||||
public:
|
||||
enum expr_type_e {
|
||||
Literal,
|
||||
};
|
||||
public:
|
||||
category_e category() const override { return ast_node_cat::Expression; }
|
||||
|
||||
stmt_type_e stmt_type() const override { return Expression; }
|
||||
|
||||
virtual expr_type_e expr_type() const = 0;
|
||||
};
|
||||
|
||||
class lit_node : public expr_node {
|
||||
public:
|
||||
enum lit_type_e {
|
||||
Integer,
|
||||
Char,
|
||||
};
|
||||
public:
|
||||
category_e category() const override { return ast_node_cat::Literal; }
|
||||
|
||||
expr_type_e expr_type() const override { return Literal; }
|
||||
|
||||
virtual lit_type_e lit_type() const = 0;
|
||||
};
|
||||
|
||||
struct int_lit_node final : public lit_node {
|
||||
int_lit_node(std::uint64_t value)
|
||||
: value(value) {}
|
||||
|
||||
lit_type_e lit_type() const override { return Integer; }
|
||||
|
||||
std::uint64_t value;
|
||||
};
|
||||
|
||||
struct char_lit_node final : public lit_node {
|
||||
char_lit_node(char value)
|
||||
: value(value) {}
|
||||
|
||||
lit_type_e lit_type() const override { return Char; }
|
||||
|
||||
char value;
|
||||
};
|
||||
|
||||
struct ast {
|
||||
std::vector<decl_node*> decls;
|
||||
};
|
||||
|
||||
} // namespace furc
|
||||
|
||||
#endif // FURC_FRONT_AST_HPP
|
||||
@@ -0,0 +1,46 @@
|
||||
#ifndef FURC_FRONT_LEXER_HPP
|
||||
#define FURC_FRONT_LEXER_HPP
|
||||
|
||||
#include "furc/front/token.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
namespace furc {
|
||||
|
||||
class lexer {
|
||||
public:
|
||||
lexer(std::string_view filepath, std::string_view content)
|
||||
: m_filepath(filepath), m_content(content) {}
|
||||
|
||||
~lexer() = default;
|
||||
|
||||
lexer(lexer&&) noexcept = default;
|
||||
lexer& operator=(lexer&&) noexcept = default;
|
||||
|
||||
lexer(const lexer&) = delete;
|
||||
lexer& operator=(const lexer&) = delete;
|
||||
public:
|
||||
token next_token();
|
||||
token peek_token();
|
||||
token skip_token();
|
||||
private:
|
||||
void next();
|
||||
constexpr char get(std::size_t offset = 0) const;
|
||||
void skip_spaces();
|
||||
|
||||
constexpr token::location location() const;
|
||||
private:
|
||||
std::string_view m_filepath;
|
||||
std::string_view m_content;
|
||||
std::size_t m_cursor = 0;
|
||||
std::size_t m_row = 0;
|
||||
std::size_t m_lineStart = 0;
|
||||
|
||||
std::optional<token> m_peekToken;
|
||||
};
|
||||
|
||||
} // namespace furc
|
||||
|
||||
#endif // FURC_FRONT_LEXER_HPP
|
||||
@@ -0,0 +1,48 @@
|
||||
#ifndef FURC_FRONT_PARSER_HPP
|
||||
#define FURC_FRONT_PARSER_HPP
|
||||
|
||||
#include "furc/front/ast.hpp"
|
||||
#include "furc/front/lexer.hpp"
|
||||
#include "furlang/arena.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace furc {
|
||||
|
||||
class parser {
|
||||
public:
|
||||
parser(lexer&& lexer, furlang::arena& arena)
|
||||
: m_lexer(std::move(lexer)), m_arena(&arena) {}
|
||||
|
||||
~parser() = default;
|
||||
|
||||
parser(parser&&) noexcept = default;
|
||||
parser& operator=(parser&&) noexcept = default;
|
||||
|
||||
parser(const parser&) = delete;
|
||||
parser& operator=(const parser&) = delete;
|
||||
public:
|
||||
ast parse();
|
||||
private:
|
||||
stmt_node* parse_stmt();
|
||||
decl_node* parse_decl();
|
||||
expr_node* parse_expr();
|
||||
lit_node* parse_lit();
|
||||
|
||||
ast_type parse_type();
|
||||
comp_stmt_node parse_comp();
|
||||
private:
|
||||
template <typename... Types>
|
||||
token eat_token(Types... types) {
|
||||
auto token = m_lexer.next_token();
|
||||
if (((token.type == types) || ...)) return token;
|
||||
throw std::runtime_error("unexpected token");
|
||||
}
|
||||
private:
|
||||
lexer m_lexer;
|
||||
furlang::arena* m_arena;
|
||||
};
|
||||
|
||||
} // namespace furc
|
||||
|
||||
#endif // FURC_FRONT_PARSER_HPP
|
||||
@@ -0,0 +1,206 @@
|
||||
#ifndef FURC_FRONT_TOKEN_HPP
|
||||
#define FURC_FRONT_TOKEN_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <ostream>
|
||||
#include <string_view>
|
||||
|
||||
namespace furc {
|
||||
|
||||
struct token {
|
||||
enum type {
|
||||
Identifier = 0,
|
||||
Integer,
|
||||
String,
|
||||
Char,
|
||||
|
||||
LParen, /**< `(` */
|
||||
RParen, /**< `)` */
|
||||
LBrace, /**< `{` */
|
||||
RBrace, /**< `}` */
|
||||
LBracket, /**< `[` */
|
||||
RBracket, /**< `]` */
|
||||
Semicolon, /**< `;` */
|
||||
Colon, /**< `:` */
|
||||
Comma, /**< `,` */
|
||||
Dot, /**< `.` */
|
||||
|
||||
Plus, /**< `+` */
|
||||
Minus, /**< `-` */
|
||||
Star, /**< `*` */
|
||||
Slash, /**< `/` */
|
||||
Percent, /**< `%` */
|
||||
Ampersand, /**< `&` */
|
||||
Pipe, /**< `|` */
|
||||
Hat, /**< `^` */
|
||||
DblAmpersand, /**< `&&` */
|
||||
DblPipe, /**< `||` */
|
||||
|
||||
DblPlus, /**< `++` */
|
||||
DblMinus, /**< `--` */
|
||||
ExMark, /**< `!` */
|
||||
CatEars, /**< `^^` */
|
||||
|
||||
Equals, /**< `=` */
|
||||
PlusEquals, /**< `+=` */
|
||||
MinusEquals, /**< `-=` */
|
||||
StarEquals, /**< `*=` */
|
||||
SlashEquals, /**< `/=` */
|
||||
PercentEquals, /**< `%=` */
|
||||
AmpersandEquals, /**< `&=` */
|
||||
PipeEquals, /**< `|=` */
|
||||
HatEquals, /**< `^=` */
|
||||
|
||||
DblEquals, /**< `==` */
|
||||
ExEquals, /**< `!=` */
|
||||
LessThan, /**< `<` */
|
||||
LessEquals, /**< `<=` */
|
||||
GreaterThan, /**< `>` */
|
||||
GreaterEquals, /**< `>=` */
|
||||
|
||||
SlimArrow, /**< `->` */
|
||||
// My brother just another me
|
||||
FatArrow, /**< `=>` */
|
||||
|
||||
Monkey, /**< `@` */
|
||||
Sha256, /**< `#` */
|
||||
|
||||
Func, /**< `func` */
|
||||
Return, /**< `return` */
|
||||
If, /**< `if` */
|
||||
Else, /**< `else` */
|
||||
While, /**< `while` */
|
||||
Public, /**< `public` */
|
||||
Private, /**< `private` */
|
||||
|
||||
Pointerof, /**< `pointerof` */
|
||||
Sizeof, /**< `sizeof` */
|
||||
Lengthof, /**< `lengthof` */
|
||||
|
||||
S8, /**< `s8` */
|
||||
U8, /**< `u8` */
|
||||
S16, /**< `s16` */
|
||||
U16, /**< `u16` */
|
||||
S32, /**< `s32` */
|
||||
U32, /**< `u32` */
|
||||
S64, /**< `s64` */
|
||||
U64, /**< `u64` */
|
||||
|
||||
// Errors:
|
||||
UnexpectedCharacter,
|
||||
UnexpectedEOF,
|
||||
EndOfFile,
|
||||
} type;
|
||||
union value {
|
||||
std::nullptr_t null = nullptr;
|
||||
std::uint64_t integer;
|
||||
std::string_view string;
|
||||
char character;
|
||||
} value;
|
||||
|
||||
struct location {
|
||||
std::string_view filepath;
|
||||
std::size_t row = 0;
|
||||
std::size_t col = 0;
|
||||
} loc;
|
||||
|
||||
token(location loc, enum type type)
|
||||
: loc(loc), type(type) {}
|
||||
|
||||
token(location loc, std::uint64_t integer)
|
||||
: loc(loc), type(Integer) {
|
||||
value.integer = integer;
|
||||
}
|
||||
|
||||
token(location loc, enum type type, std::string_view string)
|
||||
: loc(loc), type(type) {
|
||||
value.string = string;
|
||||
}
|
||||
|
||||
token(location loc, enum type type, char character)
|
||||
: loc(loc), type(type) {
|
||||
value.character = character;
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const token& token) {
|
||||
switch (token.type) {
|
||||
case token::Identifier: return os << token.value.string;
|
||||
case token::String: return os << '"' << token.value.string << '"';
|
||||
case token::Char: return os << '\'' << token.value.character << '\'';
|
||||
case token::Integer: return os << token.value.integer;
|
||||
case token::LParen: return os << "(";
|
||||
case token::RParen: return os << ")";
|
||||
case token::LBrace: return os << "{";
|
||||
case token::RBrace: return os << "}";
|
||||
case token::LBracket: return os << "[";
|
||||
case token::RBracket: return os << "]";
|
||||
case token::Semicolon: return os << ";";
|
||||
case token::Colon: return os << ":";
|
||||
case token::Comma: return os << ",";
|
||||
case token::Dot: return os << ".";
|
||||
case token::Plus: return os << "+";
|
||||
case token::Minus: return os << "-";
|
||||
case token::Star: return os << "*";
|
||||
case token::Slash: return os << "/";
|
||||
case token::Percent: return os << "%";
|
||||
case token::Ampersand: return os << "&";
|
||||
case token::Pipe: return os << "|";
|
||||
case token::Hat: return os << "^";
|
||||
case token::DblAmpersand: return os << "&&";
|
||||
case token::DblPipe: return os << "||";
|
||||
case token::DblPlus: return os << "++";
|
||||
case token::DblMinus: return os << "--";
|
||||
case token::ExMark: return os << "!";
|
||||
case token::CatEars: return os << "^^";
|
||||
case token::Equals: return os << "=";
|
||||
case token::PlusEquals: return os << "+=";
|
||||
case token::MinusEquals: return os << "-=";
|
||||
case token::StarEquals: return os << "*=";
|
||||
case token::SlashEquals: return os << "/=";
|
||||
case token::PercentEquals: return os << "%=";
|
||||
case token::AmpersandEquals: return os << "&=";
|
||||
case token::PipeEquals: return os << "|=";
|
||||
case token::HatEquals: return os << "^=";
|
||||
case token::DblEquals: return os << "==";
|
||||
case token::ExEquals: return os << "!=";
|
||||
case token::LessThan: return os << "<";
|
||||
case token::LessEquals: return os << "<=";
|
||||
case token::GreaterThan: return os << ">";
|
||||
case token::GreaterEquals: return os << ">=";
|
||||
case token::SlimArrow: return os << "->";
|
||||
case token::FatArrow: return os << "=>";
|
||||
case token::Monkey: return os << "@";
|
||||
case token::Sha256: return os << "#";
|
||||
case token::Func: return os << "func";
|
||||
case token::Return: return os << "return";
|
||||
case token::If: return os << "if";
|
||||
case token::Else: return os << "else";
|
||||
case token::While: return os << "while";
|
||||
case token::Public: return os << "public";
|
||||
case token::Private: return os << "private";
|
||||
case token::Pointerof: return os << "pointerof";
|
||||
case token::Sizeof: return os << "sizeof";
|
||||
case token::Lengthof: return os << "lengthof";
|
||||
case token::S8: return os << "s8";
|
||||
case token::U8: return os << "u8";
|
||||
case token::S16: return os << "s16";
|
||||
case token::U16: return os << "u16";
|
||||
case token::S32: return os << "s32";
|
||||
case token::U32: return os << "u32";
|
||||
case token::S64: return os << "s64";
|
||||
case token::U64: return os << "u64";
|
||||
case token::UnexpectedCharacter: return os << "Unexpected character `" << token.value.character << "`";
|
||||
case token::UnexpectedEOF: return os << "Unexpected End Of File";
|
||||
case token::EndOfFile: return os << "End Of File";
|
||||
}
|
||||
|
||||
return os;
|
||||
}
|
||||
};
|
||||
|
||||
using token_t = enum token::type;
|
||||
|
||||
} // namespace furc
|
||||
|
||||
#endif // FURC_FRONT_TOKEN_HPP
|
||||
@@ -0,0 +1,179 @@
|
||||
#include "furc/front/lexer.hpp"
|
||||
|
||||
#include <cctype>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace furc {
|
||||
|
||||
token lexer::next_token() {
|
||||
if (m_peekToken.has_value()) {
|
||||
auto tok = m_peekToken.value();
|
||||
m_peekToken = {};
|
||||
return tok;
|
||||
}
|
||||
|
||||
skip_spaces();
|
||||
|
||||
if (m_cursor >= m_content.size()) return { location(), token::EndOfFile };
|
||||
|
||||
auto loc = location();
|
||||
|
||||
if (std::isdigit(get()) != 0) {}
|
||||
|
||||
if (std::isalnum(get()) != 0 || get() == '_') {
|
||||
static std::unordered_map<std::string_view, token_t> s_keywords = {
|
||||
{ "func", token::Func },
|
||||
{ "return", token::Return },
|
||||
{ "if", token::If },
|
||||
{ "else", token::Else },
|
||||
{ "while", token::While },
|
||||
{ "public", token::Public },
|
||||
{ "private", token::Private },
|
||||
{ "pointerof", token::Pointerof },
|
||||
{ "sizeof", token::Sizeof },
|
||||
{ "lengthof", token::Lengthof },
|
||||
{ "s8", token::S8 },
|
||||
{ "u8", token::U8 },
|
||||
{ "s16", token::S16 },
|
||||
{ "u16", token::U16 },
|
||||
{ "s32", token::S32 },
|
||||
{ "u32", token::U32 },
|
||||
{ "s64", token::S64 },
|
||||
{ "u64", token::U64 },
|
||||
};
|
||||
|
||||
std::size_t begin = m_cursor;
|
||||
next();
|
||||
while (m_cursor < m_content.size() && (std::isalnum(get()) != 0 || get() == '_'))
|
||||
next();
|
||||
std::string_view name = m_content.substr(begin, m_cursor - begin);
|
||||
if (auto it = s_keywords.find(name); it != s_keywords.end()) {
|
||||
return { loc, it->second };
|
||||
}
|
||||
return { loc, token::Identifier, name };
|
||||
}
|
||||
|
||||
if (get() == '"') {
|
||||
next();
|
||||
|
||||
std::size_t begin = m_cursor;
|
||||
while (m_cursor < m_content.size() && get() != '"')
|
||||
next();
|
||||
if (m_cursor >= m_content.size()) return { location(), token::UnexpectedEOF };
|
||||
|
||||
next();
|
||||
return { loc, token::String, m_content.substr(begin, m_cursor - begin - 1) };
|
||||
}
|
||||
|
||||
if (get() == '\'') {
|
||||
next();
|
||||
bool slash = get() == '\\';
|
||||
if (slash) next();
|
||||
auto loc2 = location();
|
||||
char character = get();
|
||||
next();
|
||||
if (get() != '\'') return { location(), token::UnexpectedCharacter, get() };
|
||||
next();
|
||||
if (slash) {
|
||||
switch (character) {
|
||||
case '\\': character = '\\'; break;
|
||||
case 'n': character = '\n'; break;
|
||||
case 'r': character = '\r'; break;
|
||||
case 't': character = '\t'; break;
|
||||
default: return { loc2, token::UnexpectedCharacter, character };
|
||||
}
|
||||
}
|
||||
return { loc, token::Char, character };
|
||||
}
|
||||
|
||||
static std::unordered_map<std::string_view, token_t> s_tokens = {
|
||||
{ "(", token::LParen },
|
||||
{ ")", token::RParen },
|
||||
{ "{", token::LBrace },
|
||||
{ "}", token::RBrace },
|
||||
{ "[", token::LBracket },
|
||||
{ "]", token::RBracket },
|
||||
{ ";", token::Semicolon },
|
||||
{ ":", token::Colon },
|
||||
{ ",", token::Comma },
|
||||
{ ".", token::Dot },
|
||||
{ "+", token::Plus },
|
||||
{ "-", token::Minus },
|
||||
{ "*", token::Star },
|
||||
{ "/", token::Slash },
|
||||
{ "%", token::Percent },
|
||||
{ "&", token::Ampersand },
|
||||
{ "|", token::Pipe },
|
||||
{ "^", token::Hat },
|
||||
{ "&&", token::DblAmpersand },
|
||||
{ "||", token::DblPipe },
|
||||
{ "++", token::DblPlus },
|
||||
{ "--", token::DblMinus },
|
||||
{ "!", token::ExMark },
|
||||
{ "^^", token::CatEars },
|
||||
{ "=", token::Equals },
|
||||
{ "+=", token::PlusEquals },
|
||||
{ "-=", token::MinusEquals },
|
||||
{ "*=", token::StarEquals },
|
||||
{ "/=", token::SlashEquals },
|
||||
{ "%=", token::PercentEquals },
|
||||
{ "&=", token::AmpersandEquals },
|
||||
{ "|=", token::PipeEquals },
|
||||
{ "^=", token::HatEquals },
|
||||
{ "==", token::DblEquals },
|
||||
{ "!=", token::ExEquals },
|
||||
{ "<", token::LessThan },
|
||||
{ "<=", token::LessEquals },
|
||||
{ ">", token::GreaterThan },
|
||||
{ ">=", token::GreaterEquals },
|
||||
{ "->", token::SlimArrow },
|
||||
{ "=>", token::FatArrow },
|
||||
{ "@", token::Monkey },
|
||||
{ "#", token::Sha256 },
|
||||
};
|
||||
|
||||
std::size_t begin = m_cursor;
|
||||
std::size_t len = 1;
|
||||
while (begin + len - 1 < m_content.size() && s_tokens.find(m_content.substr(begin, len)) != s_tokens.end())
|
||||
++len;
|
||||
|
||||
if (len > 1) {
|
||||
auto type = s_tokens[m_content.substr(begin, len - 1)];
|
||||
m_cursor += len - 1;
|
||||
return { loc, type };
|
||||
}
|
||||
|
||||
return { loc, token::UnexpectedCharacter, get() };
|
||||
}
|
||||
|
||||
token lexer::peek_token() {
|
||||
if (m_peekToken.has_value()) return m_peekToken.value();
|
||||
auto tok = next_token();
|
||||
m_peekToken = tok;
|
||||
return tok;
|
||||
}
|
||||
|
||||
token lexer::skip_token() {
|
||||
m_peekToken = {};
|
||||
return next_token();
|
||||
}
|
||||
|
||||
void lexer::next() {
|
||||
if (m_cursor < m_content.size()) ++m_cursor;
|
||||
}
|
||||
|
||||
constexpr char lexer::get(std::size_t offset) const {
|
||||
return m_content[m_cursor + offset];
|
||||
}
|
||||
|
||||
void lexer::skip_spaces() {
|
||||
while (m_cursor < m_content.size() && std::isspace(get()) != 0)
|
||||
++m_cursor;
|
||||
}
|
||||
|
||||
constexpr token::location lexer::location() const {
|
||||
return { m_filepath, m_row, m_cursor - m_lineStart };
|
||||
}
|
||||
|
||||
} // namespace furc
|
||||
@@ -0,0 +1,133 @@
|
||||
#include "furc/front/parser.hpp"
|
||||
|
||||
#include "furc/front/ast.hpp"
|
||||
#include "furc/front/token.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
namespace furc {
|
||||
|
||||
ast parser::parse() {
|
||||
ast tree;
|
||||
while (m_lexer.peek_token().type != token::EndOfFile) {
|
||||
auto* decl = parse_decl();
|
||||
assert(decl);
|
||||
tree.decls.push_back(decl);
|
||||
}
|
||||
|
||||
return std::move(tree);
|
||||
}
|
||||
|
||||
stmt_node* parser::parse_stmt() {
|
||||
switch (m_lexer.peek_token().type) {
|
||||
case token::LBrace: return m_arena->allocate<comp_stmt_node>(parse_comp());
|
||||
default: break;
|
||||
}
|
||||
|
||||
try {
|
||||
return parse_decl();
|
||||
} catch (...) {
|
||||
return parse_expr();
|
||||
}
|
||||
}
|
||||
|
||||
decl_node* parser::parse_decl() {
|
||||
auto first = eat_token(token::Identifier, token::Func);
|
||||
if (first.type == token::Func) {
|
||||
func_decl_node func;
|
||||
|
||||
func.name = std::string(eat_token(token::Identifier).value.string);
|
||||
eat_token(token::LParen);
|
||||
if (m_lexer.peek_token().type != token::RParen) {
|
||||
do {
|
||||
var_decl_node param;
|
||||
|
||||
param.name = std::string(eat_token(token::Identifier).value.string);
|
||||
eat_token(token::Colon);
|
||||
param.type = parse_type();
|
||||
if (m_lexer.peek_token().type == token::Equals) {
|
||||
m_lexer.next_token();
|
||||
param.init = parse_expr();
|
||||
}
|
||||
func.params.emplace_back(std::move(param));
|
||||
} while (eat_token(token::Comma, token::RParen).type == token::Comma);
|
||||
} else {
|
||||
eat_token(token::RParen);
|
||||
}
|
||||
|
||||
if (m_lexer.peek_token().type == token::SlimArrow) {
|
||||
m_lexer.next_token();
|
||||
func.type = parse_type();
|
||||
}
|
||||
|
||||
if (m_lexer.peek_token().type == token::Semicolon) {
|
||||
m_lexer.next_token();
|
||||
return m_arena->allocate<func_decl_node>(std::move(func));
|
||||
}
|
||||
|
||||
func.body = parse_comp();
|
||||
|
||||
return m_arena->allocate<func_decl_node>(std::move(func));
|
||||
}
|
||||
|
||||
var_decl_node var;
|
||||
var.name = std::string(first.value.string);
|
||||
eat_token(token::Colon); // TODO: Auto-deduce the type
|
||||
var.type = parse_type();
|
||||
if (eat_token(token::Equals, token::Semicolon).type == token::Equals) {
|
||||
var.init = parse_expr();
|
||||
eat_token(token::Semicolon);
|
||||
}
|
||||
|
||||
return m_arena->allocate<var_decl_node>(std::move(var));
|
||||
}
|
||||
|
||||
expr_node* parser::parse_expr() {
|
||||
if (auto* lit = parse_lit(); lit != nullptr) return lit;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
lit_node* parser::parse_lit() {
|
||||
auto token = eat_token(token::Integer, token::Char);
|
||||
switch (token.type) {
|
||||
case token::Integer: {
|
||||
return m_arena->allocate<int_lit_node>(int_lit_node(token.value.integer));
|
||||
}
|
||||
case token::Char: {
|
||||
return m_arena->allocate<char_lit_node>(char_lit_node(token.value.character));
|
||||
}
|
||||
default: throw std::runtime_error("unreachable");
|
||||
}
|
||||
}
|
||||
|
||||
ast_type parser::parse_type() {
|
||||
auto token =
|
||||
eat_token(token::S8, token::U8, token::S16, token::U16, token::S32, token::U32, token::S64, token::U64);
|
||||
switch (token.type) {
|
||||
case token::S8: return { ast_type::S8 };
|
||||
case token::U8: return { ast_type::U8 };
|
||||
case token::S16: return { ast_type::S16 };
|
||||
case token::U16: return { ast_type::U16 };
|
||||
case token::S32: return { ast_type::S32 };
|
||||
case token::U32: return { ast_type::U32 };
|
||||
case token::S64: return { ast_type::S64 };
|
||||
case token::U64: return { ast_type::U64 };
|
||||
default: throw std::runtime_error("unreachable");
|
||||
}
|
||||
}
|
||||
|
||||
comp_stmt_node parser::parse_comp() {
|
||||
comp_stmt_node comp;
|
||||
|
||||
eat_token(token::LBrace);
|
||||
while (m_lexer.peek_token().type != token::EndOfFile && m_lexer.peek_token().type != token::RBrace) {
|
||||
comp.stmts.push_back(parse_stmt());
|
||||
}
|
||||
eat_token(token::RBrace);
|
||||
|
||||
return comp;
|
||||
}
|
||||
|
||||
} // namespace furc
|
||||
+9
-2
@@ -1,7 +1,14 @@
|
||||
#include <iostream>
|
||||
#include "furc/front/lexer.hpp"
|
||||
#include "furc/front/parser.hpp"
|
||||
#include "furlang/arena.hpp"
|
||||
|
||||
int main(void) {
|
||||
std::cout << "Farewell, stasiu!\n";
|
||||
furlang::arena arena;
|
||||
|
||||
furc::lexer lexer = { "<AK>", "func main(argc: u64) -> s32 { x: s32 = '\\\\'; }" };
|
||||
furc::parser parser = { std::move(lexer), arena };
|
||||
|
||||
auto program = parser.parse();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user