From 79b1aeb45fdf144eaba30a92b660c48514912e36 Mon Sep 17 00:00:00 2001 From: Karma Riuk Date: Tue, 8 Jul 2025 18:14:37 +0200 Subject: [PATCH] can now parse identifiers and integer literals --- src/ast/expressions/integer.cpp | 15 ++++++++++++++ src/ast/expressions/integer.hpp | 15 ++++++++++++++ src/parser/parser.cpp | 34 ++++++++++++++++++++++++++----- src/parser/parser.hpp | 8 ++++++-- src/parser/precedence.hpp | 11 ++++++++++ test/parser/expression.cpp | 36 ++++++++++++++++++++++++++++++++- 6 files changed, 111 insertions(+), 8 deletions(-) create mode 100644 src/ast/expressions/integer.cpp create mode 100644 src/ast/expressions/integer.hpp create mode 100644 src/parser/precedence.hpp diff --git a/src/ast/expressions/integer.cpp b/src/ast/expressions/integer.cpp new file mode 100644 index 0000000..da80922 --- /dev/null +++ b/src/ast/expressions/integer.cpp @@ -0,0 +1,15 @@ +#include "integer.hpp" + +namespace ast { + integer_literal::integer_literal(token::token token, int value) + : token(std::move(token)), + value(value) {} + + std::string integer_literal::token_literal() const { + return token.literal; + }; + + std::string integer_literal::str() const { + return token.literal; + }; +} // namespace ast diff --git a/src/ast/expressions/integer.hpp b/src/ast/expressions/integer.hpp new file mode 100644 index 0000000..d6127ad --- /dev/null +++ b/src/ast/expressions/integer.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include "ast/ast.hpp" +#include "token/token.hpp" + +namespace ast { + struct integer_literal : expression { + integer_literal(token::token token, int value); + token::token token; + int value; + + std::string token_literal() const override; + std::string str() const override; + }; +} // namespace ast diff --git a/src/parser/parser.cpp b/src/parser/parser.cpp index 35cf831..bb454da 100644 --- a/src/parser/parser.cpp +++ b/src/parser/parser.cpp @@ -1,5 +1,9 @@ #include "parser.hpp" +#include "ast/expressions/identifier.hpp" +#include "ast/expressions/integer.hpp" +#include "token/type.hpp" + #include namespace parser { @@ -9,6 +13,16 @@ namespace parser { next(token::type::ILLEGAL, "") { next_token(); next_token(); + + register_prefix( + token::type::IDENTIFIER, + std::bind(&parser::parse_identifier, this) + ); + + register_prefix( + token::type::INT, + std::bind(&parser::parse_integer, this) + ); } void parser::next_token() { @@ -45,11 +59,13 @@ namespace parser { } } - ast::expression* parser::parse_expression() { - // TODO: we are currently skipping expressions until we encounter a - // semicolon - for (; current.type != token::type::SEMICOLON; next_token()) {} - return nullptr; + ast::expression* parser::parse_expression(precedence) { + auto it = prefix_parse_fns.find(current.type); + if (it == prefix_parse_fns.end()) + return nullptr; + + prefix_parse_fn func = it->second; + return func(); }; ast::return_stmt* parser::parse_return() { @@ -126,4 +142,12 @@ namespace parser { void parser::register_infix(token::type type, infix_parse_fn fn) { infix_parse_fns[type] = fn; }; + + ast::expression* parser::parse_identifier() { + return new ast::identifier(current, current.literal); + }; + + ast::expression* parser::parse_integer() { + return new ast::integer_literal(current, std::stoi(current.literal)); + }; } // namespace parser diff --git a/src/parser/parser.hpp b/src/parser/parser.hpp index a628440..ede83de 100644 --- a/src/parser/parser.hpp +++ b/src/parser/parser.hpp @@ -7,6 +7,7 @@ #include "ast/statements/let.hpp" #include "ast/statements/return.hpp" #include "lexer/lexer.hpp" +#include "precedence.hpp" #include "token/token.hpp" #include @@ -18,7 +19,7 @@ namespace parser { using infix_parse_fn = std::function; struct parser { - parser(lexer::lexer& lexer); + parser(lexer::lexer&); ~parser(); std::vector errors; @@ -34,7 +35,7 @@ namespace parser { void next_token(); void skip_until_semicolon(); ast::statement* parse_statement(); - ast::expression* parse_expression(); + ast::expression* parse_expression(precedence = precedence::LOWEST); ast::let_stmt* parse_let(); ast::return_stmt* parse_return(); ast::expression_stmt* parse_expression_stmt(); @@ -43,5 +44,8 @@ namespace parser { void register_prefix(token::type, prefix_parse_fn); void register_infix(token::type, infix_parse_fn); + + ast::expression* parse_identifier(); + ast::expression* parse_integer(); }; } // namespace parser diff --git a/src/parser/precedence.hpp b/src/parser/precedence.hpp new file mode 100644 index 0000000..3169280 --- /dev/null +++ b/src/parser/precedence.hpp @@ -0,0 +1,11 @@ +namespace parser { + enum class precedence { + LOWEST, + EQUALS, + LESS_GREATER, + SUM, + PRODUCT, + PREFIX, + CALL + }; +} diff --git a/test/parser/expression.cpp b/test/parser/expression.cpp index dcb6cf4..ce7fe0d 100644 --- a/test/parser/expression.cpp +++ b/test/parser/expression.cpp @@ -1,10 +1,14 @@ #include "ast/expressions/identifier.hpp" +#include "ast/expressions/integer.hpp" #include "utils.hpp" #include TEST_SUITE("Parser: expression") { - TEST_CASE_FIXTURE(ParserFixture, "Simple expression statement") { + TEST_CASE_FIXTURE( + ParserFixture, + "Simple expression statement with identifier" + ) { setup("foobar;"); REQUIRE(program->statements.size() == 1); ast::expression_stmt* expression_stmt; @@ -29,4 +33,34 @@ TEST_SUITE("Parser: expression") { REQUIRE(ident->value == "foobar"); REQUIRE(ident->token_literal() == "foobar"); }; + + TEST_CASE_FIXTURE( + ParserFixture, + "Simple expression statement with integer" + ) { + setup("5;"); + REQUIRE(program->statements.size() == 1); + ast::expression_stmt* expression_stmt; + REQUIRE_NOTHROW( + expression_stmt = + dynamic_cast(program->statements[0]) + ); + REQUIRE_MESSAGE( + expression_stmt != nullptr, + "Couldn't cast statement to an expression statement" + ); + + ast::integer_literal* int_lit; + REQUIRE_NOTHROW( + int_lit = + dynamic_cast(expression_stmt->expression) + ); + REQUIRE_MESSAGE( + int_lit != nullptr, + "Couldn't cast expression to an identifier" + ); + + REQUIRE(int_lit->value == 5); + REQUIRE(int_lit->token_literal() == "5"); + }; }