From db485af7f6be38592f9627241facdb2f563b650c Mon Sep 17 00:00:00 2001 From: CHatingPython Date: Thu, 6 Aug 2026 10:54:54 +0200 Subject: [PATCH] feat(furc): implement a basic parser --- furc/include/furc/front/ast.hpp | 152 +++++++++++++++++++++++++++++ furc/include/furc/front/parser.hpp | 48 +++++++++ furc/src/front/parser.cpp | 133 +++++++++++++++++++++++++ furc/src/main.cpp | 24 ++--- 4 files changed, 343 insertions(+), 14 deletions(-) create mode 100644 furc/include/furc/front/ast.hpp create mode 100644 furc/include/furc/front/parser.hpp create mode 100644 furc/src/front/parser.cpp diff --git a/furc/include/furc/front/ast.hpp b/furc/include/furc/front/ast.hpp new file mode 100644 index 0000000..e49ea38 --- /dev/null +++ b/furc/include/furc/front/ast.hpp @@ -0,0 +1,152 @@ +#ifndef FURC_FRONT_AST_HPP +#define FURC_FRONT_AST_HPP + +#include +#include +#include +#include + +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 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 params; + std::optional 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 decls; +}; + +} // namespace furc + +#endif // FURC_FRONT_AST_HPP diff --git a/furc/include/furc/front/parser.hpp b/furc/include/furc/front/parser.hpp new file mode 100644 index 0000000..e5ca185 --- /dev/null +++ b/furc/include/furc/front/parser.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 + +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 + 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 diff --git a/furc/src/front/parser.cpp b/furc/src/front/parser.cpp new file mode 100644 index 0000000..80d3531 --- /dev/null +++ b/furc/src/front/parser.cpp @@ -0,0 +1,133 @@ +#include "furc/front/parser.hpp" + +#include "furc/front/ast.hpp" +#include "furc/front/token.hpp" + +#include +#include +#include + +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(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(std::move(func)); + } + + func.body = parse_comp(); + + return m_arena->allocate(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(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(token.value.integer)); + } + case token::Char: { + return m_arena->allocate(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 diff --git a/furc/src/main.cpp b/furc/src/main.cpp index 7919698..e28a81d 100644 --- a/furc/src/main.cpp +++ b/furc/src/main.cpp @@ -1,18 +1,14 @@ #include "furc/front/lexer.hpp" - -#include +#include "furc/front/parser.hpp" +#include "furlang/arena.hpp" int main(void) { - furc::lexer lexer = { "", "func main(argc: u64) -> s32 { return '\\\\'; }" }; - while (true) { - furc::token token = lexer.next_token(); - std::cout << token.loc.filepath << ':' << token.loc.row + 1 << ':' << token.loc.col + 1 << ": " << token - << '\n'; - switch (token.type) { - case furc::token::UnexpectedCharacter: - case furc::token::UnexpectedEOF: return 1; - case furc::token::EndOfFile: return 0; - default: break; - } - } + furlang::arena arena; + + furc::lexer lexer = { "", "func main(argc: u64) -> s32 { x: s32 = '\\\\'; }" }; + furc::parser parser = { std::move(lexer), arena }; + + auto program = parser.parse(); + + return 0; }