diff --git a/src/errors.cc b/src/errors.cc index 0f90965..b0f7fbb 100644 --- a/src/errors.cc +++ b/src/errors.cc @@ -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 tokens) { error_heading(std::cerr, std::nullopt, Error_Level::Bug); diff --git a/src/interpreter.cc b/src/interpreter.cc index e61317a..93e6252 100644 --- a/src/interpreter.cc +++ b/src/interpreter.cc @@ -2,6 +2,19 @@ #include +static auto numeric_binary_operator(auto binop) +{ + return [binop = std::move(binop)](std::vector 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 args) -> Value { + functions["say"] = [&out](std::vector 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 Interpreter::eval(Ast &&ast) @@ -41,28 +59,18 @@ Result Interpreter::eval(Ast &&ast) { std::vector 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; diff --git a/src/musique.hh b/src/musique.hh index d457323..70f2cf0 100644 --- a/src/musique.hh +++ b/src/musique.hh @@ -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 functions; + std::unordered_map 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); diff --git a/src/tests/interpreter.cc b/src/tests/interpreter.cc index 9ac3c49..f4bbe25 100644 --- a/src/tests/interpreter.cc +++ b/src/tests/interpreter.cc @@ -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") = [] {