Skip to content

Commit

Permalink
feat: add support for binary operators <= and >= (#182)
Browse files Browse the repository at this point in the history
  • Loading branch information
hrzlgnm authored Dec 29, 2024
1 parent b8962b2 commit ec40742
Show file tree
Hide file tree
Showing 11 changed files with 222 additions and 83 deletions.
30 changes: 16 additions & 14 deletions source/code/code.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ auto operator<<(std::ostream& ostream, opcodes opcode) -> std::ostream&
return ostream << "mul";
case div:
return ostream << "div";
case opcodes::floor_div:
case floor_div:
return ostream << "floor_div";
case pop:
return ostream << "pop";
Expand Down Expand Up @@ -79,32 +79,34 @@ auto operator<<(std::ostream& ostream, opcodes opcode) -> std::ostream&
return ostream << "get_free";
case current_closure:
return ostream << "current_closure";
case opcodes::mod:
case mod:
return ostream << "mod";
case opcodes::bit_and:
case bit_and:
return ostream << "bit_and";
case opcodes::bit_or:
case bit_or:
return ostream << "bit_or";
case opcodes::bit_xor:
case bit_xor:
return ostream << "bit_xor";
case opcodes::bit_lsh:
case bit_lsh:
return ostream << "bit_lsh";
case opcodes::bit_rsh:
case bit_rsh:
return ostream << "bit_rsh";
case opcodes::logical_and:
case logical_and:
return ostream << "logical_and";
case opcodes::logical_or:
case logical_or:
return ostream << "logical_or";
case opcodes::set_free:
case set_free:
return ostream << "set_free";
case opcodes::get_outer:
case get_outer:
return ostream << "get_outer";
case opcodes::set_outer:
case set_outer:
return ostream << "set_outer";
case opcodes::brake:
case brake:
return ostream << "break";
case opcodes::cont:
case cont:
return ostream << "continue";
case greater_equal:
return ostream << "greater_equal";
}
throw std::runtime_error(
fmt::format("operator <<(std::ostream&) for {} is not implemented yet", static_cast<uint8_t>(opcode)));
Expand Down
2 changes: 2 additions & 0 deletions source/code/code.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ enum class opcodes : uint8_t
equal,
not_equal,
greater_than,
greater_equal,
minus,
bang,
jump_not_truthy,
Expand Down Expand Up @@ -97,6 +98,7 @@ const definition_type definitions {
{opcodes::equal, definition {.name = "OpEqual"}},
{opcodes::not_equal, definition {.name = "OpNotEquql"}},
{opcodes::greater_than, definition {.name = "OpGreaterThan"}},
{opcodes::greater_equal, definition {.name = "OpGreaterEuqal"}},
{opcodes::minus, definition {.name = "OpMinus"}},
{opcodes::bang, definition {.name = "OpBang"}},
{opcodes::jump_not_truthy, definition {.name = "OpJumpNotTruthy", .operand_widths = {2}}},
Expand Down
29 changes: 29 additions & 0 deletions source/compiler/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,12 @@ void compiler::visit(const binary_expression& expr)
emit(opcodes::greater_than);
return;
}
if (expr.op == token_type::less_equal) {
expr.right->accept(*this);
expr.left->accept(*this);
emit(opcodes::greater_equal);
return;
}
expr.left->accept(*this);
expr.right->accept(*this);
switch (expr.op) {
Expand Down Expand Up @@ -301,6 +307,9 @@ void compiler::visit(const binary_expression& expr)
case token_type::greater_than:
emit(opcodes::greater_than);
break;
case token_type::greater_equal:
emit(opcodes::greater_equal);
break;
case token_type::equals:
emit(opcodes::equal);
break;
Expand Down Expand Up @@ -815,6 +824,16 @@ TEST_CASE("booleanExpressions")
make(pop),
},
},
ctc {
"1 >= 2",
{{1}, {2}},
{
make(constant, 0),
make(constant, 1),
make(greater_equal),
make(pop),
},
},
ctc {
"1 < 2",
{{2}, {1}},
Expand All @@ -825,6 +844,16 @@ TEST_CASE("booleanExpressions")
make(pop),
},
},
ctc {
"1 <= 2",
{{2}, {1}},
{
make(constant, 0),
make(constant, 1),
make(greater_equal),
make(pop),
},
},
ctc {
"1 == 2",
{{1}, {2}},
Expand Down
6 changes: 6 additions & 0 deletions source/eval/evaluator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,12 @@ auto apply_binary_operator(token_type oper, const object* left, const object* ri
return *left / *right;
case less_than:
return *right > *left;
case less_equal:
return *right >= *left;
case greater_than:
return *left > *right;
case greater_equal:
return *left >= *right;
case equals:
return *left == *right;
case not_equals:
Expand Down Expand Up @@ -711,6 +715,8 @@ TEST_CASE("booleanExpression")
et {"false", false},
et {"1 < 2", true},
et {"1 > 2", false},
et {"1 <= 2", true},
et {"1 >= 2", false},
et {"1 < 1", false},
et {"1 > 1", false},
et {"1.1 > 1.1", false},
Expand Down
117 changes: 64 additions & 53 deletions source/lexer/lexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,16 @@ auto build_two_token_lookup() -> two_token_lookup
.type = double_slash,
.literal = "//",
}});
lookup.insert({{greater_than, assign},
{
.type = greater_equal,
.literal = ">=",
}});
lookup.insert({{less_than, assign},
{
.type = less_equal,
.literal = "<=",
}});
return lookup;
}

