can now parse identifiers and integer literals
This commit is contained in:
15
src/ast/expressions/integer.cpp
Normal file
15
src/ast/expressions/integer.cpp
Normal file
@@ -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
|
15
src/ast/expressions/integer.hpp
Normal file
15
src/ast/expressions/integer.hpp
Normal file
@@ -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
|
@@ -1,5 +1,9 @@
|
|||||||
#include "parser.hpp"
|
#include "parser.hpp"
|
||||||
|
|
||||||
|
#include "ast/expressions/identifier.hpp"
|
||||||
|
#include "ast/expressions/integer.hpp"
|
||||||
|
#include "token/type.hpp"
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
namespace parser {
|
namespace parser {
|
||||||
@@ -9,6 +13,16 @@ namespace parser {
|
|||||||
next(token::type::ILLEGAL, "") {
|
next(token::type::ILLEGAL, "") {
|
||||||
next_token();
|
next_token();
|
||||||
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() {
|
void parser::next_token() {
|
||||||
@@ -45,11 +59,13 @@ namespace parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::expression* parser::parse_expression() {
|
ast::expression* parser::parse_expression(precedence) {
|
||||||
// TODO: we are currently skipping expressions until we encounter a
|
auto it = prefix_parse_fns.find(current.type);
|
||||||
// semicolon
|
if (it == prefix_parse_fns.end())
|
||||||
for (; current.type != token::type::SEMICOLON; next_token()) {}
|
return nullptr;
|
||||||
return nullptr;
|
|
||||||
|
prefix_parse_fn func = it->second;
|
||||||
|
return func();
|
||||||
};
|
};
|
||||||
|
|
||||||
ast::return_stmt* parser::parse_return() {
|
ast::return_stmt* parser::parse_return() {
|
||||||
@@ -126,4 +142,12 @@ namespace parser {
|
|||||||
void parser::register_infix(token::type type, infix_parse_fn fn) {
|
void parser::register_infix(token::type type, infix_parse_fn fn) {
|
||||||
infix_parse_fns[type] = 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
|
} // namespace parser
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
#include "ast/statements/let.hpp"
|
#include "ast/statements/let.hpp"
|
||||||
#include "ast/statements/return.hpp"
|
#include "ast/statements/return.hpp"
|
||||||
#include "lexer/lexer.hpp"
|
#include "lexer/lexer.hpp"
|
||||||
|
#include "precedence.hpp"
|
||||||
#include "token/token.hpp"
|
#include "token/token.hpp"
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@@ -18,7 +19,7 @@ namespace parser {
|
|||||||
using infix_parse_fn = std::function<ast::expression*(ast::expression*)>;
|
using infix_parse_fn = std::function<ast::expression*(ast::expression*)>;
|
||||||
|
|
||||||
struct parser {
|
struct parser {
|
||||||
parser(lexer::lexer& lexer);
|
parser(lexer::lexer&);
|
||||||
~parser();
|
~parser();
|
||||||
std::vector<ast::error::error*> errors;
|
std::vector<ast::error::error*> errors;
|
||||||
|
|
||||||
@@ -34,7 +35,7 @@ namespace parser {
|
|||||||
void next_token();
|
void next_token();
|
||||||
void skip_until_semicolon();
|
void skip_until_semicolon();
|
||||||
ast::statement* parse_statement();
|
ast::statement* parse_statement();
|
||||||
ast::expression* parse_expression();
|
ast::expression* parse_expression(precedence = precedence::LOWEST);
|
||||||
ast::let_stmt* parse_let();
|
ast::let_stmt* parse_let();
|
||||||
ast::return_stmt* parse_return();
|
ast::return_stmt* parse_return();
|
||||||
ast::expression_stmt* parse_expression_stmt();
|
ast::expression_stmt* parse_expression_stmt();
|
||||||
@@ -43,5 +44,8 @@ namespace parser {
|
|||||||
|
|
||||||
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_integer();
|
||||||
};
|
};
|
||||||
} // namespace parser
|
} // namespace parser
|
||||||
|
11
src/parser/precedence.hpp
Normal file
11
src/parser/precedence.hpp
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
namespace parser {
|
||||||
|
enum class precedence {
|
||||||
|
LOWEST,
|
||||||
|
EQUALS,
|
||||||
|
LESS_GREATER,
|
||||||
|
SUM,
|
||||||
|
PRODUCT,
|
||||||
|
PREFIX,
|
||||||
|
CALL
|
||||||
|
};
|
||||||
|
}
|
@@ -1,10 +1,14 @@
|
|||||||
#include "ast/expressions/identifier.hpp"
|
#include "ast/expressions/identifier.hpp"
|
||||||
|
#include "ast/expressions/integer.hpp"
|
||||||
#include "utils.hpp"
|
#include "utils.hpp"
|
||||||
|
|
||||||
#include <doctest.h>
|
#include <doctest.h>
|
||||||
|
|
||||||
TEST_SUITE("Parser: expression") {
|
TEST_SUITE("Parser: expression") {
|
||||||
TEST_CASE_FIXTURE(ParserFixture, "Simple expression statement") {
|
TEST_CASE_FIXTURE(
|
||||||
|
ParserFixture,
|
||||||
|
"Simple expression statement with identifier"
|
||||||
|
) {
|
||||||
setup("foobar;");
|
setup("foobar;");
|
||||||
REQUIRE(program->statements.size() == 1);
|
REQUIRE(program->statements.size() == 1);
|
||||||
ast::expression_stmt* expression_stmt;
|
ast::expression_stmt* expression_stmt;
|
||||||
@@ -29,4 +33,34 @@ TEST_SUITE("Parser: expression") {
|
|||||||
REQUIRE(ident->value == "foobar");
|
REQUIRE(ident->value == "foobar");
|
||||||
REQUIRE(ident->token_literal() == "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<ast::expression_stmt*>(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<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