Overloaded operators
This commit is contained in:
parent
5b3cfca2a8
commit
de1ed62f6b
@ -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();
|
||||
|
@ -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") = [] {
|
||||
|
Loading…
Reference in New Issue
Block a user