Improved error location reporting
This commit is contained in:
parent
99482d4c7c
commit
183907a0ed
@ -297,6 +297,17 @@ struct [[nodiscard("This value may contain critical error, so it should NOT be i
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fill error location if it's empty and we have an error
|
||||||
|
inline Result<T> with_location(Location location) &&
|
||||||
|
{
|
||||||
|
if (!Storage::has_value()) {
|
||||||
|
if (auto& target = Storage::error().location; !target || target == Location{}) {
|
||||||
|
target = location;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
inline tl::expected<T, Error> to_expected() &&
|
inline tl::expected<T, Error> to_expected() &&
|
||||||
{
|
{
|
||||||
return *static_cast<Storage*>(this);
|
return *static_cast<Storage*>(this);
|
||||||
|
@ -156,8 +156,9 @@ std::ostream& operator<<(std::ostream& os, Error const& err)
|
|||||||
|
|
||||||
auto const loc = err.location;
|
auto const loc = err.location;
|
||||||
auto const print_error_line = [&] {
|
auto const print_error_line = [&] {
|
||||||
if (loc->filename != "") {
|
if (loc) {
|
||||||
Lines::the.print(os, std::string(loc->filename), loc->line, loc->line);
|
Lines::the.print(os, std::string(loc->filename), loc->line, loc->line);
|
||||||
|
os << '\n';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -179,7 +180,6 @@ std::ostream& operator<<(std::ostream& os, Error const& err)
|
|||||||
|
|
||||||
print_error_line();
|
print_error_line();
|
||||||
|
|
||||||
os << "\n";
|
|
||||||
os << "Variables can only be references in scope (block) where they been created\n";
|
os << "Variables can only be references in scope (block) where they been created\n";
|
||||||
os << "or from parent blocks to variable block\n";
|
os << "or from parent blocks to variable block\n";
|
||||||
},
|
},
|
||||||
@ -202,7 +202,7 @@ std::ostream& operator<<(std::ostream& os, Error const& err)
|
|||||||
print_error_line();
|
print_error_line();
|
||||||
|
|
||||||
if (err.reason == std::errc::result_out_of_range) {
|
if (err.reason == std::errc::result_out_of_range) {
|
||||||
os << "\nDeclared number is outside of valid range of numbers that can be represented.\n";
|
os << "Declared number is outside of valid range of numbers that can be represented.\n";
|
||||||
os << "Only numbers in range [" << Min << ", " << Max << "] are supported\n";
|
os << "Only numbers in range [" << Min << ", " << Max << "] are supported\n";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -214,7 +214,7 @@ std::ostream& operator<<(std::ostream& os, Error const& err)
|
|||||||
|
|
||||||
print_error_line();
|
print_error_line();
|
||||||
|
|
||||||
os << pretty::begin_comment << "\nThis error is considered an internal one. It should not be displayed to the end user.\n";
|
os << pretty::begin_comment << "This error is considered an internal one. It should not be displayed to the end user.\n";
|
||||||
os << "\n";
|
os << "\n";
|
||||||
os << "This error message is temporary and will be replaced by better one in the future\n";
|
os << "This error message is temporary and will be replaced by better one in the future\n";
|
||||||
os << pretty::end;
|
os << pretty::end;
|
||||||
@ -226,7 +226,7 @@ std::ostream& operator<<(std::ostream& os, Error const& err)
|
|||||||
print_error_line();
|
print_error_line();
|
||||||
|
|
||||||
if (err.what == "var") {
|
if (err.what == "var") {
|
||||||
os << "\nIf you want to create variable inside expression try wrapping them inside parentheses like this:\n";
|
os << "If you want to create variable inside expression try wrapping them inside parentheses like this:\n";
|
||||||
os << " 10 + (var i = 20)\n";
|
os << " 10 + (var i = 20)\n";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -237,7 +237,7 @@ std::ostream& operator<<(std::ostream& os, Error const& err)
|
|||||||
print_error_line();
|
print_error_line();
|
||||||
|
|
||||||
if (err.type_name == "chord") {
|
if (err.type_name == "chord") {
|
||||||
os << "\nTry renaming to different name or appending with something that is not part of chord literal like 'x'\n";
|
os << "Try renaming to different name or appending with something that is not part of chord literal like 'x'\n";
|
||||||
|
|
||||||
os << pretty::begin_comment <<
|
os << pretty::begin_comment <<
|
||||||
"\nMusical notation names are reserved for chord and note notations,\n"
|
"\nMusical notation names are reserved for chord and note notations,\n"
|
||||||
@ -251,6 +251,9 @@ std::ostream& operator<<(std::ostream& os, Error const& err)
|
|||||||
case errors::Unsupported_Types_For::Function:
|
case errors::Unsupported_Types_For::Function:
|
||||||
{
|
{
|
||||||
os << "I tried to call function '" << err.name << "' but you gave me wrong types for it!\n";
|
os << "I tried to call function '" << err.name << "' but you gave me wrong types for it!\n";
|
||||||
|
|
||||||
|
print_error_line();
|
||||||
|
|
||||||
os << "Make sure that all values matches one of supported signatures listed below!\n";
|
os << "Make sure that all values matches one of supported signatures listed below!\n";
|
||||||
os << '\n';
|
os << '\n';
|
||||||
|
|
||||||
@ -264,6 +267,8 @@ std::ostream& operator<<(std::ostream& os, Error const& err)
|
|||||||
os << "I tried and failed to evaluate operator '" << err.name << "' due to values with wrong types provided\n";
|
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 << "Make sure that both values matches one of supported signatures listed below!\n";
|
||||||
os << '\n';
|
os << '\n';
|
||||||
|
print_error_line();
|
||||||
|
|
||||||
|
|
||||||
if (err.name == "+") { os << "Addition only supports:\n"; }
|
if (err.name == "+") { os << "Addition only supports:\n"; }
|
||||||
else { os << "Operator '" << err.name << "' only supports:\n"; }
|
else { os << "Operator '" << err.name << "' only supports:\n"; }
|
||||||
@ -276,7 +281,24 @@ std::ostream& operator<<(std::ostream& os, Error const& err)
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
[&](errors::Not_Callable const&) { unimplemented(); },
|
[&](errors::Not_Callable const& err) {
|
||||||
|
os << "Value of type " << err.type << " cannot be called.\n";
|
||||||
|
os << "\n";
|
||||||
|
|
||||||
|
print_error_line();
|
||||||
|
|
||||||
|
os << "Only values of this types can be called:\n";
|
||||||
|
os << " - musical values like c, c47, (c&g) can be called to provide octave and duration\n";
|
||||||
|
os << " - blocks can be called to compute their body like this: [i | i + 1] 3\n";
|
||||||
|
os << " - builtin functions like if, par, floor\n";
|
||||||
|
os << "\n";
|
||||||
|
|
||||||
|
os << pretty::begin_comment;
|
||||||
|
os << "Parhaps you forgot to include semicolon between successive elements?\n";
|
||||||
|
os << "Common problem is writing [4 3] as list with elements 4 and 3,\n";
|
||||||
|
os << "when correct code for such expression would be [4;3]\n";
|
||||||
|
os << pretty::end;
|
||||||
|
},
|
||||||
[&](errors::Undefined_Operator const&) { unimplemented(); },
|
[&](errors::Undefined_Operator const&) { unimplemented(); },
|
||||||
[&](errors::Unexpected_Keyword const&) { unimplemented(); },
|
[&](errors::Unexpected_Keyword const&) { unimplemented(); },
|
||||||
[&](errors::Unexpected_Empty_Source const&) { unimplemented(); }
|
[&](errors::Unexpected_Empty_Source const&) { unimplemented(); }
|
||||||
|
@ -87,18 +87,20 @@ Result<Value> Interpreter::eval(Ast &&ast)
|
|||||||
|
|
||||||
Value *v = env->find(std::string(lhs.token.source));
|
Value *v = env->find(std::string(lhs.token.source));
|
||||||
assert(v, "Cannot resolve variable: "s + std::string(lhs.token.source)); // TODO(assert)
|
assert(v, "Cannot resolve variable: "s + std::string(lhs.token.source)); // TODO(assert)
|
||||||
return *v = Try(eval(std::move(rhs)));
|
return *v = Try(eval(std::move(rhs)).with_location(ast.token.location));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ast.token.source == "and" || ast.token.source == "or") {
|
if (ast.token.source == "and" || ast.token.source == "or") {
|
||||||
auto lhs = std::move(ast.arguments.front());
|
auto lhs = std::move(ast.arguments.front());
|
||||||
auto rhs = std::move(ast.arguments.back());
|
auto rhs = std::move(ast.arguments.back());
|
||||||
|
|
||||||
auto result = Try(eval(std::move(lhs)));
|
auto lhs_loc = lhs.location, rhs_loc = rhs.location;
|
||||||
|
|
||||||
|
auto result = Try(eval(std::move(lhs)).with_location(std::move(lhs_loc)));
|
||||||
if (ast.token.source == "or" ? result.truthy() : result.falsy()) {
|
if (ast.token.source == "or" ? result.truthy() : result.falsy()) {
|
||||||
return result;
|
return result;
|
||||||
} else {
|
} else {
|
||||||
return eval(std::move(rhs));
|
return eval(std::move(rhs)).with_location(rhs_loc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,12 +117,15 @@ Result<Value> Interpreter::eval(Ast &&ast)
|
|||||||
|
|
||||||
auto lhs = std::move(ast.arguments.front());
|
auto lhs = std::move(ast.arguments.front());
|
||||||
auto rhs = std::move(ast.arguments.back());
|
auto rhs = std::move(ast.arguments.back());
|
||||||
|
auto const rhs_loc = rhs.location;
|
||||||
assert(lhs.type == Ast::Type::Literal && lhs.token.type == Token::Type::Symbol,
|
assert(lhs.type == Ast::Type::Literal && lhs.token.type == Token::Type::Symbol,
|
||||||
"Currently LHS of assigment must be an identifier"); // TODO(assert)
|
"Currently LHS of assigment must be an identifier"); // TODO(assert)
|
||||||
|
|
||||||
Value *v = env->find(std::string(lhs.token.source));
|
Value *v = env->find(std::string(lhs.token.source));
|
||||||
assert(v, "Cannot resolve variable: "s + std::string(lhs.token.source)); // TODO(assert)
|
assert(v, "Cannot resolve variable: "s + std::string(lhs.token.source)); // TODO(assert)
|
||||||
return *v = Try(op->second(*this, { *v, Try(eval(std::move(rhs))) }));
|
return *v = Try(op->second(*this, {
|
||||||
|
*v, Try(eval(std::move(rhs)).with_location(rhs_loc))
|
||||||
|
}).with_location(ast.token.location));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Error {
|
return Error {
|
||||||
@ -132,10 +137,11 @@ Result<Value> Interpreter::eval(Ast &&ast)
|
|||||||
std::vector<Value> values;
|
std::vector<Value> values;
|
||||||
values.reserve(ast.arguments.size());
|
values.reserve(ast.arguments.size());
|
||||||
for (auto& a : ast.arguments) {
|
for (auto& a : ast.arguments) {
|
||||||
values.push_back(Try(eval(std::move(a))));
|
auto const a_loc = a.location;
|
||||||
|
values.push_back(Try(eval(std::move(a)).with_location(a_loc)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return op->second(*this, std::move(values));
|
return op->second(*this, std::move(values)).with_location(ast.token.location);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -153,6 +159,7 @@ Result<Value> Interpreter::eval(Ast &&ast)
|
|||||||
|
|
||||||
case Ast::Type::Call:
|
case Ast::Type::Call:
|
||||||
{
|
{
|
||||||
|
auto call_location = ast.arguments.front().location;
|
||||||
Value func = Try(eval(std::move(ast.arguments.front())));
|
Value func = Try(eval(std::move(ast.arguments.front())));
|
||||||
|
|
||||||
std::vector<Value> values;
|
std::vector<Value> values;
|
||||||
@ -160,7 +167,8 @@ Result<Value> Interpreter::eval(Ast &&ast)
|
|||||||
for (auto& a : std::span(ast.arguments).subspan(1)) {
|
for (auto& a : std::span(ast.arguments).subspan(1)) {
|
||||||
values.push_back(Try(eval(std::move(a))));
|
values.push_back(Try(eval(std::move(a))));
|
||||||
}
|
}
|
||||||
return std::move(func)(*this, std::move(values));
|
return std::move(func)(*this, std::move(values))
|
||||||
|
.with_location(std::move(call_location));
|
||||||
}
|
}
|
||||||
|
|
||||||
case Ast::Type::Variable_Declaration:
|
case Ast::Type::Variable_Declaration:
|
||||||
|
12
src/main.cc
12
src/main.cc
@ -53,6 +53,17 @@ static std::string_view pop(std::span<char const*> &span)
|
|||||||
std::exit(1);
|
std::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void print_repl_help()
|
||||||
|
{
|
||||||
|
std::cout <<
|
||||||
|
"List of all available commands of Musique interactive mode:\n"
|
||||||
|
":exit - quit interactive mode\n"
|
||||||
|
":help - prints this help message\n"
|
||||||
|
":!<command> - allows for execution of any shell command\n"
|
||||||
|
":clear - clears screen\n"
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
/// Trim spaces from left an right
|
/// Trim spaces from left an right
|
||||||
static void trim(std::string_view &s)
|
static void trim(std::string_view &s)
|
||||||
{
|
{
|
||||||
@ -301,6 +312,7 @@ static Result<void> Main(std::span<char const*> args)
|
|||||||
if (command == "exit") { break; }
|
if (command == "exit") { break; }
|
||||||
if (command == "clear") { std::cout << "\x1b[1;1H\x1b[2J" << std::flush; continue; }
|
if (command == "clear") { std::cout << "\x1b[1;1H\x1b[2J" << std::flush; continue; }
|
||||||
if (command.starts_with('!')) { Ignore(system(command.data() + 1)); continue; }
|
if (command.starts_with('!')) { Ignore(system(command.data() + 1)); continue; }
|
||||||
|
if (command == "help") { print_repl_help(); continue; }
|
||||||
std::cerr << "musique: error: unrecognized REPL command '" << command << '\'' << std::endl;
|
std::cerr << "musique: error: unrecognized REPL command '" << command << '\'' << std::endl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -168,11 +168,8 @@ Result<Value> Value::operator()(Interpreter &i, std::vector<Value> args)
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
// TODO Fill location
|
|
||||||
return Error {
|
return Error {
|
||||||
.details = errors::Not_Callable {
|
.details = errors::Not_Callable { .type = type_name(type) },
|
||||||
.type = type_name(type)
|
|
||||||
},
|
|
||||||
.location = std::nullopt,
|
.location = std::nullopt,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user