From de1ed62f6b6a2ce58459fa93775d2a36bc40bd2a Mon Sep 17 00:00:00 2001 From: Robert Bendun Date: Sun, 22 May 2022 04:58:45 +0200 Subject: [PATCH] Overloaded operators --- src/interpreter.cc | 44 ++++++++++++++++++++++++++----- src/tests/interpreter.cc | 57 +++++++++++++++++++++++++++++++--------- 2 files changed, 81 insertions(+), 20 deletions(-) diff --git a/src/interpreter.cc b/src/interpreter.cc index 4b72372..932d86b 100644 --- a/src/interpreter.cc +++ b/src/interpreter.cc @@ -2,7 +2,7 @@ #include -static auto binary_operator(auto binop) +constexpr auto binary_operator(auto binop) { return [binop = std::move(binop)](Interpreter&, std::vector args) -> Result { auto result = std::move(args.front()); @@ -20,6 +20,34 @@ static auto binary_operator(auto binop) }; } +constexpr auto equality_operator(auto binop) +{ + return [binop = std::move(binop)](Interpreter&, std::vector args) -> Result { + assert(args.size() == 2, "(in)Equality only allows for 2 operands"); // TODO(assert) + return Value::boolean(binop(std::move(args.front()), std::move(args.back()))); + }; +} + +constexpr auto comparison_operator(auto binop) +{ + return [binop = std::move(binop)](Interpreter&, std::vector args) -> Result { + assert(args.size() == 2, "(in)Equality only allows for 2 operands"); // TODO(assert) + assert(args.front().type == args.back().type, "Only values of the same type can be ordered"); // TODO(assert) + + switch (args.front().type) { + case Value::Type::Number: + return Value::boolean(binop(std::move(args.front()).n, std::move(args.back()).n)); + + case Value::Type::Bool: + return Value::boolean(binop(std::move(args.front()).b, std::move(args.back()).b)); + + default: + assert(false, "Cannot compare value of given types"); // TODO(assert) + } + unreachable(); + }; +} + Interpreter::Interpreter() : Interpreter(std::cout) { @@ -69,12 +97,13 @@ Interpreter::Interpreter(std::ostream& out) operators["*"] = binary_operator(std::multiplies<>{}); operators["/"] = binary_operator(std::divides<>{}); - operators["<"] = binary_operator(std::less<>{}); - operators[">"] = binary_operator(std::greater<>{}); - operators["<="] = binary_operator(std::less_equal<>{}); - operators[">="] = binary_operator(std::greater_equal<>{}); - operators["=="] = binary_operator(std::equal_to<>{}); - operators["!="] = binary_operator(std::not_equal_to<>{}); + operators["<"] = comparison_operator(std::less<>{}); + operators[">"] = comparison_operator(std::greater<>{}); + operators["<="] = comparison_operator(std::less_equal<>{}); + operators[">="] = comparison_operator(std::greater_equal<>{}); + + operators["=="] = equality_operator(std::equal_to<>{}); + operators["!="] = equality_operator(std::not_equal_to<>{}); } Result Interpreter::eval(Ast &&ast) @@ -161,6 +190,7 @@ Result Interpreter::eval(Ast &&ast) unimplemented(); } } + void Interpreter::enter_scope() { env = env->enter(); diff --git a/src/tests/interpreter.cc b/src/tests/interpreter.cc index 5bfc9ee..4232b0b 100644 --- a/src/tests/interpreter.cc +++ b/src/tests/interpreter.cc @@ -19,7 +19,7 @@ void evaluates_to(Value value, std::string_view source_code, reflection::source_ capture_errors([=]() -> Result { Interpreter interpreter; auto result = Try(interpreter.eval(Try(Parser::parse(source_code, "test")))); - expect(eq(result, value)); + expect(eq(result, value), sl); return {}; }, sl)(); } @@ -38,9 +38,10 @@ void produces_output(std::string_view source_code, std::string_view expected_out suite intepreter_test = [] { "Interpreter"_test = [] { should("evaluate literals") = [] { - evaluates_to(Value{}, "nil"); + evaluates_to(Value::boolean(false), "false"); + evaluates_to(Value::boolean(true), "true"); evaluates_to(Value::number(Number(10)), "10"); - // evaluates_to(Value::symbol("notexistingsymbol"), "notexistingsymbol"); + evaluates_to(Value{}, "nil"); }; should("evaluate arithmetic") = [] { @@ -60,20 +61,50 @@ suite intepreter_test = [] { }; should("allows only for calling which is callable") = [] { - Interpreter i; + evaluates_to(Value::number(Number(0)), "[i|i] 0"); { - auto result = Parser::parse("10 20", "test").and_then([&](Ast &&ast) { return i.eval(std::move(ast)); }); - expect(!result.has_value()) << "Expected code to have failed"; - expect(eq(result.error().type, errors::Not_Callable)); - } - { - i.env->force_define("call_me", Value::number(Number(10))); - auto result = Parser::parse("call_me 20", "test").and_then([&](Ast &&ast) { return i.eval(std::move(ast)); }); - expect(!result.has_value()) << "Expected code to have failed"; - expect(eq(result.error().type, errors::Not_Callable)); + Interpreter i; + { + auto result = Parser::parse("10 20", "test").and_then([&](Ast &&ast) { return i.eval(std::move(ast)); }); + expect(!result.has_value()) << "Expected code to have failed"; + expect(eq(result.error().type, errors::Not_Callable)); + } + { + i.env->force_define("call_me", Value::number(Number(10))); + auto result = Parser::parse("call_me 20", "test").and_then([&](Ast &&ast) { return i.eval(std::move(ast)); }); + expect(!result.has_value()) << "Expected code to have failed"; + expect(eq(result.error().type, errors::Not_Callable)); + } } }; + should("allow for value (in)equality comparisons") = [] { + evaluates_to(Value::boolean(true), "nil == nil"); + evaluates_to(Value::boolean(false), "nil != nil"); + + evaluates_to(Value::boolean(true), "true == true"); + evaluates_to(Value::boolean(false), "true != true"); + evaluates_to(Value::boolean(false), "true == false"); + evaluates_to(Value::boolean(true), "true != false"); + + evaluates_to(Value::boolean(true), "0 == 0"); + evaluates_to(Value::boolean(false), "0 != 0"); + evaluates_to(Value::boolean(true), "1 != 0"); + evaluates_to(Value::boolean(false), "1 == 0"); + }; + + should("allow for value ordering comparisons") = [] { + evaluates_to(Value::boolean(false), "true < true"); + evaluates_to(Value::boolean(true), "true <= true"); + evaluates_to(Value::boolean(true), "false < true"); + evaluates_to(Value::boolean(false), "false > true"); + + evaluates_to(Value::boolean(false), "0 < 0"); + evaluates_to(Value::boolean(true), "0 <= 0"); + evaluates_to(Value::boolean(true), "1 < 2"); + evaluates_to(Value::boolean(false), "1 > 2"); + }; + // Added to explicitly test against bug that was in old implementation of enviroments. // Previously this test would segfault should("allow assigning result of function calls to a variable") = [] {