From 757114a3c5b748567b42f273c7b78ca039ae983c Mon Sep 17 00:00:00 2001 From: Spartan322 Date: Tue, 28 Nov 2023 05:09:26 -0500 Subject: Add `deps/dryad` -> https://github.com/Spartan322/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` --- .clang-format | 8 +- .gitmodules | 9 + deps/SCsub | 78 +++- deps/dryad | 1 + deps/fmt | 1 + deps/range-v3 | 1 + include/openvic-dataloader/AbstractSyntaxTree.hpp | 111 ++++++ include/openvic-dataloader/DiagnosticLogger.hpp | 390 +++++++++++++++++++ include/openvic-dataloader/Error.hpp | 195 ++++++++++ include/openvic-dataloader/File.hpp | 69 ++++ include/openvic-dataloader/NodeLocation.hpp | 33 ++ include/openvic-dataloader/ParseState.hpp | 120 ++++++ include/openvic-dataloader/Parser.hpp | 31 ++ include/openvic-dataloader/csv/LineObject.hpp | 2 +- include/openvic-dataloader/csv/Parser.hpp | 25 +- include/openvic-dataloader/detail/BasicParser.hpp | 37 -- .../openvic-dataloader/detail/CallbackOStream.hpp | 31 +- include/openvic-dataloader/detail/ClassExport.hpp | 9 - include/openvic-dataloader/detail/Concepts.hpp | 21 -- .../detail/LexyFwdDeclaration.hpp | 8 + .../openvic-dataloader/detail/LexyReportError.hpp | 107 ++++++ .../detail/OStreamOutputIterator.hpp | 22 ++ .../detail/OptionalConstexpr.hpp | 9 - include/openvic-dataloader/detail/PointerHash.hpp | 23 -- include/openvic-dataloader/detail/SelfType.hpp | 28 -- include/openvic-dataloader/detail/TypeName.hpp | 52 --- .../openvic-dataloader/detail/VectorConstexpr.hpp | 9 - .../openvic-dataloader/detail/utility/Concepts.hpp | 45 +++ .../detail/utility/Constexpr.hpp | 15 + .../detail/utility/ErrorRange.hpp | 11 + .../detail/utility/PointerHash.hpp | 23 ++ .../openvic-dataloader/detail/utility/SelfType.hpp | 28 ++ .../openvic-dataloader/detail/utility/TypeName.hpp | 52 +++ .../openvic-dataloader/detail/utility/Utility.hpp | 38 ++ .../v2script/AbstractSyntaxTree.hpp | 398 +++++++------------- .../v2script/NodeLocationMap.hpp | 91 ----- include/openvic-dataloader/v2script/Parser.hpp | 46 ++- src/headless/main.cpp | 112 +++--- src/openvic-dataloader/AbstractSyntaxTree.cpp | 27 ++ src/openvic-dataloader/DiagnosticLogger.cpp | 16 + src/openvic-dataloader/File.cpp | 9 + src/openvic-dataloader/NodeLocation.cpp | 26 ++ src/openvic-dataloader/csv/CsvGrammar.hpp | 201 +++++----- src/openvic-dataloader/csv/CsvParseState.hpp | 28 ++ src/openvic-dataloader/csv/Parser.cpp | 161 +++++--- .../detail/BasicBufferHandler.hpp | 51 --- src/openvic-dataloader/detail/BasicParser.cpp | 51 --- src/openvic-dataloader/detail/BufferError.hpp | 17 + src/openvic-dataloader/detail/DetectUtf8.hpp | 12 +- src/openvic-dataloader/detail/LexyLitRange.hpp | 16 - src/openvic-dataloader/detail/LexyReportError.hpp | 106 ------ src/openvic-dataloader/detail/NullBuff.hpp | 8 +- .../detail/OStreamOutputIterator.hpp | 21 -- src/openvic-dataloader/detail/ParseHandler.hpp | 145 +++++++ src/openvic-dataloader/detail/Parser.cpp | 43 +++ src/openvic-dataloader/detail/StringLiteral.hpp | 232 ++++++++++++ src/openvic-dataloader/detail/Warnings.hpp | 4 +- src/openvic-dataloader/detail/dsl.hpp | 150 ++++++++ .../v2script/AbstractSyntaxTree.cpp | 417 +++++++-------------- .../v2script/AiBehaviorGrammar.hpp | 18 +- .../v2script/DecisionGrammar.hpp | 115 ++---- src/openvic-dataloader/v2script/EffectGrammar.hpp | 31 +- src/openvic-dataloader/v2script/EventGrammar.hpp | 214 ++++------- .../v2script/LuaDefinesGrammar.hpp | 82 ++-- .../v2script/ModifierGrammar.hpp | 49 ++- src/openvic-dataloader/v2script/Parser.cpp | 280 +++++++------- src/openvic-dataloader/v2script/SimpleGrammar.hpp | 262 +++++++------ src/openvic-dataloader/v2script/TriggerGrammar.hpp | 31 +- 68 files changed, 3200 insertions(+), 1912 deletions(-) create mode 160000 deps/dryad create mode 160000 deps/fmt create mode 160000 deps/range-v3 create mode 100644 include/openvic-dataloader/AbstractSyntaxTree.hpp create mode 100644 include/openvic-dataloader/DiagnosticLogger.hpp create mode 100644 include/openvic-dataloader/Error.hpp create mode 100644 include/openvic-dataloader/File.hpp create mode 100644 include/openvic-dataloader/NodeLocation.hpp create mode 100644 include/openvic-dataloader/ParseState.hpp create mode 100644 include/openvic-dataloader/Parser.hpp delete mode 100644 include/openvic-dataloader/detail/BasicParser.hpp delete mode 100644 include/openvic-dataloader/detail/ClassExport.hpp delete mode 100644 include/openvic-dataloader/detail/Concepts.hpp create mode 100644 include/openvic-dataloader/detail/LexyFwdDeclaration.hpp create mode 100644 include/openvic-dataloader/detail/LexyReportError.hpp create mode 100644 include/openvic-dataloader/detail/OStreamOutputIterator.hpp delete mode 100644 include/openvic-dataloader/detail/OptionalConstexpr.hpp delete mode 100644 include/openvic-dataloader/detail/PointerHash.hpp delete mode 100644 include/openvic-dataloader/detail/SelfType.hpp delete mode 100644 include/openvic-dataloader/detail/TypeName.hpp delete mode 100644 include/openvic-dataloader/detail/VectorConstexpr.hpp create mode 100644 include/openvic-dataloader/detail/utility/Concepts.hpp create mode 100644 include/openvic-dataloader/detail/utility/Constexpr.hpp create mode 100644 include/openvic-dataloader/detail/utility/ErrorRange.hpp create mode 100644 include/openvic-dataloader/detail/utility/PointerHash.hpp create mode 100644 include/openvic-dataloader/detail/utility/SelfType.hpp create mode 100644 include/openvic-dataloader/detail/utility/TypeName.hpp create mode 100644 include/openvic-dataloader/detail/utility/Utility.hpp delete mode 100644 include/openvic-dataloader/v2script/NodeLocationMap.hpp create mode 100644 src/openvic-dataloader/AbstractSyntaxTree.cpp create mode 100644 src/openvic-dataloader/DiagnosticLogger.cpp create mode 100644 src/openvic-dataloader/File.cpp create mode 100644 src/openvic-dataloader/NodeLocation.cpp create mode 100644 src/openvic-dataloader/csv/CsvParseState.hpp delete mode 100644 src/openvic-dataloader/detail/BasicBufferHandler.hpp delete mode 100644 src/openvic-dataloader/detail/BasicParser.cpp create mode 100644 src/openvic-dataloader/detail/BufferError.hpp delete mode 100644 src/openvic-dataloader/detail/LexyLitRange.hpp delete mode 100644 src/openvic-dataloader/detail/LexyReportError.hpp delete mode 100644 src/openvic-dataloader/detail/OStreamOutputIterator.hpp create mode 100644 src/openvic-dataloader/detail/ParseHandler.hpp create mode 100644 src/openvic-dataloader/detail/Parser.cpp create mode 100644 src/openvic-dataloader/detail/StringLiteral.hpp create mode 100644 src/openvic-dataloader/detail/dsl.hpp diff --git a/.clang-format b/.clang-format index 86fc638..93baaea 100644 --- a/.clang-format +++ b/.clang-format @@ -55,7 +55,11 @@ IncludeCategories: Priority: 3 - Regex: ^ +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +namespace ovdl { + struct AbstractSyntaxTree { + struct SymbolId; + using index_type = std::uint32_t; + using symbol_type = dryad::symbol; + using symbol_interner_type = dryad::symbol_interner; + + symbol_type intern(const char* str, std::size_t length); + symbol_type intern(std::string_view str); + const char* intern_cstr(const char* str, std::size_t length); + const char* intern_cstr(std::string_view str); + symbol_interner_type& symbol_interner(); + const symbol_interner_type& symbol_interner() const; + + protected: + symbol_interner_type _symbol_interner; + }; + + template + concept IsAst = + std::derived_from && + requires( + T t, + const T ct, + const typename T::node_type* node, + NodeLocation loc // + ) { + requires IsFile; + typename T::root_node_type; + typename T::node_type; + requires std::derived_from; + { t.set_location(node, loc) } -> std::same_as; + { t.location_of(node) } -> std::same_as; + { t.root() } -> std::same_as; + { ct.root() } -> std::same_as; + { t.file() } -> std::same_as; + { ct.file() } -> std::same_as; + }; + + template RootNodeT> + struct BasicAbstractSyntaxTree : AbstractSyntaxTree { + using file_type = FileT; + using root_node_type = RootNodeT; + using node_type = typename file_type::node_type; + + explicit BasicAbstractSyntaxTree(file_type&& file) : _file(std::move(file)) {} + explicit BasicAbstractSyntaxTree(lexy::buffer&& buffer) : _file(std::move(buffer)) {} + + void set_location(const node_type* n, NodeLocation loc) { + _file.set_location(n, loc); + } + + NodeLocation location_of(const node_type* n) const { + return _file.location_of(n); + } + + root_node_type* root() { + return _tree.root(); + } + + const root_node_type* root() const { + return _tree.root(); + } + + file_type& file() { + return _file; + } + + const file_type& file() const { + return _file; + } + + template + T* create(NodeLocation loc, Args&&... args) { + auto node = _tree.template create(DRYAD_FWD(args)...); + set_location(node, loc); + return node; + } + + template + T* create(const char* begin, const char* end, Args&&... args) { + return create(NodeLocation::make_from(begin, end), DRYAD_FWD(args)...); + } + + void set_root(root_node_type* node) { + _tree.set_root(node); + } + + protected: + dryad::tree _tree; + file_type _file; + }; +} \ No newline at end of file diff --git a/include/openvic-dataloader/DiagnosticLogger.hpp b/include/openvic-dataloader/DiagnosticLogger.hpp new file mode 100644 index 0000000..1fa2784 --- /dev/null +++ b/include/openvic-dataloader/DiagnosticLogger.hpp @@ -0,0 +1,390 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "openvic-dataloader/detail/CallbackOStream.hpp" +#include "openvic-dataloader/detail/utility/ErrorRange.hpp" +#include "openvic-dataloader/detail/utility/Utility.hpp" + +#include + +namespace ovdl { + struct DiagnosticLogger { + using AnnotationKind = lexy_ext::annotation_kind; + using DiagnosticKind = lexy_ext::diagnostic_kind; + + using error_range = detail::error_range; + + explicit operator bool() const; + bool errored() const; + bool warned() const; + + NodeLocation location_of(const error::Error* error) const; + + template Logger> + struct ErrorCallback { + ErrorCallback(Logger& logger) : _logger(&logger) {} + + struct sink_t { + using return_type = std::size_t; + + template + void operator()(lexy::error_context const& context, lexy::error_for const& error) { + using Reader = lexy::input_reader; + error::Error* result; + + if constexpr (std::is_same_v) { + auto string = lexy::_detail::make_literal_lexeme(error.string(), error.length()); + NodeLocation loc = NodeLocation::make_from(string.begin(), string.end()); + auto message = _logger.intern_cstr(fmt::format("expected '{}'", string.data())); + result = _logger.template create(loc, message, context.production()); + } else if constexpr (std::is_same_v) { + auto string = lexy::_detail::make_literal_lexeme(error.string(), error.length()); + NodeLocation loc = NodeLocation::make_from(string.begin(), string.end()); + auto message = _logger.intern_cstr(fmt::format("expected keyword '{}'", string.data())); + result = _logger.template create(loc, message, context.production()); + } else if constexpr (std::is_same_v) { + auto message = _logger.intern_cstr(fmt::format("expected {}", error.name())); + result = _logger.template create(error.position(), message, context.production()); + } else { + NodeLocation loc = NodeLocation::make_from(error.begin(), error.end()); + auto message = _logger.intern_cstr(error.message()); + result = _logger.template create(loc, message, context.production()); + } + + if constexpr (requires { _logger.insert(result); }) { + _logger.insert(result); + } + + _count++; + } + + std::size_t finish() && { + return _count; + } + + Logger& _logger; + std::size_t _count; + }; + + constexpr auto sink() const { + return sink_t { *_logger, 0 }; + } + + mutable Logger* _logger; + }; + + template + T* create(NodeLocation loc, Args&&... args) { + using node_creator = dryad::node_creator; + T* result = _tree.create(DRYAD_FWD(args)...); + _map.insert(result, loc); + return result; + } + + template + T* create() { + using node_creator = dryad::node_creator; + T* result = _tree.create(); + return result; + } + + protected: + bool _errored = false; + bool _warned = false; + dryad::node_map _map; + dryad::tree _tree; + + struct SymbolId; + using index_type = std::uint32_t; + using symbol_type = dryad::symbol; + using symbol_interner_type = dryad::symbol_interner; + symbol_interner_type _symbol_interner; + + public: + symbol_type intern(const char* str, std::size_t length) { + return _symbol_interner.intern(str, length); + } + symbol_type intern(std::string_view str) { + return intern(str.data(), str.size()); + } + const char* intern_cstr(const char* str, std::size_t length) { + return intern(str, length).c_str(_symbol_interner); + } + const char* intern_cstr(std::string_view str) { + return intern_cstr(str.data(), str.size()); + } + symbol_interner_type& symbol_interner() { + return _symbol_interner; + } + const symbol_interner_type& symbol_interner() const { + return _symbol_interner; + } + }; + + template + struct BasicDiagnosticLogger : DiagnosticLogger { + using file_type = FileT; + + template + using format_str = fmt::basic_format_string...>; + + explicit BasicDiagnosticLogger(const file_type& file) + : _file(&file) { + _tree.set_root(_tree.create()); + } + + struct Writer; + + template + Writer error(format_str fmt, Args&&... args) { + return log(DiagnosticKind::error, fmt, std::forward(args)...); + } + + template + Writer warning(format_str fmt, Args&&... args) { + return log(DiagnosticKind::warning, fmt, std::forward(args)...); + } + + template + Writer note(format_str fmt, Args&&... args) { + return log(DiagnosticKind::note, fmt, std::forward(args)...); + } + + template + Writer info(format_str fmt, Args&&... args) { + return log(DiagnosticKind::info, fmt, std::forward(args)...); + } + + template + Writer debug(format_str fmt, Args&&... args) { + return log(DiagnosticKind::debug, fmt, std::forward(args)...); + } + + template + Writer fixit(format_str fmt, Args&&... args) { + return log(DiagnosticKind::fixit, fmt, std::forward(args)...); + } + + template + Writer help(format_str fmt, Args&&... args) { + return log(DiagnosticKind::help, fmt, std::forward(args)...); + } + + Writer error(std::string_view sv) { + return log(DiagnosticKind::error, fmt::runtime(sv)); + } + + Writer warning(std::string_view sv) { + return log(DiagnosticKind::warning, fmt::runtime(sv)); + } + + Writer note(std::string_view sv) { + return log(DiagnosticKind::note, fmt::runtime(sv)); + } + + Writer info(std::string_view sv) { + return log(DiagnosticKind::info, fmt::runtime(sv)); + } + + Writer debug(std::string_view sv) { + return log(DiagnosticKind::debug, fmt::runtime(sv)); + } + + Writer fixit(std::string_view sv) { + return log(DiagnosticKind::fixit, fmt::runtime(sv)); + } + + Writer help(std::string_view sv) { + return log(DiagnosticKind::help, fmt::runtime(sv)); + } + + auto error_callback() { + return ErrorCallback(*this); + } + + template + static void _write_to_buffer(const CharT* s, std::streamsize n, void* output_str) { + auto* output = reinterpret_cast*>(output_str); + output->append(s, n); + } + + template + auto make_callback_stream(std::basic_string& output) { + return detail::make_callback_stream(&_write_to_buffer, reinterpret_cast(&output)); + } + + template + detail::OStreamOutputIterator make_ostream_iterator(std::basic_ostream& stream) { + return detail::OStreamOutputIterator { stream }; + } + + struct Writer { + template + [[nodiscard]] Writer& primary(NodeLocation loc, format_str fmt, Args&&... args) { + return annotation(AnnotationKind::primary, loc, fmt, std::forward(args)...); + } + + template + [[nodiscard]] Writer& secondary(NodeLocation loc, format_str fmt, Args&&... args) { + return annotation(AnnotationKind::secondary, loc, fmt, std::forward(args)...); + } + + [[nodiscard]] Writer& primary(NodeLocation loc, std::string_view sv) { + return annotation(AnnotationKind::primary, loc, fmt::runtime(sv)); + } + + [[nodiscard]] Writer& secondary(NodeLocation loc, std::string_view sv) { + return annotation(AnnotationKind::secondary, loc, fmt::runtime(sv)); + } + + void finish() {} + + template + [[nodiscard]] Writer& annotation(AnnotationKind kind, NodeLocation loc, format_str fmt, Args&&... args) { + auto begin_loc = lexy::get_input_location(_file->buffer(), loc.begin()); + + std::basic_string output; + auto stream = _logger.make_callback_stream(output); + auto iter = _logger.make_ostream_iterator(stream); + + _impl.write_empty_annotation(iter); + _impl.write_annotation(iter, kind, begin_loc, loc.end(), + [&](auto out, lexy::visualization_options) { + return lexy::_detail::write_str(out, fmt::format(fmt, std::forward(args)...).c_str()); + }); + + error::Annotation* annotation; + auto message = _logger.intern_cstr(output); + switch (kind) { + case AnnotationKind::primary: + _logger.create(loc, message); + break; + case AnnotationKind::secondary: + _logger.create(loc, message); + break; + default: detail::unreachable(); + } + _semantic->push_back(annotation); + return *this; + } + + private: + Writer(BasicDiagnosticLogger& logger, const file_type* file, error::Semantic* semantic) + : _file(file), + _impl(file->buffer(), { lexy::visualize_fancy }), + _logger(logger), + _semantic(semantic) {} + + const file_type* _file; + lexy_ext::diagnostic_writer> _impl; + BasicDiagnosticLogger& _logger; + error::Semantic* _semantic; + + friend BasicDiagnosticLogger; + }; + + using diagnostic_writer = lexy_ext::diagnostic_writer>; + + template T, typename... Args> + void log_with_impl(diagnostic_writer& impl, T* error, DiagnosticKind kind, format_str fmt, Args&&... args) { + std::basic_string output; + auto stream = make_callback_stream(output); + auto iter = make_ostream_iterator(stream); + + impl.write_message(iter, kind, + [&](auto out, lexy::visualization_options) { + return lexy::_detail::write_str(out, fmt::format(fmt, std::forward(args)...).c_str()); + }); + impl.write_path(iter, _file->path()); + + auto message = intern_cstr(output); + error->_set_message(message); + insert(error); + } + + template T, typename... Args> + void log_with_error(T* error, DiagnosticKind kind, format_str fmt, Args&&... args) { + auto impl = diagnostic_writer { _file->buffer() }; + log_with_impl(impl, error, kind, fmt, std::forward(args)...); + } + + template T, typename... Args> + void create_log(DiagnosticKind kind, format_str fmt, Args&&... args) { + log_with_error(create(), kind, fmt, std::forward(args)...); + } + + template + Writer log(DiagnosticKind kind, format_str fmt, Args&&... args) { + error::Semantic* semantic; + + switch (kind) { + case DiagnosticKind::error: + semantic = create(); + break; + case DiagnosticKind::warning: + semantic = create(); + break; + case DiagnosticKind::info: + semantic = create(); + break; + case DiagnosticKind::debug: + semantic = create(); + break; + case DiagnosticKind::fixit: + semantic = create(); + break; + case DiagnosticKind::help: + semantic = create(); + break; + default: detail::unreachable(); + } + + Writer result(*this, _file, semantic); + + log_with_impl(result._impl, semantic, kind, fmt, std::forward(args)...); + + if (kind == DiagnosticKind::error) + _errored = true; + if (kind == DiagnosticKind::warning) + _warned = true; + + return result; + } + + error_range get_errors() const { + return _tree.root()->errors(); + } + + private: + void insert(error::Error* root) { + _tree.root()->insert_back(root); + } + + const file_type* _file; + }; +} \ No newline at end of file diff --git a/include/openvic-dataloader/Error.hpp b/include/openvic-dataloader/Error.hpp new file mode 100644 index 0000000..fd3254d --- /dev/null +++ b/include/openvic-dataloader/Error.hpp @@ -0,0 +1,195 @@ +#pragma once + +#include +#include + +#include +#include + +#include +#include + +namespace ovdl { + template + struct BasicDiagnosticLogger; +} + +namespace ovdl::error { + enum class [[nodiscard]] ErrorKind : std::uint64_t { + Root, + + BufferError, + + // Parse Error // + ExpectedLiteral, + ExpectedKeyword, + ExpectedCharClass, + GenericParseError, + + FirstParseError = ExpectedLiteral, + LastParseError = GenericParseError, + + // Semantic Diagnostic // + SemanticError, + SemanticWarning, + SemanticInfo, + SemanticDebug, + SemanticFixit, + SemanticHelp, + + FirstSemantic = SemanticError, + LastSemantic = SemanticHelp, + + PrimaryAnnotation, + SecondaryAnnotation, + + FirstAnnotation = PrimaryAnnotation, + LastAnnotation = SecondaryAnnotation, + }; + + static constexpr std::string_view get_kind_name(ErrorKind kind) { + switch (kind) { + using enum ErrorKind; + case ExpectedLiteral: return "expected literal"; + case ExpectedKeyword: return "expected keyword"; + case ExpectedCharClass: return "expected char class"; + case GenericParseError: return "generic"; + default: detail::unreachable(); + } + } + + struct Error : dryad::abstract_node_all { + const char* message() const { return _message; } + + protected: + DRYAD_ABSTRACT_NODE_CTOR(Error); + + void _set_message(const char* message) { _message = message; } + const char* _message; + + template + friend struct ovdl::BasicDiagnosticLogger; + }; + + using ErrorList = dryad::unlinked_node_list; + + struct Annotation; + using AnnotationList = dryad::unlinked_node_list; + + struct Root : dryad::basic_node> { + explicit Root(dryad::node_ctor ctor) : node_base(ctor) {} + + DRYAD_CHILD_NODE_RANGE_GETTER(Error, errors, nullptr, this); + + void insert_back(Error* error) { + insert_child_after(_last, error); + _last = error; + } + + private: + Error* _last = nullptr; + }; + + struct BufferError : dryad::basic_node { + explicit BufferError(dryad::node_ctor ctor, const char* message) : node_base(ctor) { + _set_message(message); + } + + explicit BufferError(dryad::node_ctor ctor) : node_base(ctor) {} + }; + + struct ParseError : dryad::abstract_node_range { + const char* production_name() const { return _production_name; } + + protected: + explicit ParseError(dryad::node_ctor ctor, + ErrorKind kind, + const char* message, + const char* production_name) + : node_base(ctor, kind), + _production_name(production_name) { + _set_message(message); + }; + + const char* _production_name; + }; + + template + struct _ParseError_t : dryad::basic_node { + explicit _ParseError_t(dryad::node_ctor ctor, const char* message, const char* production_name) + : dryad::basic_node(ctor, message, production_name) {} + }; + + using ExpectedLiteral = _ParseError_t; + using ExpectedKeyword = _ParseError_t; + using ExpectedCharClass = _ParseError_t; + using GenericParseError = _ParseError_t; + + struct Semantic : dryad::abstract_node_range, ErrorKind::FirstSemantic, ErrorKind::LastSemantic> { + DRYAD_CHILD_NODE_RANGE_GETTER(Annotation, annotations, nullptr, this); + + void push_back(Annotation* annotation); + void push_back(AnnotationList p_annotations); + + protected: + explicit Semantic(dryad::node_ctor ctor, ErrorKind kind) + : node_base(ctor, kind) {}; + + explicit Semantic(dryad::node_ctor ctor, ErrorKind kind, const char* message) + : node_base(ctor, kind) { + insert_child_list_after(nullptr, AnnotationList {}); + _set_message(message); + }; + + explicit Semantic(dryad::node_ctor ctor, ErrorKind kind, const char* message, AnnotationList annotations) + : node_base(ctor, kind) { + insert_child_list_after(nullptr, annotations); + _set_message(message); + }; + }; + + template + struct _SemanticError_t : dryad::basic_node { + using base_node = dryad::basic_node; + + explicit _SemanticError_t(dryad::node_ctor ctor) + : base_node(ctor) {} + + explicit _SemanticError_t(dryad::node_ctor ctor, const char* message) + : base_node(ctor, message) {} + + explicit _SemanticError_t(dryad::node_ctor ctor, const char* message, AnnotationList annotations) + : base_node(ctor, message, annotations) {} + }; + + using SemanticError = _SemanticError_t; + using SemanticWarning = _SemanticError_t; + using SemanticInfo = _SemanticError_t; + using SemanticDebug = _SemanticError_t; + using SemanticFixit = _SemanticError_t; + using SemanticHelp = _SemanticError_t; + + struct Annotation : dryad::abstract_node_range { + protected: + explicit Annotation(dryad::node_ctor ctor, ErrorKind kind, const char* message) : node_base(ctor, kind) { + _set_message(message); + } + }; + + template + struct _Annotation_t : dryad::basic_node { + explicit _Annotation_t(dryad::node_ctor ctor, const char* message) + : dryad::basic_node(ctor, message) {} + }; + + using PrimaryAnnotation = _Annotation_t; + using SecondaryAnnotation = _Annotation_t; + + inline void Semantic::push_back(Annotation* annotation) { + insert_child_after(annotations().end().deref(), annotation); + } + + inline void Semantic::push_back(AnnotationList p_annotations) { + insert_child_list_after(annotations().end().deref(), p_annotations); + } +} \ No newline at end of file diff --git a/include/openvic-dataloader/File.hpp b/include/openvic-dataloader/File.hpp new file mode 100644 index 0000000..caa4a0a --- /dev/null +++ b/include/openvic-dataloader/File.hpp @@ -0,0 +1,69 @@ +#pragma once + +#include + +#include +#include + +#include + +namespace ovdl { + template + concept IsEncoding = requires(T t) { + typename T::char_type; + typename T::int_type; + { T::template is_secondary_char_type() } -> std::same_as; + { T::eof() } -> std::same_as; + { T::to_int_type(typename T::char_type {}) } -> std::same_as; + }; + + struct File { + explicit File(const char* path); + + const char* path() const noexcept; + + protected: + const char* _path; + }; + + template + concept IsFile = + std::derived_from && IsEncoding && + requires(T t, const typename T::node_type* node, NodeLocation location) { + { t.buffer() } -> std::same_as&>; + { t.set_location(node, location) } -> std::same_as; + { t.location_of(node) } -> std::same_as; + }; + + template + struct BasicFile : File { + using encoding_type = EncodingT; + using node_type = NodeT; + + explicit BasicFile(const char* path, lexy::buffer&& buffer) + : File(path), + _buffer(static_cast&&>(buffer)) {} + + explicit BasicFile(lexy::buffer&& buffer) + : File(""), + _buffer(static_cast&&>(buffer)) {} + + const lexy::buffer& buffer() const { + return _buffer; + } + + void set_location(const node_type* n, NodeLocation loc) { + _map.insert(n, loc); + } + + NodeLocation location_of(const node_type* n) const { + auto result = _map.lookup(n); + DRYAD_ASSERT(result != nullptr, "every Node should have a NodeLocation"); + return *result; + } + + protected: + lexy::buffer _buffer; + dryad::node_map _map; + }; +} \ No newline at end of file diff --git a/include/openvic-dataloader/NodeLocation.hpp b/include/openvic-dataloader/NodeLocation.hpp new file mode 100644 index 0000000..117560b --- /dev/null +++ b/include/openvic-dataloader/NodeLocation.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include + +namespace ovdl { + struct NodeLocation { + const char* _begin = nullptr; + const char* _end = nullptr; + + NodeLocation(); + NodeLocation(const char* pos); + NodeLocation(const char* begin, const char* end); + + NodeLocation(const NodeLocation&) noexcept; + NodeLocation& operator=(const NodeLocation&); + + NodeLocation(NodeLocation&&); + NodeLocation& operator=(NodeLocation&&); + + const char* begin() const; + const char* end() const; + + bool is_synthesized() const; + + static NodeLocation make_from(const char* begin, const char* end); + }; + + struct FilePosition { + std::uint32_t start_line = std::uint32_t(-1), end_line = std::uint32_t(-1), start_column = std::uint32_t(-1), end_column = std::uint32_t(-1); + + inline constexpr bool is_empty() { return start_line == std::uint32_t(-1) && end_line == std::uint32_t(-1) && start_column == std::uint32_t(-1) && end_column == std::uint32_t(-1); } + }; +} \ No newline at end of file diff --git a/include/openvic-dataloader/ParseState.hpp b/include/openvic-dataloader/ParseState.hpp new file mode 100644 index 0000000..5655606 --- /dev/null +++ b/include/openvic-dataloader/ParseState.hpp @@ -0,0 +1,120 @@ +#pragma once + +#include + +#include +#include + +#include + +namespace ovdl { + template + concept IsParseState = requires( + T t, + const T ct, + typename T::ast_type::file_type&& file, + lexy::buffer&& buffer, + const char* path // + ) { + requires IsAst; + requires std::derived_from; + { T { std::move(file) } } -> std::same_as; + { T { std::move(buffer) } } -> std::same_as; + { T { path, std::move(buffer) } } -> std::same_as; + { t.ast() } -> std::same_as; + { ct.ast() } -> std::same_as; + { t.logger() } -> std::same_as; + { ct.logger() } -> std::same_as; + }; + + template + struct ParseState { + using ast_type = AstT; + using diagnostic_logger_type = BasicDiagnosticLogger; + + ParseState(typename ast_type::file_type&& file) + : _ast { std::move(file) }, + _logger { _ast.file() } {} + + ParseState(lexy::buffer&& buffer) + : ParseState(typename ast_type::file_type { std::move(buffer) }) {} + + ParseState(const char* path, lexy::buffer&& buffer) + : ParseState(typename ast_type::file_type { path, std::move(buffer) }) {} + + ast_type& ast() { + return _ast; + } + + const ast_type& ast() const { + return _ast; + } + + diagnostic_logger_type& logger() { + return _logger; + } + + const diagnostic_logger_type& logger() const { + return _logger; + } + + private: + ast_type _ast; + diagnostic_logger_type _logger; + }; + + template + concept IsFileParseState = requires( + T t, + const T ct, + typename T::file_type&& file, + lexy::buffer&& buffer, + const char* path // + ) { + requires IsFile; + requires std::derived_from; + { T { std::move(file) } } -> std::same_as; + { T { std::move(buffer) } } -> std::same_as; + { T { path, std::move(buffer) } } -> std::same_as; + { t.file() } -> std::same_as; + { ct.file() } -> std::same_as; + { t.logger() } -> std::same_as; + { ct.logger() } -> std::same_as; + }; + + template + struct FileParseState { + using file_type = FileT; + using diagnostic_logger_type = BasicDiagnosticLogger; + + FileParseState(file_type&& file) + : _file { std::move(file) }, + _logger { file } {} + + FileParseState(lexy::buffer&& buffer) + : FileParseState(file_type { std::move(buffer) }) {} + + FileParseState(const char* path, lexy::buffer&& buffer) + : FileParseState(file_type { path, std::move(buffer) }) {} + + file_type& file() { + return _file; + } + + const file_type& file() const { + return _file; + } + + diagnostic_logger_type& logger() { + return _logger; + } + + const diagnostic_logger_type& logger() const { + return _logger; + } + + private: + file_type _file; + diagnostic_logger_type _logger; + }; +} \ No newline at end of file diff --git a/include/openvic-dataloader/Parser.hpp b/include/openvic-dataloader/Parser.hpp new file mode 100644 index 0000000..c1f266b --- /dev/null +++ b/include/openvic-dataloader/Parser.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include +#include + +#include +#include + +namespace ovdl::detail { + struct BasicParser { + BasicParser(); + + void set_error_log_to_null(); + void set_error_log_to_stderr(); + void set_error_log_to_stdout(); + void set_error_log_to(std::basic_ostream& stream); + + bool has_error() const; + bool has_fatal_error() const; + bool has_warning() const; + + std::string_view get_file_path() const; + + protected: + std::reference_wrapper _error_stream; + std::string _file_path; + bool _has_fatal_error = false; + bool _has_error = false; + bool _has_warning = false; + }; +} \ No newline at end of file diff --git a/include/openvic-dataloader/csv/LineObject.hpp b/include/openvic-dataloader/csv/LineObject.hpp index 87b4d31..ca632cd 100644 --- a/include/openvic-dataloader/csv/LineObject.hpp +++ b/include/openvic-dataloader/csv/LineObject.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include namespace ovdl::csv { /// LineObject should be able to recognize the differences between: diff --git a/include/openvic-dataloader/csv/Parser.hpp b/include/openvic-dataloader/csv/Parser.hpp index aa2a0fe..ccf732a 100644 --- a/include/openvic-dataloader/csv/Parser.hpp +++ b/include/openvic-dataloader/csv/Parser.hpp @@ -2,8 +2,13 @@ #include +#include +#include #include -#include +#include +#include + +#include namespace ovdl::csv { enum class EncodingType { @@ -15,6 +20,7 @@ namespace ovdl::csv { class Parser final : public detail::BasicParser { public: Parser(); + Parser(std::basic_ostream& error_stream); static Parser from_buffer(const char* data, std::size_t size); static Parser from_buffer(const char* start, const char* end); @@ -28,24 +34,33 @@ namespace ovdl::csv { constexpr Parser& load_from_file(const char* path); Parser& load_from_file(const std::filesystem::path& path); - constexpr Parser& load_from_file(const detail::Has_c_str auto& path); + constexpr Parser& load_from_file(const detail::HasCstr auto& path) { + return load_from_file(path.c_str()); + } bool parse_csv(bool handle_strings = false); const std::vector& get_lines() const; + using error_range = ovdl::detail::error_range; + Parser::error_range get_errors() const; + + const FilePosition get_error_position(const error::Error* error) const; + + void print_errors_to(std::basic_ostream& stream) const; + Parser(Parser&&); Parser& operator=(Parser&&); ~Parser(); private: - class BufferHandler; - std::unique_ptr _buffer_handler; + class ParseHandler; + std::unique_ptr _parse_handler; std::vector _lines; template - constexpr void _run_load_func(detail::LoadCallback auto func, Args... args); + constexpr void _run_load_func(detail::LoadCallback auto func, Args... args); }; using Windows1252Parser = Parser; diff --git a/include/openvic-dataloader/detail/BasicParser.hpp b/include/openvic-dataloader/detail/BasicParser.hpp deleted file mode 100644 index 7524bb5..0000000 --- a/include/openvic-dataloader/detail/BasicParser.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include -#include -#include - -#include -#include -#include - -namespace ovdl::detail { - class BasicParser { - public: - BasicParser(); - - void set_error_log_to_null(); - void set_error_log_to_stderr(); - void set_error_log_to_stdout(); - void set_error_log_to(std::basic_ostream& stream); - - bool has_error() const; - bool has_fatal_error() const; - bool has_warning() const; - - const std::vector& get_errors() const; - const std::vector& get_warnings() const; - std::string_view get_file_path() const; - - protected: - std::vector _errors; - std::vector _warnings; - - std::reference_wrapper _error_stream; - std::string _file_path; - bool _has_fatal_error = false; - }; -} \ No newline at end of file diff --git a/include/openvic-dataloader/detail/CallbackOStream.hpp b/include/openvic-dataloader/detail/CallbackOStream.hpp index 641d53f..f7cfc4a 100644 --- a/include/openvic-dataloader/detail/CallbackOStream.hpp +++ b/include/openvic-dataloader/detail/CallbackOStream.hpp @@ -6,10 +6,10 @@ #include namespace ovdl::detail { - template> - class BasicCallbackStreamBuffer : public std::basic_streambuf { + template> + class BasicCallbackStreamBuffer : public std::basic_streambuf { public: - using base_type = std::basic_streambuf; + using base_type = std::basic_streambuf; using callback_type = Callback; using char_type = typename base_type::char_type; using int_type = typename base_type::int_type; @@ -29,11 +29,12 @@ namespace ovdl::detail { }; int_type overflow(int_type ch) override { + auto c = static_cast(ch); if constexpr (std::is_same_v) { - _callback(&ch, 1, _user_data); + _callback(&c, 1, _user_data); return 1; } else { - return _callback(&ch, 1, _user_data); // returns the number of characters successfully written. + return _callback(&c, 1, _user_data); // returns the number of characters successfully written. } } @@ -64,22 +65,28 @@ namespace ovdl::detail { CallbackWStreamBuffer(Callback cb, void* user_data = nullptr) : base_type(cb, user_data) {} }; - template> - class BasicCallbackStream : public std::basic_ostream { + template> + class BasicCallbackStream : public std::basic_ostream { public: - using base_type = std::basic_ostream; + using base_type = std::basic_ostream; BasicCallbackStream(Callback cb, void* user_data = nullptr) : m_sbuf(cb, user_data), - std::basic_ios(&m_sbuf), - std::basic_ostream(&m_sbuf) { - std::basic_ios::init(&m_sbuf); + std::basic_ios(&m_sbuf), + std::basic_ostream(&m_sbuf) { + std::basic_ios::init(&m_sbuf); } private: - BasicCallbackStreamBuffer m_sbuf; + BasicCallbackStreamBuffer m_sbuf; }; + template + auto make_callback_stream(auto&& cb, void* user_data = nullptr) { + using Callback = std::decay_t; + return BasicCallbackStream { std::forward(cb), user_data }; + } + template class CallbackStream : public BasicCallbackStream { public: diff --git a/include/openvic-dataloader/detail/ClassExport.hpp b/include/openvic-dataloader/detail/ClassExport.hpp deleted file mode 100644 index 27098ed..0000000 --- a/include/openvic-dataloader/detail/ClassExport.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#ifdef _MSC_VER -#define OVDL_EXPORT __declspec(dllexport) -#elif defined(__GNUC__) -#define OVDL_EXPORT __attribute__((visibility("default"))) -#else -#define OVDL_EXPORT -#endif \ No newline at end of file diff --git a/include/openvic-dataloader/detail/Concepts.hpp b/include/openvic-dataloader/detail/Concepts.hpp deleted file mode 100644 index 3ca210c..0000000 --- a/include/openvic-dataloader/detail/Concepts.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include -#include -#include - -#include - -namespace ovdl::detail { - template - concept LoadCallback = - requires(T t, Self* self, Args... args) { - { t(self, std::forward(args)...) } -> std::same_as>; - }; - - template - concept Has_c_str = - requires(T t) { - { t.c_str() } -> std::same_as; - }; -} \ No newline at end of file diff --git a/include/openvic-dataloader/detail/LexyFwdDeclaration.hpp b/include/openvic-dataloader/detail/LexyFwdDeclaration.hpp new file mode 100644 index 0000000..554c88d --- /dev/null +++ b/include/openvic-dataloader/detail/LexyFwdDeclaration.hpp @@ -0,0 +1,8 @@ +#pragma once + +namespace lexy { + struct default_encoding; + + template + struct buffer; +} \ No newline at end of file diff --git a/include/openvic-dataloader/detail/LexyReportError.hpp b/include/openvic-dataloader/detail/LexyReportError.hpp new file mode 100644 index 0000000..3c32bd1 --- /dev/null +++ b/include/openvic-dataloader/detail/LexyReportError.hpp @@ -0,0 +1,107 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "openvic-dataloader/detail/utility/Concepts.hpp" + +#include + +namespace ovdl::detail { + template + struct _ReportError { + OutputIterator _iter; + lexy::visualization_options _opts; + const char* _path; + + struct _sink { + OutputIterator _iter; + lexy::visualization_options _opts; + const char* _path; + std::size_t _count; + std::vector _errors; + + using return_type = std::vector; + + template + void operator()(const lexy::error_context& context, const lexy::error& error) { + _iter = lexy_ext::_detail::write_error(_iter, context, error, _opts, _path); + ++_count; + + // Convert the context location and error location into line/column information. + auto context_location = lexy::get_input_location(context.input(), context.position()); + auto location = lexy::get_input_location(context.input(), error.position(), context_location.anchor()); + + std::basic_stringstream message; + + // Write the main annotation. + if constexpr (std::is_same_v) { + auto string = lexy::_detail::make_literal_lexeme(error.string(), error.length()); + + message << "expected '" << string.data() << '\''; + } else if constexpr (std::is_same_v) { + auto string = lexy::_detail::make_literal_lexeme(error.string(), error.length()); + + message << "expected keyword '" << string.data() << '\''; + } else if constexpr (std::is_same_v) { + 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::HasCstr auto& path_object) const { + return path(path_object.c_str()); + } + + /// Specifies an output iterator where the errors are written to. + template + constexpr _ReportError to(OI out) const { + return { out, _opts, _path }; + } + + /// Overrides visualization options. + constexpr _ReportError opts(lexy::visualization_options opts) const { + return { _iter, opts, _path }; + } + }; + + constexpr auto ReporError = _ReportError {}; +} \ No newline at end of file diff --git a/include/openvic-dataloader/detail/OStreamOutputIterator.hpp b/include/openvic-dataloader/detail/OStreamOutputIterator.hpp new file mode 100644 index 0000000..8f120c7 --- /dev/null +++ b/include/openvic-dataloader/detail/OStreamOutputIterator.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +namespace ovdl::detail { + struct OStreamOutputIterator { + std::reference_wrapper _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/include/openvic-dataloader/detail/OptionalConstexpr.hpp b/include/openvic-dataloader/detail/OptionalConstexpr.hpp deleted file mode 100644 index bcb12a7..0000000 --- a/include/openvic-dataloader/detail/OptionalConstexpr.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -// THANK YOU APPLE FOR YOUR UTTER DISREGARD FOR C++20 - -#if __cpp_lib_optional >= 202106L -#define OVDL_OPTIONAL_CONSTEXPR constexpr -#else -#define OVDL_OPTIONAL_CONSTEXPR inline -#endif \ No newline at end of file diff --git a/include/openvic-dataloader/detail/PointerHash.hpp b/include/openvic-dataloader/detail/PointerHash.hpp deleted file mode 100644 index c0d28bc..0000000 --- a/include/openvic-dataloader/detail/PointerHash.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include - -namespace ovdl::detail { - /* hash any pointer */ - template - struct PointerHash { - using type = T; - using ptr_type = T*; - using const_type = const T; - using const_ptr_type = const T*; - using const_ptr_const_type = const const_ptr_type; - constexpr std::size_t operator()(const_ptr_const_type pointer) const { - auto addr = reinterpret_cast(pointer); -#if SIZE_MAX < UINTPTR_MAX - /* size_t is not large enough to hold the pointer’s memory address */ - addr %= SIZE_MAX; /* truncate the address so it is small enough to fit in a size_t */ -#endif - return addr; - } - }; -} \ No newline at end of file diff --git a/include/openvic-dataloader/detail/SelfType.hpp b/include/openvic-dataloader/detail/SelfType.hpp deleted file mode 100644 index 5209700..0000000 --- a/include/openvic-dataloader/detail/SelfType.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include - -namespace ovdl::detail { -#if !defined(_MSC_VER) -#pragma GCC diagnostic push -#pragma clang diagnostic ignored "-Wunknown-warning-option" -#pragma GCC diagnostic ignored "-Wnon-template-friend" -#endif - template - struct Reader { - friend auto adl_GetSelfType(Reader); - }; - - template - struct Writer { - friend auto adl_GetSelfType(Reader) { return U {}; } - }; -#if !defined(_MSC_VER) -#pragma GCC diagnostic pop -#endif - - inline void adl_GetSelfType() {} - - template - using Read = std::remove_pointer_t {}))>; -} diff --git a/include/openvic-dataloader/detail/TypeName.hpp b/include/openvic-dataloader/detail/TypeName.hpp deleted file mode 100644 index 1a34a0f..0000000 --- a/include/openvic-dataloader/detail/TypeName.hpp +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace ovdl::detail { - - template - constexpr auto substring_as_array(std::string_view str, std::index_sequence) { - return std::array { str[Idxs]... }; - } - - template - constexpr auto type_name_array() { -#if defined(__clang__) - constexpr auto prefix = std::string_view { "[T = " }; - constexpr auto suffix = std::string_view { "]" }; - constexpr auto function = std::string_view { __PRETTY_FUNCTION__ }; -#elif defined(__GNUC__) - constexpr auto prefix = std::string_view { "with T = " }; - constexpr auto suffix = std::string_view { "]" }; - constexpr auto function = std::string_view { __PRETTY_FUNCTION__ }; -#elif defined(_MSC_VER) - constexpr auto prefix = std::string_view { "type_name_array<" }; - constexpr auto suffix = std::string_view { ">(void)" }; - constexpr auto function = std::string_view { __FUNCSIG__ }; -#else -#error Unsupported compiler -#endif - - constexpr auto start = function.find(prefix) + prefix.size(); - constexpr auto end = function.rfind(suffix); - - static_assert(start < end); - - constexpr auto name = function.substr(start, (end - start)); - return substring_as_array(name, std::make_index_sequence {}); - } - - template - struct type_name_holder { - static inline constexpr auto value = type_name_array(); - }; - - template - constexpr auto type_name() -> std::string_view { - constexpr auto& value = type_name_holder::value; - return std::string_view { value.data(), value.size() }; - } -} \ No newline at end of file diff --git a/include/openvic-dataloader/detail/VectorConstexpr.hpp b/include/openvic-dataloader/detail/VectorConstexpr.hpp deleted file mode 100644 index 7e7fa34..0000000 --- a/include/openvic-dataloader/detail/VectorConstexpr.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -// THANK YOU APPLE FOR YOUR UTTER DISREGARD FOR C++20 - -#if __cpp_lib_constexpr_vector >= 201907L -#define OVDL_VECTOR_CONSTEXPR constexpr -#else -#define OVDL_VECTOR_CONSTEXPR inline -#endif \ No newline at end of file diff --git a/include/openvic-dataloader/detail/utility/Concepts.hpp b/include/openvic-dataloader/detail/utility/Concepts.hpp new file mode 100644 index 0000000..0ba91cc --- /dev/null +++ b/include/openvic-dataloader/detail/utility/Concepts.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include +#include +#include +#include + +namespace ovdl { + struct NodeLocation; + struct File; + namespace detail { + enum class buffer_error : std::uint8_t; + } +} + +namespace ovdl::detail { + template + concept any_of = (std::same_as || ...); + + template + concept HasCstr = + requires(T t) { + { t.c_str() } -> std::same_as; + }; + + template + concept HasPath = requires(T& t) { + { t.path() } -> std::same_as; + }; + + template + concept LoadCallback = + requires(T&& t, Self&& self, Args&&... args) { + { std::invoke(std::forward(t), std::forward(self), std::forward(args)...) } -> std::same_as; + }; + + template + concept IsEncoding = requires(T t) { + typename T::char_type; + typename T::int_type; + { T::template is_secondary_char_type() } -> std::same_as; + { T::eof() } -> std::same_as; + { T::to_int_type(typename T::char_type {}) } -> std::same_as; + }; +} \ No newline at end of file diff --git a/include/openvic-dataloader/detail/utility/Constexpr.hpp b/include/openvic-dataloader/detail/utility/Constexpr.hpp new file mode 100644 index 0000000..49479c5 --- /dev/null +++ b/include/openvic-dataloader/detail/utility/Constexpr.hpp @@ -0,0 +1,15 @@ +#pragma once + +// THANK YOU APPLE FOR YOUR UTTER DISREGARD FOR C++20 + +#if __cpp_lib_optional >= 202106L +#define OVDL_OPTIONAL_CONSTEXPR constexpr +#else +#define OVDL_OPTIONAL_CONSTEXPR inline +#endif + +#if __cpp_lib_constexpr_vector >= 201907L +#define OVDL_VECTOR_CONSTEXPR constexpr +#else +#define OVDL_VECTOR_CONSTEXPR inline +#endif \ No newline at end of file diff --git a/include/openvic-dataloader/detail/utility/ErrorRange.hpp b/include/openvic-dataloader/detail/utility/ErrorRange.hpp new file mode 100644 index 0000000..a427f6c --- /dev/null +++ b/include/openvic-dataloader/detail/utility/ErrorRange.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include + +#include + +#include + +namespace ovdl::detail { + using error_range = decltype(std::declval()->errors()); +} \ No newline at end of file diff --git a/include/openvic-dataloader/detail/utility/PointerHash.hpp b/include/openvic-dataloader/detail/utility/PointerHash.hpp new file mode 100644 index 0000000..c0d28bc --- /dev/null +++ b/include/openvic-dataloader/detail/utility/PointerHash.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include + +namespace ovdl::detail { + /* hash any pointer */ + template + struct PointerHash { + using type = T; + using ptr_type = T*; + using const_type = const T; + using const_ptr_type = const T*; + using const_ptr_const_type = const const_ptr_type; + constexpr std::size_t operator()(const_ptr_const_type pointer) const { + auto addr = reinterpret_cast(pointer); +#if SIZE_MAX < UINTPTR_MAX + /* size_t is not large enough to hold the pointer’s memory address */ + addr %= SIZE_MAX; /* truncate the address so it is small enough to fit in a size_t */ +#endif + return addr; + } + }; +} \ No newline at end of file diff --git a/include/openvic-dataloader/detail/utility/SelfType.hpp b/include/openvic-dataloader/detail/utility/SelfType.hpp new file mode 100644 index 0000000..5209700 --- /dev/null +++ b/include/openvic-dataloader/detail/utility/SelfType.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include + +namespace ovdl::detail { +#if !defined(_MSC_VER) +#pragma GCC diagnostic push +#pragma clang diagnostic ignored "-Wunknown-warning-option" +#pragma GCC diagnostic ignored "-Wnon-template-friend" +#endif + template + struct Reader { + friend auto adl_GetSelfType(Reader); + }; + + template + struct Writer { + friend auto adl_GetSelfType(Reader) { return U {}; } + }; +#if !defined(_MSC_VER) +#pragma GCC diagnostic pop +#endif + + inline void adl_GetSelfType() {} + + template + using Read = std::remove_pointer_t {}))>; +} diff --git a/include/openvic-dataloader/detail/utility/TypeName.hpp b/include/openvic-dataloader/detail/utility/TypeName.hpp new file mode 100644 index 0000000..1a34a0f --- /dev/null +++ b/include/openvic-dataloader/detail/utility/TypeName.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include +#include + +namespace ovdl::detail { + + template + constexpr auto substring_as_array(std::string_view str, std::index_sequence) { + return std::array { str[Idxs]... }; + } + + template + constexpr auto type_name_array() { +#if defined(__clang__) + constexpr auto prefix = std::string_view { "[T = " }; + constexpr auto suffix = std::string_view { "]" }; + constexpr auto function = std::string_view { __PRETTY_FUNCTION__ }; +#elif defined(__GNUC__) + constexpr auto prefix = std::string_view { "with T = " }; + constexpr auto suffix = std::string_view { "]" }; + constexpr auto function = std::string_view { __PRETTY_FUNCTION__ }; +#elif defined(_MSC_VER) + constexpr auto prefix = std::string_view { "type_name_array<" }; + constexpr auto suffix = std::string_view { ">(void)" }; + constexpr auto function = std::string_view { __FUNCSIG__ }; +#else +#error Unsupported compiler +#endif + + constexpr auto start = function.find(prefix) + prefix.size(); + constexpr auto end = function.rfind(suffix); + + static_assert(start < end); + + constexpr auto name = function.substr(start, (end - start)); + return substring_as_array(name, std::make_index_sequence {}); + } + + template + struct type_name_holder { + static inline constexpr auto value = type_name_array(); + }; + + template + constexpr auto type_name() -> std::string_view { + constexpr auto& value = type_name_holder::value; + return std::string_view { value.data(), value.size() }; + } +} \ No newline at end of file diff --git a/include/openvic-dataloader/detail/utility/Utility.hpp b/include/openvic-dataloader/detail/utility/Utility.hpp new file mode 100644 index 0000000..138a029 --- /dev/null +++ b/include/openvic-dataloader/detail/utility/Utility.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include +#include + +#include "openvic-dataloader/detail/utility/TypeName.hpp" + +namespace ovdl::detail { + [[noreturn]] inline void unreachable() { + // Uses compiler specific extensions if possible. + // Even if no extension is used, undefined behavior is still raised by + // an empty function body and the noreturn attribute. +#ifdef __GNUC__ // GCC, Clang, ICC + __builtin_unreachable(); +#elif defined(_MSC_VER) // MSVC + __assume(false); +#endif + } + + template + constexpr std::string_view get_kind_name() { + constexpr auto name = type_name(); + + return name; + } + + template + requires std::is_enum_v + constexpr std::underlying_type_t to_underlying(EnumT e) { + return static_cast>(e); + } + + template + requires std::is_enum_v + constexpr EnumT from_underlying(std::underlying_type_t ut) { + return static_cast(ut); + } +} \ No newline at end of file diff --git a/include/openvic-dataloader/v2script/AbstractSyntaxTree.hpp b/include/openvic-dataloader/v2script/AbstractSyntaxTree.hpp index 689ab6e..1e7b2c3 100644 --- a/include/openvic-dataloader/v2script/AbstractSyntaxTree.hpp +++ b/include/openvic-dataloader/v2script/AbstractSyntaxTree.hpp @@ -1,310 +1,200 @@ #pragma once -#include -#include -#include -#include -#include +#include #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include -namespace lexy { - struct nullopt; -} +#include +#include +#include +#include +#include +#include -namespace ovdl::v2script { - class Parser; -} +namespace ovdl::v2script::ast { + enum class NodeKind { + FileTree, -#define OVDL_PRINT_FUNC_DEF std::ostream& print(std::ostream& stream, std::size_t indent) const override + // FlatValues // + IdentifierValue, // straight_identifier_value + StringValue, // "plain string value" -// defines get_type_static and get_type for string type naming -#define OVDL_RT_TYPE_DEF \ - static constexpr std::string_view get_type_static() { return ::ovdl::detail::type_name(); } \ - constexpr std::string_view get_type() const override { return ::ovdl::detail::type_name>(); } + FirstFlatValue = IdentifierValue, + LastFlatValue = StringValue, -// defines type for self-class referencing -#define OVDL_TYPE_DEFINE_SELF \ - struct _self_type_tag {}; \ - constexpr auto _self_type_helper() -> decltype(::ovdl::detail::Writer<_self_type_tag, decltype(this)> {}); \ - using type = ::ovdl::detail::Read<_self_type_tag>; + // Values // + ListValue, // { } + NullValue, -namespace ovdl::v2script::ast { + FirstValue = FirstFlatValue, + LastValue = NullValue, - struct Node; - using NodePtr = Node*; - using NodeCPtr = const Node*; - using NodeUPtr = std::unique_ptr; + // Statements // + EventStatement, // (country_event|province_event) = { id = ... } + AssignStatement, // = + ValueStatement, // - struct NodeLocation { - const char* _begin = nullptr; - const char* _end = nullptr; + FirstStatement = EventStatement, + LastStatement = ValueStatement, + }; - NodeLocation() = default; - NodeLocation(const char* pos) : _begin(pos), - _end(pos) {} - NodeLocation(const char* begin, const char* end) : _begin(begin), - _end(end) {} + constexpr std::string_view get_kind_name(NodeKind kind) { + switch (kind) { + using enum NodeKind; + case FileTree: return "file tree"; + case IdentifierValue: return "identifier value"; + case StringValue: return "string value"; + case ListValue: return "list value"; + case NullValue: return "null value"; + case EventStatement: return "event statement"; + case AssignStatement: return "assign statement"; + case ValueStatement: return "value statement"; + default: detail::unreachable(); + } + } - NodeLocation(const NodeLocation&) = default; - NodeLocation& operator=(const NodeLocation&) = default; + using Node = dryad::node; + using NodeList = dryad::unlinked_node_list; - NodeLocation(NodeLocation&&) = default; - NodeLocation& operator=(NodeLocation&&) = default; + struct Value; - const char* begin() const { return _begin; } - const char* end() const { return _end; } + struct FlatValue; + struct IdentifierValue; + struct StringValue; - static inline NodeLocation make_from(const char* begin, const char* end) { - end++; - if (begin >= end) return NodeLocation(begin); - return NodeLocation(begin, end); - } - }; + struct ListValue; - struct Node { - Node(const Node&) = delete; - Node& operator=(const Node&) = delete; - Node(NodeLocation location) : _location(location) {} - Node(Node&&) = default; - Node& operator=(Node&&) = default; - virtual ~Node() = default; + struct Statement; + using StatementList = dryad::unlinked_node_list; - virtual std::ostream& print(std::ostream& stream, std::size_t indent) const = 0; - static std::ostream& print_ptr(std::ostream& stream, NodeCPtr node, std::size_t indent); - explicit operator std::string() const; + struct EventStatement; + using EventStatementList = dryad::unlinked_node_list; - static constexpr std::string_view get_type_static() { return detail::type_name(); } - constexpr virtual std::string_view get_type() const = 0; + struct AssignStatement; + using AssignStatementList = dryad::unlinked_node_list; - static constexpr std::string_view get_base_type_static() { return detail::type_name(); } - constexpr virtual std::string_view get_base_type() const { return get_base_type_static(); } + struct Value : dryad::abstract_node_range { + DRYAD_ABSTRACT_NODE_CTOR(Value); + }; - template - constexpr bool is_type() const { - return get_type().compare(detail::type_name()) == 0; + struct FlatValue : dryad::abstract_node_range { + AbstractSyntaxTree::symbol_type value() const { + return _value; } - template - constexpr bool is_derived_from() const { - return is_type() || get_base_type().compare(detail::type_name()) == 0; + const char* value(const AbstractSyntaxTree::symbol_interner_type& symbols) const { + return _value.c_str(symbols); } - template - constexpr T* cast_to() { - if (is_derived_from() || is_type()) return (static_cast(this)); - return nullptr; - } + protected: + explicit FlatValue(dryad::node_ctor ctor, NodeKind kind, AbstractSyntaxTree::symbol_type value) + : node_base(ctor, kind), + _value(value) {} - template - constexpr const T* const cast_to() const { - if (is_derived_from() || is_type()) return (static_cast(this)); - return nullptr; - } + protected: + AbstractSyntaxTree::symbol_type _value; + }; - const NodeLocation location() const { return _location; } + struct IdentifierValue : dryad::basic_node { + explicit IdentifierValue(dryad::node_ctor ctor, AbstractSyntaxTree::symbol_type value) : node_base(ctor, value) {} + }; - struct line_col { - uint32_t line; - uint32_t column; - }; + struct StringValue : dryad::basic_node { + explicit StringValue(dryad::node_ctor ctor, AbstractSyntaxTree::symbol_type value) : node_base(ctor, value) {} + }; - private: - friend class ::ovdl::v2script::Parser; - const line_col get_begin_line_col(const Parser& parser) const; - const line_col get_end_line_col(const Parser& parser) const; + struct ListValue : dryad::basic_node> { + explicit ListValue(dryad::node_ctor ctor, StatementList statements); + explicit ListValue(dryad::node_ctor ctor, AssignStatementList statements) + : node_base(ctor) { + insert_child_list_after(nullptr, statements); + } + + explicit ListValue(dryad::node_ctor ctor) : ListValue(ctor, StatementList {}) { + } + + DRYAD_CHILD_NODE_RANGE_GETTER(Statement, statements, nullptr, this->node_after(_last_statement)); private: - NodeLocation _location; + Node* _last_statement; }; - inline std::ostream& operator<<(std::ostream& stream, Node const& node) { - return node.print(stream, 0); - } - inline std::ostream& operator<<(std::ostream& stream, NodeCPtr node) { - return Node::print_ptr(stream, node, 0); - } - inline std::ostream& operator<<(std::ostream& stream, Node::line_col const& val) { - return stream << '(' << val.line << ':' << val.column << ')'; - } + struct NullValue : dryad::basic_node { + explicit NullValue(dryad::node_ctor ctor) : node_base(ctor) {} + }; - template - NodePtr make_node_ptr(Args&&... args) { - if constexpr (std::is_pointer_v) { - return new T(std::forward(args)...); - } else { - return NodePtr(new T(std::forward(args)...)); + struct Statement : dryad::abstract_node_range, NodeKind::FirstStatement, NodeKind::LastStatement> { + explicit Statement(dryad::node_ctor ctor, NodeKind kind, Value* right) + : node_base(ctor, kind) { + insert_child_after(nullptr, right); } - } - template - To& cast_node_ptr(const From& from) { - if constexpr (std::is_pointer_v) { - return *static_cast(from); - } else { - return *static_cast(from.get()); + explicit Statement(dryad::node_ctor ctor, NodeKind kind, Value* left, Value* right) + : node_base(ctor, kind) { + insert_child_after(nullptr, left); + insert_child_after(left, right); } - } + }; - template - const To& cast_node_cptr(const From& from) { - if constexpr (std::is_pointer_v) { - return *static_cast(from); - } else { - return *static_cast(from.get()); + struct EventStatement : dryad::basic_node { + explicit EventStatement(dryad::node_ctor ctor, bool is_province_event, ListValue* list) + : basic_node(ctor, list), + _is_province_event(is_province_event) { } - } - void copy_into_node_ptr_vector(const std::vector& source, std::vector& dest); - - struct AbstractStringNode : public Node { - std::string _name; - AbstractStringNode(); - AbstractStringNode(std::string&& name, bool allow_newline); - AbstractStringNode(NodeLocation location); - AbstractStringNode(NodeLocation location, std::string&& name, bool allow_newline); - OVDL_TYPE_DEFINE_SELF; - OVDL_RT_TYPE_DEF; - OVDL_PRINT_FUNC_DEF; - static constexpr std::string_view get_base_type_static() { return detail::type_name(); } - constexpr std::string_view get_base_type() const override { return ::ovdl::detail::type_name>(); } - }; + bool is_province_event() const { return _is_province_event; } -#define OVDL_AST_STRING_NODE(NAME) \ - struct NAME final : public AbstractStringNode { \ - NAME(); \ - NAME(std::string&& name, bool allow_newline = true); \ - NAME(lexy::nullopt); \ - NAME(NodeLocation location); \ - NAME(NodeLocation location, std::string&& name, bool allow_newline = true); \ - NAME(NodeLocation location, lexy::nullopt); \ - OVDL_TYPE_DEFINE_SELF; \ - OVDL_RT_TYPE_DEF; \ - OVDL_PRINT_FUNC_DEF; \ - } + DRYAD_CHILD_NODE_GETTER(Value, right, nullptr); - // Value Expression Nodes - OVDL_AST_STRING_NODE(IdentifierNode); - OVDL_AST_STRING_NODE(StringNode); - - // Assignment Nodes - OVDL_AST_STRING_NODE(FactorNode); - OVDL_AST_STRING_NODE(MonthNode); - OVDL_AST_STRING_NODE(NameNode); - OVDL_AST_STRING_NODE(FireOnlyNode); - OVDL_AST_STRING_NODE(IdNode); - OVDL_AST_STRING_NODE(TitleNode); - OVDL_AST_STRING_NODE(DescNode); - OVDL_AST_STRING_NODE(PictureNode); - OVDL_AST_STRING_NODE(IsTriggeredNode); - -#undef OVDL_AST_STRING_NODE - - struct AssignNode final : public Node { - std::string _name; - NodeUPtr _initializer; - AssignNode(NodeLocation location, NodeCPtr name, NodePtr init); - OVDL_TYPE_DEFINE_SELF; - OVDL_RT_TYPE_DEF; - OVDL_PRINT_FUNC_DEF; + private: + bool _is_province_event; }; - struct AbstractListNode : public Node { - std::vector _statements; - AbstractListNode(const std::vector& statements = std::vector {}); - AbstractListNode(NodeLocation location, const std::vector& statements = std::vector {}); - OVDL_TYPE_DEFINE_SELF; - OVDL_RT_TYPE_DEF; - OVDL_PRINT_FUNC_DEF; - static constexpr std::string_view get_base_type_static() { return detail::type_name(); } - constexpr std::string_view get_base_type() const override { return ::ovdl::detail::type_name>(); } + struct AssignStatement : dryad::basic_node { + explicit AssignStatement(dryad::node_ctor ctor, Value* left, Value* right) + : node_base(ctor, left, right) { + } + DRYAD_CHILD_NODE_GETTER(Value, left, nullptr); + DRYAD_CHILD_NODE_GETTER(Value, right, left()); }; -#define OVDL_AST_LIST_NODE(NAME) \ - struct NAME final : public AbstractListNode { \ - NAME(const std::vector& statements = std::vector {}); \ - NAME(lexy::nullopt); \ - NAME(NodeLocation location, const std::vector& statements = std::vector {}); \ - NAME(NodeLocation location, lexy::nullopt); \ - OVDL_TYPE_DEFINE_SELF; \ - OVDL_RT_TYPE_DEF; \ - OVDL_PRINT_FUNC_DEF; \ - } - - OVDL_AST_LIST_NODE(FileNode); - OVDL_AST_LIST_NODE(ListNode); - - OVDL_AST_LIST_NODE(ModifierNode); - OVDL_AST_LIST_NODE(MtthNode); - OVDL_AST_LIST_NODE(EventOptionNode); - OVDL_AST_LIST_NODE(BehaviorListNode); - OVDL_AST_LIST_NODE(DecisionListNode); - -#undef OVDL_AST_LIST_NODE - -#define OVDL_AST_LIST_EXTEND(NAME) \ - NAME(lexy::nullopt); \ - NAME(NodeLocation location, lexy::nullopt); \ - OVDL_TYPE_DEFINE_SELF; \ - OVDL_RT_TYPE_DEF; \ - OVDL_PRINT_FUNC_DEF - - struct EventNode final : public AbstractListNode { - OVDL_AST_LIST_EXTEND(EventNode); - enum class Type { - Country, - Province - } _type; - EventNode(Type type, const std::vector& statements = {}); - EventNode(NodeLocation location, Type type, const std::vector& statements = {}); + struct ValueStatement : dryad::basic_node { + explicit ValueStatement(dryad::node_ctor ctor, Value* value) + : node_base(ctor, value) { + } + DRYAD_CHILD_NODE_GETTER(Value, value, nullptr); }; - struct DecisionNode final : public AbstractListNode { - OVDL_AST_LIST_EXTEND(DecisionNode); - NodeUPtr _name; - DecisionNode(NodePtr name, const std::vector& statements = {}); - DecisionNode(NodeLocation location, NodePtr name, const std::vector& statements = {}); - }; + struct FileTree : dryad::basic_node> { + explicit FileTree(dryad::node_ctor ctor, StatementList statements); + explicit FileTree(dryad::node_ctor ctor, AssignStatementList statements) : node_base(ctor) { + insert_child_list_after(nullptr, statements); + } - struct EventMtthModifierNode final : public AbstractListNode { - OVDL_AST_LIST_EXTEND(EventMtthModifierNode); - NodeUPtr _factor_value; - EventMtthModifierNode() : AbstractListNode() {} - EventMtthModifierNode(NodeLocation location) : AbstractListNode(location) {} - }; + explicit FileTree(dryad::node_ctor ctor) : FileTree(ctor, StatementList {}) { + } - // Packed single case - struct ExecutionNode final : public Node { - enum class Type { - Effect, - Trigger - } _type; - NodeUPtr _name; - NodeUPtr _initializer; - ExecutionNode(Type type, NodePtr name, NodePtr init); - ExecutionNode(NodeLocation location, Type type, NodePtr name, NodePtr init); - OVDL_TYPE_DEFINE_SELF; - OVDL_RT_TYPE_DEF; - OVDL_PRINT_FUNC_DEF; - }; + DRYAD_CHILD_NODE_RANGE_GETTER(Statement, statements, nullptr, this->node_after(_last_node)); - struct ExecutionListNode final : public AbstractListNode { - OVDL_AST_LIST_EXTEND(ExecutionListNode); - ExecutionNode::Type _type; - ExecutionListNode(ExecutionNode::Type type, const std::vector& statements); - ExecutionListNode(NodeLocation location, ExecutionNode::Type type, const std::vector& statements); + private: + Node* _last_node; }; -#undef OVDL_AST_LIST_EXTEND + using File = ovdl::BasicFile; + struct AbstractSyntaxTree : ovdl::BasicAbstractSyntaxTree { + using BasicAbstractSyntaxTree::BasicAbstractSyntaxTree; -} + std::string make_list_visualizer() const; + std::string make_native_visualizer() const; + }; + using ParseState = ovdl::ParseState; -#undef OVDL_PRINT_FUNC_DECL -#undef OVDL_PRINT_FUNC_DEF -#undef OVDL_TYPE_DEFINE_SELF \ No newline at end of file + static_assert(IsFile, "File failed IsFile concept"); + static_assert(IsAst, "AbstractSyntaxTree failed IsAst concept"); + static_assert(IsParseState, "ParseState failed IsParseState concept"); +} \ No newline at end of file diff --git a/include/openvic-dataloader/v2script/NodeLocationMap.hpp b/include/openvic-dataloader/v2script/NodeLocationMap.hpp deleted file mode 100644 index aa88d62..0000000 --- a/include/openvic-dataloader/v2script/NodeLocationMap.hpp +++ /dev/null @@ -1,91 +0,0 @@ -#pragma once - -#include - -#include -#include - -#include - -namespace ovdl::v2script::ast { - // TODO: FOR THE LOVE OF GOD USE A DIFFERENT HASH MULTIMAP TYPE - // See src/openvic-dataloader/v2script/Parser.cpp#252 - template - struct NodeLocationMap : public std::unordered_multimap, detail::PointerHash> { - NodeLocationMap() = default; - NodeLocationMap(const Input& input, const Node& top_node) { - generate_location_map(input, top_node); - } - - NodeLocationMap(const NodeLocationMap&) = default; - NodeLocationMap(NodeLocationMap&&) = default; - - NodeLocationMap& operator=(const NodeLocationMap&) = default; - NodeLocationMap& operator=(NodeLocationMap&&) = default; - - lexy::input_location_anchor generate_location_map(const Input& input, NodeCPtr node); - lexy::input_location_anchor generate_location_map(const Input& input, NodeCPtr node, lexy::input_location_anchor anchor); - lexy::input_location_anchor generate_begin_location_for(const Input& input, NodeCPtr node, lexy::input_location_anchor anchor); - lexy::input_location_anchor generate_end_location_for(const Input& input, NodeCPtr node, lexy::input_location_anchor anchor); - }; - - template - constexpr const lexy::input_location make_begin_loc(const NodeLocation location, const Input& input, lexy::input_location_anchor anchor) { - return lexy::get_input_location(input, location.begin(), anchor); - } - - template - constexpr const lexy::input_location make_begin_loc(const NodeLocation location, const Input& input) { - return lexy::get_input_location(input, location.begin()); - } - - template - constexpr const lexy::input_location make_end_loc(const NodeLocation location, const Input& input, lexy::input_location_anchor anchor) { - return lexy::get_input_location(input, location.end(), anchor); - } - - template - constexpr const lexy::input_location make_end_loc(const NodeLocation location, const Input& input) { - return lexy::get_input_location(input, location.end()); - } -} - -namespace ovdl::v2script::ast { - template - lexy::input_location_anchor NodeLocationMap::generate_location_map(const Input& input, NodeCPtr node) { - return generate_location_map(input, node, lexy::input_location_anchor(input)); - } - - template - lexy::input_location_anchor NodeLocationMap::generate_location_map(const Input& input, NodeCPtr node, lexy::input_location_anchor anchor) { - if (!node) return anchor; - anchor = generate_begin_location_for(input, node, anchor); - if (auto list_node = node->cast_to(); list_node) { - for (auto& inner_node : list_node->_statements) { - anchor = generate_location_map(input, inner_node.get(), anchor); - } - } else if (auto assign_node = node->cast_to(); assign_node) { - anchor = generate_location_map(input, assign_node->_initializer.get(), anchor); - } - // TODO: implement for EventNode, DecisionNode, EventMtthModifierNode, ExecutionNode, ExecutionListNode - if (!node->location().end() || node->location().begin() >= node->location().end()) - return anchor; - return generate_end_location_for(input, node, anchor); - } - - template - lexy::input_location_anchor NodeLocationMap::generate_begin_location_for(const Input& input, NodeCPtr node, lexy::input_location_anchor anchor) { - if (node->location().begin() == nullptr) return anchor; - lexy::input_location next_loc = make_begin_loc(node->location(), input, anchor); - this->emplace(node, next_loc); - return next_loc.anchor(); - } - - template - lexy::input_location_anchor NodeLocationMap::generate_end_location_for(const Input& input, NodeCPtr node, lexy::input_location_anchor anchor) { - if (node->location().end() == nullptr) return anchor; - lexy::input_location next_loc = make_end_loc(node->location(), input, anchor); - this->emplace(node, next_loc); - return next_loc.anchor(); - } -} \ No newline at end of file diff --git a/include/openvic-dataloader/v2script/Parser.hpp b/include/openvic-dataloader/v2script/Parser.hpp index 885946d..cef1faf 100644 --- a/include/openvic-dataloader/v2script/Parser.hpp +++ b/include/openvic-dataloader/v2script/Parser.hpp @@ -3,21 +3,26 @@ #include #include #include +#include +#include #include -#include -#include -#include -#include +#include +#include +#include +#include #include -namespace ovdl::v2script { +#include - using FileNode = ast::FileNode; +namespace ovdl::v2script { + using FileTree = ast::FileTree; + using FilePosition = ovdl::FilePosition; class Parser final : public detail::BasicParser { public: Parser(); + Parser(std::basic_ostream& error_stream); static Parser from_buffer(const char* data, std::size_t size); static Parser from_buffer(const char* start, const char* end); @@ -31,19 +36,30 @@ namespace ovdl::v2script { constexpr Parser& load_from_file(const char* path); Parser& load_from_file(const std::filesystem::path& path); - constexpr Parser& load_from_file(const detail::Has_c_str auto& path); + constexpr Parser& load_from_file(const detail::HasCstr auto& path) { + return load_from_file(path.c_str()); + } bool simple_parse(); bool event_parse(); bool decision_parse(); bool lua_defines_parse(); - const FileNode* get_file_node() const; + const FileTree* get_file_node() const; + + std::string_view value(const ovdl::v2script::ast::FlatValue& node) const; + + std::string make_native_string() const; + std::string make_list_string() const; + + const FilePosition get_position(const ast::Node* node) const; + + using error_range = ovdl::detail::error_range; + Parser::error_range get_errors() const; - void generate_node_location_map(); + const FilePosition get_error_position(const error::Error* error) const; - const ast::Node::line_col get_node_begin(const ast::NodeCPtr node) const; - const ast::Node::line_col get_node_end(const ast::NodeCPtr node) const; + void print_errors_to(std::basic_ostream& stream) const; Parser(Parser&&); Parser& operator=(Parser&&); @@ -51,12 +67,10 @@ namespace ovdl::v2script { ~Parser(); private: - friend class ::ovdl::v2script::ast::Node; - class BufferHandler; - std::unique_ptr _buffer_handler; - std::unique_ptr _file_node; + class ParseHandler; + std::unique_ptr _parse_handler; template - constexpr void _run_load_func(detail::LoadCallback auto func, Args... args); + constexpr void _run_load_func(detail::LoadCallback auto func, Args... args); }; } \ No newline at end of file diff --git a/src/headless/main.cpp b/src/headless/main.cpp index 688b479..7279a6e 100644 --- a/src/headless/main.cpp +++ b/src/headless/main.cpp @@ -12,6 +12,11 @@ #include #include +enum class VisualizationType { + Native, // = { } + List // - : +}; + std::string_view trim(std::string_view str) { std::string_view::iterator begin = str.begin(); std::string_view::iterator end = str.end(); @@ -38,7 +43,8 @@ bool insenitive_trim_eq(std::string_view lhs, std::string_view rhs) { template int print_csv(const std::string_view path) { - auto parser = ovdl::csv::Parser::from_file(path); + auto parser = ovdl::csv::Parser(std::cerr); + parser.load_from_file(path); if (parser.has_error()) { return 1; } @@ -49,9 +55,7 @@ int print_csv(const std::string_view path) { } if (parser.has_warning()) { - for (auto& warning : parser.get_warnings()) { - std::cerr << "Warning: " << warning.message << std::endl; - } + parser.print_errors_to(std::cerr); } std::cout << "lines:\t\t" << parser.get_lines().size() << std::endl; @@ -62,8 +66,9 @@ int print_csv(const std::string_view path) { return EXIT_SUCCESS; } -int print_lua(const std::string_view path) { - auto parser = ovdl::v2script::Parser::from_file(path); +int print_lua(const std::string_view path, VisualizationType visual_type) { + auto parser = ovdl::v2script::Parser(std::cerr); + parser.load_from_file(path); if (parser.has_error()) { return 1; } @@ -74,32 +79,20 @@ int print_lua(const std::string_view path) { } if (parser.has_warning()) { - for (auto& warning : parser.get_warnings()) { - std::cerr << "Warning: " << warning.message << std::endl; - } + parser.print_errors_to(std::cerr); } - parser.generate_node_location_map(); - - for (const auto& node : parser.get_file_node()->_statements) { - std::cout << node->get_type() << ": " << parser.get_node_begin(node.get()) << std::endl; - if (auto assign_node = node->cast_to(); assign_node) { - auto lnode_ptr = assign_node->_initializer.get(); - std::cout << lnode_ptr->get_type() << " begin: " << parser.get_node_begin(lnode_ptr) << std::endl; - std::cout << lnode_ptr->get_type() << " end: " << parser.get_node_end(lnode_ptr) << std::endl; - if (auto list_node = lnode_ptr->cast_to(); list_node) { - for (const auto& inode : list_node->_statements) { - std::cout << inode->get_type() << ": " << parser.get_node_begin(inode.get()) << std::endl; - } - } - } + switch (visual_type) { + using enum VisualizationType; + case Native: std::cout << parser.make_native_string() << '\n'; break; + case List: std::cout << parser.make_list_string() << '\n'; break; } - std::cout << parser.get_file_node() << std::endl; return EXIT_SUCCESS; } -int print_v2script_simple(const std::string_view path) { - auto parser = ovdl::v2script::Parser::from_file(path); +int print_v2script_simple(const std::string_view path, VisualizationType visual_type) { + auto parser = ovdl::v2script::Parser(std::cerr); + parser.load_from_file(path); if (parser.has_error()) { return 1; } @@ -110,50 +103,59 @@ int print_v2script_simple(const std::string_view path) { } if (parser.has_warning()) { - for (auto& warning : parser.get_warnings()) { - std::cerr << "Warning: " << warning.message << std::endl; - } + parser.print_errors_to(std::cerr); } - parser.generate_node_location_map(); - - for (const auto& node : parser.get_file_node()->_statements) { - std::cout << node->get_type() << ": " << parser.get_node_begin(node.get()) << std::endl; - if (auto assign_node = node->cast_to(); assign_node) { - auto lnode_ptr = assign_node->_initializer.get(); - std::cout << lnode_ptr->get_type() << " begin: " << parser.get_node_begin(lnode_ptr) << std::endl; - std::cout << lnode_ptr->get_type() << " end: " << parser.get_node_end(lnode_ptr) << std::endl; - if (auto list_node = lnode_ptr->cast_to(); list_node) { - for (const auto& inode : list_node->_statements) { - std::cout << inode->get_type() << ": " << parser.get_node_begin(inode.get()) << std::endl; - } - } - } + switch (visual_type) { + using enum VisualizationType; + case Native: std::cout << parser.make_native_string() << '\n'; break; + case List: std::cout << parser.make_list_string() << '\n'; break; } - std::cout << parser.get_file_node() << std::endl; return EXIT_SUCCESS; } int main(int argc, char** argv) { - switch (argc) { + std::vector args; + args.reserve(argc); + for (size_t index = 0; index < argc; index++) { + args.push_back(argv[index]); + } + + VisualizationType type = VisualizationType::Native; + if (args.size() >= 2) { + std::string_view type_str = args[1]; + if (insenitive_trim_eq(type_str, "list")) { + type = VisualizationType::List; + args.erase(args.begin() + 1); + } else if (insenitive_trim_eq(type_str, "native")) { + type = VisualizationType::Native; + args.erase(args.begin() + 1); + } + } + + switch (args.size()) { case 2: - if (insenitive_trim_eq(std::filesystem::path(argv[1]).extension().string(), ".lua")) { - return print_lua(argv[1]); + if (insenitive_trim_eq(std::filesystem::path(args[1]).extension().string(), ".lua")) { + return print_lua(args[1], type); } - return print_v2script_simple(argv[1]); + return print_v2script_simple(args[1], type); case 4: - if (insenitive_trim_eq(argv[1], "csv") && insenitive_trim_eq(argv[2], "utf")) - return print_csv(argv[3]); + if (insenitive_trim_eq(args[1], "csv") && insenitive_trim_eq(args[2], "utf")) + return print_csv(args[3]); goto default_jump; case 3: - if (insenitive_trim_eq(argv[1], "csv")) - return print_csv(argv[2]); + if (insenitive_trim_eq(args[1], "csv")) + return print_csv(args[2]); + if (insenitive_trim_eq(args[1], "lua")) + return print_lua(args[2], type); [[fallthrough]]; default: default_jump: - std::fprintf(stderr, "usage: %s \n", argv[0]); - std::fprintf(stderr, "usage: %s csv \n", argv[0]); - std::fprintf(stderr, "usage: %s csv utf ", argv[0]); + std::fprintf(stderr, "usage: %s \n", args[0].c_str()); + std::fprintf(stderr, "usage: %s list \n", args[0].c_str()); + std::fprintf(stderr, "usage: %s native \n", args[0].c_str()); + std::fprintf(stderr, "usage: %s lua \n", args[0].c_str()); + std::fprintf(stderr, "usage: %s csv [utf] \n", args[0].c_str()); return EXIT_FAILURE; } 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 + +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 + +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 + +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 + +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 #include +#include #include #include -#include "detail/LexyLitRange.hpp" +#include "detail/dsl.hpp" // Grammar Definitions // namespace ovdl::csv::grammar { + using EncodingType = ovdl::csv::EncodingType; + template concept ParseChars = requires() { { T::character }; @@ -51,123 +54,117 @@ namespace ovdl::csv::grammar { .map<'"'>('"'); template - struct StringValue { - static constexpr auto rule = [] { - // Arbitrary code points - auto c = Options.character - Options.control; - - auto back_escape = lexy::dsl::backslash_escape // - .symbol(); + 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(); + auto back_escape = lexy::dsl::backslash_escape // + .symbol(); - 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; - }; + auto quote_escape = lexy::dsl::escape(lexy::dsl::lit_c<'"'>) // + .template symbol(); - template - struct PlainValue { - static constexpr auto rule = [] { - if constexpr (Options.SupportStrings) { - return lexy::dsl::identifier(Options.character - (lexy::dsl::lit_b / lexy::dsl::ascii::newline)); - } else { - auto escape_check_char = Options.character - (lexy::dsl::lit_b / 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(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; - }; + 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 - struct Value { - static constexpr auto rule = [] { - if constexpr (Options.SupportStrings) { - return lexy::dsl::p> | lexy::dsl::p>; - } else { - return lexy::dsl::p>; - } - }(); - static constexpr auto value = lexy::forward; - }; - - template - struct SepConst { - static constexpr auto rule = lexy::dsl::lit_b; - static constexpr auto value = lexy::constant(1); - }; + static constexpr auto value = lexy::as_string; + }; - template - struct Seperator { - static constexpr auto rule = lexy::dsl::list(lexy::dsl::p>); - 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 / lexy::dsl::ascii::newline)); + } else { + auto escape_check_char = Options.character - (lexy::dsl::lit_b / 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(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; + }; - template - struct LineEnd { - static constexpr auto rule = lexy::dsl::list(lexy::dsl::p>, lexy::dsl::trailing_sep(lexy::dsl::p>)); - static constexpr auto value = lexy::fold_inplace( - std::initializer_list {}, - [](ovdl::csv::LineObject& result, auto&& arg) { - if constexpr (std::is_same_v, 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(arg + result.back().first), ""); + struct Value { + static constexpr auto rule = [] { + if constexpr (Options.SupportStrings) { + return lexy::dsl::p | lexy::dsl::p; } else { - if (result.empty()) result.emplace_back(0u, LEXY_MOV(arg)); - else { - auto& [pos, value] = result.back(); - value = arg; - } + return lexy::dsl::p; } - }); - }; + }(); + static constexpr auto value = lexy::forward; + }; - template - 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; + static constexpr auto value = lexy::constant(1); }; - static constexpr auto rule = lexy::dsl::p> | lexy::dsl::p> >> lexy::dsl::opt(lexy::dsl::p>); - static constexpr auto value = - lexy::callback( - [](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); + static constexpr auto value = lexy::count; + }; + + struct LineEnd { + static constexpr auto rule = lexy::dsl::list(lexy::dsl::p, lexy::dsl::trailing_sep(lexy::dsl::p)); + static constexpr auto value = lexy::fold_inplace( + std::initializer_list {}, + [](ovdl::csv::LineObject& result, auto&& arg) { + if constexpr (std::is_same_v, 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(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 | lexy::dsl::p >> lexy::dsl::opt(lexy::dsl::p); + static constexpr auto value = + lexy::callback( + [](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 struct File { - static constexpr auto rule = lexy::dsl::terminator(lexy::dsl::eof).opt_list( - lexy::dsl::p> | lexy::dsl::newline - ); + static constexpr auto rule = lexy::dsl::terminator(lexy::dsl::eof).opt_list(lexy::dsl::p::Line> | lexy::dsl::newline); static constexpr auto value = lexy::as_list>; }; @@ -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 +#include +#include +#include + +#include + +template +struct LexyEncodingFrom { +}; + +template<> +struct LexyEncodingFrom { + using encoding = lexy::default_encoding; +}; + +template<> +struct LexyEncodingFrom { + using encoding = lexy::utf8_char_encoding; +}; + +template +using CsvFile = ovdl::BasicFile::encoding, std::vector>; + +template +using CsvParseState = ovdl::FileParseState>; \ 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 #include +#include #include #include -#include +#include +#include +#include #include #include #include #include -#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 -struct LexyEncodingFrom { -}; - -template<> -struct LexyEncodingFrom { - using encoding = lexy::default_encoding; -}; - -template<> -struct LexyEncodingFrom { - using encoding = lexy::utf8_char_encoding; -}; +/// ParseHandler /// template -class Parser::BufferHandler final : public detail::BasicBufferHandler::encoding> { -public: - template - std::optional> parse(const ErrorCallback& callback) { - auto result = lexy::parse(this->_buffer, callback); +struct Parser::ParseHandler final : detail::BasicFileParseHandler> { + template + std::optional parse() { + auto result = lexy::parse(this->buffer(), *this->_parse_state, this->_parse_state->logger().error_callback()); if (!result) { - return result.errors(); + return this->_parse_state->logger().get_errors(); } _lines = std::move(result.value()); return std::nullopt; @@ -59,8 +46,14 @@ private: template Parser::Parser() - : _buffer_handler(std::make_unique()) { - set_error_log_to_stderr(); + : _parse_handler(std::make_unique()) { + set_error_log_to_null(); +} + +template +Parser::Parser(std::basic_ostream& error_stream) + : _parse_handler(std::make_unique()) { + set_error_log_to(error_stream); } template @@ -115,28 +108,31 @@ Parser Parser::from_file(const std::filesystem::path& path) /// template template -constexpr void Parser::_run_load_func(detail::LoadCallback auto func, Args... args) { - _warnings.clear(); - _errors.clear(); +constexpr void Parser::_run_load_func(detail::LoadCallback auto func, Args... args) { _has_fatal_error = false; - if (auto error = func(_buffer_handler.get(), std::forward(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)...); + auto error_message = _parse_handler->make_error_from(error); + if (!error_message.empty()) { + _has_error = true; + _has_fatal_error = true; + _parse_handler->parse_state().logger().template create_log(DiagnosticLogger::DiagnosticKind::error, fmt::runtime(error_message)); + } + if (has_error() && &_error_stream.get() != &detail::cnull) { + print_errors_to(_error_stream.get()); } } template constexpr Parser& Parser::load_from_buffer(const char* data, std::size_t size) { // Type can't be deduced? - _run_load_func(std::mem_fn(&BufferHandler::load_buffer_size), data, size); + _run_load_func(std::mem_fn(&ParseHandler::load_buffer_size), data, size); return *this; } template constexpr Parser& Parser::load_from_buffer(const char* start, const char* end) { // Type can't be deduced? - _run_load_func(std::mem_fn(&BufferHandler::load_buffer), start, end); + _run_load_func(std::mem_fn(&ParseHandler::load_buffer), start, end); return *this; } @@ -149,7 +145,7 @@ template 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; } @@ -158,39 +154,35 @@ Parser& Parser::load_from_file(const std::filesystem::path& return load_from_file(path.string().c_str()); } -template -constexpr Parser& Parser::load_from_file(const detail::Has_c_str auto& path) { - return load_from_file(path.c_str()); -} - template bool Parser::parse_csv(bool handle_strings) { - if (!_buffer_handler->is_valid()) { + if (!_parse_handler->is_valid()) { return false; } - std::optional> errors; - auto report_error = ovdl::detail::ReporError.path(_file_path).to(detail::OStreamOutputIterator { _error_stream }); + std::optional::error_range> errors; + // auto report_error = ovdl::detail::ReporError.path(_file_path).to(detail::OStreamOutputIterator { _error_stream }); if constexpr (Encoding == EncodingType::Windows1252) { if (handle_strings) - errors = _buffer_handler->template parse(report_error); + errors = _parse_handler->template parse(); else - errors = _buffer_handler->template parse(report_error); + errors = _parse_handler->template parse(); } else { if (handle_strings) - errors = _buffer_handler->template parse(report_error); + errors = _parse_handler->template parse(); else - errors = _buffer_handler->template parse(report_error); + errors = _parse_handler->template parse(); } - 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& Parser::get_lines() const { return _lines; } +template +typename Parser::error_range Parser::get_errors() const { + return _parse_handler->parse_state().logger().get_errors(); +} + +template +const FilePosition Parser::get_error_position(const error::Error* error) const { + if (!error || !error->is_linked_in_tree()) { + return {}; + } + auto err_location = _parse_handler->parse_state().logger().location_of(error); + if (err_location.is_synthesized()) { + return {}; + } + + auto loc_begin = lexy::get_input_location(_parse_handler->buffer(), err_location.begin()); + FilePosition result { loc_begin.line_nr(), loc_begin.line_nr(), loc_begin.column_nr(), loc_begin.column_nr() }; + if (err_location.begin() < err_location.end()) { + auto loc_end = lexy::get_input_location(_parse_handler->buffer(), err_location.end(), loc_begin.anchor()); + result.end_line = loc_end.line_nr(); + result.end_column = loc_end.column_nr(); + } + return result; +} + +template +void Parser::print_errors_to(std::basic_ostream& stream) const { + auto errors = get_errors(); + if (errors.empty()) return; + for (const auto error : errors) { + dryad::visit_tree( + error, + [&](const error::BufferError* buffer_error) { + stream << buffer_error->message() << '\n'; + }, + [&](const error::ParseError* parse_error) { + stream << parse_error->message() << '\n'; + }, + [&](dryad::child_visitor 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; template class ovdl::csv::Parser; \ 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 - -#include -#include - -#include -#include -#include - -#include "detail/Errors.hpp" - -namespace ovdl::detail { - template - class BasicBufferHandler { - public: - using encoding_type = Encoding; - - OVDL_OPTIONAL_CONSTEXPR bool is_valid() const { - return _buffer.data() != nullptr; - } - - OVDL_OPTIONAL_CONSTEXPR std::optional load_buffer_size(const char* data, std::size_t size) { - _buffer = lexy::buffer(data, size); - return std::nullopt; - } - - OVDL_OPTIONAL_CONSTEXPR std::optional load_buffer(const char* start, const char* end) { - _buffer = lexy::buffer(start, end); - return std::nullopt; - } - - std::optional load_file(const char* path) { - auto file = lexy::read_file(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 _buffer; - }; -} \ No newline at end of file diff --git a/src/openvic-dataloader/detail/BasicParser.cpp b/src/openvic-dataloader/detail/BasicParser.cpp deleted file mode 100644 index 212bf00..0000000 --- a/src/openvic-dataloader/detail/BasicParser.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include -#include - -#include - -#include "detail/NullBuff.hpp" - -using namespace ovdl; -using namespace ovdl::detail; - -BasicParser::BasicParser() : _error_stream(detail::cnull) {} - -void BasicParser::set_error_log_to_null() { - set_error_log_to(detail::cnull); -} - -void BasicParser::set_error_log_to_stderr() { - set_error_log_to(std::cerr); -} - -void BasicParser::set_error_log_to_stdout() { - set_error_log_to(std::cout); -} - -void BasicParser::set_error_log_to(std::basic_ostream& stream) { - _error_stream = stream; -} - -bool BasicParser::has_error() const { - return !_errors.empty(); -} - -bool BasicParser::has_fatal_error() const { - return _has_fatal_error; -} - -bool BasicParser::has_warning() const { - return !_warnings.empty(); -} - -const std::vector& BasicParser::get_errors() const { - return _errors; -} - -const std::vector& BasicParser::get_warnings() const { - return _warnings; -} - -std::string_view BasicParser::get_file_path() const { - return _file_path; -} \ 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 + +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 #include -#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; // & 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 - -namespace ovdl::detail::lexydsl { - template - consteval auto make_range() { - if constexpr (LOW == HIGH) { - return lexy::dsl::lit_c; - } else if constexpr (LOW == (HIGH - 1)) { - return lexy::dsl::lit_c / lexy::dsl::lit_c; - } else { - return lexy::dsl::lit_c / make_range(); - } - } -} \ 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 -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include - -namespace ovdl::detail { - template - struct _ReportError { - OutputIterator _iter; - lexy::visualization_options _opts; - const char* _path; - - struct _sink { - OutputIterator _iter; - lexy::visualization_options _opts; - const char* _path; - std::size_t _count; - std::vector _errors; - - using return_type = std::vector; - - template - void operator()(const lexy::error_context& context, const lexy::error& error) { - _iter = lexy_ext::_detail::write_error(_iter, context, error, _opts, _path); - ++_count; - - // Convert the context location and error location into line/column information. - auto context_location = lexy::get_input_location(context.input(), context.position()); - auto location = lexy::get_input_location(context.input(), error.position(), context_location.anchor()); - - std::basic_stringstream message; - - // Write the main annotation. - if constexpr (std::is_same_v) { - auto string = lexy::_detail::make_literal_lexeme(error.string(), error.length()); - - message << "expected '" << string.data() << '\''; - } else if constexpr (std::is_same_v) { - auto string = lexy::_detail::make_literal_lexeme(error.string(), error.length()); - - message << "expected keyword '" << string.data() << '\''; - } else if constexpr (std::is_same_v) { - 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 - constexpr _ReportError to(OI out) const { - return { out, _opts, _path }; - } - - /// Overrides visualization options. - constexpr _ReportError opts(lexy::visualization_options opts) const { - return { _iter, opts, _path }; - } - }; - - constexpr auto ReporError = _ReportError {}; -} \ No newline at end of file diff --git a/src/openvic-dataloader/detail/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 m_sbuf; }; - typedef basic_onullstream onullstream; - typedef basic_onullstream wonullstream; + using onullstream = basic_onullstream; + using wonullstream = basic_onullstream; - 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 - -namespace ovdl::detail { - struct OStreamOutputIterator { - std::reference_wrapper _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 + +#include +#include + +#include +#include +#include + +#include "detail/BufferError.hpp" + +namespace ovdl::detail { + template + 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 + constexpr void _run_load_func(detail::LoadCallback auto func, Args... args); + }; + + template + struct BasicFileParseHandler : ParseHandler> { + 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 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 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(path); + if (!file) { + _parse_state.reset(new parse_state_type { path, lexy::buffer() }); + return ovdl::detail::from_underlying(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; + }; + + template + struct BasicStateParseHandler : ParseHandler> { + 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 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 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(path); + if (!file) { + _parse_state.reset(new parse_state_type { path, lexy::buffer() }); + return ovdl::detail::from_underlying(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; + }; +} \ No newline at end of file diff --git a/src/openvic-dataloader/detail/Parser.cpp b/src/openvic-dataloader/detail/Parser.cpp new file mode 100644 index 0000000..fd87687 --- /dev/null +++ b/src/openvic-dataloader/detail/Parser.cpp @@ -0,0 +1,43 @@ +#include +#include + +#include + +#include "detail/NullBuff.hpp" + +using namespace ovdl; +using namespace ovdl::detail; + +BasicParser::BasicParser() : _error_stream(detail::cnull) {} + +void BasicParser::set_error_log_to_null() { + set_error_log_to(detail::cnull); +} + +void BasicParser::set_error_log_to_stderr() { + set_error_log_to(std::cerr); +} + +void BasicParser::set_error_log_to_stdout() { + set_error_log_to(std::cout); +} + +void BasicParser::set_error_log_to(std::basic_ostream& stream) { + _error_stream = stream; +} + +bool BasicParser::has_error() const { + return _has_error; +} + +bool BasicParser::has_fatal_error() const { + return _has_fatal_error; +} + +bool BasicParser::has_warning() const { + return _has_warning; +} + +std::string_view BasicParser::get_file_path() const { + return _file_path; +} \ No newline at end of file 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 +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace ovdl::detail { + + template + 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 + 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; + + static constexpr size_type npos = size_type(-1); + + static constexpr auto size = std::integral_constant {}; + 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 + constexpr bool operator==(const string_literal& other) const { + return as_string_view() == other.as_string_view(); + } + + constexpr bool starts_with(std::basic_string_view 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 + constexpr bool starts_with(const string_literal& other) const { + return starts_with(other.as_string_view()); + } + + constexpr bool ends_with(std::basic_string_view 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 + constexpr bool ends_with(const string_literal& 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 result {}; + for (auto i = 0u; i != N - 2; i++) + result._data[i] = _data[i]; + return result; + } + + template + constexpr auto append(value_type ch) const noexcept { + string_literal 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 + constexpr auto append(string_literal str) const noexcept { + return *this + str; + } + + template + constexpr auto append(const value_type (&literal)[N2]) const noexcept { + return *this + literal; + } + + template + 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 {}; + 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 + 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 + 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 + 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() const& { return as_string_view(); } + constexpr operator std::basic_string_view() const&& = delete; + + constexpr std::basic_string_view as_string_view() const& { return std::basic_string_view(_data, size() - 1); } + constexpr std::basic_string_view 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 + constexpr auto operator+(const string_literal& other) const noexcept { + string_literal 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 + constexpr auto operator+(const value_type (&rhs)[N2]) const noexcept { + return *this + _to_string(rhs); + } + + template + friend constexpr auto operator+(const value_type (&lhs)[N2], string_literal rhs) noexcept { + return _to_string(lhs) + rhs; + } + }; + template + string_literal(const CharT (&)[N]) -> string_literal; + + template + string_literal(CharT) -> string_literal<1, CharT>; + + template typename T, string_literal Str, std::size_t... Idx> + auto _to_type_string(lexy::_detail::index_sequence) { + return T {}; + } + template typename T, string_literal Str> + using to_type_string = decltype(ovdl::detail::_to_type_string(lexy::_detail::make_index_sequence {})); +} + +namespace ovdl::dsl { + template + constexpr auto keyword(lexyd::_id) { + return ovdl::detail::to_type_string>::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 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 + +#include +#include + +#include +#include +#include +#include +#include + +#include "detail/StringLiteral.hpp" + +namespace ovdl::dsl { + template + constexpr auto callback(Callback... cb) { + return lexy::bind(lexy::callback(cb...), lexy::parse_state, lexy::values); + } + + template + constexpr auto sink(Sink sink) { + return lexy::bind_sink(sink, lexy::parse_state); + } + + template + constexpr auto collect(Callback callback) { + return sink(lexy::collect(callback)); + } + + template + constexpr auto collect(Callback callback) { + return sink(lexy::collect(callback)); + } + + template + constexpr auto construct = callback( + [](StateType& state, ovdl::NodeLocation loc, auto&& arg) { + if constexpr (std::is_same_v, lexy::nullopt>) + return state.ast().template create(loc); + else + return state.ast().template create(loc, DRYAD_FWD(arg)); + }, + [](StateType& state, ovdl::NodeLocation loc, auto&&... args) { + return state.ast().template create(loc, DRYAD_FWD(args)...); + }); + + template + constexpr auto construct_list = callback( + [](StateType& state, const char* begin, ListType&& arg, const char* end) { + return state.ast().template create(NodeLocation::make_from(begin, end), DRYAD_FWD(arg)); + }, + [](StateType& state, const char* begin, lexy::nullopt, const char* end) { + return state.ast().template create(NodeLocation::make_from(begin, end)); + }, + [](StateType& state, const char* begin, const char* end) { + return state.ast().template create(NodeLocation::make_from(begin, end)); + }); + + template + constexpr auto construct_list = callback( + [](StateType& state, const char* begin, ListType&& arg, const char* end) { + return state.ast().template create(NodeLocation::make_from(begin, end), DRYAD_FWD(arg)); + }, + [](StateType& state, const char* begin, lexy::nullopt, const char* end) { + return state.ast().template create(NodeLocation::make_from(begin, end)); + }); + + template + consteval auto make_range() { + if constexpr (LOW == HIGH) { + return ::lexy::dsl::lit_c; + } else if constexpr (LOW == (HIGH - 1)) { + return ::lexy::dsl::lit_c / ::lexy::dsl::lit_c; + } else { + return ::lexy::dsl::lit_c / make_range(); + } + } + + template + constexpr auto position_brackets = lexy::dsl::brackets(lexy::dsl::position(lexy::dsl::lit_c), lexy::dsl::position(lexy::dsl::lit_c)); + + 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 + constexpr auto p = lexy::dsl::position(lexy::dsl::p); + + template + static constexpr auto default_kw_value = dsl::callback( + [](ParseType& state, NodeLocation loc) { + return state.ast().template create(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(lexy::dsl::inline_); + static constexpr auto rule = lexy::dsl::position(keyword) >> lexy::dsl::equal_sign; + static constexpr auto value = Value; + }; + static constexpr auto rule = dsl::p >> Production; + static constexpr auto value = construct; + }; + + template< + IsParseState ParseType, + typename Identifier, + typename RuleValue, + ovdl::detail::string_literal Keyword, + auto Production, + auto Value> + struct fkeyword_rule : keyword_rule { + using base_type = keyword_rule; + struct context_t; + struct rule_t : base_type::rule_t { + static constexpr auto flag = lexy::dsl::context_flag; + 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; + }; + static constexpr auto make_flag = rule_t::flag.create(); + + static constexpr auto rule = dsl::p >> (rule_t::must >> rule_t::flag.set()) >> Production; + static constexpr auto value = construct; + }; + + template + struct rule_helper { + static constexpr auto flags = (Args::make_flag + ...); + static constexpr auto p = (lexy::dsl::p | ...); + }; +} \ 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 -#include -#include -#include -#include - #include #include @@ -11,281 +5,146 @@ #include #include -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& source, std::vector& 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()) { - _name = cast_node_cptr(name)._name; - } -} - -std::ostream& Node::print_ptr(std::ostream& stream, NodeCPtr node, size_t indent) { - return node != nullptr ? node->print(stream, indent) : stream << ""; -} - -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& 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& statements) : Node(location) { - copy_into_node_ptr_vector(statements, _statements); -} -AbstractListNode::AbstractListNode(const std::vector& 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& statements) : AbstractListNode(statements) {} \ - NAME::NAME(lexy::nullopt) : AbstractListNode() {} \ - NAME::NAME(NodeLocation location, const std::vector& 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 +#include -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 -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& statements) : AbstractListNode(location), - _type(type) { - copy_into_node_ptr_vector(statements, _statements); -} -EventNode::EventNode(Type type, const std::vector& 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& statements) : AbstractListNode(location), - _name(std::move(name)) { - copy_into_node_ptr_vector(statements, _statements); -} -DecisionNode::DecisionNode(NodePtr name, const std::vector& 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& statements) : AbstractListNode(location), - _type(type) { - copy_into_node_ptr_vector(statements, _statements); -} -ExecutionListNode::ExecutionListNode(ExecutionNode::Type type, const std::vector& 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 visitor, const ValueStatement* statement) { + visitor(statement->value()); + }, + [&](dryad::child_visitor visitor, const AssignStatement* statement) { + visitor(statement->left()); + if (statement->right()->kind() != NodeKind::NullValue) { + result.append(" = "); + visitor(statement->right()); + } + }, + [&](dryad::child_visitor 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 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 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 +#include #include #include "ModifierGrammar.hpp" @@ -10,23 +11,12 @@ namespace ovdl::v2script::grammar { struct AiBehaviorList { static constexpr auto rule = lexy::dsl::list(lexy::dsl::p | lexy::dsl::p); - static constexpr auto value = - lexy::as_list> >> - lexy::callback( - [](auto&& list) { - return ast::make_node_ptr(LEXY_MOV(list)); - }); + static constexpr auto value = lexy::as_list; }; struct AiBehaviorBlock { - static constexpr auto rule = lexy::dsl::curly_bracketed.opt(lexy::dsl::p); + static constexpr auto rule = dsl::curly_bracketed.opt(lexy::dsl::p); - static constexpr auto value = lexy::callback( - [](auto&& list) { - return LEXY_MOV(list); - }, - [](lexy::nullopt = {}) { - return lexy::nullopt {}; - }); + static constexpr auto value = construct_list; }; } \ 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 - #include #include #include +#include #include #include +#include #include +#include +#include -#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 _rule and _p -#define OVDL_GRAMMAR_KEYWORD_DEFINE(KW_NAME) \ - struct KW_NAME##_rule { \ - static constexpr auto keyword = LEXY_KEYWORD(#KW_NAME, lexy::dsl::inline_>); \ - static constexpr auto rule = keyword >> lexy::dsl::equal_sign; \ - static constexpr auto value = lexy::noop; \ - }; \ - static constexpr auto KW_NAME##_p = lexy::dsl::p - -// Produces _rule and _p and _rule::flag and _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_>); \ - static constexpr auto rule = keyword >> lexy::dsl::equal_sign; \ - static constexpr auto value = lexy::noop; \ - static constexpr auto flag = lexy::dsl::context_flag; \ - 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 >> (lexy::dsl::must(KW_NAME##_rule::flag.is_reset()).error + KW_NAME##_rule::flag.set()) - ////////////////// - // Macros - ////////////////// struct DecisionStatement { - template - struct _StringStatement { - static constexpr auto rule = Production >> (lexy::dsl::p> | lexy::dsl::p>); - static constexpr auto value = lexy::forward; - }; - template - static constexpr auto StringStatement = lexy::dsl::p<_StringStatement>; + using potential = fkeyword_rule<"potential", lexy::dsl::p>; + using allow = fkeyword_rule<"allow", lexy::dsl::p>; + using effect = fkeyword_rule<"effect", lexy::dsl::p>; + using ai_will_do = fkeyword_rule<"ai_will_do", lexy::dsl::p>; - 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; - 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>); - constexpr auto potential_statement = potential_p >> lexy::dsl::p; - constexpr auto allow_statement = allow_p >> lexy::dsl::p; - constexpr auto effect_statement = effect_p >> lexy::dsl::p; - constexpr auto ai_will_do_statement = ai_will_do_p >> lexy::dsl::p; + static constexpr auto value = lexy::as_list >> construct_list; + }; - return lexy::dsl::p> >> - (create_flags + lexy::dsl::equal_sign + - lexy::dsl::curly_bracketed.list( - potential_statement | - allow_statement | - effect_statement | - ai_will_do_statement | - lexy::dsl::p>)); - }(); + static constexpr auto rule = + dsl::p> >> + (helper::flags + lexy::dsl::equal_sign + lexy::dsl::p); - static constexpr auto value = - lexy::as_list> >> - lexy::callback( - [](auto&& name, auto&& list) { - return ast::make_node_ptr(LEXY_MOV(name), LEXY_MOV(list)); - }, - [](auto&& name, lexy::nullopt = {}) { - return ast::make_node_ptr(LEXY_MOV(name)); - }); + static constexpr auto value = construct; }; struct DecisionList { static constexpr auto rule = - LEXY_KEYWORD("political_decisions", lexy::dsl::inline_>) >> - (lexy::dsl::equal_sign + lexy::dsl::curly_bracketed.opt_list(lexy::dsl::p)); + ovdl::dsl::keyword<"political_decisions">(lexy::dsl::inline_>) >> + (lexy::dsl::equal_sign >> lexy::dsl::curly_bracketed.opt_list(lexy::dsl::p)); - static constexpr auto value = - lexy::as_list> >> - lexy::callback( - [](auto&& list) { - return ast::make_node_ptr(LEXY_MOV(list)); - }, - [](lexy::nullopt = {}) { - return lexy::nullopt {}; - }); + static constexpr auto value = lexy::as_list; }; 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 | // - lexy::dsl::p>); + lexy::dsl::position + lexy::dsl::terminator(lexy::dsl::eof).opt_list(lexy::dsl::p); - static constexpr auto value = lexy::as_list> >> lexy::new_; + static constexpr auto value = lexy::concat >> construct; }; - -#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 #include "SimpleGrammar.hpp" +#include "detail/dsl.hpp" namespace ovdl::v2script::grammar { struct EffectStatement { - static constexpr auto rule = lexy::dsl::inline_>; + static constexpr auto rule = lexy::dsl::p>; - static constexpr auto value = lexy::callback( - [](auto name, auto&& initalizer) { - return ast::make_node_ptr(ast::ExecutionNode::Type::Effect, LEXY_MOV(name), LEXY_MOV(initalizer)); - }); + static constexpr auto value = lexy::forward; }; struct EffectList { - static constexpr auto rule = lexy::dsl::list(lexy::dsl::p>); - - static constexpr auto value = - lexy::as_list> >> - lexy::callback( - [](auto&& list) { - return ast::make_node_ptr(ast::ExecutionNode::Type::Effect, LEXY_MOV(list)); - }); + static constexpr auto rule = lexy::dsl::list(lexy::dsl::p); + + static constexpr auto value = lexy::as_list; }; struct EffectBlock { - static constexpr auto rule = lexy::dsl::curly_bracketed.opt(lexy::dsl::p); - - static constexpr auto value = lexy::callback( - [](auto&& list) { - return LEXY_MOV(list); - }, - [](lexy::nullopt = {}) { - return lexy::nullopt {}; - }); + static constexpr auto rule = dsl::curly_bracketed.opt(lexy::dsl::p); + + static constexpr auto value = construct_list; }; } \ 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 -#include +#include +#include #include #include #include +#include + +#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 _rule and _p -#define OVDL_GRAMMAR_KEYWORD_DEFINE(KW_NAME) \ - struct KW_NAME##_rule { \ - static constexpr auto keyword = LEXY_KEYWORD(#KW_NAME, lexy::dsl::inline_>); \ - static constexpr auto rule = keyword >> lexy::dsl::equal_sign; \ - static constexpr auto value = lexy::noop; \ - }; \ - static constexpr auto KW_NAME##_p = lexy::dsl::p - -// Produces _rule and _p and _rule::flag and _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_>); \ - static constexpr auto rule = keyword >> lexy::dsl::equal_sign; \ - static constexpr auto value = lexy::noop; \ - static constexpr auto flag = lexy::dsl::context_flag; \ - 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 >> (lexy::dsl::must(KW_NAME##_rule::flag.is_reset()).error + KW_NAME##_rule::flag.set()) - ////////////////// - // Macros - ////////////////// - static constexpr auto event_symbols = lexy::symbol_table // - .map(ast::EventNode::Type::Country) - .map(ast::EventNode::Type::Province); + static constexpr auto event_symbols = lexy::symbol_table // + .map(false) + .map(true); struct EventMtthStatement { - OVDL_GRAMMAR_KEYWORD_DEFINE(months); - struct MonthValue { - static constexpr auto rule = lexy::dsl::inline_>; - static constexpr auto value = lexy::as_string | lexy::new_; + static constexpr auto rule = lexy::dsl::p>; + static constexpr auto value = dsl::callback( + [](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) | - lexy::dsl::p); + using months = keyword_rule<"months", lexy::dsl::p>; - static constexpr auto value = - lexy::as_list> >> - lexy::callback( - [](auto&& list) { - return ast::make_node_ptr(LEXY_MOV(list)); - }); - }; + static constexpr auto rule = dsl::curly_bracketed(lexy::dsl::p | lexy::dsl::p); - template - struct _StringStatement { - static constexpr auto rule = Production >> (lexy::dsl::p> | lexy::dsl::p>); - static constexpr auto value = - lexy::callback( - [](auto&& value) { - auto result = ast::make_node_ptr(std::move(static_cast(value)->_name)); - delete value; - return result; - }); + static constexpr auto value = lexy::as_list >> construct_list; }; - template - static constexpr auto StringStatement = lexy::dsl::p<_StringStatement>; + + static constexpr auto str_or_id = lexy::dsl::p::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>; static constexpr auto rule = [] { - constexpr auto create_flags = name_rule::flag.create() + ai_chance_rule::flag.create(); - - constexpr auto name_statement = StringStatement; - constexpr auto ai_chance_statement = ai_chance_p >> lexy::dsl::curly_bracketed(lexy::dsl::p); + using helper = dsl::rule_helper; - return create_flags + lexy::dsl::list(name_statement | ai_chance_statement | lexy::dsl::p); + return dsl::curly_bracketed(helper::flags + lexy::dsl::list(helper::p | lexy::dsl::p)); }(); - static constexpr auto value = - lexy::as_list> >> - lexy::callback( - [](auto&& list) { - return ast::make_node_ptr(LEXY_MOV(list)); - }); + static constexpr auto value = lexy::as_list >> construct_list; }; 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>; + using mean_time_to_happen = fkeyword_rule<"mean_time_to_happen", lexy::dsl::p>; + using trigger = keyword_rule<"trigger", lexy::dsl::p>; + using option = keyword_rule<"option", lexy::dsl::p>; + + struct EventList { + static constexpr auto rule = [] { + using helper = dsl::rule_helper; + + return helper::flags + + dsl::curly_bracketed.opt_list( + helper::p | lexy::dsl::p | lexy::dsl::p