feat(furc): implement a basic parser
This commit is contained in:
@@ -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,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,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
|
||||||
+10
-14
@@ -1,18 +1,14 @@
|
|||||||
#include "furc/front/lexer.hpp"
|
#include "furc/front/lexer.hpp"
|
||||||
|
#include "furc/front/parser.hpp"
|
||||||
#include <iostream>
|
#include "furlang/arena.hpp"
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
furc::lexer lexer = { "<AK>", "func main(argc: u64) -> s32 { return '\\\\'; }" };
|
furlang::arena arena;
|
||||||
while (true) {
|
|
||||||
furc::token token = lexer.next_token();
|
furc::lexer lexer = { "<AK>", "func main(argc: u64) -> s32 { x: s32 = '\\\\'; }" };
|
||||||
std::cout << token.loc.filepath << ':' << token.loc.row + 1 << ':' << token.loc.col + 1 << ": " << token
|
furc::parser parser = { std::move(lexer), arena };
|
||||||
<< '\n';
|
|
||||||
switch (token.type) {
|
auto program = parser.parse();
|
||||||
case furc::token::UnexpectedCharacter:
|
|
||||||
case furc::token::UnexpectedEOF: return 1;
|
return 0;
|
||||||
case furc::token::EndOfFile: return 0;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user