From 16d1a5e0d631c8c4f04b131d1942486368d144cb Mon Sep 17 00:00:00 2001 From: Robert Bendun Date: Tue, 14 Jun 2022 13:24:15 +0200 Subject: [PATCH] Introduced first iteration of typechecking errors --- src/errors.cc | 37 +++++++++++++++++++++++++++- src/interpreter.cc | 61 ++++++++++++++++++++++++++++++++++++++-------- src/musique.hh | 18 ++++++++++++-- 3 files changed, 103 insertions(+), 13 deletions(-) diff --git a/src/errors.cc b/src/errors.cc index 33b2ff6..8303c8d 100644 --- a/src/errors.cc +++ b/src/errors.cc @@ -120,7 +120,12 @@ std::ostream& operator<<(std::ostream& os, Error const& err) [](errors::Unrecognized_Character const&) { return "Unrecognized character"; }, [](errors::internal::Unexpected_Token const&) { return "Unexpected token"; }, [](errors::Expected_Expression_Separator_Before const&) { return "Missing semicolon"; }, - [](errors::Literal_As_Identifier const&) { return "Literal used in place of an identifier"; } + [](errors::Literal_As_Identifier const&) { return "Literal used in place of an identifier"; }, + [](errors::Unsupported_Types_For const& type) { + return type.type == errors::Unsupported_Types_For::Function + ? "Function called with wrong arguments" + : "Operator does not support given values"; + }, }, err.details); error_heading(os, err.location, Error_Level::Error, short_description); @@ -188,6 +193,36 @@ std::ostream& operator<<(std::ostream& os, Error const& err) } }, + [&](errors::Unsupported_Types_For const& err) { + switch (err.type) { + case errors::Unsupported_Types_For::Function: + { + os << "I tried to call function '" << err.name << "' but you gave me wrong types for it!\n"; + os << "Make sure that all values matches one of supported signatures listed below!\n"; + os << '\n'; + + for (auto const& possibility : err.possibilities) { + os << " " << possibility << '\n'; + } + } + break; + case errors::Unsupported_Types_For::Operator: + { + os << "I tried and failed to evaluate operator '" << err.name << "' due to values with wrong types provided\n"; + os << "Make sure that both values matches one of supported signatures listed below!\n"; + os << '\n'; + + if (err.name == "+") { os << "Addition only supports:\n"; } + else { os << "Operator '" << err.name << "' only supports:\n"; } + + for (auto const& possibility : err.possibilities) { + os << " " << possibility << '\n'; + } + } + break; + } + }, + [&](errors::Not_Callable const&) { unimplemented(); }, [&](errors::Undefined_Operator const&) { unimplemented(); }, [&](errors::Unexpected_Keyword const&) { unimplemented(); }, diff --git a/src/interpreter.cc b/src/interpreter.cc index a8873cf..233894c 100644 --- a/src/interpreter.cc +++ b/src/interpreter.cc @@ -119,6 +119,9 @@ Result vectorize(auto &&operation, Interpreter &interpreter, std::vector< template static Result plus_minus_operator(Interpreter &interpreter, std::vector args) { + static_assert(std::is_same_v> || std::is_same_v>, + "Error reporting depends on only one of this two types beeing provided"); + using NN = Shape; using MN = Shape; using NM = Shape; @@ -150,14 +153,29 @@ static Result plus_minus_operator(Interpreter &interpreter, std::vector, interpreter, std::move(args)); } - assert(false, "Unsupported types for this operation"); // TODO(assert) - unreachable(); + // TODO Limit possibilities based on provided types + return Error { + .details = errors::Unsupported_Types_For { + .type = errors::Unsupported_Types_For::Operator, + .name = std::is_same_v, Binary_Operation> ? "+" : "-", + .possibilities = { + "(number, number) -> number", + "(music, number) -> music", + "(number, music) -> music", + "(array, number|music) -> array", + "(number|music, array) -> array", + } + }, + .location = {}, // TODO fill location + }; } -template +template static Result binary_operator(Interpreter& interpreter, std::vector args) { + static constexpr char Name[] = { Chars..., '\0' }; + using NN = Shape; if (NN::typecheck(args)) { @@ -166,10 +184,21 @@ static Result binary_operator(Interpreter& interpreter, std::vector, interpreter, args); + return vectorize(binary_operator, interpreter, args); } - unreachable(); + return Error { + .details = errors::Unsupported_Types_For { + .type = errors::Unsupported_Types_For::Operator, + .name = Name, + .possibilities = { + "(number, number) -> number", + "(array, number) -> array", + "(number, array) -> array" + } + }, + .location = {}, // TODO fill location + }; } template @@ -343,10 +372,24 @@ Interpreter::Interpreter() }); global.force_define("if", +[](Interpreter &i, std::vector args) -> Result { - assert(args.size() == 2 || args.size() == 3, "argument count does not add up - expected: if []"); + if (args.size() != 2 && args.size() != 3) { +error: + return Error { + .details = errors::Unsupported_Types_For { + .type = errors::Unsupported_Types_For::Function, + .name = "if", + .possibilities = { + "(any, function) -> any", + "(any, function, function) -> any" + } + } + }; + } if (args.front().truthy()) { + if (not is_callable(args[1].type)) goto error; return args[1](i, {}); } else if (args.size() == 3) { + if (not is_callable(args[2].type)) goto error; return args[2](i, {}); } else { return Value{}; @@ -357,8 +400,6 @@ Interpreter::Interpreter() if (args.size() != 1 || !is_indexable(args.front().type)) { return ctx_read_write_property<&Context::length>(i, std::move(args)); } - assert(args.size() == 1, "len only accepts one argument"); - assert(args.front().type == Value::Type::Block || args.front().type == Value::Type::Array, "Only blocks and arrays can have length"); return Value::from(Number(args.front().size())); }); @@ -530,8 +571,8 @@ Interpreter::Interpreter() operators["+"] = plus_minus_operator>; operators["-"] = plus_minus_operator>; - operators["*"] = binary_operator>; - operators["/"] = binary_operator>; + operators["*"] = binary_operator, '*'>; + operators["/"] = binary_operator, '/'>; operators["<"] = comparison_operator>; operators[">"] = comparison_operator>; diff --git a/src/musique.hh b/src/musique.hh index 9ebc20c..ff63ff2 100644 --- a/src/musique.hh +++ b/src/musique.hh @@ -83,7 +83,7 @@ namespace errors std::string_view type; }; - // When user provides literal where identifier should be + /// When user provides literal where identifier should be struct Literal_As_Identifier { std::string_view type_name; @@ -91,6 +91,19 @@ namespace errors std::string_view context; }; + /// When user provides wrong type for given operation + struct Unsupported_Types_For + { + /// Type of operation + enum { Operator, Function } type; + + /// Name of operation + std::string_view name; + + /// Possible ways to use it correctly + std::vector possibilities; + }; + /// Collection of messages that are considered internal and should not be printed to the end user. namespace internal { @@ -112,12 +125,13 @@ namespace errors using Details = std::variant< Expected_Expression_Separator_Before, Failed_Numeric_Parsing, + Literal_As_Identifier, Not_Callable, Undefined_Operator, Unexpected_Empty_Source, Unexpected_Keyword, Unrecognized_Character, - Literal_As_Identifier, + Unsupported_Types_For, internal::Unexpected_Token >; }