From b7d879bf7055550d45779ee6459d947aa6daaa6f Mon Sep 17 00:00:00 2001 From: Robert Bendun Date: Thu, 9 Jun 2022 01:48:02 +0200 Subject: [PATCH] Relevant code printing when error reporting --- Makefile | 1 + src/errors.cc | 42 +++++++++++++++++++++++++++--------------- src/lines.cc | 36 ++++++++++++++++++++++++++++++++++++ src/main.cc | 6 +++++- src/musique.hh | 17 +++++++++++++++++ 5 files changed, 86 insertions(+), 16 deletions(-) create mode 100644 src/lines.cc diff --git a/Makefile b/Makefile index 79afca1..829c7a9 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,7 @@ Obj= \ errors.o \ interpreter.o \ lexer.o \ + lines.o \ location.o \ number.o \ parser.o \ diff --git a/src/errors.cc b/src/errors.cc index b11b834..33b2ff6 100644 --- a/src/errors.cc +++ b/src/errors.cc @@ -125,36 +125,47 @@ std::ostream& operator<<(std::ostream& os, Error const& err) error_heading(os, err.location, Error_Level::Error, short_description); + auto const loc = err.location; visit(Overloaded { - [&os](errors::Unrecognized_Character const& err) { + [&](errors::Unrecognized_Character const& err) { os << "I encountered character in the source code that was not supposed to be here.\n"; os << " Character Unicode code: U+" << std::hex << err.invalid_character << '\n'; os << " Character printed: '" << utf8::Print{err.invalid_character} << "'\n"; os << "\n"; + + Lines::the.print(os, std::string(loc->filename), loc->line, loc->line); + os << "Musique only accepts characters that are unicode letters or ascii numbers and punctuation\n"; }, - [&os](errors::Failed_Numeric_Parsing const& err) { + [&](errors::Failed_Numeric_Parsing const& err) { constexpr auto Max = std::numeric_limits::max(); constexpr auto Min = std::numeric_limits::min(); - os << "I tried to parse numeric literal, but I failed."; + os << "I tried to parse numeric literal, but I failed.\n\n"; + + Lines::the.print(os, std::string(loc->filename), loc->line, loc->line); + if (err.reason == std::errc::result_out_of_range) { - 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 << "\nDeclared number is outside of valid range of numbers that can be represented.\n"; + os << "Only numbers in range [" << Min << ", " << Max << "] are supported\n"; } }, - [&os](errors::internal::Unexpected_Token const& ut) { + [&](errors::internal::Unexpected_Token const& ut) { os << "I encountered unexpected token during " << ut.when << '\n'; os << " Token type: " << ut.type << '\n'; - os << " Token source: " << ut.source << '\n'; + os << " Token source: " << ut.source << "\n\n"; + + Lines::the.print(os, std::string(loc->filename), loc->line, loc->line); os << pretty::begin_comment << "\nThis error is considered an internal one. It should not be displayed to the end user.\n" << pretty::end; encourage_contact(os); }, - [&os](errors::Expected_Expression_Separator_Before const& err) { - os << "I failed to parse following code, due to missing semicolon before it!\n"; + [&](errors::Expected_Expression_Separator_Before const& err) { + os << "I failed to parse following code, due to missing semicolon before it!\n\n"; + + Lines::the.print(os, std::string(loc->filename), loc->line, loc->line); if (err.what == "var") { os << "\nIf you want to create variable inside expression try wrapping them inside parentheses like this:\n"; @@ -162,9 +173,10 @@ std::ostream& operator<<(std::ostream& os, Error const& err) } }, - [&os](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"; + [&](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"; + Lines::the.print(os, std::string(loc->filename), loc->line, loc->line); 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"; @@ -176,10 +188,10 @@ std::ostream& operator<<(std::ostream& os, Error const& err) } }, - [&os](errors::Not_Callable const&) { unimplemented(); }, - [&os](errors::Undefined_Operator const&) { unimplemented(); }, - [&os](errors::Unexpected_Keyword const&) { unimplemented(); }, - [&os](errors::Unexpected_Empty_Source const&) { unimplemented(); } + [&](errors::Not_Callable const&) { unimplemented(); }, + [&](errors::Undefined_Operator const&) { unimplemented(); }, + [&](errors::Unexpected_Keyword const&) { unimplemented(); }, + [&](errors::Unexpected_Empty_Source const&) { unimplemented(); } }, err.details); return os; diff --git a/src/lines.cc b/src/lines.cc new file mode 100644 index 0000000..5da416d --- /dev/null +++ b/src/lines.cc @@ -0,0 +1,36 @@ +#include + +#include + +Lines Lines::the; + +void Lines::add_file(std::string filename, std::string_view source) +{ + auto file = lines.insert({ filename, {} }); + auto &lines_in_file = file.first->second; + + while (not source.empty()) { + auto end = source.find('\n'); + if (end == std::string_view::npos) { + lines_in_file.push_back({ source.data(), end }); + break; + } else { + lines_in_file.push_back({ source.data(), end }); + source.remove_prefix(end+1); + } + } +} + +void Lines::add_line(std::string const& filename, std::string_view source) +{ + lines[filename].push_back(source); +} + +void Lines::print(std::ostream &os, std::string const& filename, unsigned first_line, unsigned last_line) const +{ + auto const& file = lines.at(filename); + for (auto i = first_line; i <= last_line; ++i) { + os << std::setw(3) << std::right << i << " | " << file[i-1] << '\n'; + } + os << std::flush; +} diff --git a/src/main.cc b/src/main.cc index a2a8670..e8554f5 100644 --- a/src/main.cc +++ b/src/main.cc @@ -166,11 +166,13 @@ static Result Main(std::span args) for (auto const& [is_file, argument] : runnables) { if (!is_file) { + Lines::the.add_line("", argument); Try(runner.run(argument, "")); continue; } - auto const path = argument; + auto path = argument; if (path == "-") { + path = ""; eternal_sources.emplace_back(std::istreambuf_iterator(std::cin), std::istreambuf_iterator()); } else { if (not fs::exists(path)) { @@ -181,6 +183,7 @@ static Result Main(std::span args) eternal_sources.emplace_back(std::istreambuf_iterator(source_file), std::istreambuf_iterator()); } + Lines::the.add_file(std::string(path), eternal_sources.back()); Try(runner.run(eternal_sources.back(), path)); } @@ -216,6 +219,7 @@ static Result Main(std::span args) continue; } + Lines::the.add_line("", raw); auto result = runner.run(raw, "", true); if (not result.has_value()) { std::cout << std::flush; diff --git a/src/musique.hh b/src/musique.hh index ab64760..031d42a 100644 --- a/src/musique.hh +++ b/src/musique.hh @@ -392,6 +392,23 @@ std::ostream& operator<<(std::ostream& os, Token const& tok); /// Token type debug printing std::ostream& operator<<(std::ostream& os, Token::Type type); +struct Lines +{ + static Lines the; + + /// Region of lines in files + std::unordered_map> lines; + + /// Add lines from file + void add_file(std::string filename, std::string_view source); + + /// Add single line into file (REPL usage) + void add_line(std::string const& filename, std::string_view source); + + /// Print selected region + void print(std::ostream& os, std::string const& file, unsigned first_line, unsigned last_line) const; +}; + /// Explicit marker of the end of file struct End_Of_File {};