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::Function_Not_Defined:
|
||||||
case errors::Unexpected_Token_Type:
|
case errors::Unexpected_Token_Type:
|
||||||
|
case errors::Unresolved_Operator:
|
||||||
return os << err.message << '\n';
|
return os << err.message << '\n';
|
||||||
|
|
||||||
case errors::Unexpected_Empty_Source:
|
case errors::Unexpected_Empty_Source:
|
||||||
@ -153,6 +154,15 @@ Error errors::function_not_defined(Value const& value)
|
|||||||
return err;
|
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)
|
void errors::all_tokens_were_not_parsed(std::span<Token> tokens)
|
||||||
{
|
{
|
||||||
error_heading(std::cerr, std::nullopt, Error_Level::Bug);
|
error_heading(std::cerr, std::nullopt, Error_Level::Bug);
|
||||||
|
@ -2,6 +2,19 @@
|
|||||||
|
|
||||||
#include <iostream>
|
#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::Interpreter()
|
||||||
: Interpreter(std::cout)
|
: Interpreter(std::cout)
|
||||||
{
|
{
|
||||||
@ -20,7 +33,7 @@ Interpreter::Interpreter(std::ostream& out)
|
|||||||
unreachable();
|
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) {
|
for (auto it = args.begin(); it != args.end(); ++it) {
|
||||||
out << *it;
|
out << *it;
|
||||||
if (std::next(it) != args.end())
|
if (std::next(it) != args.end())
|
||||||
@ -29,6 +42,11 @@ Interpreter::Interpreter(std::ostream& out)
|
|||||||
out << '\n';
|
out << '\n';
|
||||||
return {};
|
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)
|
Result<Value> Interpreter::eval(Ast &&ast)
|
||||||
@ -41,28 +59,18 @@ Result<Value> Interpreter::eval(Ast &&ast)
|
|||||||
{
|
{
|
||||||
std::vector<Value> values;
|
std::vector<Value> values;
|
||||||
values.reserve(ast.arguments.size());
|
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) {
|
for (auto& a : ast.arguments) {
|
||||||
values.push_back(Try(eval(std::move(a))));
|
values.push_back(Try(eval(std::move(a))));
|
||||||
}
|
}
|
||||||
|
|
||||||
Value result = values.front();
|
return op->second(std::move(values));
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -45,7 +45,8 @@ namespace errors
|
|||||||
Unexpected_Empty_Source,
|
Unexpected_Empty_Source,
|
||||||
Failed_Numeric_Parsing,
|
Failed_Numeric_Parsing,
|
||||||
|
|
||||||
Function_Not_Defined
|
Function_Not_Defined,
|
||||||
|
Unresolved_Operator
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -416,6 +417,7 @@ struct Interpreter
|
|||||||
{
|
{
|
||||||
std::ostream &out;
|
std::ostream &out;
|
||||||
std::unordered_map<std::string, Function> functions;
|
std::unordered_map<std::string, Function> functions;
|
||||||
|
std::unordered_map<std::string, Function> operators;
|
||||||
|
|
||||||
Interpreter();
|
Interpreter();
|
||||||
Interpreter(std::ostream& out);
|
Interpreter(std::ostream& out);
|
||||||
@ -437,6 +439,7 @@ namespace errors
|
|||||||
Error failed_numeric_parsing(Location location, std::errc errc, std::string_view source);
|
Error failed_numeric_parsing(Location location, std::errc errc, std::string_view source);
|
||||||
|
|
||||||
Error function_not_defined(Value const& v);
|
Error function_not_defined(Value const& v);
|
||||||
|
Error unresolved_operator(Token const& op);
|
||||||
|
|
||||||
[[noreturn]]
|
[[noreturn]]
|
||||||
void all_tokens_were_not_parsed(std::span<Token>);
|
void all_tokens_were_not_parsed(std::span<Token>);
|
||||||
|
@ -46,6 +46,8 @@ suite intepreter_test = [] {
|
|||||||
should("evaluate arithmetic") = [] {
|
should("evaluate arithmetic") = [] {
|
||||||
evaluates_to(Value::number(Number(10)), "5 + 3 + 2");
|
evaluates_to(Value::number(Number(10)), "5 + 3 + 2");
|
||||||
evaluates_to(Value::number(Number(25)), "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") = [] {
|
should("call builtin functions") = [] {
|
||||||
|
Loading…
Reference in New Issue
Block a user