diff --git a/src/ast/expressions/infix.cpp b/src/ast/expressions/infix.cpp index 7a54094..35f469d 100644 --- a/src/ast/expressions/infix.cpp +++ b/src/ast/expressions/infix.cpp @@ -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; } diff --git a/src/ast/expressions/infix.hpp b/src/ast/expressions/infix.hpp index 5338c16..fa7638f 100644 --- a/src/ast/expressions/infix.hpp +++ b/src/ast/expressions/infix.hpp @@ -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; diff --git a/src/parser/parser.cpp b/src/parser/parser.cpp index 50d9b9c..3bc98a6 100644 --- a/src/parser/parser.cpp +++ b/src/parser/parser.cpp @@ -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 diff --git a/src/parser/parser.hpp b/src/parser/parser.hpp index dd7565c..4fc5a6b 100644 --- a/src/parser/parser.hpp +++ b/src/parser/parser.hpp @@ -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 diff --git a/src/parser/precedence.cpp b/src/parser/precedence.cpp new file mode 100644 index 0000000..6c43623 --- /dev/null +++ b/src/parser/precedence.cpp @@ -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 diff --git a/src/parser/precedence.hpp b/src/parser/precedence.hpp index 3169280..a790783 100644 --- a/src/parser/precedence.hpp +++ b/src/parser/precedence.hpp @@ -1,3 +1,7 @@ +#include "token/type.hpp" + +#include + 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(p); + } +} // namespace parser diff --git a/test/parser/precedence.cpp b/test/parser/precedence.cpp new file mode 100644 index 0000000..9e41f98 --- /dev/null +++ b/test/parser/precedence.cpp @@ -0,0 +1,51 @@ +#include "parser/parser.hpp" + +#include + +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)); + } +}