From 7f1cc6f45e9e0163dd39ab1d49f09d028585386b Mon Sep 17 00:00:00 2001 From: Karma Riuk Date: Wed, 9 Jul 2025 12:02:01 +0200 Subject: [PATCH] can now parse prefix expressions! --- src/ast/errors/error.hpp | 9 ++++ src/ast/expressions/prefix.cpp | 19 +++++++ src/ast/expressions/prefix.hpp | 16 ++++++ src/parser/parser.cpp | 30 ++++++++++- src/parser/parser.hpp | 2 + test/parser/expression.cpp | 94 ++++++++++++++++------------------ 6 files changed, 118 insertions(+), 52 deletions(-) create mode 100644 src/ast/expressions/prefix.cpp create mode 100644 src/ast/expressions/prefix.hpp diff --git a/src/ast/errors/error.hpp b/src/ast/errors/error.hpp index 5057f91..4a9914b 100644 --- a/src/ast/errors/error.hpp +++ b/src/ast/errors/error.hpp @@ -1,5 +1,6 @@ #pragma once +#include "token/token.hpp" #include "token/type.hpp" namespace ast::error { @@ -12,6 +13,14 @@ namespace ast::error { explicit parser_error(const std::string& message): error(message) {} }; + struct unkown_prefix : parser_error { + token::token prefix; + + explicit unkown_prefix(token::token prefix, const std::string& message) + : parser_error(message), + prefix(prefix) {} + }; + struct expected_next : parser_error { token::type expected_type; diff --git a/src/ast/expressions/prefix.cpp b/src/ast/expressions/prefix.cpp new file mode 100644 index 0000000..0c145e3 --- /dev/null +++ b/src/ast/expressions/prefix.cpp @@ -0,0 +1,19 @@ +#include "prefix.hpp" + +#include "token/token.hpp" + +namespace ast { + + prefix_expr::prefix_expr(token::token token, std::string op) + : token(std::move(token)), + op(op), + right(nullptr) {} + + std::string prefix_expr::token_literal() const { + return token.literal; + } + + std::string prefix_expr::str() const { + return token.literal + right->str(); + } +} // namespace ast diff --git a/src/ast/expressions/prefix.hpp b/src/ast/expressions/prefix.hpp new file mode 100644 index 0000000..b17d4c1 --- /dev/null +++ b/src/ast/expressions/prefix.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include "ast/ast.hpp" +#include "token/token.hpp" + +namespace ast { + struct prefix_expr : expression { + prefix_expr(token::token token, std::string op); + token::token token; + std::string op; + expression* right; + + 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 bb454da..50d9b9c 100644 --- a/src/parser/parser.cpp +++ b/src/parser/parser.cpp @@ -1,7 +1,10 @@ #include "parser.hpp" +#include "ast/errors/error.hpp" #include "ast/expressions/identifier.hpp" #include "ast/expressions/integer.hpp" +#include "ast/expressions/prefix.hpp" +#include "token/token.hpp" #include "token/type.hpp" #include @@ -23,6 +26,16 @@ namespace parser { token::type::INT, std::bind(&parser::parse_integer, this) ); + + register_prefix( + token::type::BANG, + std::bind(&parser::parse_prefix_expr, this) + ); + + register_prefix( + token::type::MINUS, + std::bind(&parser::parse_prefix_expr, this) + ); } void parser::next_token() { @@ -61,8 +74,10 @@ namespace parser { ast::expression* parser::parse_expression(precedence) { auto it = prefix_parse_fns.find(current.type); - if (it == prefix_parse_fns.end()) + if (it == prefix_parse_fns.end()) { + unkown_prefix_error(current); return nullptr; + } prefix_parse_fn func = it->second; return func(); @@ -130,6 +145,12 @@ namespace parser { errors.push_back(new ast::error::expected_next(t, ss.str())); } + void parser::unkown_prefix_error(token::token tok) { + std::stringstream ss; + ss << "No prefix parse function for token " << tok; + errors.push_back(new ast::error::unkown_prefix(tok, ss.str())); + } + parser::~parser() { for (const auto& e : errors) delete e; @@ -150,4 +171,11 @@ namespace parser { ast::expression* parser::parse_integer() { return new ast::integer_literal(current, std::stoi(current.literal)); }; + + ast::expression* parser::parse_prefix_expr() { + ast::prefix_expr* ret = new ast::prefix_expr(current, current.literal); + next_token(); + ret->right = parse_expression(precedence::PREFIX); + return ret; + }; } // namespace parser diff --git a/src/parser/parser.hpp b/src/parser/parser.hpp index ede83de..dd7565c 100644 --- a/src/parser/parser.hpp +++ b/src/parser/parser.hpp @@ -41,11 +41,13 @@ namespace parser { ast::expression_stmt* parse_expression_stmt(); bool expect_next(token::type); void next_error(token::type); + void unkown_prefix_error(token::token); void register_prefix(token::type, prefix_parse_fn); void register_infix(token::type, infix_parse_fn); ast::expression* parse_identifier(); ast::expression* parse_integer(); + ast::expression* parse_prefix_expr(); }; } // namespace parser diff --git a/test/parser/expression.cpp b/test/parser/expression.cpp index d7db95f..fed1154 100644 --- a/test/parser/expression.cpp +++ b/test/parser/expression.cpp @@ -4,6 +4,16 @@ #include "utils.hpp" #include +#include + +void test_integer_literal(ast::expression* expr, int value) { + ast::integer_literal* int_lit = cast(expr); + + REQUIRE(int_lit->value == value); + std::ostringstream oss; + oss << value; + REQUIRE(int_lit->token_literal() == oss.str()); +} TEST_SUITE("Parser: expression") { TEST_CASE_FIXTURE( @@ -32,57 +42,39 @@ TEST_SUITE("Parser: expression") { ast::expression_stmt* expression_stmt = cast(program->statements[0]); - ast::integer_literal* int_lit = - cast(expression_stmt->expression); - - REQUIRE(int_lit->value == 5); - REQUIRE(int_lit->token_literal() == "5"); + test_integer_literal(expression_stmt->expression, 5); }; - // TEST_CASE_FIXTURE( - // ParserFixture, - // "Simple expression statement with prefix before integer" - // ) { - // SUBCASE("Prefix: '!'") { - // setup("!5;"); - // - // REQUIRE(program->statements.size() == 1); - // ast::expression_stmt* expression_stmt = - // cast(program->statements[0]); - // - // ast::prefix_expr* prefix_expr; - // REQUIRE_NOTHROW( - // prefix_expr = - // dynamic_cast(expression_stmt->expression) - // ); - // REQUIRE_MESSAGE( - // prefix_expr != nullptr, - // "Couldn't cast expression to an identifier" - // ); - // - // REQUIRE(prefix_expr->value == 5); - // REQUIRE(prefix_expr->token_literal() == "5"); - // } - // SUBCASE("Prefix: '-'") { - // setup("-15;"); - // - // REQUIRE(program->statements.size() == 1); - // ast::expression_stmt* expression_stmt = - // get_expression_stmt(program->statements[0]); - // - // 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"); - // } - // } + TEST_CASE_FIXTURE( + ParserFixture, + "Simple expression statement with prefix before integer" + ) { + SUBCASE("Prefix: '!'") { + setup("!5;"); + + REQUIRE(program->statements.size() == 1); + ast::expression_stmt* expression_stmt = + cast(program->statements[0]); + + ast::prefix_expr* prefix_expr = + cast(expression_stmt->expression); + + REQUIRE(prefix_expr->op == "!"); + test_integer_literal(prefix_expr->right, 5); + } + + SUBCASE("Prefix: '-'") { + setup("-15;"); + + REQUIRE(program->statements.size() == 1); + ast::expression_stmt* expression_stmt = + cast(program->statements[0]); + + ast::prefix_expr* prefix_expr = + cast(expression_stmt->expression); + + REQUIRE(prefix_expr->op == "-"); + test_integer_literal(prefix_expr->right, 15); + } + } }