Expand Down Expand Up @@ -298,61 +308,62 @@ return false;
[1,2];
{"foo": "bar"};
5.5 // %
& | ^ << >> && || a_b while break continue null
& | ^ << >> && || a_b while break continue null <= >=
)"};
const std::array expected_tokens {
token {.type = let, .literal = "let"}, token {.type = ident, .literal = "five"},
token {.type = assign, .literal = "="}, token {.type = integer, .literal = "5"},
token {.type = semicolon, .literal = ";"}, token {.type = let, .literal = "let"},
token {.type = ident, .literal = "ten"}, token {.type = assign, .literal = "="},
token {.type = integer, .literal = "10"}, token {.type = semicolon, .literal = ";"},
token {.type = let, .literal = "let"}, token {.type = ident, .literal = "add"},
token {.type = assign, .literal = "="}, token {.type = function, .literal = "fn"},
token {.type = lparen, .literal = "("}, token {.type = ident, .literal = "x"},
token {.type = comma, .literal = ","}, token {.type = ident, .literal = "y"},
token {.type = rparen, .literal = ")"}, token {.type = lsquirly, .literal = "{"},
token {.type = ident, .literal = "x"}, token {.type = plus, .literal = "+"},
token {.type = ident, .literal = "y"}, token {.type = semicolon, .literal = ";"},
token {.type = rsquirly, .literal = "}"}, token {.type = semicolon, .literal = ";"},
token {.type = let, .literal = "let"}, token {.type = ident, .literal = "result"},
token {.type = assign, .literal = "="}, token {.type = ident, .literal = "add"},
token {.type = lparen, .literal = "("}, token {.type = ident, .literal = "five"},
token {.type = comma, .literal = ","}, token {.type = ident, .literal = "ten"},
token {.type = rparen, .literal = ")"}, token {.type = semicolon, .literal = ";"},
token {.type = exclamation, .literal = "!"}, token {.type = minus, .literal = "-"},
token {.type = slash, .literal = "/"}, token {.type = asterisk, .literal = "*"},
token {.type = integer, .literal = "5"}, token {.type = semicolon, .literal = ";"},
token {.type = integer, .literal = "5"}, token {.type = less_than, .literal = "<"},
token {.type = integer, .literal = "10"}, token {.type = greater_than, .literal = ">"},
token {.type = integer, .literal = "5"}, token {.type = semicolon, .literal = ";"},
token {.type = eef, .literal = "if"}, token {.type = lparen, .literal = "("},
token {.type = integer, .literal = "5"}, token {.type = less_than, .literal = "<"},
token {.type = integer, .literal = "10"}, token {.type = rparen, .literal = ")"},
token {.type = lsquirly, .literal = "{"}, token {.type = ret, .literal = "return"},
token {.type = tru, .literal = "true"}, token {.type = semicolon, .literal = ";"},
token {.type = rsquirly, .literal = "}"}, token {.type = elze, .literal = "else"},
token {.type = lsquirly, .literal = "{"}, token {.type = ret, .literal = "return"},
token {.type = fals, .literal = "false"}, token {.type = semicolon, .literal = ";"},
token {.type = rsquirly, .literal = "}"}, token {.type = integer, .literal = "10"},
token {.type = equals, .literal = "=="}, token {.type = integer, .literal = "10"},
token {.type = semicolon, .literal = ";"}, token {.type = integer, .literal = "10"},
token {.type = not_equals, .literal = "!="}, token {.type = integer, .literal = "9"},
token {.type = semicolon, .literal = ";"}, token {.type = string, .literal = "foobar"},
token {.type = string, .literal = "foo bar"}, token {.type = string, .literal = ""},
token {.type = lbracket, .literal = "["}, token {.type = integer, .literal = "1"},
token {.type = comma, .literal = ","}, token {.type = integer, .literal = "2"},
token {.type = rbracket, .literal = "]"}, token {.type = semicolon, .literal = ";"},
token {.type = lsquirly, .literal = "{"}, token {.type = string, .literal = "foo"},
token {.type = colon, .literal = ":"}, token {.type = string, .literal = "bar"},
token {.type = rsquirly, .literal = "}"}, token {.type = semicolon, .literal = ";"},
token {.type = decimal, .literal = "5.5"}, token {.type = double_slash, .literal = "//"},
token {.type = percent, .literal = "%"}, token {.type = ampersand, .literal = "&"},
token {.type = pipe, .literal = "|"}, token {.type = caret, .literal = "^"},
token {.type = shift_left, .literal = "<<"}, token {.type = shift_right, .literal = ">>"},
token {.type = logical_and, .literal = "&&"}, token {.type = logical_or, .literal = "||"},
token {.type = ident, .literal = "a_b"}, token {.type = hwile, .literal = "while"},
token {.type = brake, .literal = "break"}, token {.type = cont, .literal = "continue"},
token {.type = null, .literal = "null"}, token {.type = eof, .literal = ""},
token {.type = let, .literal = "let"}, token {.type = ident, .literal = "five"},
token {.type = assign, .literal = "="}, token {.type = integer, .literal = "5"},
token {.type = semicolon, .literal = ";"}, token {.type = let, .literal = "let"},
token {.type = ident, .literal = "ten"}, token {.type = assign, .literal = "="},
token {.type = integer, .literal = "10"}, token {.type = semicolon, .literal = ";"},
token {.type = let, .literal = "let"}, token {.type = ident, .literal = "add"},
token {.type = assign, .literal = "="}, token {.type = function, .literal = "fn"},
token {.type = lparen, .literal = "("}, token {.type = ident, .literal = "x"},
token {.type = comma, .literal = ","}, token {.type = ident, .literal = "y"},
token {.type = rparen, .literal = ")"}, token {.type = lsquirly, .literal = "{"},
token {.type = ident, .literal = "x"}, token {.type = plus, .literal = "+"},
token {.type = ident, .literal = "y"}, token {.type = semicolon, .literal = ";"},
token {.type = rsquirly, .literal = "}"}, token {.type = semicolon, .literal = ";"},
token {.type = let, .literal = "let"}, token {.type = ident, .literal = "result"},
token {.type = assign, .literal = "="}, token {.type = ident, .literal = "add"},
token {.type = lparen, .literal = "("}, token {.type = ident, .literal = "five"},
token {.type = comma, .literal = ","}, token {.type = ident, .literal = "ten"},
token {.type = rparen, .literal = ")"}, token {.type = semicolon, .literal = ";"},
token {.type = exclamation, .literal = "!"}, token {.type = minus, .literal = "-"},
token {.type = slash, .literal = "/"}, token {.type = asterisk, .literal = "*"},
token {.type = integer, .literal = "5"}, token {.type = semicolon, .literal = ";"},
token {.type = integer, .literal = "5"}, token {.type = less_than, .literal = "<"},
token {.type = integer, .literal = "10"}, token {.type = greater_than, .literal = ">"},
token {.type = integer, .literal = "5"}, token {.type = semicolon, .literal = ";"},
token {.type = eef, .literal = "if"}, token {.type = lparen, .literal = "("},
token {.type = integer, .literal = "5"}, token {.type = less_than, .literal = "<"},
token {.type = integer, .literal = "10"}, token {.type = rparen, .literal = ")"},
token {.type = lsquirly, .literal = "{"}, token {.type = ret, .literal = "return"},
token {.type = tru, .literal = "true"}, token {.type = semicolon, .literal = ";"},
token {.type = rsquirly, .literal = "}"}, token {.type = elze, .literal = "else"},
token {.type = lsquirly, .literal = "{"}, token {.type = ret, .literal = "return"},
token {.type = fals, .literal = "false"}, token {.type = semicolon, .literal = ";"},
token {.type = rsquirly, .literal = "}"}, token {.type = integer, .literal = "10"},
token {.type = equals, .literal = "=="}, token {.type = integer, .literal = "10"},
token {.type = semicolon, .literal = ";"}, token {.type = integer, .literal = "10"},
token {.type = not_equals, .literal = "!="}, token {.type = integer, .literal = "9"},
token {.type = semicolon, .literal = ";"}, token {.type = string, .literal = "foobar"},
token {.type = string, .literal = "foo bar"}, token {.type = string, .literal = ""},
token {.type = lbracket, .literal = "["}, token {.type = integer, .literal = "1"},
token {.type = comma, .literal = ","}, token {.type = integer, .literal = "2"},
token {.type = rbracket, .literal = "]"}, token {.type = semicolon, .literal = ";"},
token {.type = lsquirly, .literal = "{"}, token {.type = string, .literal = "foo"},
token {.type = colon, .literal = ":"}, token {.type = string, .literal = "bar"},
token {.type = rsquirly, .literal = "}"}, token {.type = semicolon, .literal = ";"},
token {.type = decimal, .literal = "5.5"}, token {.type = double_slash, .literal = "//"},
token {.type = percent, .literal = "%"}, token {.type = ampersand, .literal = "&"},
token {.type = pipe, .literal = "|"}, token {.type = caret, .literal = "^"},
token {.type = shift_left, .literal = "<<"}, token {.type = shift_right, .literal = ">>"},
token {.type = logical_and, .literal = "&&"}, token {.type = logical_or, .literal = "||"},
token {.type = ident, .literal = "a_b"}, token {.type = hwile, .literal = "while"},
token {.type = brake, .literal = "break"}, token {.type = cont, .literal = "continue"},
token {.type = null, .literal = "null"}, token {.type = less_equal, .literal = "<="},
token {.type = greater_equal, .literal = ">="}, token {.type = eof, .literal = ""},
};
for (const auto& expected_token : expected_tokens) {
auto token = lxr.next_token();
Expand Down
4 changes: 4 additions & 0 deletions source/lexer/token_type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ auto operator<<(std::ostream& ostream, token_type type) -> std::ostream&
return ostream << "continue";
case null:
return ostream << "null";
case greater_equal:
return ostream << ">=";
case less_equal:
return ostream << "<=";
}
throw std::invalid_argument("invalid token_type");
}
2 changes: 2 additions & 0 deletions source/lexer/token_type.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ enum class token_type : std::uint8_t
shift_left,
logical_and,
logical_or,
greater_equal,
less_equal,

// multi character tokens
ident,
Expand Down
Loading

0 comments on commit ec40742

Please sign in to comment.