Better operator resolution mechanism
This commit is contained in:
parent
ccd2166231
commit
0b9e7e8f4a
@ -71,6 +71,7 @@ std::ostream& operator<<(std::ostream& os, Error const& err)
|
||||
|
||||
case errors::Function_Not_Defined:
|
||||
case errors::Unexpected_Token_Type:
|
||||
case errors::Unresolved_Operator:
|
||||
return os << err.message << '\n';
|
||||
|
||||
case errors::Unexpected_Empty_Source:
|
||||
@ -153,6 +154,15 @@ Error errors::function_not_defined(Value const& value)
|
||||
return err;
|
||||
}
|
||||
|
||||
Error errors::unresolved_operator(Token const& op)
|
||||
{
|
||||
Error err;
|
||||
err.type = errors::Unresolved_Operator;
|
||||
err.location = op.location;
|
||||
err.message = format("Unresolved operator '", op.source, "'");
|
||||
return err;
|
||||
}
|
||||
|
||||
void errors::all_tokens_were_not_parsed(std::span<Token> tokens)
|
||||
{
|
||||
error_heading(std::cerr, std::nullopt, Error_Level::Bug);
|
||||
|
@ -2,6 +2,19 @@
|
||||
|
||||
#include <iostream>
|
||||
|
||||
static auto numeric_binary_operator(auto binop)
|
||||
{
|
||||
return [binop = std::move(binop)](std::vector<Value> args) {
|
||||
auto result = std::move(args.front());
|
||||
for (auto &v : std::span(args).subspan(1)) {
|
||||
assert(result.type == Value::Type::Number, "LHS should be a number");
|
||||
assert(v.type == Value::Type::Number, "RHS should be a number");
|
||||
result.n = binop(std::move(result.n), v.n);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
Interpreter::Interpreter()
|
||||
: Interpreter(std::cout)
|
||||
{
|
||||
@ -20,7 +33,7 @@ Interpreter::Interpreter(std::ostream& out)
|
||||
unreachable();
|
||||
};
|
||||
|
||||
functions["say"] = [&](std::vector<Value> args) -> Value {
|
||||
functions["say"] = [&out](std::vector<Value> args) -> Value {
|
||||
for (auto it = args.begin(); it != args.end(); ++it) {
|
||||
out << *it;
|
||||
if (std::next(it) != args.end())
|
||||
@ -29,6 +42,11 @@ Interpreter::Interpreter(std::ostream& out)
|
||||
out << '\n';
|
||||
return {};
|
||||
};
|
||||
|
||||
operators["+"] = numeric_binary_operator(std::plus<>{});
|
||||
operators["-"] = numeric_binary_operator(std::minus<>{});
|
||||
operators["*"] = numeric_binary_operator(std::multiplies<>{});
|
||||
operators["/"] = numeric_binary_operator(std::divides<>{});
|
||||
}
|
||||
|
||||
Result<Value> Interpreter::eval(Ast &&ast)
|
||||
@ -41,28 +59,18 @@ Result<Value> Interpreter::eval(Ast &&ast)
|
||||
{
|
||||
std::vector<Value> values;
|
||||
values.reserve(ast.arguments.size());
|
||||
|
||||
auto op = operators.find(std::string(ast.token.source));
|
||||
|
||||
if (op == operators.end()) {
|
||||
return errors::unresolved_operator(ast.token);
|
||||
}
|
||||
|
||||
for (auto& a : ast.arguments) {
|
||||
values.push_back(Try(eval(std::move(a))));
|
||||
}
|
||||
|
||||
Value result = values.front();
|
||||
if (ast.token.source == "+") {
|
||||
for (auto &v : std::span(values).subspan(1)) {
|
||||
assert(result.type == Value::Type::Number, "LHS of + should be a number");
|
||||
assert(v.type == Value::Type::Number, "RHS of + should be a number");
|
||||
result.n += v.n;
|
||||
}
|
||||
return result;
|
||||
} else if (ast.token.source == "*") {
|
||||
for (auto &v : std::span(values).subspan(1)) {
|
||||
assert(result.type == Value::Type::Number, "LHS of * should be a number");
|
||||
assert(v.type == Value::Type::Number, "RHS of * should be a number");
|
||||
result.n *= v.n;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
unimplemented();
|
||||
return op->second(std::move(values));
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -45,7 +45,8 @@ namespace errors
|
||||
Unexpected_Empty_Source,
|
||||
Failed_Numeric_Parsing,
|
||||
|
||||
Function_Not_Defined
|
||||
Function_Not_Defined,
|
||||
Unresolved_Operator
|
||||
};
|
||||
}
|
||||
|
||||
@ -416,6 +417,7 @@ struct Interpreter
|
||||
{
|
||||
std::ostream &out;
|
||||
std::unordered_map<std::string, Function> functions;
|
||||
std::unordered_map<std::string, Function> operators;
|
||||
|
||||
Interpreter();
|
||||
Interpreter(std::ostream& out);
|
||||
@ -437,6 +439,7 @@ namespace errors
|
||||
Error failed_numeric_parsing(Location location, std::errc errc, std::string_view source);
|
||||
|
||||
Error function_not_defined(Value const& v);
|
||||
Error unresolved_operator(Token const& op);
|
||||
|
||||
[[noreturn]]
|
||||
void all_tokens_were_not_parsed(std::span<Token>);
|
||||
|
@ -46,6 +46,8 @@ suite intepreter_test = [] {
|
||||
should("evaluate arithmetic") = [] {
|
||||
evaluates_to(Value::number(Number(10)), "5 + 3 + 2");
|
||||
evaluates_to(Value::number(Number(25)), "5 * (3 + 2)");
|
||||
evaluates_to(Value::number(Number(1, 2)), "1 / 2");
|
||||
evaluates_to(Value::number(Number(-10)), "10 - 20");
|
||||
};
|
||||
|
||||
should("call builtin functions") = [] {
|
||||
|
Loading…
Reference in New Issue
Block a user