can now parse prefix expressions!

This commit is contained in:
Karma Riuk
2025-07-09 12:02:01 +02:00
parent c7a30a0028
commit 7f1cc6f45e
6 changed files with 118 additions and 52 deletions

View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
#include "token/token.hpp"
#include "token/type.hpp" #include "token/type.hpp"
namespace ast::error { namespace ast::error {
@@ -12,6 +13,14 @@ namespace ast::error {
explicit parser_error(const std::string& message): error(message) {} 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 { struct expected_next : parser_error {
token::type expected_type; token::type expected_type;

View File

@@ -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

View File

@@ -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

View File

@@ -1,7 +1,10 @@
#include "parser.hpp" #include "parser.hpp"
#include "ast/errors/error.hpp"
#include "ast/expressions/identifier.hpp" #include "ast/expressions/identifier.hpp"
#include "ast/expressions/integer.hpp" #include "ast/expressions/integer.hpp"
#include "ast/expressions/prefix.hpp"
#include "token/token.hpp"
#include "token/type.hpp" #include "token/type.hpp"
#include <sstream> #include <sstream>
@@ -23,6 +26,16 @@ namespace parser {
token::type::INT, token::type::INT,
std::bind(&parser::parse_integer, this) 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() { void parser::next_token() {
@@ -61,8 +74,10 @@ namespace parser {
ast::expression* parser::parse_expression(precedence) { ast::expression* parser::parse_expression(precedence) {
auto it = prefix_parse_fns.find(current.type); 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; return nullptr;
}
prefix_parse_fn func = it->second; prefix_parse_fn func = it->second;
return func(); return func();
@@ -130,6 +145,12 @@ namespace parser {
errors.push_back(new ast::error::expected_next(t, ss.str())); 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() { parser::~parser() {
for (const auto& e : errors) for (const auto& e : errors)
delete e; delete e;
@@ -150,4 +171,11 @@ namespace parser {
ast::expression* parser::parse_integer() { ast::expression* parser::parse_integer() {
return new ast::integer_literal(current, std::stoi(current.literal)); 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 } // namespace parser

View File

@@ -41,11 +41,13 @@ namespace parser {
ast::expression_stmt* parse_expression_stmt(); ast::expression_stmt* parse_expression_stmt();
bool expect_next(token::type); bool expect_next(token::type);
void next_error(token::type); void next_error(token::type);
void unkown_prefix_error(token::token);
void register_prefix(token::type, prefix_parse_fn); void register_prefix(token::type, prefix_parse_fn);
void register_infix(token::type, infix_parse_fn); void register_infix(token::type, infix_parse_fn);
ast::expression* parse_identifier(); ast::expression* parse_identifier();
ast::expression* parse_integer(); ast::expression* parse_integer();
ast::expression* parse_prefix_expr();
}; };
} // namespace parser } // namespace parser

View File

@@ -4,6 +4,16 @@
#include "utils.hpp" #include "utils.hpp"
#include <doctest.h> #include <doctest.h>
#include <sstream>
void test_integer_literal(ast::expression* expr, int value) {
ast::integer_literal* int_lit = cast<ast::integer_literal>(expr);
REQUIRE(int_lit->value == value);
std::ostringstream oss;
oss << value;
REQUIRE(int_lit->token_literal() == oss.str());
}
TEST_SUITE("Parser: expression") { TEST_SUITE("Parser: expression") {
TEST_CASE_FIXTURE( TEST_CASE_FIXTURE(
@@ -32,57 +42,39 @@ TEST_SUITE("Parser: expression") {
ast::expression_stmt* expression_stmt = ast::expression_stmt* expression_stmt =
cast<ast::expression_stmt>(program->statements[0]); cast<ast::expression_stmt>(program->statements[0]);
ast::integer_literal* int_lit = test_integer_literal(expression_stmt->expression, 5);
cast<ast::integer_literal>(expression_stmt->expression);
REQUIRE(int_lit->value == 5);
REQUIRE(int_lit->token_literal() == "5");
}; };
// TEST_CASE_FIXTURE( TEST_CASE_FIXTURE(
// ParserFixture, ParserFixture,
// "Simple expression statement with prefix before integer" "Simple expression statement with prefix before integer"
// ) { ) {
// SUBCASE("Prefix: '!'") { SUBCASE("Prefix: '!'") {
// setup("!5;"); setup("!5;");
//
// REQUIRE(program->statements.size() == 1); REQUIRE(program->statements.size() == 1);
// ast::expression_stmt* expression_stmt = ast::expression_stmt* expression_stmt =
// cast<ast::expression_stmt>(program->statements[0]); cast<ast::expression_stmt>(program->statements[0]);
//
// ast::prefix_expr* prefix_expr; ast::prefix_expr* prefix_expr =
// REQUIRE_NOTHROW( cast<ast::prefix_expr>(expression_stmt->expression);
// prefix_expr =
// dynamic_cast<ast::prefix_expr*>(expression_stmt->expression) REQUIRE(prefix_expr->op == "!");
// ); test_integer_literal(prefix_expr->right, 5);
// REQUIRE_MESSAGE( }
// prefix_expr != nullptr,
// "Couldn't cast expression to an identifier" SUBCASE("Prefix: '-'") {
// ); setup("-15;");
//
// REQUIRE(prefix_expr->value == 5); REQUIRE(program->statements.size() == 1);
// REQUIRE(prefix_expr->token_literal() == "5"); ast::expression_stmt* expression_stmt =
// } cast<ast::expression_stmt>(program->statements[0]);
// SUBCASE("Prefix: '-'") {
// setup("-15;"); ast::prefix_expr* prefix_expr =
// cast<ast::prefix_expr>(expression_stmt->expression);
// REQUIRE(program->statements.size() == 1);
// ast::expression_stmt* expression_stmt = REQUIRE(prefix_expr->op == "-");
// get_expression_stmt(program->statements[0]); test_integer_literal(prefix_expr->right, 15);
// }
// ast::integer_literal* int_lit; }
// REQUIRE_NOTHROW(
// int_lit = dynamic_cast<ast::integer_literal*>(
// 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");
// }
// }
} }