From 9808d275dc7a98fb26c4d67b610a5f1030891b07 Mon Sep 17 00:00:00 2001 From: Spartan322 Date: Thu, 17 Aug 2023 07:24:36 -0400 Subject: Add error retrieval --- include/openvic-dataloader/ParseData.hpp | 11 +++ include/openvic-dataloader/ParseError.hpp | 20 +++++ include/openvic-dataloader/ParseWarning.hpp | 10 +++ include/openvic-dataloader/v2script/Parser.hpp | 27 ++----- src/openvic-dataloader/detail/Errors.hpp | 4 +- src/openvic-dataloader/detail/LexyReportError.hpp | 99 +++++++++++++++++++++++ src/openvic-dataloader/detail/Warnings.hpp | 4 +- src/openvic-dataloader/v2script/Parser.cpp | 29 ++++--- 8 files changed, 165 insertions(+), 39 deletions(-) create mode 100644 include/openvic-dataloader/ParseData.hpp create mode 100644 include/openvic-dataloader/ParseError.hpp create mode 100644 include/openvic-dataloader/ParseWarning.hpp create mode 100644 src/openvic-dataloader/detail/LexyReportError.hpp diff --git a/include/openvic-dataloader/ParseData.hpp b/include/openvic-dataloader/ParseData.hpp new file mode 100644 index 0000000..8bec7d2 --- /dev/null +++ b/include/openvic-dataloader/ParseData.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include + +namespace ovdl { + struct ParseData { + const std::string production_name; + const unsigned int context_start_line; + const unsigned int context_start_column; + }; +} \ No newline at end of file diff --git a/include/openvic-dataloader/ParseError.hpp b/include/openvic-dataloader/ParseError.hpp new file mode 100644 index 0000000..f3ae287 --- /dev/null +++ b/include/openvic-dataloader/ParseError.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include "openvic-dataloader/ParseData.hpp" + +namespace ovdl { + struct ParseError { + const enum class Type : unsigned char { + Recoverable, + Fatal + } type; + const std::string message; + const int error_value; + const ParseData parse_data; + const unsigned int start_line; + const unsigned int start_column; + }; + +} \ No newline at end of file diff --git a/include/openvic-dataloader/ParseWarning.hpp b/include/openvic-dataloader/ParseWarning.hpp new file mode 100644 index 0000000..307599f --- /dev/null +++ b/include/openvic-dataloader/ParseWarning.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include + +namespace ovdl { + struct ParseWarning { + const std::string message; + const int warning_value; + }; +} \ No newline at end of file diff --git a/include/openvic-dataloader/v2script/Parser.hpp b/include/openvic-dataloader/v2script/Parser.hpp index dbbec73..e971cdb 100644 --- a/include/openvic-dataloader/v2script/Parser.hpp +++ b/include/openvic-dataloader/v2script/Parser.hpp @@ -5,9 +5,10 @@ #include #include #include -#include #include +#include +#include #include namespace ovdl::v2script { @@ -16,20 +17,6 @@ namespace ovdl::v2script { class Parser { public: - struct Error { - const enum class Type : unsigned char { - Recoverable, - Fatal - } type; - const std::string message; - const int error_value; - }; - - struct Warning { - const std::string message; - const int warning_value; - }; - Parser(); static Parser from_buffer(const char* data, std::size_t size); @@ -51,8 +38,8 @@ namespace ovdl::v2script { bool has_fatal_error() const; bool has_warning() const; - const std::vector& get_errors() const; - const std::vector& get_warnings() const; + const std::vector& get_errors() const; + const std::vector& get_warnings() const; const FileNode* get_file_node() const; @@ -62,8 +49,8 @@ namespace ovdl::v2script { ~Parser(); private: - std::vector _errors; - std::vector _warnings; + std::vector _errors; + std::vector _warnings; class BufferHandler; friend class BufferHandler; @@ -74,6 +61,6 @@ namespace ovdl::v2script { bool _has_fatal_error = false; template - inline void _run_load_func(std::optional (BufferHandler::*func)(Args...), Args... args); + inline void _run_load_func(std::optional (BufferHandler::*func)(Args...), Args... args); }; } \ No newline at end of file diff --git a/src/openvic-dataloader/detail/Errors.hpp b/src/openvic-dataloader/detail/Errors.hpp index f8ed21b..c8f2954 100644 --- a/src/openvic-dataloader/detail/Errors.hpp +++ b/src/openvic-dataloader/detail/Errors.hpp @@ -3,7 +3,7 @@ #include "openvic-dataloader/v2script/Parser.hpp" namespace ovdl::v2script::errors { - inline const v2script::Parser::Error make_no_file_error(const char* file_path) { + inline const ParseError make_no_file_error(const char* file_path) { std::string message; if (!file_path) { message = "File path not specified."; @@ -11,7 +11,7 @@ namespace ovdl::v2script::errors { message = "File '" + std::string(file_path) + "' was not found."; } - return v2script::Parser::Error { Parser::Error::Type::Fatal, message, 1 }; + return ParseError { ParseError::Type::Fatal, message, 1 }; } } diff --git a/src/openvic-dataloader/detail/LexyReportError.hpp b/src/openvic-dataloader/detail/LexyReportError.hpp new file mode 100644 index 0000000..46d7911 --- /dev/null +++ b/src/openvic-dataloader/detail/LexyReportError.hpp @@ -0,0 +1,99 @@ +#pragma once + +#include +#include +#include +#include + +#include "openvic-dataloader/ParseData.hpp" +#include +#include +#include +#include + +namespace ovdl::detail { + template + struct _ReportError { + OutputIterator _iter; + lexy::visualization_options _opts; + const char* _path; + + struct _sink { + OutputIterator _iter; + lexy::visualization_options _opts; + const char* _path; + std::size_t _count; + std::vector _errors; + + using return_type = std::vector; + + template + void operator()(const lexy::error_context& context, const lexy::error& error) { + _iter = lexy_ext::_detail::write_error(_iter, context, error, _opts, _path); + ++_count; + + // Convert the context location and error location into line/column information. + auto context_location = lexy::get_input_location(context.input(), context.position()); + auto location = lexy::get_input_location(context.input(), error.position(), context_location.anchor()); + + std::string message; + + // Write the main annotation. + if constexpr (std::is_same_v) { + auto string = lexy::_detail::make_literal_lexeme(error.string(), error.length()); + + message = std::string("expected '") + string.data() + "'"; + } else if constexpr (std::is_same_v) { + auto string = lexy::_detail::make_literal_lexeme(error.string(), error.length()); + + message = std::string("expected keyword '") + string.data() + "'"; + } else if constexpr (std::is_same_v) { + message = std::string("expected ") + error.name(); + } else { + message = error.message(); + } + + _errors.push_back( + ParseError { + ParseError::Type::Fatal, // TODO: distinguish recoverable errors from fatal errors + std::move(message), + 0, // TODO: implement proper error codes + ParseData { + context.production(), + context_location.line_nr(), + context_location.column_nr(), + }, + location.line_nr(), + location.column_nr(), + }); + } + + return_type finish() && { + if (_count != 0) + *_iter++ = '\n'; + return _errors; + } + }; + constexpr auto sink() const { + return _sink { _iter, _opts, _path, 0 }; + } + + /// Specifies a path that will be printed alongside the diagnostic. + constexpr _ReportError path(const char* path) const { + return { _iter, _opts, path }; + } + + /// Specifies an output iterator where the errors are written to. + template + constexpr _ReportError to(OI out) const { + return { out, _opts, _path }; + } + + /// Overrides visualization options. + constexpr _ReportError opts(lexy::visualization_options opts) const { + return { _iter, opts, _path }; + } + }; + + constexpr auto ReporError = _ReportError {}; +} \ No newline at end of file diff --git a/src/openvic-dataloader/detail/Warnings.hpp b/src/openvic-dataloader/detail/Warnings.hpp index f854fa8..fc0fbed 100644 --- a/src/openvic-dataloader/detail/Warnings.hpp +++ b/src/openvic-dataloader/detail/Warnings.hpp @@ -3,7 +3,7 @@ #include "openvic-dataloader/v2script/Parser.hpp" namespace ovdl::v2script::warnings { - inline const v2script::Parser::Warning make_utf8_warning(const char* file_path) { + inline const ParseWarning make_utf8_warning(const char* file_path) { constexpr std::string_view message_suffix = "This may cause problems. Prefer Windows-1252 encoding."; std::string message; @@ -13,7 +13,7 @@ namespace ovdl::v2script::warnings { message = "File '" + std::string(file_path) + "' is a UTF-8 encoded file. " + std::string(message_suffix); } - return v2script::Parser::Warning { message, 1 }; + return ParseWarning { message, 1 }; } } diff --git a/src/openvic-dataloader/v2script/Parser.cpp b/src/openvic-dataloader/v2script/Parser.cpp index c0b6bd8..7df9b99 100644 --- a/src/openvic-dataloader/v2script/Parser.cpp +++ b/src/openvic-dataloader/v2script/Parser.cpp @@ -4,13 +4,13 @@ #include #include #include -#include #include #include #include "SimpleGrammar.hpp" #include "detail/DetectUtf8.hpp" #include "detail/Errors.hpp" +#include "detail/LexyReportError.hpp" #include "detail/NullBuff.hpp" #include "detail/Warnings.hpp" #include @@ -19,7 +19,8 @@ #include #include #include -#include +#include +#include #include using namespace ovdl::v2script; @@ -32,17 +33,17 @@ public: return _buffer.size() != 0; } - std::optional load_buffer(const char* data, std::size_t size) { + std::optional load_buffer(const char* data, std::size_t size) { _buffer = lexy::buffer(data, size); return std::nullopt; } - std::optional load_buffer(const char* start, const char* end) { + std::optional load_buffer(const char* start, const char* end) { _buffer = lexy::buffer(start, end); return std::nullopt; } - std::optional load_file(const char* path) { + std::optional load_file(const char* path) { auto file = lexy::read_file(path); if (!file) { return errors::make_no_file_error(path); @@ -57,11 +58,10 @@ public: } template - std::optional> parse(const ErrorCallback& callback) { + std::optional> parse(const ErrorCallback& callback) { auto result = lexy::parse(_buffer, callback); if (!result) { - std::vector errors; - return errors; + return result.errors(); } // This is mighty frustrating _root = std::unique_ptr(result.value()); @@ -117,12 +117,12 @@ Parser Parser::from_file(const char* path) { /// @param args /// template -inline void Parser::_run_load_func(std::optional (BufferHandler::*func)(Args...), Args... args) { +inline void Parser::_run_load_func(std::optional (BufferHandler::*func)(Args...), Args... args) { _warnings.clear(); _errors.clear(); _has_fatal_error = false; if (auto error = (_buffer_handler.get()->*func)(args...); error) { - _has_fatal_error = error.value().type == Error::Type::Fatal; + _has_fatal_error = error.value().type == ParseError::Type::Fatal; _errors.push_back(error.value()); _error_stream.get() << "Error: " << _errors.back().message << '\n'; } @@ -185,13 +185,12 @@ bool Parser::simple_parse() { _warnings.push_back(warnings::make_utf8_warning(_file_path)); } - auto errors = _buffer_handler->parse(lexy_ext::report_error.path(_file_path).to(ostream_output_iterator { _error_stream })); + auto errors = _buffer_handler->parse(ovdl::detail::ReporError.path(_file_path).to(ostream_output_iterator { _error_stream })); if (errors) { _errors.reserve(errors->size()); for (auto& err : errors.value()) { - _has_fatal_error |= err.type == Error::Type::Fatal; + _has_fatal_error |= err.type == ParseError::Type::Fatal; _errors.push_back(err); - _error_stream.get() << "Error: " << err.message << '\n'; } return false; } @@ -211,11 +210,11 @@ bool Parser::has_warning() const { return !_warnings.empty(); } -const std::vector& Parser::get_errors() const { +const std::vector& Parser::get_errors() const { return _errors; } -const std::vector& Parser::get_warnings() const { +const std::vector& Parser::get_warnings() const { return _warnings; } -- cgit v1.2.3-56-ga3b1