#include #include #include #include #include #include #include #include #include #include #include #include "CsvGrammar.hpp" #include "CsvParseState.hpp" #include "detail/NullBuff.hpp" #include "detail/ParseHandler.hpp" using namespace ovdl; using namespace ovdl::csv; /// ParseHandler /// template struct Parser::ParseHandler final : detail::BasicFileParseHandler> { template std::optional parse() { auto result = lexy::parse(this->buffer(), *this->_parse_state, this->_parse_state->logger().error_callback()); if (!result) { return this->_parse_state->logger().get_errors(); } _lines = std::move(result.value()); return std::nullopt; } std::vector& get_lines() { return _lines; } private: std::vector _lines; }; /// BufferHandler /// template Parser::Parser() : _parse_handler(std::make_unique()) { set_error_log_to_null(); } template Parser::Parser(std::basic_ostream& error_stream) : _parse_handler(std::make_unique()) { set_error_log_to(error_stream); } template Parser::Parser(Parser&&) = default; template Parser& Parser::operator=(Parser&&) = default; template Parser::~Parser() = default; template Parser Parser::from_buffer(const char* data, std::size_t size) { Parser result; return std::move(result.load_from_buffer(data, size)); } template Parser Parser::from_buffer(const char* start, const char* end) { Parser result; return std::move(result.load_from_buffer(start, end)); } template Parser Parser::from_string(const std::string_view string) { Parser result; return std::move(result.load_from_string(string)); } template Parser Parser::from_file(const char* path) { Parser result; return std::move(result.load_from_file(path)); } template Parser Parser::from_file(const std::filesystem::path& path) { Parser result; return std::move(result.load_from_file(path)); } /// /// @brief Executes a function on _buffer_handler that is expected to load a buffer /// /// Expected Use: /// @code {.cpp} /// _run_load_func(&BufferHandler::, ); /// @endcode /// /// @tparam Type /// @tparam Args /// @param func /// @param args /// template template constexpr void Parser::_run_load_func(detail::LoadCallback auto func, Args... args) { _has_fatal_error = false; auto error = func(_parse_handler.get(), std::forward(args)...); auto error_message = _parse_handler->make_error_from(error); if (!error_message.empty()) { _has_error = true; _has_fatal_error = true; _parse_handler->parse_state().logger().template create_log(DiagnosticLogger::DiagnosticKind::error, fmt::runtime(error_message)); } if (has_error() && &_error_stream.get() != &detail::cnull) { print_errors_to(_error_stream.get()); } } template constexpr Parser& Parser::load_from_buffer(const char* data, std::size_t size) { // Type can't be deduced? _run_load_func(std::mem_fn(&ParseHandler::load_buffer_size), data, size); return *this; } template constexpr Parser& Parser::load_from_buffer(const char* start, const char* end) { // Type can't be deduced? _run_load_func(std::mem_fn(&ParseHandler::load_buffer), start, end); return *this; } template constexpr Parser& Parser::load_from_string(const std::string_view string) { return load_from_buffer(string.data(), string.size()); } template Parser& Parser::load_from_file(const char* path) { set_file_path(path); // Type can be deduced?? _run_load_func(std::mem_fn(&ParseHandler::load_file), path); return *this; } template Parser& Parser::load_from_file(const std::filesystem::path& path) { return load_from_file(path.string().c_str()); } template bool Parser::parse_csv(bool handle_strings) { if (!_parse_handler->is_valid()) { return false; } std::optional::error_range> errors; // auto report_error = ovdl::detail::ReporError.path(_file_path).to(detail::OStreamOutputIterator { _error_stream }); if constexpr (Encoding == EncodingType::Windows1252) { if (handle_strings) errors = _parse_handler->template parse(); else errors = _parse_handler->template parse(); } else { if (handle_strings) errors = _parse_handler->template parse(); else errors = _parse_handler->template parse(); } _has_error = _parse_handler->parse_state().logger().errored(); _has_warning = _parse_handler->parse_state().logger().warned(); if (!errors->empty()) { _has_fatal_error = true; if (&_error_stream.get() != &detail::cnull) { print_errors_to(_error_stream); } return false; } _lines = std::move(_parse_handler->get_lines()); return true; } template const std::vector& Parser::get_lines() const { return _lines; } template typename Parser::error_range Parser::get_errors() const { return _parse_handler->parse_state().logger().get_errors(); } template const FilePosition Parser::get_error_position(const error::Error* error) const { if (!error || !error->is_linked_in_tree()) { return {}; } auto err_location = _parse_handler->parse_state().logger().location_of(error); if (err_location.is_synthesized()) { return {}; } auto loc_begin = lexy::get_input_location(_parse_handler->buffer(), err_location.begin()); FilePosition result { loc_begin.line_nr(), loc_begin.line_nr(), loc_begin.column_nr(), loc_begin.column_nr() }; if (err_location.begin() < err_location.end()) { auto loc_end = lexy::get_input_location(_parse_handler->buffer(), err_location.end(), loc_begin.anchor()); result.end_line = loc_end.line_nr(); result.end_column = loc_end.column_nr(); } return result; } template void Parser::print_errors_to(std::basic_ostream& stream) const { auto errors = get_errors(); if (errors.empty()) return; for (const auto error : errors) { dryad::visit_tree( error, [&](const error::BufferError* buffer_error) { stream << "buffer error: " << buffer_error->message() << '\n'; }, [&](const error::ParseError* parse_error) { auto position = get_error_position(parse_error); std::string pos_str = fmt::format(":{}:{}: ", position.start_line, position.start_column); stream << _file_path << pos_str << "parse error for '" << parse_error->production_name() << "': " << parse_error->message() << '\n'; }, [&](dryad::child_visitor visitor, const error::Semantic* semantic) { auto position = get_error_position(semantic); std::string pos_str = ": "; if (!position.is_empty()) { pos_str = fmt::format(":{}:{}: ", position.start_line, position.start_column); } stream << _file_path << pos_str << semantic->message() << '\n'; auto annotations = semantic->annotations(); for (auto annotation : annotations) { visitor(annotation); } }, [&](const error::PrimaryAnnotation* primary) { stream << primary->message() << '\n'; }, [&](const error::SecondaryAnnotation* secondary) { stream << secondary->message() << '\n'; }); } } template class ovdl::csv::Parser; template class ovdl::csv::Parser;