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>
static auto binary_operator(auto binop)
constexpr auto binary_operator(auto binop)
{
return [binop = std::move(binop)](Interpreter&, std::vector<Value> args) -> Result<Value> {
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(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<Value> Interpreter::eval(Ast &&ast)
@ -161,6 +190,7 @@ Result<Value> Interpreter::eval(Ast &&ast)
unimplemented();
}
}
void Interpreter::enter_scope()
{
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> {
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") = [] {