Overloaded operators

This commit is contained in:
Robert Bendun 2022-05-22 04:58:45 +02:00
parent 5b3cfca2a8
commit de1ed62f6b
2 changed files with 81 additions and 20 deletions

View File

@ -2,7 +2,7 @@
#include <iostream> #include <iostream>
static auto binary_operator(auto binop) constexpr auto binary_operator(auto binop)
{ {
return [binop = std::move(binop)](Interpreter&, std::vector<Value> args) -> Result<Value> { return [binop = std::move(binop)](Interpreter&, std::vector<Value> args) -> Result<Value> {
auto result = std::move(args.front()); 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<Value> args) -> Result<Value> {
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<Value> args) -> Result<Value> {
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::Interpreter()
: Interpreter(std::cout) : Interpreter(std::cout)
{ {
@ -69,12 +97,13 @@ Interpreter::Interpreter(std::ostream& out)
operators["*"] = binary_operator(std::multiplies<>{}); operators["*"] = binary_operator(std::multiplies<>{});
operators["/"] = binary_operator(std::divides<>{}); operators["/"] = binary_operator(std::divides<>{});
operators["<"] = binary_operator(std::less<>{}); operators["<"] = comparison_operator(std::less<>{});
operators[">"] = binary_operator(std::greater<>{}); operators[">"] = comparison_operator(std::greater<>{});
operators["<="] = binary_operator(std::less_equal<>{}); operators["<="] = comparison_operator(std::less_equal<>{});
operators[">="] = binary_operator(std::greater_equal<>{}); operators[">="] = comparison_operator(std::greater_equal<>{});
operators["=="] = binary_operator(std::equal_to<>{});
operators["!="] = binary_operator(std::not_equal_to<>{}); operators["=="] = equality_operator(std::equal_to<>{});
operators["!="] = equality_operator(std::not_equal_to<>{});
} }
Result<Value> Interpreter::eval(Ast &&ast) Result<Value> Interpreter::eval(Ast &&ast)
@ -161,6 +190,7 @@ Result<Value> Interpreter::eval(Ast &&ast)
unimplemented(); unimplemented();
} }
} }
void Interpreter::enter_scope() void Interpreter::enter_scope()
{ {
env = env->enter(); env = env->enter();

View File

@ -19,7 +19,7 @@ void evaluates_to(Value value, std::string_view source_code, reflection::source_
capture_errors([=]() -> Result<void> { capture_errors([=]() -> Result<void> {
Interpreter interpreter; Interpreter interpreter;
auto result = Try(interpreter.eval(Try(Parser::parse(source_code, "test")))); auto result = Try(interpreter.eval(Try(Parser::parse(source_code, "test"))));
expect(eq(result, value)); expect(eq(result, value), sl);
return {}; return {};
}, sl)(); }, sl)();
} }
@ -38,9 +38,10 @@ void produces_output(std::string_view source_code, std::string_view expected_out
suite intepreter_test = [] { suite intepreter_test = [] {
"Interpreter"_test = [] { "Interpreter"_test = [] {
should("evaluate literals") = [] { 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::number(Number(10)), "10");
// evaluates_to(Value::symbol("notexistingsymbol"), "notexistingsymbol"); evaluates_to(Value{}, "nil");
}; };
should("evaluate arithmetic") = [] { should("evaluate arithmetic") = [] {
@ -60,20 +61,50 @@ suite intepreter_test = [] {
}; };
should("allows only for calling which is callable") = [] { 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)); }); Interpreter i;
expect(!result.has_value()) << "Expected code to have failed"; {
expect(eq(result.error().type, errors::Not_Callable)); 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"; i.env->force_define("call_me", Value::number(Number(10)));
expect(eq(result.error().type, errors::Not_Callable)); 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. // Added to explicitly test against bug that was in old implementation of enviroments.
// Previously this test would segfault // Previously this test would segfault
should("allow assigning result of function calls to a variable") = [] { should("allow assigning result of function calls to a variable") = [] {