can now parse prefix expressions!
This commit is contained in:
@@ -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;
|
||||||
|
|
||||||
|
19
src/ast/expressions/prefix.cpp
Normal file
19
src/ast/expressions/prefix.cpp
Normal 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
|
16
src/ast/expressions/prefix.hpp
Normal file
16
src/ast/expressions/prefix.hpp
Normal 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
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user