implemented infix operator parsing
This commit is contained in:
@@ -4,6 +4,13 @@
|
||||
|
||||
namespace ast {
|
||||
|
||||
infix_expr::infix_expr(
|
||||
token::token token, std::string op, ast::expression* left
|
||||
)
|
||||
: token(std::move(token)),
|
||||
op(op),
|
||||
left(left) {}
|
||||
|
||||
std::string infix_expr::token_literal() const {
|
||||
return token.literal;
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@
|
||||
|
||||
namespace ast {
|
||||
struct infix_expr : expression {
|
||||
infix_expr(token::token, std::string, ast::expression*);
|
||||
token::token token;
|
||||
expression* left;
|
||||
std::string op;
|
||||
|
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "ast/errors/error.hpp"
|
||||
#include "ast/expressions/identifier.hpp"
|
||||
#include "ast/expressions/infix.hpp"
|
||||
#include "ast/expressions/integer.hpp"
|
||||
#include "ast/expressions/prefix.hpp"
|
||||
#include "token/token.hpp"
|
||||
@@ -36,6 +37,40 @@ namespace parser {
|
||||
token::type::MINUS,
|
||||
std::bind(&parser::parse_prefix_expr, this)
|
||||
);
|
||||
|
||||
using namespace std::placeholders;
|
||||
register_infix(
|
||||
token::type::PLUS,
|
||||
std::bind(&parser::parse_infix_expr, this, _1)
|
||||
);
|
||||
register_infix(
|
||||
token::type::MINUS,
|
||||
std::bind(&parser::parse_infix_expr, this, _1)
|
||||
);
|
||||
register_infix(
|
||||
token::type::ASTERISK,
|
||||
std::bind(&parser::parse_infix_expr, this, _1)
|
||||
);
|
||||
register_infix(
|
||||
token::type::SLASH,
|
||||
std::bind(&parser::parse_infix_expr, this, _1)
|
||||
);
|
||||
register_infix(
|
||||
token::type::EQ,
|
||||
std::bind(&parser::parse_infix_expr, this, _1)
|
||||
);
|
||||
register_infix(
|
||||
token::type::NEQ,
|
||||
std::bind(&parser::parse_infix_expr, this, _1)
|
||||
);
|
||||
register_infix(
|
||||
token::type::GT,
|
||||
std::bind(&parser::parse_infix_expr, this, _1)
|
||||
);
|
||||
register_infix(
|
||||
token::type::LT,
|
||||
std::bind(&parser::parse_infix_expr, this, _1)
|
||||
);
|
||||
}
|
||||
|
||||
void parser::next_token() {
|
||||
@@ -72,15 +107,27 @@ namespace parser {
|
||||
}
|
||||
}
|
||||
|
||||
ast::expression* parser::parse_expression(precedence) {
|
||||
auto it = prefix_parse_fns.find(current.type);
|
||||
if (it == prefix_parse_fns.end()) {
|
||||
ast::expression* parser::parse_expression(precedence prec) {
|
||||
auto prefix_it = prefix_parse_fns.find(current.type);
|
||||
if (prefix_it == prefix_parse_fns.end()) {
|
||||
unkown_prefix_error(current);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
prefix_parse_fn func = it->second;
|
||||
return func();
|
||||
prefix_parse_fn prefix = prefix_it->second;
|
||||
ast::expression* left = prefix();
|
||||
|
||||
while (next.type != token::type::SEMICOLON
|
||||
&& prec < precedence_for(next.type)) {
|
||||
auto infix_it = infix_parse_fns.find(next.type);
|
||||
if (infix_it == infix_parse_fns.end())
|
||||
return left;
|
||||
next_token();
|
||||
|
||||
infix_parse_fn infix = infix_it->second;
|
||||
left = infix(left);
|
||||
}
|
||||
return left;
|
||||
};
|
||||
|
||||
ast::return_stmt* parser::parse_return() {
|
||||
@@ -178,4 +225,13 @@ namespace parser {
|
||||
ret->right = parse_expression(precedence::PREFIX);
|
||||
return ret;
|
||||
};
|
||||
|
||||
ast::expression* parser::parse_infix_expr(ast::expression* left) {
|
||||
ast::infix_expr* ret =
|
||||
new ast::infix_expr(current, current.literal, left);
|
||||
precedence prec = precedence_for(current.type);
|
||||
next_token();
|
||||
ret->right = parse_expression(prec);
|
||||
return ret;
|
||||
};
|
||||
} // namespace parser
|
||||
|
@@ -49,5 +49,7 @@ namespace parser {
|
||||
ast::expression* parse_identifier();
|
||||
ast::expression* parse_integer();
|
||||
ast::expression* parse_prefix_expr();
|
||||
|
||||
ast::expression* parse_infix_expr(ast::expression*);
|
||||
};
|
||||
} // namespace parser
|
||||
|
22
src/parser/precedence.cpp
Normal file
22
src/parser/precedence.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#include "precedence.hpp"
|
||||
|
||||
namespace parser {
|
||||
precedence precedence_for(token::type type) {
|
||||
switch (type) {
|
||||
case token::type::EQ:
|
||||
case token::type::NEQ:
|
||||
return precedence::EQUALS;
|
||||
case token::type::LT:
|
||||
case token::type::GT:
|
||||
return precedence::LESS_GREATER;
|
||||
case token::type::PLUS:
|
||||
case token::type::MINUS:
|
||||
return precedence::SUM;
|
||||
case token::type::ASTERISK:
|
||||
case token::type::SLASH:
|
||||
return precedence::PRODUCT;
|
||||
default:
|
||||
return precedence::LOWEST;
|
||||
}
|
||||
}
|
||||
} // namespace parser
|
@@ -1,3 +1,7 @@
|
||||
#include "token/type.hpp"
|
||||
|
||||
#include <ostream>
|
||||
|
||||
namespace parser {
|
||||
enum class precedence {
|
||||
LOWEST,
|
||||
@@ -8,4 +12,10 @@ namespace parser {
|
||||
PREFIX,
|
||||
CALL
|
||||
};
|
||||
|
||||
precedence precedence_for(token::type);
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, precedence& p) {
|
||||
return os << static_cast<int>(p);
|
||||
}
|
||||
} // namespace parser
|
||||
|
51
test/parser/precedence.cpp
Normal file
51
test/parser/precedence.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
#include "parser/parser.hpp"
|
||||
|
||||
#include <doctest.h>
|
||||
|
||||
TEST_SUITE("Precedence") {
|
||||
TEST_CASE("Raw precedence") {
|
||||
CHECK(parser::precedence::LOWEST < parser::precedence::EQUALS);
|
||||
CHECK(parser::precedence::LOWEST < parser::precedence::LESS_GREATER);
|
||||
CHECK(parser::precedence::LOWEST < parser::precedence::SUM);
|
||||
CHECK(parser::precedence::LOWEST < parser::precedence::PRODUCT);
|
||||
CHECK(parser::precedence::LOWEST < parser::precedence::PREFIX);
|
||||
CHECK(parser::precedence::LOWEST < parser::precedence::CALL);
|
||||
|
||||
CHECK(parser::precedence::EQUALS < parser::precedence::LESS_GREATER);
|
||||
CHECK(parser::precedence::EQUALS < parser::precedence::SUM);
|
||||
CHECK(parser::precedence::EQUALS < parser::precedence::PRODUCT);
|
||||
CHECK(parser::precedence::EQUALS < parser::precedence::PREFIX);
|
||||
CHECK(parser::precedence::EQUALS < parser::precedence::CALL);
|
||||
|
||||
CHECK(parser::precedence::LESS_GREATER < parser::precedence::SUM);
|
||||
CHECK(parser::precedence::LESS_GREATER < parser::precedence::PRODUCT);
|
||||
CHECK(parser::precedence::LESS_GREATER < parser::precedence::PREFIX);
|
||||
CHECK(parser::precedence::LESS_GREATER < parser::precedence::CALL);
|
||||
|
||||
CHECK(parser::precedence::SUM < parser::precedence::PRODUCT);
|
||||
CHECK(parser::precedence::SUM < parser::precedence::PREFIX);
|
||||
CHECK(parser::precedence::SUM < parser::precedence::CALL);
|
||||
|
||||
CHECK(parser::precedence::PRODUCT < parser::precedence::PREFIX);
|
||||
CHECK(parser::precedence::PRODUCT < parser::precedence::CALL);
|
||||
|
||||
CHECK(parser::precedence::PREFIX < parser::precedence::CALL);
|
||||
}
|
||||
|
||||
TEST_CASE("Operator precedence") {
|
||||
auto& prec = parser::precedence_for;
|
||||
CHECK(prec(token::type::EQ) == prec(token::type::NEQ));
|
||||
CHECK(prec(token::type::LT) == prec(token::type::GT));
|
||||
CHECK(prec(token::type::PLUS) == prec(token::type::MINUS));
|
||||
CHECK(prec(token::type::ASTERISK) == prec(token::type::SLASH));
|
||||
|
||||
CHECK(prec(token::type::EQ) < prec(token::type::LT));
|
||||
CHECK(prec(token::type::EQ) < prec(token::type::PLUS));
|
||||
CHECK(prec(token::type::EQ) < prec(token::type::ASTERISK));
|
||||
|
||||
CHECK(prec(token::type::LT) < prec(token::type::PLUS));
|
||||
CHECK(prec(token::type::LT) < prec(token::type::ASTERISK));
|
||||
|
||||
CHECK(prec(token::type::PLUS) < prec(token::type::ASTERISK));
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user