diff --git a/source/code/code.cpp b/source/code/code.cpp index 1d3b280..bd50e31 100644 --- a/source/code/code.cpp +++ b/source/code/code.cpp @@ -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"; @@ -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(opcode))); diff --git a/source/code/code.hpp b/source/code/code.hpp index 0eb0762..16d5948 100644 --- a/source/code/code.hpp +++ b/source/code/code.hpp @@ -32,6 +32,7 @@ enum class opcodes : uint8_t equal, not_equal, greater_than, + greater_equal, minus, bang, jump_not_truthy, @@ -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}}}, diff --git a/source/compiler/compiler.cpp b/source/compiler/compiler.cpp index caa9ab6..05a49b7 100644 --- a/source/compiler/compiler.cpp +++ b/source/compiler/compiler.cpp @@ -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) { @@ -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; @@ -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}}, @@ -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}}, diff --git a/source/eval/evaluator.cpp b/source/eval/evaluator.cpp index 9d1d2ba..56704c1 100644 --- a/source/eval/evaluator.cpp +++ b/source/eval/evaluator.cpp @@ -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: @@ -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}, diff --git a/source/lexer/lexer.cpp b/source/lexer/lexer.cpp index c9b747a..981e785 100644 --- a/source/lexer/lexer.cpp +++ b/source/lexer/lexer.cpp @@ -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; } @@ -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(); diff --git a/source/lexer/token_type.cpp b/source/lexer/token_type.cpp index ea372c7..d4472bc 100644 --- a/source/lexer/token_type.cpp +++ b/source/lexer/token_type.cpp @@ -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"); } diff --git a/source/lexer/token_type.hpp b/source/lexer/token_type.hpp index 2eaee0e..74da9d7 100644 --- a/source/lexer/token_type.hpp +++ b/source/lexer/token_type.hpp @@ -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, diff --git a/source/object/object.cpp b/source/object/object.cpp index d32b473..3adf94a 100644 --- a/source/object/object.cpp +++ b/source/object/object.cpp @@ -72,6 +72,12 @@ auto value_gt_helper(const T& lhs, const T& rhs) -> const object* return native_bool_to_object(lhs > rhs); } +template +auto value_ge_helper(const T& lhs, const T& rhs) -> const object* +{ + return native_bool_to_object(lhs >= rhs); +} + template auto gt_helper(const T* t, const object& other) -> const object* { @@ -81,6 +87,15 @@ auto gt_helper(const T* t, const object& other) -> const object* return nullptr; } +template +auto ge_helper(const T* t, const object& other) -> const object* +{ + if (other.is(t->type())) { + return native_bool_to_object(t->value >= other.as()->value); + } + return nullptr; +} + template auto multiply_sequence_helper(const T* source, integer_object::value_type count) -> const object* { @@ -208,6 +223,11 @@ auto string_object::operator>(const object& other) const -> const object* return gt_helper(this, other); } +auto string_object::operator>=(const object& other) const -> const object* +{ + return ge_helper(this, other); +} + auto string_object::operator+(const object& other) const -> const object* { if (other.is(string)) { @@ -251,6 +271,17 @@ auto boolean_object::operator>(const object& other) const -> const object* return gt_helper(this, other); } +auto boolean_object::operator>=(const object& other) const -> const object* +{ + if (other.is(integer)) { + return value_ge_helper(value_to(), other.val()); + } + if (other.is(decimal)) { + return value_ge_helper(value_to(), other.val()); + } + return ge_helper(this, other); +} + auto boolean_object::operator+(const object& other) const -> const object* { if (other.is(integer)) { @@ -424,6 +455,17 @@ auto integer_object::operator>(const object& other) const -> const object* return gt_helper(this, other); } +auto integer_object::operator>=(const object& other) const -> const object* +{ + if (other.is(boolean)) { + return value_ge_helper(value_to(), other.as()->value_to()); + } + if (other.is(decimal)) { + return value_ge_helper(value_to(), other.val()); + } + return ge_helper(this, other); +} + auto integer_object::operator+(const object& other) const -> const object* { if (other.is(integer)) { @@ -671,6 +713,17 @@ auto decimal_object::operator>(const object& other) const -> const object* return gt_helper(this, other); } +auto decimal_object::operator>=(const object& other) const -> const object* +{ + if (other.is(boolean)) { + return value_ge_helper(value, other.as()->value_to()); + } + if (other.is(integer)) { + return value_ge_helper(value, other.as()->value_to()); + } + return ge_helper(this, other); +} + auto object_floor_div(const object* lhs, const object* rhs) -> const object* { const auto* div = (*lhs / *rhs); @@ -1253,6 +1306,23 @@ TEST_SUITE("object tests") REQUIRE_EQ(false_obj > null_obj, nullptr); } + TEST_CASE("operator >=") + { + REQUIRE_EQ(int_obj >= true_obj, tru()); + REQUIRE_EQ(true_obj >= true_obj, tru()); + REQUIRE_EQ(int_obj >= integer_object {122}, tru()); + REQUIRE_EQ(int_obj >= int_obj, tru()); + REQUIRE_EQ(decimal_object {d2} >= decimal_object {d2}, tru()); + REQUIRE_EQ(decimal_object {d2} >= true_obj, tru()); + REQUIRE_EQ(str_obj >= string_object {"st"}, tru()); + REQUIRE_EQ(str_obj >= str_obj, tru()); + REQUIRE_EQ(true_obj >= decimal_object {0.9}, tru()); + REQUIRE_EQ(true_obj >= false_obj, tru()); + REQUIRE_EQ(false_obj >= false_obj, tru()); + REQUIRE_EQ(false_obj >= null_obj, nullptr); + REQUIRE_EQ(false_obj >= str_obj, nullptr); + } + TEST_CASE("operator &&") { REQUIRE_EQ(true_obj && true_obj, tru()); diff --git a/source/object/object.hpp b/source/object/object.hpp index ba23063..f599e40 100644 --- a/source/object/object.hpp +++ b/source/object/object.hpp @@ -110,6 +110,8 @@ struct object [[nodiscard]] virtual auto operator>(const object& /*other*/) const -> const object* { return nullptr; } + [[nodiscard]] virtual auto operator>=(const object& /*other*/) const -> const object* { return nullptr; } + [[nodiscard]] virtual auto operator*(const object& /*other*/) const -> const object* { return nullptr; } [[nodiscard]] virtual auto operator+(const object& /*other*/) const -> const object* { return nullptr; } @@ -177,6 +179,7 @@ struct integer_object final [[nodiscard]] auto operator==(const object& other) const -> const object* final; [[nodiscard]] auto operator>(const object& other) const -> const object* final; + [[nodiscard]] auto operator>=(const object& other) const -> const object* final; [[nodiscard]] auto operator+(const object& other) const -> const object* final; [[nodiscard]] auto operator-(const object& other) const -> const object* final; [[nodiscard]] auto operator*(const object& other) const -> const object* final; @@ -217,6 +220,7 @@ struct decimal_object final : object [[nodiscard]] auto operator==(const object& other) const -> const object* final; [[nodiscard]] auto operator>(const object& other) const -> const object* final; + [[nodiscard]] auto operator>=(const object& other) const -> const object* final; [[nodiscard]] auto operator+(const object& other) const -> const object* final; [[nodiscard]] auto operator-(const object& other) const -> const object* final; [[nodiscard]] auto operator*(const object& other) const -> const object* final; @@ -259,6 +263,7 @@ struct boolean_object final [[nodiscard]] auto operator/(const object& other) const -> const object* final; [[nodiscard]] auto operator%(const object& other) const -> const object* final; [[nodiscard]] auto operator>(const object& other) const -> const object* final; + [[nodiscard]] auto operator>=(const object& other) const -> const object* final; [[nodiscard]] auto operator&(const object& other) const -> const object* final; [[nodiscard]] auto operator|(const object& other) const -> const object* final; [[nodiscard]] auto operator^(const object& other) const -> const object* final; @@ -292,6 +297,7 @@ struct string_object final [[nodiscard]] auto hash_key() const -> key_type final; [[nodiscard]] auto operator==(const object& other) const -> const object* final; [[nodiscard]] auto operator>(const object& other) const -> const object* final; + [[nodiscard]] auto operator>=(const object& other) const -> const object* final; [[nodiscard]] auto operator+(const object& other) const -> const object* final; [[nodiscard]] auto operator*(const object& other) const -> const object* final; diff --git a/source/parser/parser.cpp b/source/parser/parser.cpp index 531c69c..71c810d 100644 --- a/source/parser/parser.cpp +++ b/source/parser/parser.cpp @@ -59,6 +59,8 @@ auto precedence_of_token(token_type type) -> std::uint8_t switch (type) { case token_type::equals: case token_type::not_equals: + case token_type::less_equal: + case token_type::greater_equal: return equals; case token_type::less_than: case token_type::greater_than: @@ -133,6 +135,8 @@ parser::parser(lexer lxr) register_binary(shift_right, [this](expression* left) { return parse_binary_expression(left); }); register_binary(logical_and, [this](expression* left) { return parse_binary_expression(left); }); register_binary(logical_or, [this](expression* left) { return parse_binary_expression(left); }); + register_binary(greater_equal, [this](expression* left) { return parse_binary_expression(left); }); + register_binary(less_equal, [this](expression* left) { return parse_binary_expression(left); }); } auto parser::parse_program() -> program* @@ -945,22 +949,12 @@ TEST_CASE("binaryExpressions") }; std::array tests { - bt {"5 + 5;", 5, plus, 5}, - bt {"5 - 5;", 5, minus, 5}, - bt {"5 * 5;", 5, asterisk, 5}, - bt {"5 / 5;", 5, slash, 5}, - bt {"5 > 5;", 5, greater_than, 5}, - bt {"5 < 5;", 5, less_than, 5}, - bt {"5 == 5;", 5, equals, 5}, - bt {"5 != 5;", 5, not_equals, 5}, - bt {"5 // 5;", 5, double_slash, 5}, - bt {"5 % 5;", 5, percent, 5}, - bt {"5 & 5;", 5, ampersand, 5}, - bt {"5 | 5;", 5, pipe, 5}, - bt {"5 ^ 5;", 5, caret, 5}, - bt {"5 << 5;", 5, shift_left, 5}, - bt {"5 >> 5;", 5, shift_right, 5}, - bt {"5 && 5;", 5, logical_and, 5}, + bt {"5 + 5;", 5, plus, 5}, bt {"5 - 5;", 5, minus, 5}, bt {"5 * 5;", 5, asterisk, 5}, + bt {"5 / 5;", 5, slash, 5}, bt {"5 > 5;", 5, greater_than, 5}, bt {"5 < 5;", 5, less_than, 5}, + bt {"5 >= 5;", 5, greater_equal, 5}, bt {"5 <= 5;", 5, less_equal, 5}, bt {"5 == 5;", 5, equals, 5}, + bt {"5 != 5;", 5, not_equals, 5}, bt {"5 // 5;", 5, double_slash, 5}, bt {"5 % 5;", 5, percent, 5}, + bt {"5 & 5;", 5, ampersand, 5}, bt {"5 | 5;", 5, pipe, 5}, bt {"5 ^ 5;", 5, caret, 5}, + bt {"5 << 5;", 5, shift_left, 5}, bt {"5 >> 5;", 5, shift_right, 5}, bt {"5 && 5;", 5, logical_and, 5}, bt {"5 || 5;", 5, logical_or, 5}, }; @@ -1085,6 +1079,14 @@ TEST_CASE("operatorPrecedence") "5 < 4 != 3 > 4", "((5 < 4) != (3 > 4))", }, + op { + "5 < 4 <= 3 > 4", + "((5 < 4) <= (3 > 4))", + }, + op { + "5 < 4 >= 3 > 4", + "((5 < 4) >= (3 > 4))", + }, op { "3 + 4 * 5 == 3 * 1 + 4 * 5", "((3 + (4 * 5)) == ((3 * 1) + (4 * 5)))", diff --git a/source/vm/vm.cpp b/source/vm/vm.cpp index d23fc37..f47accf 100644 --- a/source/vm/vm.cpp +++ b/source/vm/vm.cpp @@ -81,6 +81,7 @@ auto vm::run() -> void case opcodes::equal: case opcodes::not_equal: case opcodes::greater_than: + case opcodes::greater_equal: exec_binary_op(op); break; case opcodes::pop: @@ -287,6 +288,8 @@ auto apply_binary_operator(opcodes opcode, const object* left, const object* rig return *left != *right; case greater_than: return *left > *right; + case greater_equal: + return *left >= *right; case floor_div: return object_floor_div(left, right); default: @@ -728,6 +731,8 @@ TEST_CASE("booleanExpressions") vt {R"("b" < "a")", false}, vt {R"(1 < 2)", true}, vt {R"(1 > 2)", false}, + vt {R"(1 <= 2)", true}, + vt {R"(1 >= 2)", false}, vt {R"(1 < 1)", false}, vt {R"(1 > 1)", false}, vt {R"(true & true)", true},