Skip to content

Commit

Permalink
feat: boolean literals and grouped expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
hrzlgnm committed Jun 22, 2023
1 parent ec2da73 commit c4feb24
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 22 deletions.
18 changes: 18 additions & 0 deletions source/ast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,22 @@ auto expression_statement::string() const -> std::string
return {};
}

boolean::boolean(token tokn, bool val)
: tkn {tokn}
, value {val}
{
}

auto boolean::token_literal() const -> std::string_view
{
return tkn.literal;
}

auto boolean::string() const -> std::string
{
return std::string {tkn.literal};
}

auto integer_literal::token_literal() const -> std::string_view
{
return tkn.literal;
Expand Down Expand Up @@ -113,7 +129,9 @@ auto infix_expression::string() const -> std::string
std::ostringstream strm;
strm << "(";
strm << left->string();
strm << ' ';
strm << op;
strm << ' ';
strm << right->string();
strm << ")";
return strm.str();
Expand Down
11 changes: 11 additions & 0 deletions source/ast.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,17 @@ struct expression_statement : statement
std::unique_ptr<expression> expr {};
};

struct boolean : expression
{
boolean(token tokn, bool val);
auto token_literal() const -> std::string_view override;

auto string() const -> std::string override;

token tkn {};
bool value {};
};

struct integer_literal : expression
{
using expression::expression;
Expand Down
61 changes: 40 additions & 21 deletions source/parser.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <map>
#include <memory>
Expand All @@ -22,16 +21,25 @@ enum precedence
call,
};

const auto precedences = std::map<token_type, precedence> {
{token_type::equals, equals},
{token_type::not_equals, equals},
{token_type::less_than, lessgreater},
{token_type::greater_than, lessgreater},
{token_type::plus, sum},
{token_type::minus, sum},
{token_type::slash, product},
{token_type::asterisk, product},
};
auto precedence_of_token(token_type type) -> int
{
switch (type) {
case token_type::equals:
case token_type::not_equals:
return equals;
case token_type::less_than:
case token_type::greater_than:
return lessgreater;
case token_type::plus:
case token_type::minus:
return sum;
case token_type::slash:
case token_type::asterisk:
return product;
default:
return lowest;
}
}

parser::parser(lexer lxr)
: m_lxr(lxr)
Expand All @@ -43,6 +51,9 @@ parser::parser(lexer lxr)
register_prefix(integer, [this] { return parse_integer_literal(); });
register_prefix(exclamation, [this] { return parse_prefix_expression(); });
register_prefix(minus, [this] { return parse_prefix_expression(); });
register_prefix(tru, [this] { return parse_boolean(); });
register_prefix(fals, [this] { return parse_boolean(); });
register_prefix(lparen, [this] { return parse_grouped_expression(); });
register_infix(plus,
[this](expression_ptr left)
{ return parse_infix_expression(std::move(left)); });
Expand Down Expand Up @@ -203,6 +214,20 @@ auto parser::parse_prefix_expression() -> expression_ptr
pfx_expr->right = parse_expression(prefix);
return pfx_expr;
}
auto parser::parse_boolean() -> expression_ptr
{
return std::make_unique<boolean>(m_current_token,
cur_token_is(token_type::tru));
}
auto parser::parse_grouped_expression() -> expression_ptr
{
next_token();
auto exp = parse_expression(lowest);
if (!expect_peek(token_type::rparen)) {
return {};
}
return exp;
}

auto parser::parse_infix_expression(expression_ptr left) -> expression_ptr
{
Expand Down Expand Up @@ -235,10 +260,12 @@ auto parser::peek_error(token_type type) -> void
<< m_peek_token.type << " instead";
m_errors.push_back(strm.str());
}

auto parser::register_infix(token_type type, infix_parser infix) -> void
{
m_infix_parsers[type] = std::move(infix);
}

