Introduced first iteration of typechecking errors

This commit is contained in:
Robert Bendun 2022-06-14 13:24:15 +02:00
parent 910792e383
commit 16d1a5e0d6
3 changed files with 103 additions and 13 deletions

View File

@ -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(); },

View File

@ -119,6 +119,9 @@ Result<Value> vectorize(auto &&operation, Interpreter &interpreter, std::vector<
template<typename Binary_Operation>
static Result<Value> plus_minus_operator(Interpreter &interpreter, std::vector<Value> args)
{
static_assert(std::is_same_v<Binary_Operation, std::plus<>> || std::is_same_v<Binary_Operation, std::minus<>>,
"Error reporting depends on only one of this two types beeing provided");
using NN = Shape<Value::Type::Number, Value::Type::Number>;
using MN = Shape<Value::Type::Music, Value::Type::Number>;
using NM = Shape<Value::Type::Number, Value::Type::Music>;
@ -150,14 +153,29 @@ static Result<Value> plus_minus_operator(Interpreter &interpreter, std::vector<V
return vectorize(plus_minus_operator<Binary_Operation>, 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<std::plus<>, 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<typename Binary_Operation>
template<typename Binary_Operation, char ...Chars>
static Result<Value> binary_operator(Interpreter& interpreter, std::vector<Value> args)
{
static constexpr char Name[] = { Chars..., '\0' };
using NN = Shape<Value::Type::Number, Value::Type::Number>;
if (NN::typecheck(args)) {
@ -166,10 +184,21 @@ static Result<Value> binary_operator(Interpreter& interpreter, std::vector<Value
}
if (may_be_vectorized(args)) {
return vectorize(binary_operator<Binary_Operation>, interpreter, args);
return vectorize(binary_operator<Binary_Operation, Chars...>, 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<typename Binary_Predicate>
@ -343,10 +372,24 @@ Interpreter::Interpreter()
});
global.force_define("if", +[](Interpreter &i, std::vector<Value> args) -> Result<Value> {
assert(args.size() == 2 || args.size() == 3, "argument count does not add up - expected: if <condition> <then> [<else>]");
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<std::plus<>>;
operators["-"] = plus_minus_operator<std::minus<>>;
operators["*"] = binary_operator<std::multiplies<>>;
operators["/"] = binary_operator<std::divides<>>;
operators["*"] = binary_operator<std::multiplies<>, '*'>;
operators["/"] = binary_operator<std::divides<>, '/'>;
operators["<"] = comparison_operator<std::less<>>;
operators[">"] = comparison_operator<std::greater<>>;

View File

@ -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<std::string> 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
>;
}