diff options
author | Spartan322 <Megacake1234@gmail.com> | 2023-11-28 11:09:26 +0100 |
---|---|---|
committer | Spartan322 <Megacake1234@gmail.com> | 2024-05-09 22:11:26 +0200 |
commit | 757114a3c5b748567b42f273c7b78ca039ae983c (patch) | |
tree | e07390b682052129c91f4b157068bcdd84ceecb4 /src/openvic-dataloader | |
parent | 7211a228e68c8a6b1ad1c1c5ec68c8d720b6d2ba (diff) |
Add `deps/dryad` -> https://github.com/Spartan322/dryadadd/dryad
Add `deps/fmt` -> https://github.com/fmtlib/fmt
Add `deps/range-v3` -> https://github.com/ericniebler/range-v3
Improve parser error and warning support
Update .clang-format
Update `deps/SCsub`
Diffstat (limited to 'src/openvic-dataloader')
29 files changed, 1497 insertions, 1246 deletions
diff --git a/src/openvic-dataloader/AbstractSyntaxTree.cpp b/src/openvic-dataloader/AbstractSyntaxTree.cpp new file mode 100644 index 0000000..11a90dc --- /dev/null +++ b/src/openvic-dataloader/AbstractSyntaxTree.cpp @@ -0,0 +1,27 @@ +#include <openvic-dataloader/AbstractSyntaxTree.hpp> + +using namespace ovdl; + +AbstractSyntaxTree::symbol_type AbstractSyntaxTree::intern(const char* str, std::size_t length) { + return _symbol_interner.intern(str, length); +} + +AbstractSyntaxTree::symbol_type AbstractSyntaxTree::intern(std::string_view str) { + return intern(str.data(), str.size()); +} + +const char* AbstractSyntaxTree::intern_cstr(const char* str, std::size_t length) { + return intern(str, length).c_str(_symbol_interner); +} + +const char* AbstractSyntaxTree::intern_cstr(std::string_view str) { + return intern_cstr(str.data(), str.size()); +} + +AbstractSyntaxTree::symbol_interner_type& AbstractSyntaxTree::symbol_interner() { + return _symbol_interner; +} + +const AbstractSyntaxTree::symbol_interner_type& AbstractSyntaxTree::symbol_interner() const { + return _symbol_interner; +}
\ No newline at end of file diff --git a/src/openvic-dataloader/DiagnosticLogger.cpp b/src/openvic-dataloader/DiagnosticLogger.cpp new file mode 100644 index 0000000..aae3dcb --- /dev/null +++ b/src/openvic-dataloader/DiagnosticLogger.cpp @@ -0,0 +1,16 @@ +#include <openvic-dataloader/DiagnosticLogger.hpp> + +using namespace ovdl; + +DiagnosticLogger::operator bool() const { + return !_errored; +} + +bool DiagnosticLogger::errored() const { return _errored; } +bool DiagnosticLogger::warned() const { return _warned; } + + +NodeLocation DiagnosticLogger::location_of(const error::Error* error) const { + auto result = _map.lookup(error); + return result ? *result : NodeLocation{}; +}
\ No newline at end of file diff --git a/src/openvic-dataloader/File.cpp b/src/openvic-dataloader/File.cpp new file mode 100644 index 0000000..9b27bf0 --- /dev/null +++ b/src/openvic-dataloader/File.cpp @@ -0,0 +1,9 @@ +#include <openvic-dataloader/File.hpp> + +using namespace ovdl; + +File::File(const char* path) : _path(path) {} + +const char* File::path() const noexcept { + return _path; +}
\ No newline at end of file diff --git a/src/openvic-dataloader/NodeLocation.cpp b/src/openvic-dataloader/NodeLocation.cpp new file mode 100644 index 0000000..9e4f669 --- /dev/null +++ b/src/openvic-dataloader/NodeLocation.cpp @@ -0,0 +1,26 @@ +#include <openvic-dataloader/NodeLocation.hpp> + +using namespace ovdl; + +NodeLocation::NodeLocation() = default; +NodeLocation::NodeLocation(const char* pos) : _begin(pos), + _end(pos) {} +NodeLocation::NodeLocation(const char* begin, const char* end) : _begin(begin), + _end(end) {} + +NodeLocation::NodeLocation(const NodeLocation&) noexcept = default; +NodeLocation& NodeLocation::operator=(const NodeLocation&) = default; + +NodeLocation::NodeLocation(NodeLocation&&) = default; +NodeLocation& NodeLocation::operator=(NodeLocation&&) = default; + +const char* NodeLocation::begin() const { return _begin; } +const char* NodeLocation::end() const { return _end; } + +bool NodeLocation::is_synthesized() const { return _begin == nullptr && _end == nullptr; } + +NodeLocation NodeLocation::make_from(const char* begin, const char* end) { + end++; + if (begin >= end) return NodeLocation(begin); + return NodeLocation(begin, end); +} diff --git a/src/openvic-dataloader/csv/CsvGrammar.hpp b/src/openvic-dataloader/csv/CsvGrammar.hpp index 712bddc..5451f26 100644 --- a/src/openvic-dataloader/csv/CsvGrammar.hpp +++ b/src/openvic-dataloader/csv/CsvGrammar.hpp @@ -7,14 +7,17 @@ #include <vector> #include <openvic-dataloader/csv/LineObject.hpp> +#include <openvic-dataloader/csv/Parser.hpp> #include <lexy/callback.hpp> #include <lexy/dsl.hpp> -#include "detail/LexyLitRange.hpp" +#include "detail/dsl.hpp" // Grammar Definitions // namespace ovdl::csv::grammar { + using EncodingType = ovdl::csv::EncodingType; + template<typename T> concept ParseChars = requires() { { T::character }; @@ -51,123 +54,117 @@ namespace ovdl::csv::grammar { .map<'"'>('"'); template<ParseOptions Options> - struct StringValue { - static constexpr auto rule = [] { - // Arbitrary code points - auto c = Options.character - Options.control; - - auto back_escape = lexy::dsl::backslash_escape // - .symbol<escaped_symbols>(); + struct CsvGrammar { + struct StringValue { + static constexpr auto rule = [] { + // Arbitrary code points + auto c = Options.character - Options.control; - auto quote_escape = lexy::dsl::escape(lexy::dsl::lit_c<'"'>) // - .template symbol<escaped_quote>(); + auto back_escape = lexy::dsl::backslash_escape // + .symbol<escaped_symbols>(); - return lexy::dsl::delimited(lexy::dsl::lit_c<'"'>, lexy::dsl::not_followed_by(lexy::dsl::lit_c<'"'>, lexy::dsl::lit_c<'"'>))(c, back_escape, quote_escape); - }(); - - static constexpr auto value = lexy::as_string<std::string>; - }; + auto quote_escape = lexy::dsl::escape(lexy::dsl::lit_c<'"'>) // + .template symbol<escaped_quote>(); - template<ParseOptions Options> - struct PlainValue { - static constexpr auto rule = [] { - if constexpr (Options.SupportStrings) { - return lexy::dsl::identifier(Options.character - (lexy::dsl::lit_b<Options.SepChar> / lexy::dsl::ascii::newline)); - } else { - auto escape_check_char = Options.character - (lexy::dsl::lit_b<Options.SepChar> / lexy::dsl::ascii::newline); - auto id_check_char = escape_check_char - lexy::dsl::lit_b<'\\'>; - auto id_segment = lexy::dsl::identifier(id_check_char); - auto escape_segement = lexy::dsl::token(escape_check_char); - auto escape_sym = lexy::dsl::symbol<escaped_symbols>(escape_segement); - auto escape_rule = lexy::dsl::lit_b<'\\'> >> escape_sym; - return lexy::dsl::list(id_segment | escape_rule); - } - }(); - static constexpr auto value = lexy::as_string<std::string>; - }; + return lexy::dsl::delimited(lexy::dsl::lit_c<'"'>, lexy::dsl::not_followed_by(lexy::dsl::lit_c<'"'>, lexy::dsl::lit_c<'"'>))(c, back_escape, quote_escape); + }(); - template<ParseOptions Options> - struct Value { - static constexpr auto rule = [] { - if constexpr (Options.SupportStrings) { - return lexy::dsl::p<StringValue<Options>> | lexy::dsl::p<PlainValue<Options>>; - } else { - return lexy::dsl::p<PlainValue<Options>>; - } - }(); - static constexpr auto value = lexy::forward<std::string>; - }; - - template<ParseOptions Options> - struct SepConst { - static constexpr auto rule = lexy::dsl::lit_b<Options.SepChar>; - static constexpr auto value = lexy::constant(1); - }; + static constexpr auto value = lexy::as_string<std::string>; + }; - template<ParseOptions Options> - struct Seperator { - static constexpr auto rule = lexy::dsl::list(lexy::dsl::p<SepConst<Options>>); - static constexpr auto value = lexy::count; - }; + struct PlainValue { + static constexpr auto rule = [] { + if constexpr (Options.SupportStrings) { + return lexy::dsl::identifier(Options.character - (lexy::dsl::lit_b<Options.SepChar> / lexy::dsl::ascii::newline)); + } else { + auto escape_check_char = Options.character - (lexy::dsl::lit_b<Options.SepChar> / lexy::dsl::ascii::newline); + auto id_check_char = escape_check_char - lexy::dsl::lit_b<'\\'>; + auto id_segment = lexy::dsl::identifier(id_check_char); + auto escape_segement = lexy::dsl::token(escape_check_char); + auto escape_sym = lexy::dsl::symbol<escaped_symbols>(escape_segement); + auto escape_rule = lexy::dsl::lit_b<'\\'> >> escape_sym; + return lexy::dsl::list(id_segment | escape_rule); + } + }(); + static constexpr auto value = lexy::as_string<std::string>; + }; - template<ParseOptions Options> - struct LineEnd { - static constexpr auto rule = lexy::dsl::list(lexy::dsl::p<Value<Options>>, lexy::dsl::trailing_sep(lexy::dsl::p<Seperator<Options>>)); - static constexpr auto value = lexy::fold_inplace<ovdl::csv::LineObject>( - std::initializer_list<ovdl::csv::LineObject::value_type> {}, - [](ovdl::csv::LineObject& result, auto&& arg) { - if constexpr (std::is_same_v<std::decay_t<decltype(arg)>, std::size_t>) { - // Count seperators, adds to previous value, making it a position - using position_type = ovdl::csv::LineObject::position_type; - result.emplace_back(static_cast<position_type>(arg + result.back().first), ""); + struct Value { + static constexpr auto rule = [] { + if constexpr (Options.SupportStrings) { + return lexy::dsl::p<StringValue> | lexy::dsl::p<PlainValue>; } else { - if (result.empty()) result.emplace_back(0u, LEXY_MOV(arg)); - else { - auto& [pos, value] = result.back(); - value = arg; - } + return lexy::dsl::p<PlainValue>; } - }); - }; + }(); + static constexpr auto value = lexy::forward<std::string>; + }; - template<ParseOptions Options> - struct Line { - static constexpr auto suffix_setter(ovdl::csv::LineObject& line) { - auto& [position, value] = line.back(); - if (value.empty()) { - line.set_suffix_end(position); - line.pop_back(); - } else { - line.set_suffix_end(position + 1); - } + struct SepConst { + static constexpr auto rule = lexy::dsl::lit_b<Options.SepChar>; + static constexpr auto value = lexy::constant(1); }; - static constexpr auto rule = lexy::dsl::p<LineEnd<Options>> | lexy::dsl::p<Seperator<Options>> >> lexy::dsl::opt(lexy::dsl::p<LineEnd<Options>>); - static constexpr auto value = - lexy::callback<ovdl::csv::LineObject>( - [](ovdl::csv::LineObject&& line) { - suffix_setter(line); - return LEXY_MOV(line); - }, - [](std::size_t prefix_count, ovdl::csv::LineObject&& line) { - line.set_prefix_end(prefix_count); - // position needs to be adjusted to prefix - for (auto& [position, value] : line) { - position += prefix_count; + struct Seperator { + static constexpr auto rule = lexy::dsl::list(lexy::dsl::p<SepConst>); + static constexpr auto value = lexy::count; + }; + + struct LineEnd { + static constexpr auto rule = lexy::dsl::list(lexy::dsl::p<Value>, lexy::dsl::trailing_sep(lexy::dsl::p<Seperator>)); + static constexpr auto value = lexy::fold_inplace<ovdl::csv::LineObject>( + std::initializer_list<ovdl::csv::LineObject::value_type> {}, + [](ovdl::csv::LineObject& result, auto&& arg) { + if constexpr (std::is_same_v<std::decay_t<decltype(arg)>, std::size_t>) { + // Count seperators, adds to previous value, making it a position + using position_type = ovdl::csv::LineObject::position_type; + result.emplace_back(static_cast<position_type>(arg + result.back().first), ""); + } else { + if (result.empty()) result.emplace_back(0u, LEXY_MOV(arg)); + else { + auto& [pos, value] = result.back(); + value = arg; + } } - suffix_setter(line); - return LEXY_MOV(line); - }, - [](std::size_t suffix_count, lexy::nullopt = {}) { - return ovdl::csv::LineObject(0, {}, suffix_count + 1); }); + }; + + struct Line { + static constexpr auto suffix_setter(ovdl::csv::LineObject& line) { + auto& [position, value] = line.back(); + if (value.empty()) { + line.set_suffix_end(position); + line.pop_back(); + } else { + line.set_suffix_end(position + 1); + } + }; + + static constexpr auto rule = lexy::dsl::p<LineEnd> | lexy::dsl::p<Seperator> >> lexy::dsl::opt(lexy::dsl::p<LineEnd>); + static constexpr auto value = + lexy::callback<ovdl::csv::LineObject>( + [](ovdl::csv::LineObject&& line) { + suffix_setter(line); + return LEXY_MOV(line); + }, + [](std::size_t prefix_count, ovdl::csv::LineObject&& line) { + line.set_prefix_end(prefix_count); + // position needs to be adjusted to prefix + for (auto& [position, value] : line) { + position += prefix_count; + } + suffix_setter(line); + return LEXY_MOV(line); + }, + [](std::size_t suffix_count, lexy::nullopt = {}) { + return ovdl::csv::LineObject(0, {}, suffix_count + 1); + }); + }; }; template<ParseOptions Options> struct File { - static constexpr auto rule = lexy::dsl::terminator(lexy::dsl::eof).opt_list( - lexy::dsl::p<Line<Options>> | lexy::dsl::newline - ); + static constexpr auto rule = lexy::dsl::terminator(lexy::dsl::eof).opt_list(lexy::dsl::p<typename CsvGrammar<Options>::Line> | lexy::dsl::newline); static constexpr auto value = lexy::as_list<std::vector<ovdl::csv::LineObject>>; }; @@ -199,7 +196,7 @@ namespace ovdl::csv::grammar { namespace ovdl::csv::grammar::windows1252 { struct windows1252_t { - static constexpr auto character = detail::lexydsl::make_range<0x01, 0xFF>(); + static constexpr auto character = dsl::make_range<0x01, 0xFF>(); static constexpr auto control = lexy::dsl::ascii::control / lexy::dsl::lit_b<0x81> / lexy::dsl::lit_b<0x8D> / lexy::dsl::lit_b<0x8F> / diff --git a/src/openvic-dataloader/csv/CsvParseState.hpp b/src/openvic-dataloader/csv/CsvParseState.hpp new file mode 100644 index 0000000..2390453 --- /dev/null +++ b/src/openvic-dataloader/csv/CsvParseState.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include <openvic-dataloader/File.hpp> +#include <openvic-dataloader/ParseState.hpp> +#include <openvic-dataloader/csv/LineObject.hpp> +#include <openvic-dataloader/csv/Parser.hpp> + +#include <lexy/encoding.hpp> + +template<ovdl::csv::EncodingType> +struct LexyEncodingFrom { +}; + +template<> +struct LexyEncodingFrom<ovdl::csv::EncodingType::Windows1252> { + using encoding = lexy::default_encoding; +}; + +template<> +struct LexyEncodingFrom<ovdl::csv::EncodingType::Utf8> { + using encoding = lexy::utf8_char_encoding; +}; + +template<ovdl::csv::EncodingType Encoding> +using CsvFile = ovdl::BasicFile<typename LexyEncodingFrom<Encoding>::encoding, std::vector<ovdl::csv::LineObject>>; + +template<ovdl::csv::EncodingType Encoding> +using CsvParseState = ovdl::FileParseState<CsvFile<Encoding>>;
\ No newline at end of file diff --git a/src/openvic-dataloader/csv/Parser.cpp b/src/openvic-dataloader/csv/Parser.cpp index 0ca3402..849ea05 100644 --- a/src/openvic-dataloader/csv/Parser.cpp +++ b/src/openvic-dataloader/csv/Parser.cpp @@ -1,47 +1,34 @@ -#include <memory> #include <vector> +#include <openvic-dataloader/File.hpp> #include <openvic-dataloader/csv/LineObject.hpp> #include <openvic-dataloader/csv/Parser.hpp> -#include <openvic-dataloader/detail/ClassExport.hpp> +#include <openvic-dataloader/detail/LexyReportError.hpp> +#include <openvic-dataloader/detail/OStreamOutputIterator.hpp> +#include <openvic-dataloader/detail/utility/Utility.hpp> #include <lexy/action/parse.hpp> #include <lexy/encoding.hpp> #include <lexy/input/buffer.hpp> #include <lexy/input/file.hpp> -#include "csv/CsvGrammar.hpp" -#include "detail/BasicBufferHandler.hpp" -#include "detail/LexyReportError.hpp" -#include "detail/OStreamOutputIterator.hpp" +#include "CsvGrammar.hpp" +#include "CsvParseState.hpp" +#include "detail/NullBuff.hpp" +#include "detail/ParseHandler.hpp" using namespace ovdl; using namespace ovdl::csv; -/// BufferHandler /// - -template<EncodingType> -struct LexyEncodingFrom { -}; - -template<> -struct LexyEncodingFrom<EncodingType::Windows1252> { - using encoding = lexy::default_encoding; -}; - -template<> -struct LexyEncodingFrom<EncodingType::Utf8> { - using encoding = lexy::utf8_char_encoding; -}; +/// ParseHandler /// template<EncodingType Encoding> -class Parser<Encoding>::BufferHandler final : public detail::BasicBufferHandler<typename LexyEncodingFrom<Encoding>::encoding> { -public: - template<typename Node, typename ErrorCallback> - std::optional<std::vector<ParseError>> parse(const ErrorCallback& callback) { - auto result = lexy::parse<Node>(this->_buffer, callback); +struct Parser<Encoding>::ParseHandler final : detail::BasicFileParseHandler<CsvParseState<Encoding>> { + template<typename Node> + std::optional<DiagnosticLogger::error_range> parse() { + auto result = lexy::parse<Node>(this->buffer(), *this->_parse_state, this->_parse_state->logger().error_callback()); if (!result) { - return result.errors(); + return this->_parse_state->logger().get_errors(); } _lines = std::move(result.value()); return std::nullopt; @@ -59,8 +46,14 @@ private: template<EncodingType Encoding> Parser<Encoding>::Parser() - : _buffer_handler(std::make_unique<BufferHandler>()) { - set_error_log_to_stderr(); + : _parse_handler(std::make_unique<ParseHandler>()) { + set_error_log_to_null(); +} + +template<EncodingType Encoding> +Parser<Encoding>::Parser(std::basic_ostream<char>& error_stream) + : _parse_handler(std::make_unique<ParseHandler>()) { + set_error_log_to(error_stream); } template<EncodingType Encoding> @@ -115,28 +108,31 @@ Parser<Encoding> Parser<Encoding>::from_file(const std::filesystem::path& path) /// template<EncodingType Encoding> template<typename... Args> -constexpr void Parser<Encoding>::_run_load_func(detail::LoadCallback<BufferHandler, Args...> auto func, Args... args) { - _warnings.clear(); - _errors.clear(); +constexpr void Parser<Encoding>::_run_load_func(detail::LoadCallback<ParseHandler, Args...> auto func, Args... args) { _has_fatal_error = false; - if (auto error = func(_buffer_handler.get(), std::forward<Args>(args)...); error) { - _has_fatal_error = error.value().type == ParseError::Type::Fatal; - _errors.push_back(error.value()); - _error_stream.get() << "Error: " << _errors.back().message << '\n'; + auto error = func(_parse_handler.get(), std::forward<Args>(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<error::BufferError>(DiagnosticLogger::DiagnosticKind::error, fmt::runtime(error_message)); + } + if (has_error() && &_error_stream.get() != &detail::cnull) { + print_errors_to(_error_stream.get()); } } template<EncodingType Encoding> constexpr Parser<Encoding>& Parser<Encoding>::load_from_buffer(const char* data, std::size_t size) { // Type can't be deduced? - _run_load_func(std::mem_fn(&BufferHandler::load_buffer_size), data, size); + _run_load_func(std::mem_fn(&ParseHandler::load_buffer_size), data, size); return *this; } template<EncodingType Encoding> constexpr Parser<Encoding>& Parser<Encoding>::load_from_buffer(const char* start, const char* end) { // Type can't be deduced? - _run_load_func(std::mem_fn(&BufferHandler::load_buffer), start, end); + _run_load_func(std::mem_fn(&ParseHandler::load_buffer), start, end); return *this; } @@ -149,7 +145,7 @@ template<EncodingType Encoding> constexpr Parser<Encoding>& Parser<Encoding>::load_from_file(const char* path) { _file_path = path; // Type can be deduced?? - _run_load_func(std::mem_fn(&BufferHandler::load_file), path); + _run_load_func(std::mem_fn(&ParseHandler::load_file), path); return *this; } @@ -159,38 +155,34 @@ Parser<Encoding>& Parser<Encoding>::load_from_file(const std::filesystem::path& } template<EncodingType Encoding> -constexpr Parser<Encoding>& Parser<Encoding>::load_from_file(const detail::Has_c_str auto& path) { - return load_from_file(path.c_str()); -} - -template<EncodingType Encoding> bool Parser<Encoding>::parse_csv(bool handle_strings) { - if (!_buffer_handler->is_valid()) { + if (!_parse_handler->is_valid()) { return false; } - std::optional<std::vector<ParseError>> errors; - auto report_error = ovdl::detail::ReporError.path(_file_path).to(detail::OStreamOutputIterator { _error_stream }); + std::optional<Parser<Encoding>::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 = _buffer_handler->template parse<csv::grammar::windows1252::strings::SemiColonFile>(report_error); + errors = _parse_handler->template parse<csv::grammar::windows1252::strings::SemiColonFile>(); else - errors = _buffer_handler->template parse<csv::grammar::windows1252::SemiColonFile>(report_error); + errors = _parse_handler->template parse<csv::grammar::windows1252::SemiColonFile>(); } else { if (handle_strings) - errors = _buffer_handler->template parse<csv::grammar::utf8::strings::SemiColonFile>(report_error); + errors = _parse_handler->template parse<csv::grammar::utf8::strings::SemiColonFile>(); else - errors = _buffer_handler->template parse<csv::grammar::utf8::SemiColonFile>(report_error); + errors = _parse_handler->template parse<csv::grammar::utf8::SemiColonFile>(); } - if (errors) { - _errors.reserve(errors->size()); - for (auto& err : errors.value()) { - _has_fatal_error |= err.type == ParseError::Type::Fatal; - _errors.push_back(err); + _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(_buffer_handler->get_lines()); + _lines = std::move(_parse_handler->get_lines()); return true; } @@ -199,5 +191,60 @@ const std::vector<csv::LineObject>& Parser<Encoding>::get_lines() const { return _lines; } +template<EncodingType Encoding> +typename Parser<Encoding>::error_range Parser<Encoding>::get_errors() const { + return _parse_handler->parse_state().logger().get_errors(); +} + +template<EncodingType Encoding> +const FilePosition Parser<Encoding>::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<EncodingType Encoding> +void Parser<Encoding>::print_errors_to(std::basic_ostream<char>& 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->message() << '\n'; + }, + [&](const error::ParseError* parse_error) { + stream << parse_error->message() << '\n'; + }, + [&](dryad::child_visitor<error::ErrorKind> visitor, const error::Semantic* semantic) { + stream << semantic->message() << '\n'; + auto annotations = semantic->annotations(); + if (annotations.empty()) return; + 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<EncodingType::Windows1252>; template class ovdl::csv::Parser<EncodingType::Utf8>;
\ No newline at end of file diff --git a/src/openvic-dataloader/detail/BasicBufferHandler.hpp b/src/openvic-dataloader/detail/BasicBufferHandler.hpp deleted file mode 100644 index f26544b..0000000 --- a/src/openvic-dataloader/detail/BasicBufferHandler.hpp +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include <optional> - -#include <openvic-dataloader/ParseError.hpp> -#include <openvic-dataloader/detail/OptionalConstexpr.hpp> - -#include <lexy/encoding.hpp> -#include <lexy/input/buffer.hpp> -#include <lexy/input/file.hpp> - -#include "detail/Errors.hpp" - -namespace ovdl::detail { - template<typename Encoding = lexy::default_encoding, typename MemoryResource = void> - class BasicBufferHandler { - public: - using encoding_type = Encoding; - - OVDL_OPTIONAL_CONSTEXPR bool is_valid() const { - return _buffer.data() != nullptr; - } - - OVDL_OPTIONAL_CONSTEXPR std::optional<ovdl::ParseError> load_buffer_size(const char* data, std::size_t size) { - _buffer = lexy::buffer<Encoding, MemoryResource>(data, size); - return std::nullopt; - } - - OVDL_OPTIONAL_CONSTEXPR std::optional<ovdl::ParseError> load_buffer(const char* start, const char* end) { - _buffer = lexy::buffer<Encoding, MemoryResource>(start, end); - return std::nullopt; - } - - std::optional<ovdl::ParseError> load_file(const char* path) { - auto file = lexy::read_file<Encoding, lexy::encoding_endianness::bom, MemoryResource>(path); - if (!file) { - return ovdl::errors::make_no_file_error(path); - } - - _buffer = file.buffer(); - return std::nullopt; - } - - const auto& get_buffer() const { - return _buffer; - } - - protected: - lexy::buffer<Encoding, MemoryResource> _buffer; - }; -}
\ No newline at end of file diff --git a/src/openvic-dataloader/detail/BufferError.hpp b/src/openvic-dataloader/detail/BufferError.hpp new file mode 100644 index 0000000..1fbb0f4 --- /dev/null +++ b/src/openvic-dataloader/detail/BufferError.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include <cstdint> + +namespace ovdl::detail { + enum class buffer_error : std::uint8_t { + success, + /// An internal OS error, such as failure to read from the file. + os_error, + /// The file was not found. + file_not_found, + /// The file cannot be opened. + permission_denied, + /// The buffer failed to handle the data + buffer_is_null + }; +}
\ No newline at end of file diff --git a/src/openvic-dataloader/detail/DetectUtf8.hpp b/src/openvic-dataloader/detail/DetectUtf8.hpp index 2045b3c..e9d0350 100644 --- a/src/openvic-dataloader/detail/DetectUtf8.hpp +++ b/src/openvic-dataloader/detail/DetectUtf8.hpp @@ -3,7 +3,7 @@ #include <lexy/action/match.hpp> #include <lexy/dsl.hpp> -#include "detail/LexyLitRange.hpp" +#include "detail/dsl.hpp" namespace ovdl::detail { namespace detect_utf8 { @@ -18,15 +18,15 @@ namespace ovdl::detail { constexpr auto is_not_ascii_flag = lexy::dsl::context_flag<DetectUtf8>; // & 0b10000000 == 0b00000000 - constexpr auto ascii_values = lexydsl::make_range<0b00000000, 0b01111111>(); + constexpr auto ascii_values = dsl::make_range<0b00000000, 0b01111111>(); // & 0b11100000 == 0b11000000 - constexpr auto two_byte = lexydsl::make_range<0b11000000, 0b11011111>(); + constexpr auto two_byte = dsl::make_range<0b11000000, 0b11011111>(); // & 0b11110000 == 0b11100000 - constexpr auto three_byte = lexydsl::make_range<0b11100000, 0b11101111>(); + constexpr auto three_byte = dsl::make_range<0b11100000, 0b11101111>(); // & 0b11111000 == 0b11110000 - constexpr auto four_byte = lexydsl::make_range<0b11110000, 0b11110111>(); + constexpr auto four_byte = dsl::make_range<0b11110000, 0b11110111>(); // & 0b11000000 == 0b10000000 - constexpr auto check_bytes = lexydsl::make_range<0b10000000, 0b10111111>(); + constexpr auto check_bytes = dsl::make_range<0b10000000, 0b10111111>(); constexpr auto utf8_check = ((four_byte >> lexy::dsl::times<3>(check_bytes)) | diff --git a/src/openvic-dataloader/detail/LexyLitRange.hpp b/src/openvic-dataloader/detail/LexyLitRange.hpp deleted file mode 100644 index a6761a8..0000000 --- a/src/openvic-dataloader/detail/LexyLitRange.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include <lexy/dsl/literal.hpp> - -namespace ovdl::detail::lexydsl { - template<unsigned char LOW, unsigned char HIGH> - consteval auto make_range() { - if constexpr (LOW == HIGH) { - return lexy::dsl::lit_c<LOW>; - } else if constexpr (LOW == (HIGH - 1)) { - return lexy::dsl::lit_c<LOW> / lexy::dsl::lit_c<HIGH>; - } else { - return lexy::dsl::lit_c<LOW> / make_range<LOW + 1, HIGH>(); - } - } -}
\ No newline at end of file diff --git a/src/openvic-dataloader/detail/LexyReportError.hpp b/src/openvic-dataloader/detail/LexyReportError.hpp deleted file mode 100644 index 213090b..0000000 --- a/src/openvic-dataloader/detail/LexyReportError.hpp +++ /dev/null @@ -1,106 +0,0 @@ -#pragma once - -#include <cstddef> -#include <sstream> -#include <utility> -#include <vector> - -#include <openvic-dataloader/ParseData.hpp> -#include <openvic-dataloader/ParseError.hpp> -#include <openvic-dataloader/detail/Concepts.hpp> - -#include <lexy/input_location.hpp> -#include <lexy/visualize.hpp> - -#include <lexy_ext/report_error.hpp> - -namespace ovdl::detail { - template<typename OutputIterator> - 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<ParseError> _errors; - - using return_type = std::vector<ParseError>; - - template<typename Input, typename Reader, typename Tag> - void operator()(const lexy::error_context<Input>& context, const lexy::error<Reader, Tag>& 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::basic_stringstream<typename Reader::encoding::char_type> message; - - // Write the main annotation. - if constexpr (std::is_same_v<Tag, lexy::expected_literal>) { - auto string = lexy::_detail::make_literal_lexeme<typename Reader::encoding>(error.string(), error.length()); - - message << "expected '" << string.data() << '\''; - } else if constexpr (std::is_same_v<Tag, lexy::expected_keyword>) { - auto string = lexy::_detail::make_literal_lexeme<typename Reader::encoding>(error.string(), error.length()); - - message << "expected keyword '" << string.data() << '\''; - } else if constexpr (std::is_same_v<Tag, lexy::expected_char_class>) { - message << "expected " << error.name(); - } else { - message << error.message(); - } - - _errors.push_back( - ParseError { - ParseError::Type::Fatal, // TODO: distinguish recoverable errors from fatal errors - std::move(message.str()), - 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 }; - } - - constexpr _ReportError path(const detail::Has_c_str auto& path_object) const { - return path(path_object.c_str()); - } - - /// Specifies an output iterator where the errors are written to. - template<typename OI> - constexpr _ReportError<OI> 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<lexy::stderr_output_iterator> {}; -}
\ No newline at end of file diff --git a/src/openvic-dataloader/detail/NullBuff.hpp b/src/openvic-dataloader/detail/NullBuff.hpp index baf9e1b..e5c6f4a 100644 --- a/src/openvic-dataloader/detail/NullBuff.hpp +++ b/src/openvic-dataloader/detail/NullBuff.hpp @@ -22,9 +22,9 @@ namespace ovdl::detail { basic_nullbuf<cT, traits> m_sbuf; }; - typedef basic_onullstream<char> onullstream; - typedef basic_onullstream<wchar_t> wonullstream; + using onullstream = basic_onullstream<char>; + using wonullstream = basic_onullstream<wchar_t>; - inline onullstream cnull; - inline onullstream wcnull; + static inline onullstream cnull; + static inline onullstream wcnull; }
\ No newline at end of file diff --git a/src/openvic-dataloader/detail/OStreamOutputIterator.hpp b/src/openvic-dataloader/detail/OStreamOutputIterator.hpp deleted file mode 100644 index 81f6c89..0000000 --- a/src/openvic-dataloader/detail/OStreamOutputIterator.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include <ostream> - -namespace ovdl::detail { - struct OStreamOutputIterator { - std::reference_wrapper<std::ostream> _stream; - - auto operator*() const noexcept { - return *this; - } - auto operator++(int) const noexcept { - return *this; - } - - OStreamOutputIterator& operator=(char c) { - _stream.get().put(c); - return *this; - } - }; -}
\ No newline at end of file diff --git a/src/openvic-dataloader/detail/ParseHandler.hpp b/src/openvic-dataloader/detail/ParseHandler.hpp new file mode 100644 index 0000000..fbec0d7 --- /dev/null +++ b/src/openvic-dataloader/detail/ParseHandler.hpp @@ -0,0 +1,145 @@ +#pragma once + +#include <utility> + +#include <openvic-dataloader/ParseState.hpp> +#include <openvic-dataloader/detail/utility/Concepts.hpp> + +#include <lexy/encoding.hpp> +#include <lexy/input/buffer.hpp> +#include <lexy/input/file.hpp> + +#include "detail/BufferError.hpp" + +namespace ovdl::detail { + template<typename Derived> + struct ParseHandler { + std::string make_error_from(buffer_error error) { + switch (error) { + using enum ovdl::detail::buffer_error; + case buffer_is_null: + return "Buffer could not be loaded."; + case os_error: + return "OS file error."; + case file_not_found: + return "File not found."; + case permission_denied: + return "Read Permission denied."; + default: + return ""; + } + } + + template<typename... Args> + constexpr void _run_load_func(detail::LoadCallback<Derived, Args...> auto func, Args... args); + }; + + template<IsFileParseState ParseState, typename MemoryResource = void> + struct BasicFileParseHandler : ParseHandler<BasicFileParseHandler<ParseState, MemoryResource>> { + using parse_state_type = ParseState; + using encoding_type = typename parse_state_type::file_type::encoding_type; + + constexpr bool is_valid() const { + if (!_parse_state) return false; + return buffer().data() != nullptr; + } + + constexpr buffer_error load_buffer_size(const char* data, std::size_t size) { + lexy::buffer<encoding_type, MemoryResource> buffer(data, size); + if (buffer.data() == nullptr) return buffer_error::buffer_is_null; + _parse_state.reset(new parse_state_type { std::move(buffer) }); + return is_valid() ? buffer_error::success : buffer_error::buffer_is_null; + } + + constexpr buffer_error load_buffer(const char* start, const char* end) { + lexy::buffer<encoding_type, MemoryResource> buffer(start, end); + if (buffer.data() == nullptr) return buffer_error::buffer_is_null; + _parse_state.reset(new parse_state_type { std::move(buffer) }); + return is_valid() ? buffer_error::success : buffer_error::buffer_is_null; + } + + buffer_error load_file(const char* path) { + lexy::read_file_result file = lexy::read_file<encoding_type, lexy::encoding_endianness::bom, MemoryResource>(path); + if (!file) { + _parse_state.reset(new parse_state_type { path, lexy::buffer<typename parse_state_type::file_type::encoding_type>() }); + return ovdl::detail::from_underlying<buffer_error>(ovdl::detail::to_underlying(file.error())); + } + _parse_state.reset(new parse_state_type { path, std::move(file).buffer() }); + return is_valid() ? buffer_error::success : buffer_error::buffer_is_null; + } + + const char* path() const { + if (!_parse_state) return ""; + return _parse_state->file().path(); + } + + parse_state_type& parse_state() { + return *_parse_state; + } + + const parse_state_type& parse_state() const { + return *_parse_state; + } + + constexpr const auto& buffer() const { + return _parse_state->file().buffer(); + } + + protected: + std::unique_ptr<parse_state_type> _parse_state; + }; + + template<IsParseState ParseState, typename MemoryResource = void> + struct BasicStateParseHandler : ParseHandler<BasicStateParseHandler<ParseState, MemoryResource>> { + using parse_state_type = ParseState; + using encoding_type = typename parse_state_type::ast_type::file_type::encoding_type; + + constexpr bool is_valid() const { + if (!_parse_state) return false; + return buffer().data() != nullptr; + } + + constexpr buffer_error load_buffer_size(const char* data, std::size_t size) { + lexy::buffer<encoding_type, MemoryResource> buffer(data, size); + _parse_state.reset(new parse_state_type { std::move(buffer) }); + return is_valid() ? buffer_error::success : buffer_error::buffer_is_null; + } + + constexpr buffer_error load_buffer(const char* start, const char* end) { + lexy::buffer<encoding_type, MemoryResource> buffer(start, end); + _parse_state.reset(new parse_state_type { std::move(buffer) }); + return is_valid() ? buffer_error::success : buffer_error::buffer_is_null; + } + + buffer_error load_file(const char* path) { + lexy::read_file_result file = lexy::read_file<encoding_type, lexy::encoding_endianness::bom, MemoryResource>(path); + if (!file) { + _parse_state.reset(new parse_state_type { path, lexy::buffer<typename parse_state_type::ast_type::file_type::encoding_type>() }); + return ovdl::detail::from_underlying<buffer_error>(ovdl::detail::to_underlying(file.error())); + } + + _parse_state.reset(new parse_state_type { path, std::move(file).buffer() }); + return is_valid() ? buffer_error::success : buffer_error::buffer_is_null; + } + + const char* path() const { + if (!_parse_state) return ""; + return _parse_state->ast().file().path(); + } + + parse_state_type& parse_state() { + return *_parse_state; + } + + const parse_state_type& parse_state() const { + return *_parse_state; + } + + constexpr const auto& buffer() const { + return _parse_state->ast().file().buffer(); + } + + protected: + std::unique_ptr<parse_state_type> _parse_state; + }; +}
\ No newline at end of file diff --git a/src/openvic-dataloader/detail/BasicParser.cpp b/src/openvic-dataloader/detail/Parser.cpp index 212bf00..fd87687 100644 --- a/src/openvic-dataloader/detail/BasicParser.cpp +++ b/src/openvic-dataloader/detail/Parser.cpp @@ -1,7 +1,7 @@ #include <iostream> #include <ostream> -#include <openvic-dataloader/detail/BasicParser.hpp> +#include <openvic-dataloader/Parser.hpp> #include "detail/NullBuff.hpp" @@ -27,7 +27,7 @@ void BasicParser::set_error_log_to(std::basic_ostream<char>& stream) { } bool BasicParser::has_error() const { - return !_errors.empty(); + return _has_error; } bool BasicParser::has_fatal_error() const { @@ -35,15 +35,7 @@ bool BasicParser::has_fatal_error() const { } bool BasicParser::has_warning() const { - return !_warnings.empty(); -} - -const std::vector<ovdl::ParseError>& BasicParser::get_errors() const { - return _errors; -} - -const std::vector<ovdl::ParseWarning>& BasicParser::get_warnings() const { - return _warnings; + return _has_warning; } std::string_view BasicParser::get_file_path() const { diff --git a/src/openvic-dataloader/detail/StringLiteral.hpp b/src/openvic-dataloader/detail/StringLiteral.hpp new file mode 100644 index 0000000..985b087 --- /dev/null +++ b/src/openvic-dataloader/detail/StringLiteral.hpp @@ -0,0 +1,232 @@ +#pragma once + +#include <algorithm> +#include <array> +#include <cstddef> +#include <string> +#include <string_view> +#include <type_traits> + +#include <lexy/_detail/config.hpp> +#include <lexy/_detail/integer_sequence.hpp> +#include <lexy/dsl/identifier.hpp> + +namespace ovdl::detail { + + template<std::size_t N, typename CharT> + struct string_literal; + + struct _string_literal { + protected: + static constexpr auto _to_string(const auto& input) { return string_literal(input); } + static constexpr auto _concat(const auto&... input) { return string_literal(_to_string(input)...); } + }; + + template<std::size_t N, typename CharT = char> + struct string_literal : _string_literal { + using value_type = CharT; + using pointer = value_type*; + using const_pointer = const value_type*; + using reference = value_type&; + using const_reference = const value_type&; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using traits_type = std::char_traits<value_type>; + + static constexpr size_type npos = size_type(-1); + + static constexpr auto size = std::integral_constant<std::size_t, N - 1> {}; + static constexpr auto length = size; + + value_type _data[N]; + + constexpr string_literal() noexcept = default; + + constexpr string_literal(const value_type (&literal)[N]) noexcept { + for (auto i = 0u; i != N; i++) + _data[i] = literal[i]; + } + + constexpr string_literal(value_type c) noexcept : _data {} { + _data[0] = c; + } + + constexpr auto begin() noexcept { return std::begin(_data); } + constexpr auto end() noexcept { return std::end(_data); } + constexpr auto cbegin() const noexcept { return std::cbegin(_data); } + constexpr auto cend() const noexcept { return std::cend(_data); } + constexpr auto rbegin() noexcept { return std::rbegin(_data); } + constexpr auto rend() noexcept { return std::rend(_data); } + constexpr auto crbegin() const noexcept { return std::crbegin(_data); } + constexpr auto crend() const noexcept { return std::crend(_data); } + + constexpr auto front() const noexcept { return as_string_view().front(); } + constexpr auto back() const noexcept { return as_string_view().back(); } + constexpr auto at(size_type pos) const { return as_string_view().at(pos); } + + constexpr auto operator[](size_type pos) const noexcept { return as_string_view()[pos]; } + + constexpr bool empty() const { return as_string_view().empty(); } + + template<size_type N2> + constexpr bool operator==(const string_literal<N2, value_type>& other) const { + return as_string_view() == other.as_string_view(); + } + + constexpr bool starts_with(std::basic_string_view<value_type> sv) const noexcept { return as_string_view().starts_with(sv); } + constexpr bool starts_with(value_type ch) const noexcept { return as_string_view().starts_with(ch); } + constexpr bool starts_with(const value_type* s) const { return as_string_view().starts_with(s); } + + template<size_type N2> + constexpr bool starts_with(const string_literal<N2, value_type>& other) const { + return starts_with(other.as_string_view()); + } + + constexpr bool ends_with(std::basic_string_view<value_type> sv) const noexcept { return as_string_view().ends_with(sv); } + constexpr bool ends_with(value_type ch) const noexcept { return as_string_view().ends_with(ch); } + constexpr bool ends_with(const value_type* s) const { return as_string_view().ends_with(s); } + + template<size_type N2> + constexpr bool ends_with(const string_literal<N2, value_type>& other) const { + return ends_with(other.as_string_view()); + } + + constexpr auto clear() const noexcept { + return string_literal<0, value_type> {}; + } + + constexpr auto push_back(value_type c) const noexcept { + return *this + string_literal { c }; + } + + constexpr auto pop_back() const noexcept { + string_literal<N - 1, value_type> result {}; + for (auto i = 0u; i != N - 2; i++) + result._data[i] = _data[i]; + return result; + } + + template<size_type count> + constexpr auto append(value_type ch) const noexcept { + string_literal<N + count, value_type> result {}; + for (auto i = 0u; i != N; i++) + result._data[i] = _data[i]; + + for (auto i = N; i != N + count; i++) + result._data[i] = ch; + + return result; + } + + template<size_type N2> + constexpr auto append(string_literal<N2, value_type> str) const noexcept { + return *this + str; + } + + template<size_type N2> + constexpr auto append(const value_type (&literal)[N2]) const noexcept { + return *this + literal; + } + + template<size_type pos = 0, size_type count = npos> + constexpr auto substr() const noexcept { + static_assert(pos <= N, "pos must be less than or equal to N"); + constexpr size_type result_size = std::min(count - pos, N - pos); + + string_literal<result_size, value_type> result {}; + for (size_type i = 0u, i2 = pos; i != result_size; i++, i2++) + result._data[i] = _data[i2]; + return result; + } + + constexpr auto substr() const noexcept { + return substr<>(); + } + + constexpr std::string_view substr(size_type pos, size_type count = npos) const noexcept { + return as_string_view().substr(pos, count); + } + + constexpr size_type find(std::string_view str, size_type pos = 0) const noexcept { + return as_string_view().find(str, pos); + } + + template<size_type N2> + constexpr size_type find(const value_type (&literal)[N2], size_type pos = 0) const noexcept { + return as_string_view().find(literal, pos, N2 - 2); + } + + constexpr size_type rfind(std::string_view str, size_type pos = 0) const noexcept { + return as_string_view().rfind(str, pos); + } + + template<size_type N2> + constexpr size_type rfind(const value_type (&literal)[N2], size_type pos = 0) const noexcept { + return as_string_view().find(literal, pos, N2 - 2); + } + + constexpr int compare(std::string_view str) const noexcept { + return as_string_view().compare(str); + } + + template<size_type N2> + constexpr int compare(const value_type (&literal)[N2]) const noexcept { + return as_string_view().compare(0, N, literal, N2 - 2); + } + + constexpr operator std::basic_string_view<value_type>() const& { return as_string_view(); } + constexpr operator std::basic_string_view<value_type>() const&& = delete; + + constexpr std::basic_string_view<value_type> as_string_view() const& { return std::basic_string_view<value_type>(_data, size() - 1); } + constexpr std::basic_string_view<value_type> as_string_view() const&& = delete; + + constexpr operator const value_type*() const& { return c_str(); } + constexpr operator const value_type*() const&& = delete; + + constexpr const value_type* c_str() const& { return _data; } + constexpr const value_type* c_str() const&& = delete; + + constexpr const value_type* data() const& { return _data; } + constexpr const value_type* data() const&& = delete; + + template<size_type N2> + constexpr auto operator+(const string_literal<N2, value_type>& other) const noexcept { + string_literal<N + N2 - 1, value_type> result {}; + for (size_type i = 0u; i != N; i++) + result._data[i] = _data[i]; + + for (size_type i = N - 1, i2 = 0; i2 != N2; i++, i2++) + result._data[i] = other._data[i2]; + return result; + } + + template<size_type N2> + constexpr auto operator+(const value_type (&rhs)[N2]) const noexcept { + return *this + _to_string(rhs); + } + + template<size_type N2> + friend constexpr auto operator+(const value_type (&lhs)[N2], string_literal<N, value_type> rhs) noexcept { + return _to_string(lhs) + rhs; + } + }; + template<std::size_t N, typename CharT = char> + string_literal(const CharT (&)[N]) -> string_literal<N, CharT>; + + template<typename CharT = char> + string_literal(CharT) -> string_literal<1, CharT>; + + template<template<typename C, C... Cs> typename T, string_literal Str, std::size_t... Idx> + auto _to_type_string(lexy::_detail::index_sequence<Idx...>) { + return T<typename decltype(Str)::value_type, Str._data[Idx]...> {}; + } + template<template<typename C, C... Cs> typename T, string_literal Str> + using to_type_string = decltype(ovdl::detail::_to_type_string<T, Str>(lexy::_detail::make_index_sequence<decltype(Str)::size()> {})); +} + +namespace ovdl::dsl { + template<ovdl::detail::string_literal Str, typename L, typename T, typename... R> + constexpr auto keyword(lexyd::_id<L, T, R...>) { + return ovdl::detail::to_type_string<lexyd::_keyword<lexyd::_id<L, T>>::template get, Str> {}; + } +}
\ No newline at end of file diff --git a/src/openvic-dataloader/detail/Warnings.hpp b/src/openvic-dataloader/detail/Warnings.hpp index 8fc09bd..ab718bc 100644 --- a/src/openvic-dataloader/detail/Warnings.hpp +++ b/src/openvic-dataloader/detail/Warnings.hpp @@ -5,7 +5,7 @@ #include <openvic-dataloader/ParseWarning.hpp> namespace ovdl::v2script::warnings { - inline const ParseWarning make_utf8_warning(std::string_view file_path) { + inline const std::string make_utf8_warning(std::string_view file_path) { constexpr std::string_view message_suffix = "This may cause problems. Prefer Windows-1252 encoding."; std::string message; @@ -15,7 +15,7 @@ namespace ovdl::v2script::warnings { message = "File '" + std::string(file_path) + "' is a UTF-8 encoded file. " + std::string(message_suffix); } - return ParseWarning { message, 1 }; + return message; } } diff --git a/src/openvic-dataloader/detail/dsl.hpp b/src/openvic-dataloader/detail/dsl.hpp new file mode 100644 index 0000000..9b544bc --- /dev/null +++ b/src/openvic-dataloader/detail/dsl.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include <type_traits> + +#include <openvic-dataloader/NodeLocation.hpp> +#include <openvic-dataloader/ParseState.hpp> + +#include <lexy/callback/adapter.hpp> +#include <lexy/callback/bind.hpp> +#include <lexy/callback/container.hpp> +#include <lexy/callback/fold.hpp> +#include <lexy/dsl.hpp> + +#include "detail/StringLiteral.hpp" + +namespace ovdl::dsl { + template<typename ReturnType, typename... Callback> + constexpr auto callback(Callback... cb) { + return lexy::bind(lexy::callback<ReturnType>(cb...), lexy::parse_state, lexy::values); + } + + template<typename Sink> + constexpr auto sink(Sink sink) { + return lexy::bind_sink(sink, lexy::parse_state); + } + + template<typename Container, typename Callback> + constexpr auto collect(Callback callback) { + return sink(lexy::collect<Container>(callback)); + } + + template<typename Callback> + constexpr auto collect(Callback callback) { + return sink(lexy::collect(callback)); + } + + template<IsParseState StateType, typename T> + constexpr auto construct = callback<T*>( + [](StateType& state, ovdl::NodeLocation loc, auto&& arg) { + if constexpr (std::is_same_v<std::decay_t<decltype(arg)>, lexy::nullopt>) + return state.ast().template create<T>(loc); + else + return state.ast().template create<T>(loc, DRYAD_FWD(arg)); + }, + [](StateType& state, ovdl::NodeLocation loc, auto&&... args) { + return state.ast().template create<T>(loc, DRYAD_FWD(args)...); + }); + + template<IsParseState StateType, typename T, typename ListType, bool DisableEmpty = false> + constexpr auto construct_list = callback<T*>( + [](StateType& state, const char* begin, ListType&& arg, const char* end) { + return state.ast().template create<T>(NodeLocation::make_from(begin, end), DRYAD_FWD(arg)); + }, + [](StateType& state, const char* begin, lexy::nullopt, const char* end) { + return state.ast().template create<T>(NodeLocation::make_from(begin, end)); + }, + [](StateType& state, const char* begin, const char* end) { + return state.ast().template create<T>(NodeLocation::make_from(begin, end)); + }); + + template<IsParseState StateType, typename T, typename ListType> + constexpr auto construct_list<StateType, T, ListType, true> = callback<T*>( + [](StateType& state, const char* begin, ListType&& arg, const char* end) { + return state.ast().template create<T>(NodeLocation::make_from(begin, end), DRYAD_FWD(arg)); + }, + [](StateType& state, const char* begin, lexy::nullopt, const char* end) { + return state.ast().template create<T>(NodeLocation::make_from(begin, end)); + }); + + template<unsigned char LOW, unsigned char HIGH> + consteval auto make_range() { + if constexpr (LOW == HIGH) { + return ::lexy::dsl::lit_c<LOW>; + } else if constexpr (LOW == (HIGH - 1)) { + return ::lexy::dsl::lit_c<LOW> / ::lexy::dsl::lit_c<HIGH>; + } else { + return ::lexy::dsl::lit_c<LOW> / make_range<LOW + 1, HIGH>(); + } + } + + template<auto Open, auto Close> + constexpr auto position_brackets = lexy::dsl::brackets(lexy::dsl::position(lexy::dsl::lit_c<Open>), lexy::dsl::position(lexy::dsl::lit_c<Close>)); + + constexpr auto round_bracketed = position_brackets<'(', ')'>; + constexpr auto square_bracketed = position_brackets<'[', ']'>; + constexpr auto curly_bracketed = position_brackets<'{', '}'>; + constexpr auto angle_bracketed = position_brackets<'<', '>'>; + + template<typename Production> + constexpr auto p = lexy::dsl::position(lexy::dsl::p<Production>); + + template<IsParseState ParseType, typename ReturnType, ovdl::detail::string_literal Keyword> + static constexpr auto default_kw_value = dsl::callback<ReturnType*>( + [](ParseType& state, NodeLocation loc) { + return state.ast().template create<ReturnType>(loc, state.ast().intern(Keyword.data(), Keyword.size())); + }); + + template< + IsParseState ParseType, + typename Identifier, + typename RuleValue, + ovdl::detail::string_literal Keyword, + auto Production, + auto Value> + struct keyword_rule { + struct rule_t { + static constexpr auto keyword = ovdl::dsl::keyword<Keyword>(lexy::dsl::inline_<Identifier>); + static constexpr auto rule = lexy::dsl::position(keyword) >> lexy::dsl::equal_sign; + static constexpr auto value = Value; + }; + static constexpr auto rule = dsl::p<rule_t> >> Production; + static constexpr auto value = construct<ParseType, RuleValue>; + }; + + template< + IsParseState ParseType, + typename Identifier, + typename RuleValue, + ovdl::detail::string_literal Keyword, + auto Production, + auto Value> + struct fkeyword_rule : keyword_rule<ParseType, Identifier, RuleValue, Keyword, Production, Value> { + using base_type = keyword_rule<ParseType, Identifier, RuleValue, Keyword, Production, Value>; + struct context_t; + struct rule_t : base_type::rule_t { + static constexpr auto flag = lexy::dsl::context_flag<context_t>; + struct too_many_error { + static constexpr auto name = "expected event " + Keyword + " to only be found once"; + }; + static constexpr auto must = lexy::dsl::must(rule_t::flag.is_reset()) + . +// See https://stackoverflow.com/questions/77144003/use-of-template-keyword-before-dependent-template-name +// THANKS FOR NOTHING MICROSOFT, CAN'T EVEN GET THE STANDARD RIGHT +#if !defined(_MSC_VER) || defined(__clang__) + template +#endif + error<too_many_error>; + }; + static constexpr auto make_flag = rule_t::flag.create(); + + static constexpr auto rule = dsl::p<rule_t> >> (rule_t::must >> rule_t::flag.set()) >> Production; + static constexpr auto value = construct<ParseType, RuleValue>; + }; + + template<typename... Args> + struct rule_helper { + static constexpr auto flags = (Args::make_flag + ...); + static constexpr auto p = (lexy::dsl::p<Args> | ...); + }; +}
\ No newline at end of file diff --git a/src/openvic-dataloader/v2script/AbstractSyntaxTree.cpp b/src/openvic-dataloader/v2script/AbstractSyntaxTree.cpp index 182d559..4754d8e 100644 --- a/src/openvic-dataloader/v2script/AbstractSyntaxTree.cpp +++ b/src/openvic-dataloader/v2script/AbstractSyntaxTree.cpp @@ -1,9 +1,3 @@ -#include <iomanip> -#include <sstream> -#include <string> -#include <utility> -#include <vector> - #include <stddef.h> #include <openvic-dataloader/v2script/AbstractSyntaxTree.hpp> @@ -11,281 +5,146 @@ #include <lexy/dsl/option.hpp> #include <lexy/input_location.hpp> -using namespace ovdl::v2script::ast; - -static void _handle_string_characters(std::string& string, bool allow_newline) { - size_t position = 0; - for (auto& c : string) { - switch (c) { - case '\r': - case '\n': - if (allow_newline) goto END_LOOP; - c = ' '; - break; - default: break; - } - END_LOOP: - position++; - } -} - -void ovdl::v2script::ast::copy_into_node_ptr_vector(const std::vector<NodePtr>& source, std::vector<NodeUPtr>& dest) { - dest.clear(); - dest.reserve(source.size()); - for (auto&& p : source) { - dest.push_back(NodeUPtr { p }); - } -} - -AbstractStringNode::AbstractStringNode() : Node({}) {} -AbstractStringNode::AbstractStringNode(NodeLocation location, std::string&& name, bool allow_newline) : Node(location), - _name(std::move(name)) { - _handle_string_characters(_name, allow_newline); -} -AbstractStringNode::AbstractStringNode(NodeLocation location) : Node(location) {} -AbstractStringNode::AbstractStringNode(std::string&& name, bool allow_newline) : AbstractStringNode({}, std::move(name), allow_newline) {} - -std::ostream& AbstractStringNode::print(std::ostream& stream, size_t indent) const { - return stream << _name; -} - -#define OVDL_AST_STRING_NODE_DEF(NAME, ...) \ - NAME::NAME() : AbstractStringNode() {} \ - NAME::NAME(std::string&& name, bool allow_newline) : AbstractStringNode(std::move(name), allow_newline) {} \ - NAME::NAME(lexy::nullopt) : AbstractStringNode() {} \ - NAME::NAME(NodeLocation location) : AbstractStringNode(location) {} \ - NAME::NAME(NodeLocation location, std::string&& name, bool allow_newline) : AbstractStringNode(location, std::move(name), allow_newline) {} \ - NAME::NAME(NodeLocation location, lexy::nullopt) : AbstractStringNode(location, {}, true) {} \ - std::ostream& NAME::print(std::ostream& stream, size_t indent) const __VA_ARGS__ - -OVDL_AST_STRING_NODE_DEF(IdentifierNode, { - return stream << _name; -}); - -OVDL_AST_STRING_NODE_DEF(StringNode, { - return stream << '"' << _name << '"'; -}); - -OVDL_AST_STRING_NODE_DEF(FactorNode, { - return stream << "factor = " << _name; -}); - -OVDL_AST_STRING_NODE_DEF(MonthNode, { - return stream << "months = " << _name; -}); - -OVDL_AST_STRING_NODE_DEF(NameNode, { - return stream << "name = " << _name; -}); - -OVDL_AST_STRING_NODE_DEF(FireOnlyNode, { - return stream << "fire_only_once = " << _name; -}); - -OVDL_AST_STRING_NODE_DEF(IdNode, { - return stream << "id = " << _name; -}); - -OVDL_AST_STRING_NODE_DEF(TitleNode, { - return stream << "title = " << _name; -}); - -OVDL_AST_STRING_NODE_DEF(DescNode, { - return stream << "desc = " << _name; -}); - -OVDL_AST_STRING_NODE_DEF(PictureNode, { - return stream << "picture = " << _name; -}); - -OVDL_AST_STRING_NODE_DEF(IsTriggeredNode, { - return stream << "is_triggered_only = " << _name; -}); - -#undef OVDL_AST_STRING_NODE_DEF - -AssignNode::AssignNode(NodeLocation location, NodeCPtr name, NodePtr init) - : Node(location), - _initializer(std::move(init)) { - if (name->is_type<IdentifierNode>()) { - _name = cast_node_cptr<IdentifierNode>(name)._name; - } -} - -std::ostream& Node::print_ptr(std::ostream& stream, NodeCPtr node, size_t indent) { - return node != nullptr ? node->print(stream, indent) : stream << "<NULL>"; -} - -static std::ostream& print_newline_indent(std::ostream& stream, size_t indent) { - return stream << "\n" - << std::setw(indent) << std::setfill('\t') << ""; -} - -/* Starts with a newline and ends at the end of a line, and so - * should be followed by a call to print_newline_indent. - */ -static std::ostream& print_nodeuptr_vector(const std::vector<NodeUPtr>& nodes, - std::ostream& stream, size_t indent) { - for (NodeUPtr const& node : nodes) { - print_newline_indent(stream, indent); - Node::print_ptr(stream, node.get(), indent); - } - return stream; -} - -AbstractListNode::AbstractListNode(NodeLocation location, const std::vector<NodePtr>& statements) : Node(location) { - copy_into_node_ptr_vector(statements, _statements); -} -AbstractListNode::AbstractListNode(const std::vector<NodePtr>& statements) : AbstractListNode({}, statements) {} -std::ostream& AbstractListNode::print(std::ostream& stream, size_t indent) const { - stream << '{'; - if (!_statements.empty()) { - print_nodeuptr_vector(_statements, stream, indent + 1); - print_newline_indent(stream, indent); - } - return stream << "}"; -} - -#define OVDL_AST_LIST_NODE_DEF(NAME, ...) \ - NAME::NAME(const std::vector<NodePtr>& statements) : AbstractListNode(statements) {} \ - NAME::NAME(lexy::nullopt) : AbstractListNode() {} \ - NAME::NAME(NodeLocation location, const std::vector<NodePtr>& statements) : AbstractListNode(location, statements) {} \ - NAME::NAME(NodeLocation location, lexy::nullopt) : AbstractListNode(location, {}) {} \ - std::ostream& NAME::print(std::ostream& stream, size_t indent) const __VA_ARGS__ - -OVDL_AST_LIST_NODE_DEF(FileNode, { - print_nodeuptr_vector(_statements, stream, indent); - return print_newline_indent(stream, indent); -}); +#include <dryad/node.hpp> +#include <dryad/tree.hpp> -OVDL_AST_LIST_NODE_DEF(ListNode, { - stream << '{'; - if (!_statements.empty()) { - print_nodeuptr_vector(_statements, stream, indent + 1); - print_newline_indent(stream, indent); - } - return stream << "}"; -}); - -OVDL_AST_LIST_NODE_DEF(ModifierNode, { - stream << "modifier = {"; - if (!_statements.empty()) { - print_nodeuptr_vector(_statements, stream, indent + 1); - print_newline_indent(stream, indent); - } - return stream << '}'; -}); - -OVDL_AST_LIST_NODE_DEF(MtthNode, { - stream << "mean_time_to_happen = {"; - if (!_statements.empty()) { - print_nodeuptr_vector(_statements, stream, indent + 1); - print_newline_indent(stream, indent); - } - return stream << '}'; -}); +#include <range/v3/view/drop.hpp> -OVDL_AST_LIST_NODE_DEF(EventOptionNode, { - stream << "option = {"; - if (!_statements.empty()) { - print_nodeuptr_vector(_statements, stream, indent + 1); - print_newline_indent(stream, indent); - } - return stream << '}'; -}); - -OVDL_AST_LIST_NODE_DEF(BehaviorListNode, { - stream << "ai_chance = {"; // may be ai_chance or ai_will_do - if (!_statements.empty()) { - print_nodeuptr_vector(_statements, stream, indent + 1); - print_newline_indent(stream, indent); - } - return stream << '}'; -}); - -OVDL_AST_LIST_NODE_DEF(DecisionListNode, { - stream << "political_decisions = {"; - if (!_statements.empty()) { - print_nodeuptr_vector(_statements, stream, indent + 1); - print_newline_indent(stream, indent); - } - return stream << '}'; -}); - -#undef OVDL_AST_LIST_NODE_DEF - -EventNode::EventNode(NodeLocation location, Type type, const std::vector<NodePtr>& statements) : AbstractListNode(location), - _type(type) { - copy_into_node_ptr_vector(statements, _statements); -} -EventNode::EventNode(Type type, const std::vector<NodePtr>& statements) : EventNode({}, type, statements) {} -std::ostream& EventNode::print(std::ostream& stream, size_t indent) const { - switch (_type) { - case Type::Country: stream << "country_event = "; break; - case Type::Province: stream << "province_event = "; break; - } - stream << '{'; - if (!_statements.empty()) { - print_nodeuptr_vector(_statements, stream, indent + 1); - print_newline_indent(stream, indent); - } - return stream << '}'; -} - -DecisionNode::DecisionNode(NodeLocation location, NodePtr name, const std::vector<NodePtr>& statements) : AbstractListNode(location), - _name(std::move(name)) { - copy_into_node_ptr_vector(statements, _statements); -} -DecisionNode::DecisionNode(NodePtr name, const std::vector<NodePtr>& statements) : DecisionNode({}, name, statements) {} -std::ostream& DecisionNode::print(std::ostream& stream, size_t indent) const { - print_ptr(stream, _name.get(), indent) << " = {"; - if (!_statements.empty()) { - print_nodeuptr_vector(_statements, stream, indent + 1); - print_newline_indent(stream, indent); - } - return stream << '}'; -} - -ExecutionNode::ExecutionNode(NodeLocation location, Type type, NodePtr name, NodePtr init) : Node(location), - _type(type), - _name(std::move(name)), - _initializer(std::move(init)) { -} -ExecutionNode::ExecutionNode(Type type, NodePtr name, NodePtr init) : ExecutionNode({}, type, name, init) {} -std::ostream& ExecutionNode::print(std::ostream& stream, size_t indent) const { - print_ptr(stream, _name.get(), indent) << " = "; - if (_initializer) { - Node::print_ptr(stream, _initializer.get(), indent + 1); - } - return stream; -} - -ExecutionListNode::ExecutionListNode(NodeLocation location, ExecutionNode::Type type, const std::vector<NodePtr>& statements) : AbstractListNode(location), - _type(type) { - copy_into_node_ptr_vector(statements, _statements); -} -ExecutionListNode::ExecutionListNode(ExecutionNode::Type type, const std::vector<NodePtr>& statements) : ExecutionListNode({}, type, statements) {} -std::ostream& ExecutionListNode::print(std::ostream& stream, size_t indent) const { - // Only way to make a valid declared parsable file - stream << "{ "; - switch (_type) { - case ExecutionNode::Type::Effect: stream << "effect = {"; break; - case ExecutionNode::Type::Trigger: stream << "trigger = {"; break; - } - if (!_statements.empty()) { - print_nodeuptr_vector(_statements, stream, indent + 1); - print_newline_indent(stream, indent); - } - return stream << "}}"; -} +using namespace ovdl::v2script::ast; -Node::operator std::string() const { - std::stringstream ss; - ss << *this; - return ss.str(); -} +ListValue::ListValue(dryad::node_ctor ctor, StatementList statements) + : node_base(ctor) { + insert_child_list_after(nullptr, statements); + if (statements.empty()) { + _last_statement = nullptr; + } else { + _last_statement = statements.back(); + } +} + +FileTree::FileTree(dryad::node_ctor ctor, StatementList statements) : node_base(ctor) { + insert_child_list_after(nullptr, statements); + if (statements.empty()) { + _last_node = nullptr; + } else { + _last_node = statements.back(); + } +} + +// static void _handle_string_characters(std::string& string, bool allow_newline) { +// size_t position = 0; +// for (auto& c : string) { +// switch (c) { +// case '\r': +// case '\n': +// if (allow_newline) goto END_LOOP; +// c = ' '; +// break; +// default: break; +// } +// END_LOOP: +// position++; +// } +// } + +std::string AbstractSyntaxTree::make_list_visualizer() const { + const int INDENT_SIZE = 2; + + std::string result; + unsigned int level = 0; + + for (auto [event, node] : dryad::traverse(_tree)) { + if (event == dryad::traverse_event::exit) { + --level; + continue; + } -std::ostream& AssignNode::print(std::ostream& stream, size_t indent) const { - stream << _name << " = "; - return Node::print_ptr(stream, _initializer.get(), indent); + result.append(INDENT_SIZE * level, ' '); + result.append(fmt::format("- {}: ", get_kind_name(node->kind()))); + + dryad::visit_node( + node, + [&](const FlatValue* value) { + result.append(value->value(_symbol_interner)); + }, + [&](const ListValue* value) { + }, + [&](const NullValue* value) { + }, + [&](const EventStatement* statement) { + result.append(statement->is_province_event() ? "province_event" : "country_event"); + }, + [&](const AssignStatement* statement) { + }, + [&](const FileTree* tree) { + }); + + result.append(1, '\n'); + + if (event == dryad::traverse_event::enter) + ++level; + } + + return result; +} + +std::string AbstractSyntaxTree::make_native_visualizer() const { + constexpr int INDENT_SIZE = 2; + + std::string result; + unsigned int level = 0; + + dryad::visit_tree( + _tree, + [&](const IdentifierValue* value) { + result.append(value->value(_symbol_interner)); + }, + [&](const StringValue* value) { + result.append(1, '"').append(value->value(_symbol_interner)).append(1, '"'); + }, + [&](dryad::child_visitor<NodeKind> visitor, const ValueStatement* statement) { + visitor(statement->value()); + }, + [&](dryad::child_visitor<NodeKind> visitor, const AssignStatement* statement) { + visitor(statement->left()); + if (statement->right()->kind() != NodeKind::NullValue) { + result.append(" = "); + visitor(statement->right()); + } + }, + [&](dryad::child_visitor<NodeKind> visitor, const ListValue* value) { + result.append(1, '{'); + level++; + for (const auto& statement : value->statements()) { + result + .append(1, '\n') + .append(INDENT_SIZE * level, ' '); + visitor(statement); + } + level--; + result + .append(1, '\n') + .append(INDENT_SIZE * level, ' ') + .append(1, '}'); + }, + [&](dryad::child_visitor<NodeKind> visitor, const EventStatement* statement) { + result.append(statement->is_province_event() ? "province_event" : "country_event"); + if (statement->right()->kind() != NodeKind::NullValue) { + result.append(" = "); + visitor(statement->right()); + } + }, + [&](dryad::child_visitor<NodeKind> visitor, const FileTree* value) { + auto statements = value->statements(); + visitor(*statements.begin()); + + for (const auto& statement : statements | ranges::views::drop(1)) { + result + .append(1, '\n') + .append(INDENT_SIZE * level, ' '); + visitor(statement); + } + }); + + return result; }
\ No newline at end of file diff --git a/src/openvic-dataloader/v2script/AiBehaviorGrammar.hpp b/src/openvic-dataloader/v2script/AiBehaviorGrammar.hpp index e04f447..7fcd13d 100644 --- a/src/openvic-dataloader/v2script/AiBehaviorGrammar.hpp +++ b/src/openvic-dataloader/v2script/AiBehaviorGrammar.hpp @@ -2,6 +2,7 @@ #include <openvic-dataloader/v2script/AbstractSyntaxTree.hpp> +#include <lexy/callback/forward.hpp> #include <lexy/dsl.hpp> #include "ModifierGrammar.hpp" @@ -10,23 +11,12 @@ namespace ovdl::v2script::grammar { struct AiBehaviorList { static constexpr auto rule = lexy::dsl::list(lexy::dsl::p<FactorStatement> | lexy::dsl::p<ModifierStatement>); - static constexpr auto value = - lexy::as_list<std::vector<ast::NodePtr>> >> - lexy::callback<ast::NodePtr>( - [](auto&& list) { - return ast::make_node_ptr<ast::BehaviorListNode>(LEXY_MOV(list)); - }); + static constexpr auto value = lexy::as_list<ast::AssignStatementList>; }; struct AiBehaviorBlock { - static constexpr auto rule = lexy::dsl::curly_bracketed.opt(lexy::dsl::p<AiBehaviorList>); + static constexpr auto rule = dsl::curly_bracketed.opt(lexy::dsl::p<AiBehaviorList>); - static constexpr auto value = lexy::callback<ast::NodePtr>( - [](auto&& list) { - return LEXY_MOV(list); - }, - [](lexy::nullopt = {}) { - return lexy::nullopt {}; - }); + static constexpr auto value = construct_list<ast::ListValue>; }; }
\ No newline at end of file diff --git a/src/openvic-dataloader/v2script/DecisionGrammar.hpp b/src/openvic-dataloader/v2script/DecisionGrammar.hpp index 3bd5cba..05cb8cc 100644 --- a/src/openvic-dataloader/v2script/DecisionGrammar.hpp +++ b/src/openvic-dataloader/v2script/DecisionGrammar.hpp @@ -1,109 +1,49 @@ #pragma once -#include <vector> - #include <openvic-dataloader/v2script/AbstractSyntaxTree.hpp> #include <lexy/callback.hpp> #include <lexy/callback/adapter.hpp> +#include <lexy/callback/constant.hpp> #include <lexy/callback/container.hpp> #include <lexy/dsl.hpp> +#include <lexy/dsl/brackets.hpp> #include <lexy/dsl/option.hpp> +#include <lexy/dsl/position.hpp> +#include <lexy/dsl/production.hpp> -#include "AiBehaviorGrammar.hpp" -#include "SimpleGrammar.hpp" -#include "TriggerGrammar.hpp" +#include "v2script/AiBehaviorGrammar.hpp" +#include "v2script/TriggerGrammar.hpp" // Decision Grammar Definitions // namespace ovdl::v2script::grammar { - ////////////////// - // Macros - ////////////////// -// Produces <KW_NAME>_rule and <KW_NAME>_p -#define OVDL_GRAMMAR_KEYWORD_DEFINE(KW_NAME) \ - struct KW_NAME##_rule { \ - static constexpr auto keyword = LEXY_KEYWORD(#KW_NAME, lexy::dsl::inline_<Identifier<StringEscapeOption>>); \ - static constexpr auto rule = keyword >> lexy::dsl::equal_sign; \ - static constexpr auto value = lexy::noop; \ - }; \ - static constexpr auto KW_NAME##_p = lexy::dsl::p<KW_NAME##_rule> - -// Produces <KW_NAME>_rule and <KW_NAME>_p and <KW_NAME>_rule::flag and <KW_NAME>_rule::too_many_error -#define OVDL_GRAMMAR_KEYWORD_FLAG_DEFINE(KW_NAME) \ - struct KW_NAME##_rule { \ - static constexpr auto keyword = LEXY_KEYWORD(#KW_NAME, lexy::dsl::inline_<Identifier<StringEscapeOption>>); \ - static constexpr auto rule = keyword >> lexy::dsl::equal_sign; \ - static constexpr auto value = lexy::noop; \ - static constexpr auto flag = lexy::dsl::context_flag<struct KW_NAME##_context>; \ - struct too_many_error { \ - static constexpr auto name = "expected left side " #KW_NAME " to be found once"; \ - }; \ - }; \ - static constexpr auto KW_NAME##_p = lexy::dsl::p<KW_NAME##_rule> >> (lexy::dsl::must(KW_NAME##_rule::flag.is_reset()).error<KW_NAME##_rule::too_many_error> + KW_NAME##_rule::flag.set()) - ////////////////// - // Macros - ////////////////// struct DecisionStatement { - template<auto Production, typename AstNode> - struct _StringStatement { - static constexpr auto rule = Production >> (lexy::dsl::p<StringExpression<StringEscapeOption>> | lexy::dsl::p<Identifier<StringEscapeOption>>); - static constexpr auto value = lexy::forward<ast::NodePtr>; - }; - template<auto Production, typename AstNode> - static constexpr auto StringStatement = lexy::dsl::p<_StringStatement<Production, AstNode>>; + using potential = fkeyword_rule<"potential", lexy::dsl::p<TriggerBlock>>; + using allow = fkeyword_rule<"allow", lexy::dsl::p<TriggerBlock>>; + using effect = fkeyword_rule<"effect", lexy::dsl::p<TriggerBlock>>; + using ai_will_do = fkeyword_rule<"ai_will_do", lexy::dsl::p<AiBehaviorBlock>>; - OVDL_GRAMMAR_KEYWORD_FLAG_DEFINE(potential); - OVDL_GRAMMAR_KEYWORD_FLAG_DEFINE(allow); - OVDL_GRAMMAR_KEYWORD_FLAG_DEFINE(effect); - OVDL_GRAMMAR_KEYWORD_FLAG_DEFINE(ai_will_do); + using helper = dsl::rule_helper<potential, allow, effect, ai_will_do>; - static constexpr auto rule = [] { - constexpr auto create_flags = - potential_rule::flag.create() + - allow_rule::flag.create() + - effect_rule::flag.create() + - ai_will_do_rule::flag.create(); + struct List { + static constexpr auto rule = dsl::curly_bracketed.opt_list(helper::p | lexy::dsl::p<SAssignStatement<StringEscapeOption>>); - constexpr auto potential_statement = potential_p >> lexy::dsl::p<TriggerBlock>; - constexpr auto allow_statement = allow_p >> lexy::dsl::p<TriggerBlock>; - constexpr auto effect_statement = effect_p >> lexy::dsl::p<TriggerBlock>; - constexpr auto ai_will_do_statement = ai_will_do_p >> lexy::dsl::p<AiBehaviorBlock>; + static constexpr auto value = lexy::as_list<ast::AssignStatementList> >> construct_list<ast::ListValue>; + }; - return lexy::dsl::p<Identifier<StringEscapeOption>> >> - (create_flags + lexy::dsl::equal_sign + - lexy::dsl::curly_bracketed.list( - potential_statement | - allow_statement | - effect_statement | - ai_will_do_statement | - lexy::dsl::p<SimpleAssignmentStatement<StringEscapeOption>>)); - }(); + static constexpr auto rule = + dsl::p<Identifier<StringEscapeOption>> >> + (helper::flags + lexy::dsl::equal_sign + lexy::dsl::p<List>); - static constexpr auto value = - lexy::as_list<std::vector<ast::NodePtr>> >> - lexy::callback<ast::NodePtr>( - [](auto&& name, auto&& list) { - return ast::make_node_ptr<ast::DecisionNode>(LEXY_MOV(name), LEXY_MOV(list)); - }, - [](auto&& name, lexy::nullopt = {}) { - return ast::make_node_ptr<ast::DecisionNode>(LEXY_MOV(name)); - }); + static constexpr auto value = construct<ast::AssignStatement>; }; struct DecisionList { static constexpr auto rule = - LEXY_KEYWORD("political_decisions", lexy::dsl::inline_<Identifier<StringEscapeOption>>) >> - (lexy::dsl::equal_sign + lexy::dsl::curly_bracketed.opt_list(lexy::dsl::p<DecisionStatement>)); + ovdl::dsl::keyword<"political_decisions">(lexy::dsl::inline_<Identifier<StringEscapeOption>>) >> + (lexy::dsl::equal_sign >> lexy::dsl::curly_bracketed.opt_list(lexy::dsl::p<DecisionStatement>)); - static constexpr auto value = - lexy::as_list<std::vector<ast::NodePtr>> >> - lexy::callback<ast::NodePtr>( - [](auto&& list) { - return ast::make_node_ptr<ast::DecisionListNode>(LEXY_MOV(list)); - }, - [](lexy::nullopt = {}) { - return lexy::nullopt {}; - }); + static constexpr auto value = lexy::as_list<ast::AssignStatementList>; }; struct DecisionFile { @@ -111,15 +51,8 @@ namespace ovdl::v2script::grammar { static constexpr auto whitespace = whitespace_specifier | comment_specifier; static constexpr auto rule = - lexy::dsl::terminator(lexy::dsl::eof).list( // - lexy::dsl::p<DecisionList> | // - lexy::dsl::p<SimpleAssignmentStatement<StringEscapeOption>>); + lexy::dsl::position + lexy::dsl::terminator(lexy::dsl::eof).opt_list(lexy::dsl::p<DecisionList>); - static constexpr auto value = lexy::as_list<std::vector<ast::NodePtr>> >> lexy::new_<ast::FileNode, ast::NodePtr>; + static constexpr auto value = lexy::concat<ast::AssignStatementList> >> construct<ast::FileTree>; }; - -#undef OVDL_GRAMMAR_KEYWORD_DEFINE -#undef OVDL_GRAMMAR_KEYWORD_FLAG_DEFINE -#undef OVDL_GRAMMAR_KEYWORD_STATEMENT -#undef OVDL_GRAMMAR_KEYWORD_FLAG_STATEMENT }
\ No newline at end of file diff --git a/src/openvic-dataloader/v2script/EffectGrammar.hpp b/src/openvic-dataloader/v2script/EffectGrammar.hpp index 1b85382..10f8348 100644 --- a/src/openvic-dataloader/v2script/EffectGrammar.hpp +++ b/src/openvic-dataloader/v2script/EffectGrammar.hpp @@ -6,37 +6,24 @@ #include <lexy/dsl.hpp> #include "SimpleGrammar.hpp" +#include "detail/dsl.hpp" namespace ovdl::v2script::grammar { struct EffectStatement { - static constexpr auto rule = lexy::dsl::inline_<SimpleAssignmentStatement<StringEscapeOption>>; + static constexpr auto rule = lexy::dsl::p<SAssignStatement<StringEscapeOption>>; - static constexpr auto value = lexy::callback<ast::NodePtr>( - [](auto name, auto&& initalizer) { - return ast::make_node_ptr<ast::ExecutionNode>(ast::ExecutionNode::Type::Effect, LEXY_MOV(name), LEXY_MOV(initalizer)); - }); + static constexpr auto value = lexy::forward<ast::AssignStatement*>; }; struct EffectList { - static constexpr auto rule = lexy::dsl::list(lexy::dsl::p<SimpleAssignmentStatement<StringEscapeOption>>); - - static constexpr auto value = - lexy::as_list<std::vector<ast::NodePtr>> >> - lexy::callback<ast::NodePtr>( - [](auto&& list) { - return ast::make_node_ptr<ast::ExecutionListNode>(ast::ExecutionNode::Type::Effect, LEXY_MOV(list)); - }); + static constexpr auto rule = lexy::dsl::list(lexy::dsl::p<EffectStatement>); + + static constexpr auto value = lexy::as_list<ast::AssignStatementList>; }; struct EffectBlock { - static constexpr auto rule = lexy::dsl::curly_bracketed.opt(lexy::dsl::p<EffectList>); - - static constexpr auto value = lexy::callback<ast::NodePtr>( - [](auto&& list) { - return LEXY_MOV(list); - }, - [](lexy::nullopt = {}) { - return lexy::nullopt {}; - }); + static constexpr auto rule = dsl::curly_bracketed.opt(lexy::dsl::p<EffectList>); + + static constexpr auto value = construct_list<ast::ListValue>; }; }
\ No newline at end of file diff --git a/src/openvic-dataloader/v2script/EventGrammar.hpp b/src/openvic-dataloader/v2script/EventGrammar.hpp index 57cd170..c81a173 100644 --- a/src/openvic-dataloader/v2script/EventGrammar.hpp +++ b/src/openvic-dataloader/v2script/EventGrammar.hpp @@ -1,168 +1,109 @@ #pragma once -#include <string> -#include <vector> +#include <cctype> +#include <cstdlib> #include <openvic-dataloader/v2script/AbstractSyntaxTree.hpp> #include <lexy/callback.hpp> #include <lexy/dsl.hpp> +#include <lexy/grammar.hpp> + +#include "openvic-dataloader/NodeLocation.hpp" -#include "AiBehaviorGrammar.hpp" -#include "EffectGrammar.hpp" -#include "ModifierGrammar.hpp" #include "SimpleGrammar.hpp" -#include "TriggerGrammar.hpp" +#include "detail/dsl.hpp" +#include "v2script/AiBehaviorGrammar.hpp" +#include "v2script/EffectGrammar.hpp" +#include "v2script/ModifierGrammar.hpp" // Event Grammar Definitions // namespace ovdl::v2script::grammar { - ////////////////// - // Macros - ////////////////// -// Produces <KW_NAME>_rule and <KW_NAME>_p -#define OVDL_GRAMMAR_KEYWORD_DEFINE(KW_NAME) \ - struct KW_NAME##_rule { \ - static constexpr auto keyword = LEXY_KEYWORD(#KW_NAME, lexy::dsl::inline_<Identifier<StringEscapeOption>>); \ - static constexpr auto rule = keyword >> lexy::dsl::equal_sign; \ - static constexpr auto value = lexy::noop; \ - }; \ - static constexpr auto KW_NAME##_p = lexy::dsl::p<KW_NAME##_rule> - -// Produces <KW_NAME>_rule and <KW_NAME>_p and <KW_NAME>_rule::flag and <KW_NAME>_rule::too_many_error -#define OVDL_GRAMMAR_KEYWORD_FLAG_DEFINE(KW_NAME) \ - struct KW_NAME##_rule { \ - static constexpr auto keyword = LEXY_KEYWORD(#KW_NAME, lexy::dsl::inline_<Identifier<StringEscapeOption>>); \ - static constexpr auto rule = keyword >> lexy::dsl::equal_sign; \ - static constexpr auto value = lexy::noop; \ - static constexpr auto flag = lexy::dsl::context_flag<struct KW_NAME##_context>; \ - struct too_many_error { \ - static constexpr auto name = "expected left side " #KW_NAME " to be found once"; \ - }; \ - }; \ - static constexpr auto KW_NAME##_p = lexy::dsl::p<KW_NAME##_rule> >> (lexy::dsl::must(KW_NAME##_rule::flag.is_reset()).error<KW_NAME##_rule::too_many_error> + KW_NAME##_rule::flag.set()) - ////////////////// - // Macros - ////////////////// - static constexpr auto event_symbols = lexy::symbol_table<ast::EventNode::Type> // - .map<LEXY_SYMBOL("country_event")>(ast::EventNode::Type::Country) - .map<LEXY_SYMBOL("province_event")>(ast::EventNode::Type::Province); + static constexpr auto event_symbols = lexy::symbol_table<bool> // + .map<LEXY_SYMBOL("country_event")>(false) + .map<LEXY_SYMBOL("province_event")>(true); struct EventMtthStatement { - OVDL_GRAMMAR_KEYWORD_DEFINE(months); - struct MonthValue { - static constexpr auto rule = lexy::dsl::inline_<Identifier<StringEscapeOption>>; - static constexpr auto value = lexy::as_string<std::string> | lexy::new_<ast::MonthNode, ast::NodePtr>; + static constexpr auto rule = lexy::dsl::p<Identifier<StringEscapeOption>>; + static constexpr auto value = dsl::callback<ast::IdentifierValue*>( + [](ast::ParseState& state, ast::IdentifierValue* value) { + bool is_number = true; + for (auto* current = value->value(state.ast().symbol_interner()); *current; current++) { + is_number = is_number && std::isdigit(*current); + if (!is_number) break; + } + if (!is_number) { + state.logger().warning("month is not an integer") // + .primary(state.ast().location_of(value), "here") + .finish(); + } + return value; + }); }; - static constexpr auto rule = lexy::dsl::list( - (months_p >> lexy::dsl::p<MonthValue>) | - lexy::dsl::p<ModifierStatement>); + using months = keyword_rule<"months", lexy::dsl::p<MonthValue>>; - static constexpr auto value = - lexy::as_list<std::vector<ast::NodePtr>> >> - lexy::callback<ast::NodePtr>( - [](auto&& list) { - return ast::make_node_ptr<ast::MtthNode>(LEXY_MOV(list)); - }); - }; + static constexpr auto rule = dsl::curly_bracketed(lexy::dsl::p<months> | lexy::dsl::p<ModifierStatement>); - template<auto Production, typename AstNode> - struct _StringStatement { - static constexpr auto rule = Production >> (lexy::dsl::p<StringExpression<StringEscapeOption>> | lexy::dsl::p<Identifier<StringEscapeOption>>); - static constexpr auto value = - lexy::callback<ast::NodePtr>( - [](auto&& value) { - auto result = ast::make_node_ptr<AstNode>(std::move(static_cast<ast::AbstractStringNode*>(value)->_name)); - delete value; - return result; - }); + static constexpr auto value = lexy::as_list<ast::AssignStatementList> >> construct_list<ast::ListValue, true>; }; - template<auto Production, typename AstNode> - static constexpr auto StringStatement = lexy::dsl::p<_StringStatement<Production, AstNode>>; + + static constexpr auto str_or_id = lexy::dsl::p<SimpleGrammar<StringEscapeOption>::ValueExpression>; struct EventOptionList { - OVDL_GRAMMAR_KEYWORD_FLAG_DEFINE(name); - OVDL_GRAMMAR_KEYWORD_FLAG_DEFINE(ai_chance); + using name = fkeyword_rule<"name", str_or_id>; + using ai_chance = fkeyword_rule<"ai_chance", lexy::dsl::p<AiBehaviorBlock>>; static constexpr auto rule = [] { - constexpr auto create_flags = name_rule::flag.create() + ai_chance_rule::flag.create(); - - constexpr auto name_statement = StringStatement<name_p, ast::NameNode>; - constexpr auto ai_chance_statement = ai_chance_p >> lexy::dsl::curly_bracketed(lexy::dsl::p<AiBehaviorList>); + using helper = dsl::rule_helper<name, ai_chance>; - return create_flags + lexy::dsl::list(name_statement | ai_chance_statement | lexy::dsl::p<EffectList>); + return dsl::curly_bracketed(helper::flags + lexy::dsl::list(helper::p | lexy::dsl::p<EffectStatement>)); }(); - static constexpr auto value = - lexy::as_list<std::vector<ast::NodePtr>> >> - lexy::callback<ast::NodePtr>( - [](auto&& list) { - return ast::make_node_ptr<ast::EventOptionNode>(LEXY_MOV(list)); - }); + static constexpr auto value = lexy::as_list<ast::AssignStatementList> >> construct_list<ast::ListValue, true>; }; struct EventStatement { - OVDL_GRAMMAR_KEYWORD_FLAG_DEFINE(id); - OVDL_GRAMMAR_KEYWORD_FLAG_DEFINE(title); - OVDL_GRAMMAR_KEYWORD_FLAG_DEFINE(desc); - OVDL_GRAMMAR_KEYWORD_FLAG_DEFINE(picture); - OVDL_GRAMMAR_KEYWORD_FLAG_DEFINE(is_triggered_only); - OVDL_GRAMMAR_KEYWORD_FLAG_DEFINE(fire_only_once); - OVDL_GRAMMAR_KEYWORD_FLAG_DEFINE(immediate); - OVDL_GRAMMAR_KEYWORD_FLAG_DEFINE(mean_time_to_happen); - OVDL_GRAMMAR_KEYWORD_DEFINE(trigger); - OVDL_GRAMMAR_KEYWORD_DEFINE(option); + using id = fkeyword_rule<"id", str_or_id>; + using title = fkeyword_rule<"title", str_or_id>; + using desc = fkeyword_rule<"desc", str_or_id>; + using picture = fkeyword_rule<"picture", str_or_id>; + using is_triggered_only = fkeyword_rule<"is_triggered_only", str_or_id>; + using fire_only_once = fkeyword_rule<"fire_only_once", str_or_id>; + using immediate = fkeyword_rule<"immediate", lexy::dsl::p<EffectBlock>>; + using mean_time_to_happen = fkeyword_rule<"mean_time_to_happen", lexy::dsl::p<EventMtthStatement>>; + using trigger = keyword_rule<"trigger", lexy::dsl::p<TriggerBlock>>; + using option = keyword_rule<"option", lexy::dsl::p<EventOptionList>>; + + struct EventList { + static constexpr auto rule = [] { + using helper = dsl::rule_helper<id, title, desc, picture, is_triggered_only, fire_only_once, immediate, mean_time_to_happen>; + + return helper::flags + + dsl::curly_bracketed.opt_list( + helper::p | lexy::dsl::p<trigger> | lexy::dsl::p<option> | + lexy::dsl::p<SAssignStatement<StringEscapeOption>>); + }(); + + static constexpr auto value = lexy::as_list<ast::AssignStatementList> >> construct_list<ast::ListValue, true>; + }; - static constexpr auto rule = [] { - constexpr auto symbol_value = lexy::dsl::symbol<event_symbols>(lexy::dsl::inline_<Identifier<StringEscapeOption>>); - - constexpr auto create_flags = - id_rule::flag.create() + - title_rule::flag.create() + - desc_rule::flag.create() + - picture_rule::flag.create() + - is_triggered_only_rule::flag.create() + - fire_only_once_rule::flag.create() + - immediate_rule::flag.create() + - mean_time_to_happen_rule::flag.create(); - - constexpr auto id_statement = StringStatement<id_p, ast::IdNode>; - constexpr auto title_statement = StringStatement<title_p, ast::TitleNode>; - constexpr auto desc_statement = StringStatement<desc_p, ast::DescNode>; - constexpr auto picture_statement = StringStatement<picture_p, ast::PictureNode>; - constexpr auto is_triggered_only_statement = StringStatement<is_triggered_only_p, ast::IsTriggeredNode>; - constexpr auto fire_only_once_statement = StringStatement<fire_only_once_p, ast::FireOnlyNode>; - constexpr auto immediate_statement = immediate_p >> lexy::dsl::p<EffectBlock>; - constexpr auto mean_time_to_happen_statement = mean_time_to_happen_p >> lexy::dsl::curly_bracketed(lexy::dsl::p<EventMtthStatement>); - - constexpr auto trigger_statement = trigger_p >> lexy::dsl::curly_bracketed.opt(lexy::dsl::p<TriggerList>); - constexpr auto option_statement = option_p >> lexy::dsl::curly_bracketed(lexy::dsl::p<EventOptionList>); - - return symbol_value >> - (create_flags + lexy::dsl::equal_sign + - lexy::dsl::curly_bracketed.opt_list( - id_statement | - title_statement | - desc_statement | - picture_statement | - is_triggered_only_statement | - fire_only_once_statement | - immediate_statement | - mean_time_to_happen_statement | - trigger_statement | - option_statement | - lexy::dsl::p<SimpleAssignmentStatement<StringEscapeOption>>)); - }(); + static constexpr auto rule = dsl::p<Identifier<StringEscapeOption>> >> lexy::dsl::equal_sign >> lexy::dsl::p<EventList>; static constexpr auto value = - lexy::as_list<std::vector<ast::NodePtr>> >> - lexy::callback<ast::NodePtr>( - [](auto& type, auto&& list) { - return ast::make_node_ptr<ast::EventNode>(type, LEXY_MOV(list)); - }, - [](auto& type, lexy::nullopt = {}) { - return ast::make_node_ptr<ast::EventNode>(type); + dsl::callback<ast::EventStatement*>( + [](ast::ParseState& state, NodeLocation loc, ast::IdentifierValue* name, ast::ListValue* list) { + static auto country_decl = state.ast().intern_cstr("country_event"); + static auto province_decl = state.ast().intern_cstr("province_event"); + + if (name->value(state.ast().symbol_interner()) != country_decl || name->value(state.ast().symbol_interner()) != province_decl) { + state.logger().warning("event declarator \"{}\" is not {} or {}", name->value(state.ast().symbol_interner()), country_decl, province_decl) // + .primary(loc, "here") + .finish(); + } + + return state.ast().create<ast::EventStatement>(loc, name->value(state.ast().symbol_interner()) == province_decl, list); }); }; @@ -170,13 +111,8 @@ namespace ovdl::v2script::grammar { // Allow arbitrary spaces between individual tokens. static constexpr auto whitespace = whitespace_specifier | comment_specifier; - static constexpr auto rule = lexy::dsl::terminator(lexy::dsl::eof).list(lexy::dsl::p<EventStatement> | lexy::dsl::p<SimpleAssignmentStatement<StringEscapeOption>>); + static constexpr auto rule = lexy::dsl::position + lexy::dsl::terminator(lexy::dsl::eof).list(lexy::dsl::p<EventStatement> | lexy::dsl::p<SAssignStatement<StringEscapeOption>>); - static constexpr auto value = lexy::as_list<std::vector<ast::NodePtr>> >> lexy::new_<ast::FileNode, ast::NodePtr>; + static constexpr auto value = lexy::as_list<ast::StatementList> >> construct<ast::FileTree>; }; - -#undef OVDL_GRAMMAR_KEYWORD_DEFINE -#undef OVDL_GRAMMAR_KEYWORD_FLAG_DEFINE -#undef OVDL_GRAMMAR_KEYWORD_STATEMENT -#undef OVDL_GRAMMAR_KEYWORD_FLAG_STATEMENT }
\ No newline at end of file diff --git a/src/openvic-dataloader/v2script/LuaDefinesGrammar.hpp b/src/openvic-dataloader/v2script/LuaDefinesGrammar.hpp index b64d0f9..4d27d3e 100644 --- a/src/openvic-dataloader/v2script/LuaDefinesGrammar.hpp +++ b/src/openvic-dataloader/v2script/LuaDefinesGrammar.hpp @@ -1,11 +1,25 @@ #pragma once +#include <lexy/_detail/config.hpp> #include <lexy/dsl.hpp> +#include "openvic-dataloader/v2script/AbstractSyntaxTree.hpp" + #include "SimpleGrammar.hpp" -#include "detail/LexyLitRange.hpp" +#include "detail/dsl.hpp" namespace ovdl::v2script::lua::grammar { + template<typename ReturnType, typename... Callback> + constexpr auto callback(Callback... cb) { + return dsl::callback<ReturnType>(cb...); + } + + template<typename T> + constexpr auto construct = v2script::grammar::construct<T>; + + template<typename T> + constexpr auto construct_list = v2script::grammar::construct_list<T>; + struct ParseOptions { }; @@ -17,20 +31,20 @@ namespace ovdl::v2script::lua::grammar { template<ParseOptions Options> struct Identifier { static constexpr auto rule = lexy::dsl::identifier(lexy::dsl::ascii::alpha_underscore, lexy::dsl::ascii::alpha_digit_underscore); - static constexpr auto value = lexy::callback<ast::NodePtr>( - [](auto lexeme) { - std::string str(lexeme.data(), lexeme.size()); - return ast::make_node_ptr<ast::IdentifierNode>(ast::NodeLocation { lexeme.begin(), lexeme.end() }, LEXY_MOV(str)); + static constexpr auto value = callback<ast::IdentifierValue*>( + [](ast::ParseState& state, auto lexeme) { + auto value = state.ast().intern(lexeme.data(), lexeme.size()); + return state.ast().create<ast::IdentifierValue>(lexeme.begin(), lexeme.end(), value); }); }; template<ParseOptions Options> struct Value { static constexpr auto rule = lexy::dsl::identifier(lexy::dsl::ascii::digit / lexy::dsl::lit_c<'.'> / lexy::dsl::lit_c<'-'>); - static constexpr auto value = lexy::callback<ast::NodePtr>( - [](auto lexeme) { - std::string str(lexeme.data(), lexeme.size()); - return ast::make_node_ptr<ast::IdentifierNode>(ast::NodeLocation { lexeme.begin(), lexeme.end() }, LEXY_MOV(str)); + static constexpr auto value = callback<ast::IdentifierValue*>( + [](ast::ParseState& state, auto lexeme) { + auto value = state.ast().intern(lexeme.data(), lexeme.size()); + return state.ast().create<ast::IdentifierValue>(lexeme.begin(), lexeme.end(), value); }); }; @@ -38,66 +52,50 @@ namespace ovdl::v2script::lua::grammar { struct String { static constexpr auto rule = [] { // Arbitrary code points that aren't control characters. - auto c = ovdl::detail::lexydsl::make_range<0x20, 0xFF>() - lexy::dsl::ascii::control; + auto c = dsl::make_range<0x20, 0xFF>() - lexy::dsl::ascii::control; return lexy::dsl::delimited(lexy::dsl::position(lexy::dsl::lit_b<'"'>))(c) | lexy::dsl::delimited(lexy::dsl::position(lexy::dsl::lit_b<'\''>))(c); }(); static constexpr auto value = lexy::as_string<std::string> >> - lexy::callback<ast::NodePtr>( - [](const char* begin, auto&& str, const char* end) { - return ast::make_node_ptr<ast::StringNode>(ast::NodeLocation::make_from(begin, end), LEXY_MOV(str)); + callback<ast::StringValue*>( + [](ast::ParseState& state, const char* begin, const std::string& str, const char* end) { + auto value = state.ast().intern(str.data(), str.length()); + return state.ast().create<ast::StringValue>(begin, end, value); }); }; template<ParseOptions Options> struct Expression { static constexpr auto rule = lexy::dsl::p<Value<Options>> | lexy::dsl::p<String<Options>>; - static constexpr auto value = lexy::forward<ast::NodePtr>; + static constexpr auto value = lexy::forward<ast::Value*>; }; template<ParseOptions Options> struct AssignmentStatement { static constexpr auto rule = - lexy::dsl::position(lexy::dsl::p<Identifier<Options>>) >> + dsl::p<Identifier<Options>> >> lexy::dsl::equal_sign >> (lexy::dsl::p<Expression<Options>> | lexy::dsl::recurse_branch<StatementListBlock<Options>>); - static constexpr auto value = lexy::callback<ast::NodePtr>( - [](const char* pos, auto name, lexy::nullopt = {}) { - return LEXY_MOV(name); - }, - [](auto name, lexy::nullopt = {}) { - return LEXY_MOV(name); - }, - [](const char* pos, auto name, auto&& initalizer) { - return ast::make_node_ptr<ast::AssignNode>(pos, LEXY_MOV(name), LEXY_MOV(initalizer)); + static constexpr auto value = callback<ast::AssignStatement*>( + [](ast::ParseState& state, const char* pos, ast::IdentifierValue* name, ast::Value* initializer) { + return state.ast().create<ast::AssignStatement>(pos, name, initializer); }); }; template<ParseOptions Options> struct StatementListBlock { static constexpr auto rule = - lexy::dsl::position(lexy::dsl::curly_bracketed.open()) >> - lexy::dsl::opt( - lexy::dsl::list( - lexy::dsl::recurse_branch<AssignmentStatement<Options>>, - lexy::dsl::trailing_sep(lexy::dsl::lit_c<','>))) >> - lexy::dsl::position(lexy::dsl::curly_bracketed.close()); + dsl::curly_bracketed( + lexy::dsl::opt( + lexy::dsl::list( + lexy::dsl::recurse_branch<AssignmentStatement<Options>>, + lexy::dsl::trailing_sep(lexy::dsl::lit_c<','>)))); static constexpr auto value = - lexy::as_list<std::vector<ast::NodePtr>> >> - lexy::callback<ast::NodePtr>( - [](const char* begin, lexy::nullopt, const char* end) { - return ast::make_node_ptr<ast::ListNode>(ast::NodeLocation::make_from(begin, end)); - }, - [](const char* begin, auto&& list, const char* end) { - return ast::make_node_ptr<ast::ListNode>(ast::NodeLocation::make_from(begin, end), LEXY_MOV(list)); - }, - [](const char* begin, auto& list, const char* end) { - return ast::make_node_ptr<ast::ListNode>(ast::NodeLocation::make_from(begin, end), list); - }); + lexy::as_list<ast::AssignStatementList> >> construct_list<ast::ListValue>; }; template<ParseOptions Options = ParseOptions {}> @@ -107,6 +105,6 @@ namespace ovdl::v2script::lua::grammar { static constexpr auto rule = lexy::dsl::position + lexy::dsl::terminator(lexy::dsl::eof).opt_list(lexy::dsl::p<AssignmentStatement<Options>>); - static constexpr auto value = lexy::as_list<std::vector<ast::NodePtr>> >> lexy::new_<ast::FileNode, ast::NodePtr>; + static constexpr auto value = lexy::as_list<ast::AssignStatementList> >> construct<ast::FileTree>; }; }
\ No newline at end of file diff --git a/src/openvic-dataloader/v2script/ModifierGrammar.hpp b/src/openvic-dataloader/v2script/ModifierGrammar.hpp index 96e928c..5b937d5 100644 --- a/src/openvic-dataloader/v2script/ModifierGrammar.hpp +++ b/src/openvic-dataloader/v2script/ModifierGrammar.hpp @@ -2,32 +2,55 @@ #include <openvic-dataloader/v2script/AbstractSyntaxTree.hpp> +#include <lexy/callback/container.hpp> #include <lexy/dsl.hpp> +#include <dryad/node.hpp> +#include <dryad/tree.hpp> + +#include "openvic-dataloader/NodeLocation.hpp" + #include "SimpleGrammar.hpp" #include "TriggerGrammar.hpp" +#include "detail/dsl.hpp" namespace ovdl::v2script::grammar { constexpr auto modifier_keyword = LEXY_KEYWORD("modifier", lexy::dsl::inline_<Identifier<StringEscapeOption>>); constexpr auto factor_keyword = LEXY_KEYWORD("factor", lexy::dsl::inline_<Identifier<StringEscapeOption>>); struct FactorStatement { - static constexpr auto rule = factor_keyword >> lexy::dsl::equal_sign + lexy::dsl::inline_<Identifier<StringEscapeOption>>; - static constexpr auto value = lexy::as_string<std::string> | lexy::new_<ast::FactorNode, ast::NodePtr>; + static constexpr auto rule = lexy::dsl::position(factor_keyword) >> (lexy::dsl::equal_sign + lexy::dsl::p<Identifier<StringEscapeOption>>); + static constexpr auto value = dsl::callback<ast::AssignStatement*>( + [](ast::ParseState& state, NodeLocation loc, ast::IdentifierValue* value) { + auto* factor = state.ast().create<ast::IdentifierValue>(loc, state.ast().intern("factor")); + return state.ast().create<ast::AssignStatement>(loc, factor, value); + }); + }; + + struct ModifierList { + struct expected_factor { + static constexpr auto name = "expected factor in modifier"; + }; + + static constexpr auto rule = [] { + auto factor_flag = lexy::dsl::context_flag<ModifierList>; + + auto element = (lexy::dsl::p<FactorStatement> >> factor_flag.set()) | lexy::dsl::p<TriggerStatement>; + + return dsl::curly_bracketed.list(factor_flag.create() + element) >> lexy::dsl::must(factor_flag.is_reset()).error<expected_factor>; + }(); + + static constexpr auto value = lexy::as_list<ast::AssignStatementList> >> construct_list<ast::ListValue>; }; struct ModifierStatement { static constexpr auto rule = - modifier_keyword >> - lexy::dsl::curly_bracketed.list( - lexy::dsl::p<FactorStatement> | - lexy::dsl::p<TriggerList>); - - static constexpr auto value = - lexy::as_list<std::vector<ast::NodePtr>> >> - lexy::callback<ast::NodePtr>( - [](auto&& list) { - return ast::make_node_ptr<ast::ModifierNode>(LEXY_MOV(list)); - }); + lexy::dsl::position(modifier_keyword) >> lexy::dsl::equal_sign >> lexy::dsl::p<ModifierList>; + + static constexpr auto value = dsl::callback<ast::AssignStatement*>( + [](ast::ParseState& state, NodeLocation loc, ast::ListValue* list) { + auto* factor = state.ast().create<ast::IdentifierValue>(loc, state.ast().intern("modifier")); + return state.ast().create<ast::AssignStatement>(loc, factor, list); + }); }; }
\ No newline at end of file diff --git a/src/openvic-dataloader/v2script/Parser.cpp b/src/openvic-dataloader/v2script/Parser.cpp index 3141550..29e9e80 100644 --- a/src/openvic-dataloader/v2script/Parser.cpp +++ b/src/openvic-dataloader/v2script/Parser.cpp @@ -1,29 +1,38 @@ #include "openvic-dataloader/v2script/Parser.hpp" -#include <functional> -#include <memory> +#include <iostream> #include <optional> #include <string> #include <utility> -#include <vector> +#include <openvic-dataloader/DiagnosticLogger.hpp> +#include <openvic-dataloader/NodeLocation.hpp> #include <openvic-dataloader/ParseError.hpp> #include <openvic-dataloader/ParseWarning.hpp> -#include <openvic-dataloader/detail/Concepts.hpp> +#include <openvic-dataloader/detail/LexyReportError.hpp> +#include <openvic-dataloader/detail/OStreamOutputIterator.hpp> +#include <openvic-dataloader/detail/utility/Concepts.hpp> +#include <openvic-dataloader/detail/utility/Utility.hpp> #include <openvic-dataloader/v2script/AbstractSyntaxTree.hpp> -#include <openvic-dataloader/v2script/NodeLocationMap.hpp> #include <lexy/action/parse.hpp> #include <lexy/encoding.hpp> #include <lexy/input/buffer.hpp> #include <lexy/input/file.hpp> +#include <lexy/input_location.hpp> #include <lexy/lexeme.hpp> #include <lexy/visualize.hpp> -#include "detail/BasicBufferHandler.hpp" +#include <dryad/node.hpp> +#include <dryad/tree.hpp> + +#include <fmt/core.h> + +#include "openvic-dataloader/Error.hpp" + #include "detail/DetectUtf8.hpp" -#include "detail/LexyReportError.hpp" -#include "detail/OStreamOutputIterator.hpp" +#include "detail/NullBuff.hpp" +#include "detail/ParseHandler.hpp" #include "detail/Warnings.hpp" #include "v2script/DecisionGrammar.hpp" #include "v2script/EventGrammar.hpp" @@ -35,42 +44,36 @@ using namespace ovdl::v2script; /// BufferHandler /// -class Parser::BufferHandler final : public detail::BasicBufferHandler<> { -public: +struct Parser::ParseHandler final : detail::BasicStateParseHandler<v2script::ast::ParseState> { constexpr bool is_exclusive_utf8() const { - return detail::is_utf8_no_ascii(_buffer); + return detail::is_utf8_no_ascii(buffer()); } - template<typename Node, typename ErrorCallback> - std::optional<std::vector<ParseError>> parse(const ErrorCallback& callback) { - auto result = lexy::parse<Node>(_buffer, callback); + template<typename Node> + std::optional<DiagnosticLogger::error_range> parse() { + auto result = lexy::parse<Node>(buffer(), *_parse_state, _parse_state->logger().error_callback()); if (!result) { - return result.errors(); + return _parse_state->logger().get_errors(); } - // This is mighty frustrating - _root = std::unique_ptr<ast::Node>(result.value()); + _parse_state->ast().set_root(result.value()); return std::nullopt; } - std::unique_ptr<ast::Node>& get_root() { - return _root; + ast::FileTree* root() { + return _parse_state->ast().root(); } - - ast::NodeLocationMap<decltype(_buffer)>& get_location_map() { - return _location_map; - } - -private: - friend class ::ovdl::v2script::ast::Node; - std::unique_ptr<ast::Node> _root; - ast::NodeLocationMap<decltype(_buffer)> _location_map; }; /// BufferHandler /// Parser::Parser() - : _buffer_handler(std::make_unique<BufferHandler>()) { - set_error_log_to_stderr(); + : _parse_handler(std::make_unique<ParseHandler>()) { + set_error_log_to_null(); +} + +Parser::Parser(std::basic_ostream<char>& error_stream) + : _parse_handler(std::make_unique<ParseHandler>()) { + set_error_log_to(error_stream); } Parser::Parser(Parser&&) = default; @@ -116,26 +119,29 @@ Parser Parser::from_file(const std::filesystem::path& path) { /// @param args /// template<typename... Args> -constexpr void Parser::_run_load_func(detail::LoadCallback<BufferHandler, Args...> auto func, Args... args) { - _warnings.clear(); - _errors.clear(); +constexpr void Parser::_run_load_func(detail::LoadCallback<Parser::ParseHandler*, Args...> auto func, Args... args) { _has_fatal_error = false; - if (auto error = func(_buffer_handler.get(), std::forward<Args>(args)...); error) { - _has_fatal_error = error.value().type == ParseError::Type::Fatal; - _errors.push_back(error.value()); - _error_stream.get() << "Error: " << _errors.back().message << '\n'; + auto error = func(_parse_handler.get(), std::forward<Args>(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().create_log<error::BufferError>(DiagnosticLogger::DiagnosticKind::error, fmt::runtime(error_message)); + } + if (has_error() && &_error_stream.get() != &detail::cnull) { + print_errors_to(_error_stream.get()); } } constexpr Parser& Parser::load_from_buffer(const char* data, std::size_t size) { // Type can't be deduced? - _run_load_func(std::mem_fn(&BufferHandler::load_buffer_size), data, size); + _run_load_func(std::mem_fn(&ParseHandler::load_buffer_size), data, size); return *this; } constexpr Parser& Parser::load_from_buffer(const char* start, const char* end) { // Type can't be deduced? - _run_load_func(std::mem_fn(&BufferHandler::load_buffer), start, end); + _run_load_func(std::mem_fn(&ParseHandler::load_buffer), start, end); return *this; } @@ -146,7 +152,7 @@ constexpr Parser& Parser::load_from_string(const std::string_view string) { constexpr Parser& Parser::load_from_file(const char* path) { _file_path = path; // Type can be deduced?? - _run_load_func(std::mem_fn(&BufferHandler::load_file), path); + _run_load_func(std::mem_fn(&ParseHandler::load_file), path); return *this; } @@ -154,10 +160,6 @@ Parser& Parser::load_from_file(const std::filesystem::path& path) { return load_from_file(path.string().c_str()); } -constexpr Parser& Parser::load_from_file(const detail::Has_c_str auto& path) { - return load_from_file(path.c_str()); -} - /* REQUIREMENTS: * DAT-23 * DAT-26 @@ -165,149 +167,175 @@ constexpr Parser& Parser::load_from_file(const detail::Has_c_str auto& path) { * DAT-29 */ bool Parser::simple_parse() { - if (!_buffer_handler->is_valid()) { + if (!_parse_handler->is_valid()) { return false; } - if (_buffer_handler->is_exclusive_utf8()) { - _warnings.push_back(warnings::make_utf8_warning(_file_path)); + if (_parse_handler->is_exclusive_utf8()) { + _parse_handler->parse_state().logger().warning(warnings::make_utf8_warning(_file_path)); } - auto errors = _buffer_handler->parse<grammar::File<grammar::NoStringEscapeOption>>(ovdl::detail::ReporError.path(_file_path).to(detail::OStreamOutputIterator { _error_stream })); - if (errors) { - _errors.reserve(errors->size()); - for (auto& err : errors.value()) { - _has_fatal_error |= err.type == ParseError::Type::Fatal; - _errors.push_back(err); + auto errors = _parse_handler->parse<grammar::File<grammar::NoStringEscapeOption>>(); + _has_error = _parse_handler->parse_state().logger().errored(); + _has_warning = _parse_handler->parse_state().logger().warned(); + if (!_parse_handler->root()) { + _has_fatal_error = true; + if (&_error_stream.get() != &detail::cnull) { + print_errors_to(_error_stream); } return false; } - _file_node.reset(static_cast<ast::FileNode*>(_buffer_handler->get_root().release())); return true; } bool Parser::event_parse() { - if (!_buffer_handler->is_valid()) { + if (!_parse_handler->is_valid()) { return false; } - if (_buffer_handler->is_exclusive_utf8()) { - _warnings.push_back(warnings::make_utf8_warning(_file_path)); + if (_parse_handler->is_exclusive_utf8()) { + _parse_handler->parse_state().logger().warning(warnings::make_utf8_warning(_file_path)); } - auto errors = _buffer_handler->parse<grammar::EventFile>(ovdl::detail::ReporError.path(_file_path).to(detail::OStreamOutputIterator { _error_stream })); - if (errors) { - _errors.reserve(errors->size()); - for (auto& err : errors.value()) { - _has_fatal_error |= err.type == ParseError::Type::Fatal; - _errors.push_back(err); + auto errors = _parse_handler->parse<grammar::EventFile>(); + _has_error = _parse_handler->parse_state().logger().errored(); + _has_warning = _parse_handler->parse_state().logger().warned(); + if (!_parse_handler->root()) { + _has_fatal_error = true; + if (&_error_stream.get() != &detail::cnull) { + print_errors_to(_error_stream); } return false; } - _file_node.reset(static_cast<ast::FileNode*>(_buffer_handler->get_root().release())); return true; } bool Parser::decision_parse() { - if (!_buffer_handler->is_valid()) { + if (!_parse_handler->is_valid()) { return false; } - if (_buffer_handler->is_exclusive_utf8()) { - _warnings.push_back(warnings::make_utf8_warning(_file_path)); + if (_parse_handler->is_exclusive_utf8()) { + _parse_handler->parse_state().logger().warning(warnings::make_utf8_warning(_file_path)); } - auto errors = _buffer_handler->parse<grammar::DecisionFile>(ovdl::detail::ReporError.path(_file_path).to(detail::OStreamOutputIterator { _error_stream })); - if (errors) { - _errors.reserve(errors->size()); - for (auto& err : errors.value()) { - _has_fatal_error |= err.type == ParseError::Type::Fatal; - _errors.push_back(err); + auto errors = _parse_handler->parse<grammar::DecisionFile>(); + _has_error = _parse_handler->parse_state().logger().errored(); + _has_warning = _parse_handler->parse_state().logger().warned(); + if (!_parse_handler->root()) { + _has_fatal_error = true; + if (&_error_stream.get() != &detail::cnull) { + print_errors_to(_error_stream); } return false; } - _file_node.reset(static_cast<ast::FileNode*>(_buffer_handler->get_root().release())); return true; } bool Parser::lua_defines_parse() { - if (!_buffer_handler->is_valid()) { + if (!_parse_handler->is_valid()) { return false; } - if (_buffer_handler->is_exclusive_utf8()) { - _warnings.push_back(warnings::make_utf8_warning(_file_path)); + if (_parse_handler->is_exclusive_utf8()) { + _parse_handler->parse_state().logger().warning(warnings::make_utf8_warning(_file_path)); } - auto errors = _buffer_handler->parse<lua::grammar::File<>>(ovdl::detail::ReporError.path(_file_path).to(detail::OStreamOutputIterator { _error_stream })); - if (errors) { - _errors.reserve(errors->size()); - for (auto& err : errors.value()) { - _has_fatal_error |= err.type == ParseError::Type::Fatal; - _errors.push_back(err); + auto errors = _parse_handler->parse<lua::grammar::File<>>(); + _has_error = _parse_handler->parse_state().logger().errored(); + _has_warning = _parse_handler->parse_state().logger().warned(); + if (!_parse_handler->root()) { + _has_fatal_error = true; + if (&_error_stream.get() != &detail::cnull) { + print_errors_to(_error_stream); } return false; } - _file_node.reset(static_cast<ast::FileNode*>(_buffer_handler->get_root().release())); return true; } -const FileNode* Parser::get_file_node() const { - return _file_node.get(); +const FileTree* Parser::get_file_node() const { + return _parse_handler->root(); } -void Parser::generate_node_location_map() { - _buffer_handler->get_location_map().clear(); - _buffer_handler->get_location_map().generate_location_map(_buffer_handler->get_buffer(), get_file_node()); +std::string_view Parser::value(const ovdl::v2script::ast::FlatValue& node) const { + return node.value(_parse_handler->parse_state().ast().symbol_interner()); } -const ast::Node::line_col Parser::get_node_begin(const ast::NodeCPtr node) const { - if (!node) return { 0, 0 }; - return node->get_begin_line_col(*this); +std::string Parser::make_native_string() const { + return _parse_handler->parse_state().ast().make_native_visualizer(); } -const ast::Node::line_col Parser::get_node_end(const ast::NodeCPtr node) const { - if (!node) return { 0, 0 }; - return node->get_end_line_col(*this); +std::string Parser::make_list_string() const { + return _parse_handler->parse_state().ast().make_list_visualizer(); } -const ast::Node::line_col ast::Node::get_begin_line_col(const Parser& parser) const { - if (!parser._buffer_handler->is_valid() || parser._buffer_handler->_location_map.empty()) return {}; - line_col result {}; - auto [itr, range_end] = parser._buffer_handler->_location_map.equal_range(this); - if (itr != range_end) { - result.line = itr->second.line_nr(); - result.column = itr->second.column_nr(); +const FilePosition Parser::get_position(const ast::Node* node) const { + if (!node || !node->is_linked_in_tree()) { + return {}; } - // Standard doesn't really guarantee the direction of the range's sequence, but only GCC goes backwards - // TODO: DON'T USE STANDARD UNORDERED_MULTIMAP -#if defined(__GNUC__) && !defined(__clang__) - itr++; - if (itr != range_end) { - result.line = itr->second.line_nr(); - result.column = itr->second.column_nr(); + auto node_location = _parse_handler->parse_state().ast().location_of(node); + if (node_location.is_synthesized()) { + return {}; + } + + auto loc_begin = lexy::get_input_location(_parse_handler->buffer(), node_location.begin()); + FilePosition result { loc_begin.line_nr(), loc_begin.line_nr(), loc_begin.column_nr(), loc_begin.column_nr() }; + if (node_location.begin() < node_location.end()) { + auto loc_end = lexy::get_input_location(_parse_handler->buffer(), node_location.end(), loc_begin.anchor()); + result.end_line = loc_end.line_nr(); + result.end_column = loc_end.column_nr(); } -#endif return result; } -const ast::Node::line_col ast::Node::get_end_line_col(const Parser& parser) const { - if (!parser._buffer_handler->is_valid() || parser._buffer_handler->_location_map.empty()) return {}; - line_col result {}; - auto [itr, range_end] = parser._buffer_handler->_location_map.equal_range(this); - if (itr != range_end) { - result.line = itr->second.line_nr(); - result.column = itr->second.column_nr(); +Parser::error_range Parser::get_errors() const { + return _parse_handler->parse_state().logger().get_errors(); +} + +const FilePosition Parser::get_error_position(const error::Error* error) const { + if (!error || !error->is_linked_in_tree()) { + return {}; } - // Standard doesn't really guarantee the direction of the range's sequence, but only GCC goes backwards - // TODO: DON'T USE STANDARD UNORDERED_MULTIMAP -#if defined(__GNUC__) && !defined(__clang__) - return result; -#endif - itr++; - if (itr != range_end) { - result.line = itr->second.line_nr(); - result.column = itr->second.column_nr(); + 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; +} + +void Parser::print_errors_to(std::basic_ostream<char>& 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->message() << '\n'; + }, + [&](const error::ParseError* parse_error) { + stream << parse_error->message() << '\n'; + }, + [&](dryad::child_visitor<error::ErrorKind> visitor, const error::Semantic* semantic) { + stream << 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'; + }); + } }
\ No newline at end of file diff --git a/src/openvic-dataloader/v2script/SimpleGrammar.hpp b/src/openvic-dataloader/v2script/SimpleGrammar.hpp index 7a59123..bd4adaa 100644 --- a/src/openvic-dataloader/v2script/SimpleGrammar.hpp +++ b/src/openvic-dataloader/v2script/SimpleGrammar.hpp @@ -1,14 +1,12 @@ #pragma once -#include <string> -#include <vector> - +#include <openvic-dataloader/NodeLocation.hpp> #include <openvic-dataloader/v2script/AbstractSyntaxTree.hpp> #include <lexy/callback.hpp> #include <lexy/dsl.hpp> -#include "detail/LexyLitRange.hpp" +#include "detail/dsl.hpp" // Grammar Definitions // /* REQUIREMENTS: @@ -21,6 +19,11 @@ * DAT-643 */ namespace ovdl::v2script::grammar { + template<typename T> + constexpr auto construct = dsl::construct<ast::ParseState, T>; + template<typename T, bool DisableEmpty = false, typename ListType = ast::AssignStatementList> + constexpr auto construct_list = dsl::construct_list<ast::ParseState, T, ListType, DisableEmpty>; + struct ParseOptions { /// @brief Makes string parsing avoid string escapes bool NoStringEscape; @@ -29,11 +32,6 @@ namespace ovdl::v2script::grammar { static constexpr ParseOptions NoStringEscapeOption = ParseOptions { true }; static constexpr ParseOptions StringEscapeOption = ParseOptions { false }; - template<ParseOptions Options> - struct StatementListBlock; - template<ParseOptions Options> - struct AssignmentStatement; - /* REQUIREMENTS: DAT-630 */ static constexpr auto whitespace_specifier = lexy::dsl::ascii::blank / lexy::dsl::ascii::newline; /* REQUIREMENTS: DAT-631 */ @@ -47,18 +45,18 @@ namespace ovdl::v2script::grammar { lexy::dsl::ascii::alpha_digit_underscore / LEXY_ASCII_ONE_OF("+:@%&'-.") / lexy::dsl::lit_b<0x8A> / lexy::dsl::lit_b<0x8C> / lexy::dsl::lit_b<0x8E> / lexy::dsl::lit_b<0x92> / lexy::dsl::lit_b<0x97> / lexy::dsl::lit_b<0x9A> / lexy::dsl::lit_b<0x9C> / - detail::lexydsl::make_range<0x9E, 0x9F>() / - detail::lexydsl::make_range<0xC0, 0xD6>() / - detail::lexydsl::make_range<0xD8, 0xF6>() / - detail::lexydsl::make_range<0xF8, 0xFF>(); + dsl::make_range<0x9E, 0x9F>() / + dsl::make_range<0xC0, 0xD6>() / + dsl::make_range<0xD8, 0xF6>() / + dsl::make_range<0xF8, 0xFF>(); static constexpr auto windows_1251_data_specifier_additions = - detail::lexydsl::make_range<0x80, 0x81>() / lexy::dsl::lit_b<0x83> / lexy::dsl::lit_b<0x8D> / lexy::dsl::lit_b<0x8F> / + dsl::make_range<0x80, 0x81>() / lexy::dsl::lit_b<0x83> / lexy::dsl::lit_b<0x8D> / lexy::dsl::lit_b<0x8F> / lexy::dsl::lit_b<0x90> / lexy::dsl::lit_b<0x9D> / lexy::dsl::lit_b<0x9F> / - detail::lexydsl::make_range<0xA1, 0xA3>() / lexy::dsl::lit_b<0xA5> / lexy::dsl::lit_b<0xA8> / lexy::dsl::lit_b<0xAA> / + dsl::make_range<0xA1, 0xA3>() / lexy::dsl::lit_b<0xA5> / lexy::dsl::lit_b<0xA8> / lexy::dsl::lit_b<0xAA> / lexy::dsl::lit_b<0xAF> / - detail::lexydsl::make_range<0xB2, 0xB4>() / lexy::dsl::lit_b<0xB8> / lexy::dsl::lit_b<0xBA> / - detail::lexydsl::make_range<0xBC, 0xBF>() / + dsl::make_range<0xB2, 0xB4>() / lexy::dsl::lit_b<0xB8> / lexy::dsl::lit_b<0xBA> / + dsl::make_range<0xBC, 0xBF>() / lexy::dsl::lit_b<0xD7> / lexy::dsl::lit_b<0xF7>; static constexpr auto data_specifier = windows_1252_data_specifier / windows_1251_data_specifier_additions; @@ -77,125 +75,145 @@ namespace ovdl::v2script::grammar { .map<'t'>('\t'); template<ParseOptions Options> - struct Identifier { - static constexpr auto rule = lexy::dsl::identifier(data_char_class); - static constexpr auto value = lexy::callback<ast::NodePtr>( - [](auto lexeme) { - std::string str(lexeme.data(), lexeme.size()); - return ast::make_node_ptr<ast::IdentifierNode>(ast::NodeLocation { lexeme.begin(), lexeme.end() }, LEXY_MOV(str)); - }); - }; - - /* REQUIREMENTS: - * DAT-633 - * DAT-634 - */ - template<ParseOptions Options> - struct StringExpression { - static constexpr auto rule = [] { - if constexpr (Options.NoStringEscape) { - auto c = ovdl::detail::lexydsl::make_range<0x20, 0xFF>() / lexy::dsl::lit_b<0x07> / lexy::dsl::lit_b<0x09> / lexy::dsl::lit_b<0x0A> / lexy::dsl::lit_b<0x0D>; - return lexy::dsl::delimited(lexy::dsl::position(lexy::dsl::lit_b<'"'>))(c); - } else { - // Arbitrary code points that aren't control characters. - auto c = ovdl::detail::lexydsl::make_range<0x20, 0xFF>() - lexy::dsl::ascii::control; - - // Escape sequences start with a backlash. - // They either map one of the symbols, - // or a Unicode code point of the form uXXXX. - auto escape = lexy::dsl::backslash_escape // - .symbol<escaped_symbols>(); - return lexy::dsl::delimited(lexy::dsl::position(lexy::dsl::lit_b<'"'>))(c, escape); - } - }(); - - static constexpr auto value = - lexy::as_string<std::string> >> - lexy::callback<ast::NodePtr>( - [](const char* begin, auto&& str, const char* end) { - return ast::make_node_ptr<ast::StringNode>(ast::NodeLocation::make_from(begin, end), LEXY_MOV(str), Options.NoStringEscape); + struct SimpleGrammar { + struct StatementListBlock; + + struct Identifier { + static constexpr auto rule = lexy::dsl::identifier(data_char_class); + static constexpr auto value = dsl::callback<ast::IdentifierValue*>( + [](ast::ParseState& state, auto lexeme) { + auto value = state.ast().intern(lexeme.data(), lexeme.size()); + return state.ast().create<ast::IdentifierValue>(ovdl::NodeLocation::make_from(lexeme.begin(), lexeme.end()), value); }); - }; - - /* REQUIREMENTS: DAT-638 */ - template<ParseOptions Options> - struct ValueExpression { - static constexpr auto rule = lexy::dsl::p<Identifier<Options>> | lexy::dsl::p<StringExpression<Options>>; - static constexpr auto value = lexy::forward<ast::NodePtr>; + }; + + /* REQUIREMENTS: + * DAT-633 + * DAT-634 + */ + struct StringExpression { + static constexpr auto rule = [] { + if constexpr (Options.NoStringEscape) { + auto c = dsl::make_range<0x20, 0xFF>() / lexy::dsl::lit_b<0x07> / lexy::dsl::lit_b<0x09> / lexy::dsl::lit_b<0x0A> / lexy::dsl::lit_b<0x0D>; + return lexy::dsl::delimited(lexy::dsl::position(lexy::dsl::lit_b<'"'>))(c); + } else { + // Arbitrary code points that aren't control characters. + auto c = dsl::make_range<0x20, 0xFF>() - lexy::dsl::ascii::control; + + // Escape sequences start with a backlash. + // They either map one of the symbols, + // or a Unicode code point of the form uXXXX. + auto escape = lexy::dsl::backslash_escape // + .symbol<escaped_symbols>(); + return lexy::dsl::delimited(lexy::dsl::position(lexy::dsl::lit_b<'"'>))(c, escape); + } + }(); + + static constexpr auto value = + lexy::as_string<std::string> >> + dsl::callback<ast::StringValue*>( + [](ast::ParseState& state, const char* begin, auto&& str, const char* end) { + auto value = state.ast().intern(str.data(), str.length()); + return state.ast().create<ast::StringValue>(ovdl::NodeLocation::make_from(begin, end), value); + }); + }; + + /* REQUIREMENTS: DAT-638 */ + struct ValueExpression { + static constexpr auto rule = lexy::dsl::p<Identifier> | lexy::dsl::p<StringExpression>; + static constexpr auto value = lexy::forward<ast::Value*>; + }; + + struct SimpleAssignmentStatement { + static constexpr auto rule = + dsl::p<Identifier> >> + (lexy::dsl::equal_sign >> + (lexy::dsl::p<ValueExpression> | lexy::dsl::recurse_branch<StatementListBlock>)); + + static constexpr auto value = construct<ast::AssignStatement>; + }; + + /* REQUIREMENTS: DAT-639 */ + struct AssignmentStatement { + static constexpr auto rule = + dsl::p<Identifier> >> + (lexy::dsl::equal_sign >> + (lexy::dsl::p<ValueExpression> | lexy::dsl::recurse_branch<StatementListBlock>) | + lexy::dsl::else_ >> lexy::dsl::return_) | + dsl::p<StringExpression> | + lexy::dsl::recurse_branch<StatementListBlock>; + + static constexpr auto value = dsl::callback<ast::Statement*>( + [](ast::ParseState& state, const char* pos, ast::IdentifierValue* name, ast::Value* initializer) { + return state.ast().create<ast::AssignStatement>(pos, name, initializer); + }, + [](ast::ParseState& state, const char* pos, ast::Value* left, lexy::nullopt = {}) { + return state.ast().create<ast::ValueStatement>(pos, left); + }, + [](ast::ParseState& state, ast::Value* left) { + return state.ast().create<ast::ValueStatement>(state.ast().location_of(left), left); + }); + }; + + /* REQUIREMENTS: DAT-640 */ + struct StatementListBlock { + static constexpr auto rule = + dsl::curly_bracketed( + (lexy::dsl::opt(lexy::dsl::list(lexy::dsl::recurse_branch<AssignmentStatement>)) + + lexy::dsl::opt(lexy::dsl::semicolon))); + + static constexpr auto value = + lexy::as_list<ast::StatementList> >> + dsl::callback<ast::ListValue*>( + [](ast::ParseState& state, const char* begin, auto&& list, const char* end) { + if constexpr (std::is_same_v<std::decay_t<decltype(list)>, lexy::nullopt>) { + return state.ast().create<ast::ListValue>(ovdl::NodeLocation::make_from(begin, end)); + } else { + return state.ast().create<ast::ListValue>(ovdl::NodeLocation::make_from(begin, end), LEXY_MOV(list)); + } + }, + [](ast::ParseState& state, const char* begin, auto&& list, auto&& semicolon, const char* end) { + if constexpr (std::is_same_v<std::decay_t<decltype(list)>, lexy::nullopt>) { + return state.ast().create<ast::ListValue>(ovdl::NodeLocation::make_from(begin, end)); + } else { + return state.ast().create<ast::ListValue>(ovdl::NodeLocation::make_from(begin, end), LEXY_MOV(list)); + } + }); + }; }; template<ParseOptions Options> - struct SimpleAssignmentStatement { - static constexpr auto rule = - lexy::dsl::position(lexy::dsl::p<Identifier<Options>>) >> - (lexy::dsl::equal_sign + - (lexy::dsl::p<ValueExpression<Options>> | lexy::dsl::recurse_branch<StatementListBlock<Options>>)); - - static constexpr auto value = lexy::callback<ast::NodePtr>( - [](const char* pos, auto name, auto&& initalizer) { - return ast::make_node_ptr<ast::AssignNode>(pos, LEXY_MOV(name), LEXY_MOV(initalizer)); - }); - }; + using StringExpression = typename SimpleGrammar<Options>::StringExpression; - /* REQUIREMENTS: DAT-639 */ template<ParseOptions Options> - struct AssignmentStatement { - static constexpr auto rule = - lexy::dsl::position(lexy::dsl::p<Identifier<Options>>) >> - (lexy::dsl::equal_sign >> - (lexy::dsl::p<ValueExpression<Options>> | lexy::dsl::recurse_branch<StatementListBlock<Options>>) | - lexy::dsl::else_ >> lexy::dsl::return_) | - lexy::dsl::p<StringExpression<Options>> | - lexy::dsl::recurse_branch<StatementListBlock<Options>>; - - static constexpr auto value = lexy::callback<ast::NodePtr>( - [](const char* pos, auto name, lexy::nullopt = {}) { - return LEXY_MOV(name); - }, - [](auto name, lexy::nullopt = {}) { - return LEXY_MOV(name); - }, - [](const char* pos, auto name, auto&& initalizer) { - return ast::make_node_ptr<ast::AssignNode>(pos, LEXY_MOV(name), LEXY_MOV(initalizer)); - }); - }; + using Identifier = typename SimpleGrammar<Options>::Identifier; - /* REQUIREMENTS: DAT-640 */ template<ParseOptions Options> - struct StatementListBlock { - static constexpr auto rule = - lexy::dsl::position(lexy::dsl::curly_bracketed.open()) >> - (lexy::dsl::opt(lexy::dsl::list(lexy::dsl::recurse_branch<AssignmentStatement<Options>>)) + - lexy::dsl::opt(lexy::dsl::semicolon)) >> - lexy::dsl::position(lexy::dsl::curly_bracketed.close()); - - static constexpr auto value = - lexy::as_list<std::vector<ast::NodePtr>> >> - lexy::callback<ast::NodePtr>( - [](const char* begin, lexy::nullopt, const char* end) { - return ast::make_node_ptr<ast::ListNode>(ast::NodeLocation::make_from(begin, end)); - }, - [](const char* begin, auto&& list, const char* end) { - return ast::make_node_ptr<ast::ListNode>(ast::NodeLocation::make_from(begin, end), LEXY_MOV(list)); - }, - [](const char* begin, lexy::nullopt, lexy::nullopt, const char* end) { - return ast::make_node_ptr<ast::ListNode>(ast::NodeLocation::make_from(begin, end)); - }, - [](const char* begin, auto&& list, lexy::nullopt, const char* end) { - return ast::make_node_ptr<ast::ListNode>(ast::NodeLocation::make_from(begin, end), LEXY_MOV(list)); - }, - [](const char* begin, auto& list, const char* end) { - return ast::make_node_ptr<ast::ListNode>(ast::NodeLocation::make_from(begin, end), list); - }); - }; + using SAssignStatement = typename SimpleGrammar<Options>::SimpleAssignmentStatement; + + template<ovdl::detail::string_literal Keyword, auto Production, auto Value = dsl::default_kw_value<ast::ParseState, ast::IdentifierValue, Keyword>> + using keyword_rule = dsl::keyword_rule< + ast::ParseState, + Identifier<StringEscapeOption>, + ast::AssignStatement, + Keyword, Production, Value>; + + template<ovdl::detail::string_literal Keyword, auto Production, auto Value = dsl::default_kw_value<ast::ParseState, ast::IdentifierValue, Keyword>> + using fkeyword_rule = dsl::fkeyword_rule< + ast::ParseState, + Identifier<StringEscapeOption>, + ast::AssignStatement, + Keyword, Production, Value>; template<ParseOptions Options> struct File { // Allow arbitrary spaces between individual tokens. static constexpr auto whitespace = whitespace_specifier | comment_specifier; - static constexpr auto rule = lexy::dsl::position + lexy::dsl::terminator(lexy::dsl::eof).opt_list(lexy::dsl::p<AssignmentStatement<Options>>); + static constexpr auto rule = lexy::dsl::position( + lexy::dsl::terminator(lexy::dsl::eof) + .opt_list(lexy::dsl::p<typename SimpleGrammar<Options>::AssignmentStatement>)); - static constexpr auto value = lexy::as_list<std::vector<ast::NodePtr>> >> lexy::new_<ast::FileNode, ast::NodePtr>; + static constexpr auto value = lexy::as_list<ast::StatementList> >> construct<ast::FileTree>; }; } diff --git a/src/openvic-dataloader/v2script/TriggerGrammar.hpp b/src/openvic-dataloader/v2script/TriggerGrammar.hpp index 3849b12..095d115 100644 --- a/src/openvic-dataloader/v2script/TriggerGrammar.hpp +++ b/src/openvic-dataloader/v2script/TriggerGrammar.hpp @@ -6,37 +6,24 @@ #include <lexy/dsl.hpp> #include "SimpleGrammar.hpp" +#include "detail/dsl.hpp" namespace ovdl::v2script::grammar { struct TriggerStatement { - static constexpr auto rule = lexy::dsl::inline_<SimpleAssignmentStatement<StringEscapeOption>>; + static constexpr auto rule = lexy::dsl::p<SAssignStatement<StringEscapeOption>>; - static constexpr auto value = lexy::callback<ast::NodePtr>( - [](auto name, auto&& initalizer) { - return ast::make_node_ptr<ast::ExecutionNode>({}, ast::ExecutionNode::Type::Trigger, LEXY_MOV(name), LEXY_MOV(initalizer)); - }); + static constexpr auto value = lexy::forward<ast::AssignStatement*>; }; struct TriggerList { - static constexpr auto rule = lexy::dsl::list(lexy::dsl::p<SimpleAssignmentStatement<StringEscapeOption>>); - - static constexpr auto value = - lexy::as_list<std::vector<ast::NodePtr>> >> - lexy::callback<ast::NodePtr>( - [](auto&& list) { - return ast::make_node_ptr<ast::ExecutionListNode>(ast::ExecutionNode::Type::Trigger, LEXY_MOV(list)); - }); + static constexpr auto rule = lexy::dsl::list(lexy::dsl::p<TriggerStatement>); + + static constexpr auto value = lexy::as_list<ast::AssignStatementList>; }; struct TriggerBlock { - static constexpr auto rule = lexy::dsl::curly_bracketed.opt(lexy::dsl::p<TriggerList>); - - static constexpr auto value = lexy::callback<ast::NodePtr>( - [](auto&& list) { - return LEXY_MOV(list); - }, - [](lexy::nullopt = {}) { - return lexy::nullopt {}; - }); + static constexpr auto rule = dsl::curly_bracketed.opt(lexy::dsl::p<TriggerList>); + + static constexpr auto value = construct_list<ast::ListValue>; }; }
\ No newline at end of file |