Unexpected empty source error improved

This commit is contained in:
Robert Bendun 2022-08-21 20:01:59 +02:00
parent 183907a0ed
commit cb5eedb2a5
3 changed files with 72 additions and 55 deletions

View File

@ -41,6 +41,47 @@ using i64 = std::int64_t;
using usize = std::size_t;
using isize = std::ptrdiff_t;
/// \brief Location describes code position in `file line column` format.
/// It's used both to represent position in source files provided
// to interpreter and internal interpreter usage.
struct Location
{
std::string_view filename = "<unnamed>"; ///< File that location is pointing to
usize line = 1; ///< Line number (1 based) that location is pointing to
usize column = 1; ///< Column number (1 based) that location is pointing to
/// Advances line and column numbers based on provided rune
///
/// If rune is newline, then column is reset to 1, and line number is incremented.
/// Otherwise column number is incremented.
///
/// @param rune Rune from which column and line numbers advancements are made.
Location& advance(u32 rune);
bool operator==(Location const& rhs) const = default;
//! Creates location at default filename with specified line and column number
static Location at(usize line, usize column);
// Used to describe location of function call in interpreter (internal use only)
#if defined(__cpp_lib_source_location)
static Location caller(std::source_location loc = std::source_location::current());
#elif (__has_builtin(__builtin_FILE) and __has_builtin(__builtin_LINE))
static Location caller(char const* file = __builtin_FILE(), usize line = __builtin_LINE());
#else
#error Cannot implement Location::caller function
/// Returns location of call in interpreter source code.
///
/// Example of reporting where `foo()` was beeing called:
/// @code
/// void foo(Location loc = Location::caller()) { std::cout << loc << '\n'; }
/// @endcode
static Location caller();
#endif
};
std::ostream& operator<<(std::ostream& os, Location const& location);
/// Error handling related functions and definitions
namespace errors
{
@ -53,6 +94,8 @@ namespace errors
/// When parser was expecting code but encountered end of file
struct Unexpected_Empty_Source
{
enum { Block_Without_Closing_Bracket } reason;
std::optional<Location> start;
};
/// When user passed numeric literal too big for numeric type
@ -183,47 +226,6 @@ namespace pretty
template<typename ...Lambdas>
struct Overloaded : Lambdas... { using Lambdas::operator()...; };
/// \brief Location describes code position in `file line column` format.
/// It's used both to represent position in source files provided
// to interpreter and internal interpreter usage.
struct Location
{
std::string_view filename = "<unnamed>"; ///< File that location is pointing to
usize line = 1; ///< Line number (1 based) that location is pointing to
usize column = 1; ///< Column number (1 based) that location is pointing to
/// Advances line and column numbers based on provided rune
///
/// If rune is newline, then column is reset to 1, and line number is incremented.
/// Otherwise column number is incremented.
///
/// @param rune Rune from which column and line numbers advancements are made.
Location& advance(u32 rune);
bool operator==(Location const& rhs) const = default;
//! Creates location at default filename with specified line and column number
static Location at(usize line, usize column);
// Used to describe location of function call in interpreter (internal use only)
#if defined(__cpp_lib_source_location)
static Location caller(std::source_location loc = std::source_location::current());
#elif (__has_builtin(__builtin_FILE) and __has_builtin(__builtin_LINE))
static Location caller(char const* file = __builtin_FILE(), usize line = __builtin_LINE());
#else
#error Cannot implement Location::caller function
/// Returns location of call in interpreter source code.
///
/// Example of reporting where `foo()` was beeing called:
/// @code
/// void foo(Location loc = Location::caller()) { std::cout << loc << '\n'; }
/// @endcode
static Location caller();
#endif
};
std::ostream& operator<<(std::ostream& os, Location const& location);
/// Guards that program exits if condition does not hold
void assert(bool condition, std::string message, Location loc = Location::caller());

View File

@ -155,7 +155,7 @@ std::ostream& operator<<(std::ostream& os, Error const& err)
error_heading(os, err.location, Error_Level::Error, short_description);
auto const loc = err.location;
auto const print_error_line = [&] {
auto const print_error_line = [&] (std::optional<Location> loc) {
if (loc) {
Lines::the.print(os, std::string(loc->filename), loc->line, loc->line);
os << '\n';
@ -167,7 +167,7 @@ std::ostream& operator<<(std::ostream& os, Error const& err)
os << "I cannot '" << err.name << "' due to lack of MIDI " << (err.is_input ? "input" : "output") << "connection\n";
os << "\n";
print_error_line();
print_error_line(loc);
os << "You can connect to given MIDI device by specifing port when running musique command like:\n";
os << (err.is_input ? " --input" : " --output") << " PORT\n";
@ -178,7 +178,7 @@ std::ostream& operator<<(std::ostream& os, Error const& err)
os << "I can't find it in surrounding scope or in one of parent's scopes\n";
os << "\n";
print_error_line();
print_error_line(loc);
os << "Variables can only be references in scope (block) where they been created\n";
os << "or from parent blocks to variable block\n";
@ -189,7 +189,7 @@ std::ostream& operator<<(std::ostream& os, Error const& err)
os << " Character printed: '" << utf8::Print{err.invalid_character} << "'\n";
os << "\n";
print_error_line();
print_error_line(loc);
os << "Musique only accepts characters that are unicode letters or ascii numbers and punctuation\n";
},
@ -199,7 +199,7 @@ std::ostream& operator<<(std::ostream& os, Error const& err)
constexpr auto Min = std::numeric_limits<decltype(Number::num)>::min();
os << "I tried to parse numeric literal, but I failed.\n\n";
print_error_line();
print_error_line(loc);
if (err.reason == std::errc::result_out_of_range) {
os << "Declared number is outside of valid range of numbers that can be represented.\n";
@ -212,7 +212,7 @@ std::ostream& operator<<(std::ostream& os, Error const& err)
os << " Token type: " << ut.type << '\n';
os << " Token source: " << ut.source << "\n\n";
print_error_line();
print_error_line(loc);
os << pretty::begin_comment << "This error is considered an internal one. It should not be displayed to the end user.\n";
os << "\n";
@ -223,7 +223,7 @@ std::ostream& operator<<(std::ostream& os, Error const& err)
[&](errors::Expected_Expression_Separator_Before const& err) {
os << "I failed to parse following code, due to missing semicolon before it!\n\n";
print_error_line();
print_error_line(loc);
if (err.what == "var") {
os << "If you want to create variable inside expression try wrapping them inside parentheses like this:\n";
@ -234,7 +234,7 @@ std::ostream& operator<<(std::ostream& os, Error const& err)
[&](errors::Literal_As_Identifier const& err) {
os << "I expected an identifier in " << err.context << ", but found" << (err.type_name.empty() ? "" : " ") << err.type_name << " value = '" << err.source << "'\n\n";
print_error_line();
print_error_line(loc);
if (err.type_name == "chord") {
os << "Try renaming to different name or appending with something that is not part of chord literal like 'x'\n";
@ -252,7 +252,7 @@ std::ostream& operator<<(std::ostream& os, Error const& err)
{
os << "I tried to call function '" << err.name << "' but you gave me wrong types for it!\n";
print_error_line();
print_error_line(loc);
os << "Make sure that all values matches one of supported signatures listed below!\n";
os << '\n';
@ -267,7 +267,7 @@ 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 << "Make sure that both values matches one of supported signatures listed below!\n";
os << '\n';
print_error_line();
print_error_line(loc);
if (err.name == "+") { os << "Addition only supports:\n"; }
@ -285,7 +285,7 @@ std::ostream& operator<<(std::ostream& os, Error const& err)
os << "Value of type " << err.type << " cannot be called.\n";
os << "\n";
print_error_line();
print_error_line(loc);
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";
@ -299,9 +299,21 @@ std::ostream& operator<<(std::ostream& os, Error const& err)
os << "when correct code for such expression would be [4;3]\n";
os << pretty::end;
},
[&](errors::Unexpected_Empty_Source const& err) {
switch (err.reason) {
break; case errors::Unexpected_Empty_Source::Block_Without_Closing_Bracket:
os << "Reached end of input when waiting for closing of a block ']'\n";
os << "Expected block end after:\n\n";
print_error_line(loc);
if (err.start) {
os << "Which was introduced by block starting here:\n\n";
print_error_line(err.start);
}
}
},
[&](errors::Undefined_Operator const&) { unimplemented(); },
[&](errors::Unexpected_Keyword const&) { unimplemented(); },
[&](errors::Unexpected_Empty_Source const&) { unimplemented(); }
}, err.details);
return os;

View File

@ -272,8 +272,11 @@ Result<Ast> Parser::parse_atomic_expression()
}
}
return Error {
.details = errors::Unexpected_Empty_Source {},
.location = {}
.details = errors::Unexpected_Empty_Source {
.reason = errors::Unexpected_Empty_Source::Block_Without_Closing_Bracket,
.start = opening.location
},
.location = tokens.back().location
};
}
consume();