auto parser::register_prefix(token_type type, prefix_parser prefix) -> void
{
m_prefix_parsers[type] = std::move(prefix);
Expand All @@ -263,18 +290,10 @@ auto parser::no_prefix_expression_error(token_type type) -> void

auto parser::peek_precedence() const -> int
{
auto itr = precedences.find(m_peek_token.type);
if (itr != precedences.end()) {
return itr->second;
}
return lowest;
return precedence_of_token(m_peek_token.type);
}

auto parser::current_precedence() const -> int
{
auto itr = precedences.find(m_current_token.type);
if (itr != precedences.end()) {
return itr->second;
}
return lowest;
return precedence_of_token(m_current_token.type);
}
2 changes: 2 additions & 0 deletions source/parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ class parser final
auto parse_integer_literal() -> expression_ptr;
auto parse_prefix_expression() -> expression_ptr;
auto parse_infix_expression(expression_ptr left) -> expression_ptr;
auto parse_boolean() -> expression_ptr;
auto parse_grouped_expression() -> expression_ptr;

auto expect_peek(token_type type) -> bool;
auto cur_token_is(token_type type) const -> bool;
Expand Down
67 changes: 66 additions & 1 deletion test/source/lexereagen_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,14 @@ TEST(test, chapter2dot6TestPrefixExpressions)
std::string_view input;
std::string op;
int64_t integer_value;
} prefix_tests[] = {{"!5;", "!", 5}, {"-15;", "-", 15}};
} prefix_tests[] = {
// clang-format: off
// NOLINTBEGIN(*-magic-*)
{"!5;", "!", 5},
{"-15;", "-", 15}
// NOLINTEND(*-magic-*)
// clang-format: on
};

for (const auto& prefix_test : prefix_tests) {
auto prsr = parser {lexer {prefix_test.input}};
Expand Down Expand Up @@ -277,6 +284,7 @@ TEST(test, chapter2dot6TestInfixExpressions)
std::string op;
int64_t right_value;
} infix_tests[] = {
// NOLINTBEGIN(*-magic-*)
{"5 + 5;", 5, "+", 5},
{"5 - 5;", 5, "-", 5},
{"5 * 5;", 5, "*", 5},
Expand All @@ -285,6 +293,7 @@ TEST(test, chapter2dot6TestInfixExpressions)
{"5 < 5;", 5, "<", 5},
{"5 == 5;", 5, "==", 5},
{"5 != 5;", 5, "!=", 5},
// NOLINTEND(*-magic-*)
};

for (const auto& infix_test : infix_tests) {
Expand All @@ -311,3 +320,59 @@ TEST(test, chapter2dot6TestInfixExpressions)
ASSERT_EQ(infix_test.right_value, rght_ntgr_xpr->value);
}
}

TEST(test, chapter2dot8TestOperatorPrecedence)
{
struct oper_test
{
std::string_view input;
std::string expected;
} tests[] = {
{
"true",
"true",
},
{
"false",
"false",
},
{
"3 > 5 == false",
"((3 > 5) == false)",
},
{
"3 < 5 == true",
"((3 < 5) == true)",
},
{
"1 + (2 + 3) + 4",
"((1 + (2 + 3)) + 4)",
},
{

"(5 + 5) * 2",
"((5 + 5) * 2)",
},
{
"2 / (5 + 5)",
"(2 / (5 + 5))",
},
{
"-(5 + 5)",
"(-(5 + 5))",
},
{
"!(true == true)",
"(!(true == true))",
},
};
for (const auto& t : tests) {
auto prsr = parser {lexer {t.input}};

auto prgrm = prsr.parse_program();
ASSERT_TRUE(prsr.errors().empty()) << prsr.errors().at(0);
ASSERT_EQ(prgrm->statements.size(), 1);
auto* stmt = prgrm->statements[0].get();
ASSERT_EQ(t.expected, stmt->string());
}
}

0 comments on commit c4feb24

Please sign in to comment.