diff options
author | Spartan322 <Megacake1234@gmail.com> | 2023-11-28 11:09:26 +0100 |
---|---|---|
committer | Spartan322 <Megacake1234@gmail.com> | 2024-05-09 22:11:26 +0200 |
commit | 757114a3c5b748567b42f273c7b78ca039ae983c (patch) | |
tree | e07390b682052129c91f4b157068bcdd84ceecb4 | |
parent | 7211a228e68c8a6b1ad1c1c5ec68c8d720b6d2ba (diff) |
Add `deps/dryad` -> https://github.com/Spartan322/dryadadd/dryad
Add `deps/fmt` -> https://github.com/fmtlib/fmt
Add `deps/range-v3` -> https://github.com/ericniebler/range-v3
Improve parser error and warning support
Update .clang-format
Update `deps/SCsub`
60 files changed, 2892 insertions, 1607 deletions
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: ^<lexy/ Priority: 4 - - Regex: ^"openvic-dataloader/ + - Regex: ^<dryad/ Priority: 5 - - Regex: .* + - Regex: ^<fmt/ Priority: 6 + - Regex: ^"openvic-dataloader/ + Priority: 7 + - Regex: .* + Priority: 8 diff --git a/.gitmodules b/.gitmodules index 0a1353b..0b7febe 100644 --- a/.gitmodules +++ b/.gitmodules @@ -5,3 +5,12 @@ [submodule "scripts"] path = scripts url = https://github.com/OpenVicProject/scripts +[submodule "deps/dryad"] + path = deps/dryad + url = https://github.com/Spartan322/dryad +[submodule "deps/fmt"] + path = deps/fmt + url = https://github.com/fmtlib/fmt +[submodule "deps/range-v3"] + path = deps/range-v3 + url = https://github.com/ericniebler/range-v3 @@ -17,7 +17,7 @@ def build_lexy(env): lexy_env.Append(CXXFLAGS=["-std=c++20"]) lexy_env.Append(CXXFLAGS=["-pedantic-errors", "-Werror", "-Wall", "-Wextra", "-Wconversion", "-Wsign-conversion"]) - if lexy_env.get("use_llvm"): + if lexy_env["CXX"] == "clang++": lexy_env.Append(CXXFLAGS=["-Wno-shift-op-parentheses", "-Wno-parentheses-equality"]) else: lexy_env.Append(CXXFLAGS=[ @@ -41,5 +41,79 @@ def build_lexy(env): env.Append(LIBPATH=[lexy_env.Dir("lexy/src")]) env.Prepend(LIBS=[library_name]) +def build_dryad(env): + paths = ["dryad/include"] + sources = env.GlobRecursive("*.cpp", paths) + env.dryad_sources = sources + + env.Append(CPPPATH=[env.Dir("dryad/include")]) + if env.get("is_msvc", False): + env.Append(CXXFLAGS=["/external:I", env.Dir("dryad/include"), "/external:W0"]) + else: + env.Append(CXXFLAGS=["-isystem", env.Dir("dryad/include")]) + +def build_fmt(env): + fmt_env = env.Clone() + + # Require C++20 + if fmt_env.get("is_msvc", False): + fmt_env.Append(CXXFLAGS=["/std:c++20"]) + + fmt_env.Append(CXXFLAGS=["/WX", "/W3", "/D", "_CRT_SECURE_NO_WARNINGS"]) + else: + fmt_env.Append(CXXFLAGS=["-std=c++20"]) + + fmt_env.Append(CXXFLAGS=[ + "-Werror", "-Wall", "-Wextra", "-pedantic", "-Wconversion", "-Wundef" + ]) + if fmt_env["CXX"] == "clang++": + fmt_env.Append(CXXFLAGS=[ + "-Wweak-vtables", "-Wshadow", + "-Wno-gnu-zero-variadic-macro-arguments" + ]) + else: + fmt_env.Append(CXXFLAGS=[ + "-Wsign-conversion", "-Wold-style-cast", + "-Wundef", "-Wredundant-decls", "-Wwrite-strings", + "-Wpointer-arith", "-Wcast-qual", "-Wformat=2", + "-Wmissing-include-dirs", "-Wcast-align", "-Wctor-dtor-privacy", + "-Wdisabled-optimization", "-Winvalid-pch", "-Woverloaded-virtual", + "-Wconversion", "-Wundef", "-Wno-ctor-dtor-privacy", "-Wno-format-nonliteral", + "-Wno-dangling-else", "-Wno-unused-local-typedefs", "-Wdouble-promotion", + "-Wtrampolines", "-Wzero-as-null-pointer-constant", "-Wuseless-cast", + "-Wvector-operation-performance", "-Wsized-deallocation", "-Wshadow", + "-Wshift-overflow=2", "-Wnull-dereference", "-Wduplicated-cond" + ]) + + paths = ["fmt/include", "fmt/src"] + fmt_env.Append(CPPPATH=[[fmt_env.Dir(p) for p in paths]]) + sources = env.GlobRecursive("*.cc", paths, "fmt.cc") + env.lexy_sources = sources + library_name = "libfmt" + env["LIBSUFFIX"] + library = fmt_env.StaticLibrary(target="fmt/src/" + library_name, source=sources) + Default(library) + + env.Append(CPPPATH=[fmt_env.Dir("fmt/include")]) + if env.get("is_msvc", False): + env.Append(CXXFLAGS=["/external:I", fmt_env.Dir("fmt/include"), "/external:W0"]) + else: + env.Append(CXXFLAGS=["-isystem", fmt_env.Dir("fmt/include")]) + env.Append(CXXFLAGS=[""]) + env.Append(LIBPATH=[fmt_env.Dir("fmt/src")]) + env.Prepend(LIBS=[library_name]) + +def build_range_v3(env): + paths = ["range-v3/include"] + sources = env.GlobRecursive("*.cpp", paths) + env.range_v3_sources = sources + + env.Append(CPPPATH=[env.Dir("range-v3/include")]) + if env.get("is_msvc", False): + env.Append(CXXFLAGS=["/external:I", env.Dir("range-v3/include"), "/external:W0"]) + else: + env.Append(CXXFLAGS=["-isystem", env.Dir("range-v3/include")]) -build_lexy(env)
\ No newline at end of file +build_dryad(env) +build_fmt(env) +build_lexy(env) +build_range_v3(env)
\ No newline at end of file diff --git a/deps/dryad b/deps/dryad new file mode 160000 +Subproject 3aa3d7606cb007436bb3433ddf83b8bdcf1ecc4 diff --git a/deps/fmt b/deps/fmt new file mode 160000 +Subproject f5e54359df4c26b6230fc61d38aa29458139308 diff --git a/deps/range-v3 b/deps/range-v3 new file mode 160000 +Subproject 97452bb3eb74a73fc86504421a6a27c92bce6b9 diff --git a/include/openvic-dataloader/AbstractSyntaxTree.hpp b/include/openvic-dataloader/AbstractSyntaxTree.hpp new file mode 100644 index 0000000..db81eeb --- /dev/null +++ b/include/openvic-dataloader/AbstractSyntaxTree.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include <concepts> +#include <cstdio> +#include <string_view> +#include <utility> + +#include <openvic-dataloader/File.hpp> +#include <openvic-dataloader/NodeLocation.hpp> +#include <openvic-dataloader/detail/utility/Utility.hpp> + +#include <dryad/node.hpp> +#include <dryad/node_map.hpp> +#include <dryad/symbol.hpp> +#include <dryad/tree.hpp> + +#include <fmt/core.h> + +namespace ovdl { + struct AbstractSyntaxTree { + struct SymbolId; + using index_type = std::uint32_t; + using symbol_type = dryad::symbol<SymbolId, index_type>; + using symbol_interner_type = dryad::symbol_interner<SymbolId, char, index_type>; + + 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<typename T> + concept IsAst = + std::derived_from<T, AbstractSyntaxTree> && + requires( + T t, + const T ct, + const typename T::node_type* node, + NodeLocation loc // + ) { + requires IsFile<typename T::file_type>; + typename T::root_node_type; + typename T::node_type; + requires std::derived_from<typename T::root_node_type, typename T::node_type>; + { t.set_location(node, loc) } -> std::same_as<void>; + { t.location_of(node) } -> std::same_as<NodeLocation>; + { t.root() } -> std::same_as<typename T::root_node_type*>; + { ct.root() } -> std::same_as<const typename T::root_node_type*>; + { t.file() } -> std::same_as<typename T::file_type&>; + { ct.file() } -> std::same_as<const typename T::file_type&>; + }; + + template<IsFile FileT, std::derived_from<typename FileT::node_type> 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<typename file_type::encoding_type, void>&& 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<typename T, typename... Args> + T* create(NodeLocation loc, Args&&... args) { + auto node = _tree.template create<T>(DRYAD_FWD(args)...); + set_location(node, loc); + return node; + } + + template<typename T, typename... Args> + T* create(const char* begin, const char* end, Args&&... args) { + return create<T>(NodeLocation::make_from(begin, end), DRYAD_FWD(args)...); + } + + void set_root(root_node_type* node) { + _tree.set_root(node); + } + + protected: + dryad::tree<root_node_type> _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 <concepts> +#include <cstdarg> +#include <cstdio> +#include <ostream> +#include <string> +#include <utility> + +#include <openvic-dataloader/AbstractSyntaxTree.hpp> +#include <openvic-dataloader/Error.hpp> +#include <openvic-dataloader/File.hpp> +#include <openvic-dataloader/NodeLocation.hpp> +#include <openvic-dataloader/detail/LexyReportError.hpp> +#include <openvic-dataloader/detail/OStreamOutputIterator.hpp> + +#include <lexy/input/base.hpp> +#include <lexy/input/buffer.hpp> +#include <lexy/visualize.hpp> + +#include <dryad/_detail/config.hpp> +#include <dryad/abstract_node.hpp> +#include <dryad/arena.hpp> +#include <dryad/node.hpp> +#include <dryad/tree.hpp> + +#include <fmt/core.h> + +#include "openvic-dataloader/detail/CallbackOStream.hpp" +#include "openvic-dataloader/detail/utility/ErrorRange.hpp" +#include "openvic-dataloader/detail/utility/Utility.hpp" + +#include <lexy_ext/report_error.hpp> + +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<std::derived_from<DiagnosticLogger> Logger> + struct ErrorCallback { + ErrorCallback(Logger& logger) : _logger(&logger) {} + + struct sink_t { + using return_type = std::size_t; + + template<typename Input, typename Tag> + void operator()(lexy::error_context<Input> const& context, lexy::error_for<Input, Tag> const& error) { + using Reader = lexy::input_reader<Input>; + error::Error* result; + + if constexpr (std::is_same_v<Tag, lexy::expected_literal>) { + auto string = lexy::_detail::make_literal_lexeme<typename Reader::encoding>(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<error::ExpectedLiteral>(loc, message, context.production()); + } else if constexpr (std::is_same_v<Tag, lexy::expected_keyword>) { + auto string = lexy::_detail::make_literal_lexeme<typename Reader::encoding>(error.string(), error.length()); + NodeLocation loc = NodeLocation::make_from(string.begin(), string.end()); + auto message = _logger.intern_cstr(fmt::format("expected keyword '{}'", string.data())); + result = _logger.template create<error::ExpectedKeyword>(loc, message, context.production()); + } else if constexpr (std::is_same_v<Tag, lexy::expected_char_class>) { + auto message = _logger.intern_cstr(fmt::format("expected {}", error.name())); + result = _logger.template create<error::ExpectedCharClass>(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<error::GenericParseError>(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<typename T, typename... Args> + T* create(NodeLocation loc, Args&&... args) { + using node_creator = dryad::node_creator<decltype(DRYAD_DECLVAL(T).kind()), void>; + T* result = _tree.create<T>(DRYAD_FWD(args)...); + _map.insert(result, loc); + return result; + } + + template<typename T> + T* create() { + using node_creator = dryad::node_creator<decltype(DRYAD_DECLVAL(T).kind()), void>; + T* result = _tree.create<T>(); + return result; + } + + protected: + bool _errored = false; + bool _warned = false; + dryad::node_map<const error::Error, NodeLocation> _map; + dryad::tree<error::Root> _tree; + + struct SymbolId; + using index_type = std::uint32_t; + using symbol_type = dryad::symbol<SymbolId, index_type>; + using symbol_interner_type = dryad::symbol_interner<SymbolId, char, index_type>; + 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<IsFile FileT> + struct BasicDiagnosticLogger : DiagnosticLogger { + using file_type = FileT; + + template<typename... Args> + using format_str = fmt::basic_format_string<char, fmt::type_identity_t<Args>...>; + + explicit BasicDiagnosticLogger(const file_type& file) + : _file(&file) { + _tree.set_root(_tree.create<error::Root>()); + } + + struct Writer; + + template<typename... Args> + Writer error(format_str<Args...> fmt, Args&&... args) { + return log(DiagnosticKind::error, fmt, std::forward<Args>(args)...); + } + + template<typename... Args> + Writer warning(format_str<Args...> fmt, Args&&... args) { + return log(DiagnosticKind::warning, fmt, std::forward<Args>(args)...); + } + + template<typename... Args> + Writer note(format_str<Args...> fmt, Args&&... args) { + return log(DiagnosticKind::note, fmt, std::forward<Args>(args)...); + } + + template<typename... Args> + Writer info(format_str<Args...> fmt, Args&&... args) { + return log(DiagnosticKind::info, fmt, std::forward<Args>(args)...); + } + + template<typename... Args> + Writer debug(format_str<Args...> fmt, Args&&... args) { + return log(DiagnosticKind::debug, fmt, std::forward<Args>(args)...); + } + + template<typename... Args> + Writer fixit(format_str<Args...> fmt, Args&&... args) { + return log(DiagnosticKind::fixit, fmt, std::forward<Args>(args)...); + } + + template<typename... Args> + Writer help(format_str<Args...> fmt, Args&&... args) { + return log(DiagnosticKind::help, fmt, std::forward<Args>(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<typename CharT> + static void _write_to_buffer(const CharT* s, std::streamsize n, void* output_str) { + auto* output = reinterpret_cast<std::basic_string<CharT>*>(output_str); + output->append(s, n); + } + + template<typename CharT> + auto make_callback_stream(std::basic_string<CharT>& output) { + return detail::make_callback_stream<CharT>(&_write_to_buffer<CharT>, reinterpret_cast<void*>(&output)); + } + + template<typename CharT> + detail::OStreamOutputIterator make_ostream_iterator(std::basic_ostream<CharT>& stream) { + return detail::OStreamOutputIterator { stream }; + } + + struct Writer { + template<typename... Args> + [[nodiscard]] Writer& primary(NodeLocation loc, format_str<Args...> fmt, Args&&... args) { + return annotation(AnnotationKind::primary, loc, fmt, std::forward<Args>(args)...); + } + + template<typename... Args> + [[nodiscard]] Writer& secondary(NodeLocation loc, format_str<Args...> fmt, Args&&... args) { + return annotation(AnnotationKind::secondary, loc, fmt, std::forward<Args>(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<typename... Args> + [[nodiscard]] Writer& annotation(AnnotationKind kind, NodeLocation loc, format_str<Args...> fmt, Args&&... args) { + auto begin_loc = lexy::get_input_location(_file->buffer(), loc.begin()); + + std::basic_string<typename decltype(fmt.get())::value_type> 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>(args)...).c_str()); + }); + + error::Annotation* annotation; + auto message = _logger.intern_cstr(output); + switch (kind) { + case AnnotationKind::primary: + _logger.create<error::PrimaryAnnotation>(loc, message); + break; + case AnnotationKind::secondary: + _logger.create<error::SecondaryAnnotation>(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<lexy::buffer<typename file_type::encoding_type>> _impl; + BasicDiagnosticLogger& _logger; + error::Semantic* _semantic; + + friend BasicDiagnosticLogger; + }; + + using diagnostic_writer = lexy_ext::diagnostic_writer<lexy::buffer<typename file_type::encoding_type>>; + + template<std::derived_from<error::Error> T, typename... Args> + void log_with_impl(diagnostic_writer& impl, T* error, DiagnosticKind kind, format_str<Args...> fmt, Args&&... args) { + std::basic_string<typename decltype(fmt.get())::value_type> 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>(args)...).c_str()); + }); + impl.write_path(iter, _file->path()); + + auto message = intern_cstr(output); + error->_set_message(message); + insert(error); + } + + template<std::derived_from<error::Error> T, typename... Args> + void log_with_error(T* error, DiagnosticKind kind, format_str<Args...> fmt, Args&&... args) { + auto impl = diagnostic_writer { _file->buffer() }; + log_with_impl(impl, error, kind, fmt, std::forward<Args>(args)...); + } + + template<std::derived_from<error::Error> T, typename... Args> + void create_log(DiagnosticKind kind, format_str<Args...> fmt, Args&&... args) { + log_with_error(create<T>(), kind, fmt, std::forward<Args>(args)...); + } + + template<typename... Args> + Writer log(DiagnosticKind kind, format_str<Args...> fmt, Args&&... args) { + error::Semantic* semantic; + + switch (kind) { + case DiagnosticKind::error: + semantic = create<error::SemanticError>(); + break; + case DiagnosticKind::warning: + semantic = create<error::SemanticWarning>(); + break; + case DiagnosticKind::info: + semantic = create<error::SemanticInfo>(); + break; + case DiagnosticKind::debug: + semantic = create<error::SemanticDebug>(); + break; + case DiagnosticKind::fixit: + semantic = create<error::SemanticFixit>(); + break; + case DiagnosticKind::help: + semantic = create<error::SemanticHelp>(); + break; + default: detail::unreachable(); + } + + Writer result(*this, _file, semantic); + + log_with_impl(result._impl, semantic, kind, fmt, std::forward<Args>(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 <cstdint> +#include <string_view> + +#include <openvic-dataloader/File.hpp> +#include <openvic-dataloader/detail/utility/Utility.hpp> + +#include <dryad/abstract_node.hpp> +#include <dryad/node.hpp> + +namespace ovdl { + template<IsFile> + 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<ErrorKind> { + const char* message() const { return _message; } + + protected: + DRYAD_ABSTRACT_NODE_CTOR(Error); + + void _set_message(const char* message) { _message = message; } + const char* _message; + + template<IsFile> + friend struct ovdl::BasicDiagnosticLogger; + }; + + using ErrorList = dryad::unlinked_node_list<Error>; + + struct Annotation; + using AnnotationList = dryad::unlinked_node_list<Annotation>; + + struct Root : dryad::basic_node<ErrorKind::Root, dryad::container_node<Error>> { + 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<ErrorKind::BufferError, Error> { + 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<Error, ErrorKind::FirstParseError, ErrorKind::LastParseError> { + 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<ErrorKind NodeKind> + struct _ParseError_t : dryad::basic_node<NodeKind, ParseError> { + explicit _ParseError_t(dryad::node_ctor ctor, const char* message, const char* production_name) + : dryad::basic_node<NodeKind, ParseError>(ctor, message, production_name) {} + }; + + using ExpectedLiteral = _ParseError_t<ErrorKind::ExpectedLiteral>; + using ExpectedKeyword = _ParseError_t<ErrorKind::ExpectedKeyword>; + using ExpectedCharClass = _ParseError_t<ErrorKind::ExpectedCharClass>; + using GenericParseError = _ParseError_t<ErrorKind::GenericParseError>; + + struct Semantic : dryad::abstract_node_range<dryad::container_node<Error>, 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<ErrorKind NodeKind> + struct _SemanticError_t : dryad::basic_node<NodeKind, Semantic> { + using base_node = dryad::basic_node<NodeKind, Semantic>; + + 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<ErrorKind::SemanticError>; + using SemanticWarning = _SemanticError_t<ErrorKind::SemanticWarning>; + using SemanticInfo = _SemanticError_t<ErrorKind::SemanticInfo>; + using SemanticDebug = _SemanticError_t<ErrorKind::SemanticDebug>; + using SemanticFixit = _SemanticError_t<ErrorKind::SemanticFixit>; + using SemanticHelp = _SemanticError_t<ErrorKind::SemanticHelp>; + + struct Annotation : dryad::abstract_node_range<Error, ErrorKind::FirstAnnotation, ErrorKind::LastAnnotation> { + protected: + explicit Annotation(dryad::node_ctor ctor, ErrorKind kind, const char* message) : node_base(ctor, kind) { + _set_message(message); + } + }; + + template<ErrorKind NodeKind> + struct _Annotation_t : dryad::basic_node<NodeKind, Annotation> { + explicit _Annotation_t(dryad::node_ctor ctor, const char* message) + : dryad::basic_node<NodeKind, Annotation>(ctor, message) {} + }; + + using PrimaryAnnotation = _Annotation_t<ErrorKind::PrimaryAnnotation>; + using SecondaryAnnotation = _Annotation_t<ErrorKind::SecondaryAnnotation>; + + 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 <concepts> + +#include <openvic-dataloader/NodeLocation.hpp> +#include <openvic-dataloader/detail/LexyFwdDeclaration.hpp> + +#include <dryad/node_map.hpp> + +namespace ovdl { + template<typename T> + concept IsEncoding = requires(T t) { + typename T::char_type; + typename T::int_type; + { T::template is_secondary_char_type<typename T::char_type>() } -> std::same_as<bool>; + { T::eof() } -> std::same_as<typename T::int_type>; + { T::to_int_type(typename T::char_type {}) } -> std::same_as<typename T::int_type>; + }; + + struct File { + explicit File(const char* path); + + const char* path() const noexcept; + + protected: + const char* _path; + }; + + template<typename T> + concept IsFile = + std::derived_from<T, File> && IsEncoding<typename T::encoding_type> && + requires(T t, const typename T::node_type* node, NodeLocation location) { + { t.buffer() } -> std::same_as<const lexy::buffer<typename T::encoding_type, void>&>; + { t.set_location(node, location) } -> std::same_as<void>; + { t.location_of(node) } -> std::same_as<NodeLocation>; + }; + + template<typename EncodingT, typename NodeT> + struct BasicFile : File { + using encoding_type = EncodingT; + using node_type = NodeT; + + explicit BasicFile(const char* path, lexy::buffer<encoding_type, void>&& buffer) + : File(path), + _buffer(static_cast<std ::remove_reference_t<decltype(buffer)>&&>(buffer)) {} + + explicit BasicFile(lexy::buffer<encoding_type, void>&& buffer) + : File(""), + _buffer(static_cast<std ::remove_reference_t<decltype(buffer)>&&>(buffer)) {} + + const lexy::buffer<encoding_type, void>& 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<encoding_type, void> _buffer; + dryad::node_map<const node_type, NodeLocation> _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 <cstdint> + +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 <concepts> + +#include <openvic-dataloader/AbstractSyntaxTree.hpp> +#include <openvic-dataloader/DiagnosticLogger.hpp> + +#include <dryad/tree.hpp> + +namespace ovdl { + template<typename T> + concept IsParseState = requires( + T t, + const T ct, + typename T::ast_type::file_type&& file, + lexy::buffer<typename T::ast_type::file_type::encoding_type>&& buffer, + const char* path // + ) { + requires IsAst<typename T::ast_type>; + requires std::derived_from<typename T::diagnostic_logger_type, DiagnosticLogger>; + { T { std::move(file) } } -> std::same_as<T>; + { T { std::move(buffer) } } -> std::same_as<T>; + { T { path, std::move(buffer) } } -> std::same_as<T>; + { t.ast() } -> std::same_as<typename T::ast_type&>; + { ct.ast() } -> std::same_as<const typename T::ast_type&>; + { t.logger() } -> std::same_as<typename T::diagnostic_logger_type&>; + { ct.logger() } -> std::same_as<const typename T::diagnostic_logger_type&>; + }; + + template<IsAst AstT> + struct ParseState { + using ast_type = AstT; + using diagnostic_logger_type = BasicDiagnosticLogger<typename ast_type::file_type>; + + ParseState(typename ast_type::file_type&& file) + : _ast { std::move(file) }, + _logger { _ast.file() } {} + + ParseState(lexy::buffer<typename ast_type::file_type::encoding_type>&& buffer) + : ParseState(typename ast_type::file_type { std::move(buffer) }) {} + + ParseState(const char* path, lexy::buffer<typename ast_type::file_type::encoding_type>&& 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<typename T> + concept IsFileParseState = requires( + T t, + const T ct, + typename T::file_type&& file, + lexy::buffer<typename T::file_type::encoding_type>&& buffer, + const char* path // + ) { + requires IsFile<typename T::file_type>; + requires std::derived_from<typename T::diagnostic_logger_type, DiagnosticLogger>; + { T { std::move(file) } } -> std::same_as<T>; + { T { std::move(buffer) } } -> std::same_as<T>; + { T { path, std::move(buffer) } } -> std::same_as<T>; + { t.file() } -> std::same_as<typename T::file_type&>; + { ct.file() } -> std::same_as<const typename T::file_type&>; + { t.logger() } -> std::same_as<typename T::diagnostic_logger_type&>; + { ct.logger() } -> std::same_as<const typename T::diagnostic_logger_type&>; + }; + + template<IsFile FileT> + struct FileParseState { + using file_type = FileT; + using diagnostic_logger_type = BasicDiagnosticLogger<file_type>; + + FileParseState(file_type&& file) + : _file { std::move(file) }, + _logger { file } {} + + FileParseState(lexy::buffer<typename file_type::encoding_type>&& buffer) + : FileParseState(file_type { std::move(buffer) }) {} + + FileParseState(const char* path, lexy::buffer<typename file_type::encoding_type>&& 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/detail/BasicParser.hpp b/include/openvic-dataloader/Parser.hpp index 7524bb5..c1f266b 100644 --- a/include/openvic-dataloader/detail/BasicParser.hpp +++ b/include/openvic-dataloader/Parser.hpp @@ -2,15 +2,12 @@ #include <string> #include <string_view> -#include <vector> #include <openvic-dataloader/ParseError.hpp> #include <openvic-dataloader/ParseWarning.hpp> -#include <openvic-dataloader/detail/Concepts.hpp> namespace ovdl::detail { - class BasicParser { - public: + struct BasicParser { BasicParser(); void set_error_log_to_null(); @@ -22,16 +19,13 @@ namespace ovdl::detail { bool has_fatal_error() const; bool has_warning() const; - const std::vector<ParseError>& get_errors() const; - const std::vector<ParseWarning>& get_warnings() const; std::string_view get_file_path() const; protected: - std::vector<ParseError> _errors; - std::vector<ParseWarning> _warnings; - std::reference_wrapper<std::ostream> _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 <utility> #include <vector> -#include <openvic-dataloader/detail/VectorConstexpr.hpp> +#include <openvic-dataloader/detail/utility/Constexpr.hpp> 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 <filesystem> +#include <openvic-dataloader/Error.hpp> +#include <openvic-dataloader/Parser.hpp> #include <openvic-dataloader/csv/LineObject.hpp> -#include <openvic-dataloader/detail/BasicParser.hpp> +#include <openvic-dataloader/detail/utility/Concepts.hpp> +#include <openvic-dataloader/detail/utility/ErrorRange.hpp> + +#include <dryad/node.hpp> 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<char>& 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<csv::LineObject>& 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<char>& stream) const; + Parser(Parser&&); Parser& operator=(Parser&&); ~Parser(); private: - class BufferHandler; - std::unique_ptr<BufferHandler> _buffer_handler; + class ParseHandler; + std::unique_ptr<ParseHandler> _parse_handler; std::vector<csv::LineObject> _lines; template<typename... Args> - constexpr void _run_load_func(detail::LoadCallback<BufferHandler, Args...> auto func, Args... args); + constexpr void _run_load_func(detail::LoadCallback<ParseHandler, Args...> auto func, Args... args); }; using Windows1252Parser = Parser<EncodingType::Windows1252>; 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 <type_traits> namespace ovdl::detail { - template<typename Callback, class CHAR_T, class traits = std::char_traits<CHAR_T>> - class BasicCallbackStreamBuffer : public std::basic_streambuf<CHAR_T, traits> { + template<typename Callback, class CharT, class traits = std::char_traits<CharT>> + class BasicCallbackStreamBuffer : public std::basic_streambuf<CharT, traits> { public: - using base_type = std::basic_streambuf<CHAR_T, traits>; + using base_type = std::basic_streambuf<CharT, traits>; 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<char_type>(ch); if constexpr (std::is_same_v<void, typename decltype(std::function { _callback })::result_type>) { - _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<typename Callback, class CHAR_T, class traits = std::char_traits<CHAR_T>> - class BasicCallbackStream : public std::basic_ostream<CHAR_T, traits> { + template<class CharT, typename Callback, class traits = std::char_traits<CharT>> + class BasicCallbackStream : public std::basic_ostream<CharT, traits> { public: - using base_type = std::basic_ostream<CHAR_T, traits>; + using base_type = std::basic_ostream<CharT, traits>; BasicCallbackStream(Callback cb, void* user_data = nullptr) : m_sbuf(cb, user_data), - std::basic_ios<CHAR_T, traits>(&m_sbuf), - std::basic_ostream<CHAR_T, traits>(&m_sbuf) { - std::basic_ios<CHAR_T, traits>::init(&m_sbuf); + std::basic_ios<CharT, traits>(&m_sbuf), + std::basic_ostream<CharT, traits>(&m_sbuf) { + std::basic_ios<CharT, traits>::init(&m_sbuf); } private: - BasicCallbackStreamBuffer<Callback, CHAR_T, traits> m_sbuf; + BasicCallbackStreamBuffer<Callback, CharT, traits> m_sbuf; }; + template<typename CharT> + auto make_callback_stream(auto&& cb, void* user_data = nullptr) { + using Callback = std::decay_t<decltype(cb)>; + return BasicCallbackStream<CharT, Callback> { std::forward<Callback>(cb), user_data }; + } + template<typename Callback> class CallbackStream : public BasicCallbackStream<Callback, char> { 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 <concepts> -#include <optional> -#include <utility> - -#include <openvic-dataloader/ParseError.hpp> - -namespace ovdl::detail { - template<typename T, typename Self, typename... Args> - concept LoadCallback = - requires(T t, Self* self, Args... args) { - { t(self, std::forward<Args>(args)...) } -> std::same_as<std::optional<ParseError>>; - }; - - template<typename T> - concept Has_c_str = - requires(T t) { - { t.c_str() } -> std::same_as<const char*>; - }; -}
\ 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<typename Encoding, typename MemoryResource> + struct buffer; +}
\ No newline at end of file diff --git a/src/openvic-dataloader/detail/LexyReportError.hpp b/include/openvic-dataloader/detail/LexyReportError.hpp index 213090b..3c32bd1 100644 --- a/src/openvic-dataloader/detail/LexyReportError.hpp +++ b/include/openvic-dataloader/detail/LexyReportError.hpp @@ -7,11 +7,12 @@ #include <openvic-dataloader/ParseData.hpp> #include <openvic-dataloader/ParseError.hpp> -#include <openvic-dataloader/detail/Concepts.hpp> #include <lexy/input_location.hpp> #include <lexy/visualize.hpp> +#include "openvic-dataloader/detail/utility/Concepts.hpp" + #include <lexy_ext/report_error.hpp> namespace ovdl::detail { @@ -86,7 +87,7 @@ namespace ovdl::detail { return { _iter, _opts, path }; } - constexpr _ReportError path(const detail::Has_c_str auto& path_object) const { + constexpr _ReportError path(const detail::HasCstr auto& path_object) const { return path(path_object.c_str()); } diff --git a/src/openvic-dataloader/detail/OStreamOutputIterator.hpp b/include/openvic-dataloader/detail/OStreamOutputIterator.hpp index 81f6c89..8f120c7 100644 --- a/src/openvic-dataloader/detail/OStreamOutputIterator.hpp +++ b/include/openvic-dataloader/detail/OStreamOutputIterator.hpp @@ -1,5 +1,6 @@ #pragma once +#include <memory> #include <ostream> namespace ovdl::detail { 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/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 <concepts> +#include <cstdint> +#include <functional> +#include <utility> + +namespace ovdl { + struct NodeLocation; + struct File; + namespace detail { + enum class buffer_error : std::uint8_t; + } +} + +namespace ovdl::detail { + template<typename T, typename... Ts> + concept any_of = (std::same_as<T, Ts> || ...); + + template<typename T> + concept HasCstr = + requires(T t) { + { t.c_str() } -> std::same_as<const char*>; + }; + + template<typename T> + concept HasPath = requires(T& t) { + { t.path() } -> std::same_as<const char*>; + }; + + template<typename T, typename Self, typename... Args> + concept LoadCallback = + requires(T&& t, Self&& self, Args&&... args) { + { std::invoke(std::forward<T>(t), std::forward<Self>(self), std::forward<Args>(args)...) } -> std::same_as<ovdl::detail::buffer_error>; + }; + + template<typename T> + concept IsEncoding = requires(T t) { + typename T::char_type; + typename T::int_type; + { T::template is_secondary_char_type<typename T::char_type>() } -> std::same_as<bool>; + { T::eof() } -> std::same_as<typename T::int_type>; + { T::to_int_type(typename T::char_type {}) } -> std::same_as<typename T::int_type>; + }; +}
\ No newline at end of file diff --git a/include/openvic-dataloader/detail/VectorConstexpr.hpp b/include/openvic-dataloader/detail/utility/Constexpr.hpp index 7e7fa34..49479c5 100644 --- a/include/openvic-dataloader/detail/VectorConstexpr.hpp +++ b/include/openvic-dataloader/detail/utility/Constexpr.hpp @@ -2,6 +2,12 @@ // 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 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 <utility> + +#include <openvic-dataloader/Error.hpp> + +#include <dryad/node.hpp> + +namespace ovdl::detail { + using error_range = decltype(std::declval<const error::Root*>()->errors()); +}
\ No newline at end of file diff --git a/include/openvic-dataloader/detail/PointerHash.hpp b/include/openvic-dataloader/detail/utility/PointerHash.hpp index c0d28bc..c0d28bc 100644 --- a/include/openvic-dataloader/detail/PointerHash.hpp +++ b/include/openvic-dataloader/detail/utility/PointerHash.hpp diff --git a/include/openvic-dataloader/detail/SelfType.hpp b/include/openvic-dataloader/detail/utility/SelfType.hpp index 5209700..5209700 100644 --- a/include/openvic-dataloader/detail/SelfType.hpp +++ b/include/openvic-dataloader/detail/utility/SelfType.hpp diff --git a/include/openvic-dataloader/detail/TypeName.hpp b/include/openvic-dataloader/detail/utility/TypeName.hpp index 1a34a0f..1a34a0f 100644 --- a/include/openvic-dataloader/detail/TypeName.hpp +++ b/include/openvic-dataloader/detail/utility/TypeName.hpp 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 <string_view> +#include <type_traits> + +#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<typename Kind> + constexpr std::string_view get_kind_name() { + constexpr auto name = type_name<Kind>(); + + return name; + } + + template<typename EnumT> + requires std::is_enum_v<EnumT> + constexpr std::underlying_type_t<EnumT> to_underlying(EnumT e) { + return static_cast<std::underlying_type_t<EnumT>>(e); + } + + template<typename EnumT> + requires std::is_enum_v<EnumT> + constexpr EnumT from_underlying(std::underlying_type_t<EnumT> ut) { + return static_cast<EnumT>(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 <cstddef> -#include <cstdint> -#include <iostream> -#include <memory> -#include <string> +#include <cstdio> #include <string_view> -#include <type_traits> -#include <utility> -#include <vector> -#include <openvic-dataloader/detail/OptionalConstexpr.hpp> -#include <openvic-dataloader/detail/SelfType.hpp> -#include <openvic-dataloader/detail/TypeName.hpp> +#include <openvic-dataloader/AbstractSyntaxTree.hpp> +#include <openvic-dataloader/File.hpp> +#include <openvic-dataloader/NodeLocation.hpp> +#include <openvic-dataloader/ParseState.hpp> +#include <openvic-dataloader/detail/LexyFwdDeclaration.hpp> -namespace lexy { - struct nullopt; -} +#include <dryad/_detail/assert.hpp> +#include <dryad/_detail/config.hpp> +#include <dryad/abstract_node.hpp> +#include <dryad/node.hpp> +#include <dryad/symbol.hpp> +#include <dryad/tree.hpp> -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<type>(); } \ - constexpr std::string_view get_type() const override { return ::ovdl::detail::type_name<std::decay_t<decltype(*this)>>(); } + 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, // { <StatementList> } + NullValue, -namespace ovdl::v2script::ast { + FirstValue = FirstFlatValue, + LastValue = NullValue, - struct Node; - using NodePtr = Node*; - using NodeCPtr = const Node*; - using NodeUPtr = std::unique_ptr<Node>; + // Statements // + EventStatement, // (country_event|province_event) = { id = <FlatValue> ... } + AssignStatement, // <IdentifierValue> = <Value> + ValueStatement, // <Value> - 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<NodeKind>; + using NodeList = dryad::unlinked_node_list<Node>; - 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<Statement>; - 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<EventStatement>; - static constexpr std::string_view get_type_static() { return detail::type_name<Node>(); } - constexpr virtual std::string_view get_type() const = 0; + struct AssignStatement; + using AssignStatementList = dryad::unlinked_node_list<AssignStatement>; - static constexpr std::string_view get_base_type_static() { return detail::type_name<Node>(); } - constexpr virtual std::string_view get_base_type() const { return get_base_type_static(); } + struct Value : dryad::abstract_node_range<Node, NodeKind::FirstValue, NodeKind::LastValue> { + DRYAD_ABSTRACT_NODE_CTOR(Value); + }; - template<typename T> - constexpr bool is_type() const { - return get_type().compare(detail::type_name<T>()) == 0; + struct FlatValue : dryad::abstract_node_range<Value, NodeKind::FirstFlatValue, NodeKind::LastFlatValue> { + AbstractSyntaxTree::symbol_type value() const { + return _value; } - template<typename T> - constexpr bool is_derived_from() const { - return is_type<T>() || get_base_type().compare(detail::type_name<T>()) == 0; + const char* value(const AbstractSyntaxTree::symbol_interner_type& symbols) const { + return _value.c_str(symbols); } - template<typename T> - constexpr T* cast_to() { - if (is_derived_from<T>() || is_type<Node>()) return (static_cast<T*>(this)); - return nullptr; - } + protected: + explicit FlatValue(dryad::node_ctor ctor, NodeKind kind, AbstractSyntaxTree::symbol_type value) + : node_base(ctor, kind), + _value(value) {} - template<typename T> - constexpr const T* const cast_to() const { - if (is_derived_from<T>() || is_type<Node>()) return (static_cast<const T*>(this)); - return nullptr; - } + protected: + AbstractSyntaxTree::symbol_type _value; + }; - const NodeLocation location() const { return _location; } + struct IdentifierValue : dryad::basic_node<NodeKind::IdentifierValue, FlatValue> { + 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<NodeKind::StringValue, FlatValue> { + 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<NodeKind::ListValue, dryad::container_node<Value>> { + 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<NodeKind::NullValue, Value> { + explicit NullValue(dryad::node_ctor ctor) : node_base(ctor) {} + }; - template<class T, class... Args> - NodePtr make_node_ptr(Args&&... args) { - if constexpr (std::is_pointer_v<NodePtr>) { - return new T(std::forward<Args>(args)...); - } else { - return NodePtr(new T(std::forward<Args>(args)...)); + struct Statement : dryad::abstract_node_range<dryad::container_node<Node>, NodeKind::FirstStatement, NodeKind::LastStatement> { + explicit Statement(dryad::node_ctor ctor, NodeKind kind, Value* right) + : node_base(ctor, kind) { + insert_child_after(nullptr, right); } - } - template<typename To, typename From> - To& cast_node_ptr(const From& from) { - if constexpr (std::is_pointer_v<NodePtr>) { - return *static_cast<To*>(from); - } else { - return *static_cast<To*>(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<typename To, typename From> - const To& cast_node_cptr(const From& from) { - if constexpr (std::is_pointer_v<NodePtr>) { - return *static_cast<const To*>(from); - } else { - return *static_cast<const To*>(from.get()); + struct EventStatement : dryad::basic_node<NodeKind::EventStatement, Statement> { + 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<NodePtr>& source, std::vector<NodeUPtr>& 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<AbstractStringNode>(); } - constexpr std::string_view get_base_type() const override { return ::ovdl::detail::type_name<std::decay_t<decltype(*this)>>(); } - }; + 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<NodeUPtr> _statements; - AbstractListNode(const std::vector<NodePtr>& statements = std::vector<NodePtr> {}); - AbstractListNode(NodeLocation location, const std::vector<NodePtr>& statements = std::vector<NodePtr> {}); - 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<AbstractListNode>(); } - constexpr std::string_view get_base_type() const override { return ::ovdl::detail::type_name<std::decay_t<decltype(*this)>>(); } + struct AssignStatement : dryad::basic_node<NodeKind::AssignStatement, Statement> { + 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<NodePtr>& statements = std::vector<NodePtr> {}); \ - NAME(lexy::nullopt); \ - NAME(NodeLocation location, const std::vector<NodePtr>& statements = std::vector<NodePtr> {}); \ - 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<NodePtr>& statements = {}); - EventNode(NodeLocation location, Type type, const std::vector<NodePtr>& statements = {}); + struct ValueStatement : dryad::basic_node<NodeKind::ValueStatement, Statement> { + 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<NodePtr>& statements = {}); - DecisionNode(NodeLocation location, NodePtr name, const std::vector<NodePtr>& statements = {}); - }; + struct FileTree : dryad::basic_node<NodeKind::FileTree, dryad::container_node<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<NodePtr>& statements); - ExecutionListNode(NodeLocation location, ExecutionNode::Type type, const std::vector<NodePtr>& statements); + private: + Node* _last_node; }; -#undef OVDL_AST_LIST_EXTEND + using File = ovdl::BasicFile<lexy::default_encoding, Node>; + struct AbstractSyntaxTree : ovdl::BasicAbstractSyntaxTree<File, FileTree> { + using BasicAbstractSyntaxTree::BasicAbstractSyntaxTree; -} + std::string make_list_visualizer() const; + std::string make_native_visualizer() const; + }; + using ParseState = ovdl::ParseState<AbstractSyntaxTree>; -#undef OVDL_PRINT_FUNC_DECL -#undef OVDL_PRINT_FUNC_DEF -#undef OVDL_TYPE_DEFINE_SELF
\ No newline at end of file + static_assert(IsFile<ast::File>, "File failed IsFile concept"); + static_assert(IsAst<ast::AbstractSyntaxTree>, "AbstractSyntaxTree failed IsAst concept"); + static_assert(IsParseState<ast::ParseState>, "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 <unordered_map> - -#include <openvic-dataloader/detail/PointerHash.hpp> -#include <openvic-dataloader/v2script/AbstractSyntaxTree.hpp> - -#include <lexy/input_location.hpp> - -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<typename Input> - struct NodeLocationMap : public std::unordered_multimap<NodeCPtr, const lexy::input_location<Input>, detail::PointerHash<Node>> { - 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<Input> generate_location_map(const Input& input, NodeCPtr node); - lexy::input_location_anchor<Input> generate_location_map(const Input& input, NodeCPtr node, lexy::input_location_anchor<Input> anchor); - lexy::input_location_anchor<Input> generate_begin_location_for(const Input& input, NodeCPtr node, lexy::input_location_anchor<Input> anchor); - lexy::input_location_anchor<Input> generate_end_location_for(const Input& input, NodeCPtr node, lexy::input_location_anchor<Input> anchor); - }; - - template<typename Input> - constexpr const lexy::input_location<Input> make_begin_loc(const NodeLocation location, const Input& input, lexy::input_location_anchor<Input> anchor) { - return lexy::get_input_location(input, location.begin(), anchor); - } - - template<typename Input> - constexpr const lexy::input_location<Input> make_begin_loc(const NodeLocation location, const Input& input) { - return lexy::get_input_location(input, location.begin()); - } - - template<typename Input> - constexpr const lexy::input_location<Input> make_end_loc(const NodeLocation location, const Input& input, lexy::input_location_anchor<Input> anchor) { - return lexy::get_input_location(input, location.end(), anchor); - } - - template<typename Input> - constexpr const lexy::input_location<Input> make_end_loc(const NodeLocation location, const Input& input) { - return lexy::get_input_location(input, location.end()); - } -} - -namespace ovdl::v2script::ast { - template<typename Input> - lexy::input_location_anchor<Input> NodeLocationMap<Input>::generate_location_map(const Input& input, NodeCPtr node) { - return generate_location_map(input, node, lexy::input_location_anchor(input)); - } - - template<typename Input> - lexy::input_location_anchor<Input> NodeLocationMap<Input>::generate_location_map(const Input& input, NodeCPtr node, lexy::input_location_anchor<Input> anchor) { - if (!node) return anchor; - anchor = generate_begin_location_for(input, node, anchor); - if (auto list_node = node->cast_to<ast::AbstractListNode>(); 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<ast::AssignNode>(); 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<typename Input> - lexy::input_location_anchor<Input> NodeLocationMap<Input>::generate_begin_location_for(const Input& input, NodeCPtr node, lexy::input_location_anchor<Input> anchor) { - if (node->location().begin() == nullptr) return anchor; - lexy::input_location<Input> next_loc = make_begin_loc(node->location(), input, anchor); - this->emplace(node, next_loc); - return next_loc.anchor(); - } - - template<typename Input> - lexy::input_location_anchor<Input> NodeLocationMap<Input>::generate_end_location_for(const Input& input, NodeCPtr node, lexy::input_location_anchor<Input> anchor) { - if (node->location().end() == nullptr) return anchor; - lexy::input_location<Input> 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 <cstddef> #include <filesystem> #include <memory> +#include <ostream> +#include <string> #include <string_view> -#include <openvic-dataloader/ParseError.hpp> -#include <openvic-dataloader/ParseWarning.hpp> -#include <openvic-dataloader/detail/BasicParser.hpp> -#include <openvic-dataloader/detail/Concepts.hpp> +#include <openvic-dataloader/Error.hpp> +#include <openvic-dataloader/NodeLocation.hpp> +#include <openvic-dataloader/Parser.hpp> +#include <openvic-dataloader/detail/utility/Concepts.hpp> #include <openvic-dataloader/v2script/AbstractSyntaxTree.hpp> -namespace ovdl::v2script { +#include <dryad/node.hpp> - 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<char>& 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<char>& 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<BufferHandler> _buffer_handler; - std::unique_ptr<FileNode> _file_node; + class ParseHandler; + std::unique_ptr<ParseHandler> _parse_handler; template<typename... Args> - constexpr void _run_load_func(detail::LoadCallback<BufferHandler, Args...> auto func, Args... args); + constexpr void _run_load_func(detail::LoadCallback<Parser::ParseHandler*, Args...> 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 <openvic-dataloader/v2script/AbstractSyntaxTree.hpp> #include <openvic-dataloader/v2script/Parser.hpp> +enum class VisualizationType { + Native, // <name> = { <contents> } + List // - <type> : <multiline contents> +}; + 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<ovdl::csv::EncodingType Encoding> int print_csv(const std::string_view path) { - auto parser = ovdl::csv::Parser<Encoding>::from_file(path); + auto parser = ovdl::csv::Parser<Encoding>(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<ovdl::v2script::ast::AssignNode>(); 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<ovdl::v2script::ast::AbstractListNode>(); 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<ovdl::v2script::ast::AssignNode>(); 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<ovdl::v2script::ast::AbstractListNode>(); 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<std::string> 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<ovdl::csv::EncodingType::Utf8>(argv[3]); + if (insenitive_trim_eq(args[1], "csv") && insenitive_trim_eq(args[2], "utf")) + return print_csv<ovdl::csv::EncodingType::Utf8>(args[3]); goto default_jump; case 3: - if (insenitive_trim_eq(argv[1], "csv")) - return print_csv<ovdl::csv::EncodingType::Windows1252>(argv[2]); + if (insenitive_trim_eq(args[1], "csv")) + return print_csv<ovdl::csv::EncodingType::Windows1252>(args[2]); + if (insenitive_trim_eq(args[1], "lua")) + return print_lua(args[2], type); [[fallthrough]]; default: default_jump: - std::fprintf(stderr, "usage: %s <filename>\n", argv[0]); - std::fprintf(stderr, "usage: %s csv <filename>\n", argv[0]); - std::fprintf(stderr, "usage: %s csv utf <filename>", argv[0]); + std::fprintf(stderr, "usage: %s <filename>\n", args[0].c_str()); + std::fprintf(stderr, "usage: %s list <options> <filename>\n", args[0].c_str()); + std::fprintf(stderr, "usage: %s native <options> <filename>\n", args[0].c_str()); + std::fprintf(stderr, "usage: %s lua <filename>\n", args[0].c_str()); + std::fprintf(stderr, "usage: %s csv [utf] <filename>\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 <openvic-dataloader/AbstractSyntaxTree.hpp> + +using namespace ovdl; + +AbstractSyntaxTree::symbol_type AbstractSyntaxTree::intern(const char* str, std::size_t length) { + return _symbol_interner.intern(str, length); +} + +AbstractSyntaxTree::symbol_type AbstractSyntaxTree::intern(std::string_view str) { + return intern(str.data(), str.size()); +} + +const char* AbstractSyntaxTree::intern_cstr(const char* str, std::size_t length) { + return intern(str, length).c_str(_symbol_interner); +} + +const char* AbstractSyntaxTree::intern_cstr(std::string_view str) { + return intern_cstr(str.data(), str.size()); +} + +AbstractSyntaxTree::symbol_interner_type& AbstractSyntaxTree::symbol_interner() { + return _symbol_interner; +} + +const AbstractSyntaxTree::symbol_interner_type& AbstractSyntaxTree::symbol_interner() const { + return _symbol_interner; +}
\ No newline at end of file diff --git a/src/openvic-dataloader/DiagnosticLogger.cpp b/src/openvic-dataloader/DiagnosticLogger.cpp new file mode 100644 index 0000000..aae3dcb --- /dev/null +++ b/src/openvic-dataloader/DiagnosticLogger.cpp @@ -0,0 +1,16 @@ +#include <openvic-dataloader/DiagnosticLogger.hpp> + +using namespace ovdl; + +DiagnosticLogger::operator bool() const { + return !_errored; +} + +bool DiagnosticLogger::errored() const { return _errored; } +bool DiagnosticLogger::warned() const { return _warned; } + + +NodeLocation DiagnosticLogger::location_of(const error::Error* error) const { + auto result = _map.lookup(error); + return result ? *result : NodeLocation{}; +}
\ No newline at end of file diff --git a/src/openvic-dataloader/File.cpp b/src/openvic-dataloader/File.cpp new file mode 100644 index 0000000..9b27bf0 --- /dev/null +++ b/src/openvic-dataloader/File.cpp @@ -0,0 +1,9 @@ +#include <openvic-dataloader/File.hpp> + +using namespace ovdl; + +File::File(const char* path) : _path(path) {} + +const char* File::path() const noexcept { + return _path; +}
\ No newline at end of file diff --git a/src/openvic-dataloader/NodeLocation.cpp b/src/openvic-dataloader/NodeLocation.cpp new file mode 100644 index 0000000..9e4f669 --- /dev/null +++ b/src/openvic-dataloader/NodeLocation.cpp @@ -0,0 +1,26 @@ +#include <openvic-dataloader/NodeLocation.hpp> + +using namespace ovdl; + +NodeLocation::NodeLocation() = default; +NodeLocation::NodeLocation(const char* pos) : _begin(pos), + _end(pos) {} +NodeLocation::NodeLocation(const char* begin, const char* end) : _begin(begin), + _end(end) {} + +NodeLocation::NodeLocation(const NodeLocation&) noexcept = default; +NodeLocation& NodeLocation::operator=(const NodeLocation&) = default; + +NodeLocation::NodeLocation(NodeLocation&&) = default; +NodeLocation& NodeLocation::operator=(NodeLocation&&) = default; + +const char* NodeLocation::begin() const { return _begin; } +const char* NodeLocation::end() const { return _end; } + +bool NodeLocation::is_synthesized() const { return _begin == nullptr && _end == nullptr; } + +NodeLocation NodeLocation::make_from(const char* begin, const char* end) { + end++; + if (begin >= end) return NodeLocation(begin); + return NodeLocation(begin, end); +} diff --git a/src/openvic-dataloader/csv/CsvGrammar.hpp b/src/openvic-dataloader/csv/CsvGrammar.hpp index 712bddc..5451f26 100644 --- a/src/openvic-dataloader/csv/CsvGrammar.hpp +++ b/src/openvic-dataloader/csv/CsvGrammar.hpp @@ -7,14 +7,17 @@ #include <vector> #include <openvic-dataloader/csv/LineObject.hpp> +#include <openvic-dataloader/csv/Parser.hpp> #include <lexy/callback.hpp> #include <lexy/dsl.hpp> -#include "detail/LexyLitRange.hpp" +#include "detail/dsl.hpp" // Grammar Definitions // namespace ovdl::csv::grammar { + using EncodingType = ovdl::csv::EncodingType; + template<typename T> concept ParseChars = requires() { { T::character }; @@ -51,123 +54,117 @@ namespace ovdl::csv::grammar { .map<'"'>('"'); template<ParseOptions Options> - struct StringValue { - static constexpr auto rule = [] { - // Arbitrary code points - auto c = Options.character - Options.control; - - auto back_escape = lexy::dsl::backslash_escape // - .symbol<escaped_symbols>(); + struct CsvGrammar { + struct StringValue { + static constexpr auto rule = [] { + // Arbitrary code points + auto c = Options.character - Options.control; - auto quote_escape = lexy::dsl::escape(lexy::dsl::lit_c<'"'>) // - .template symbol<escaped_quote>(); + auto back_escape = lexy::dsl::backslash_escape // + .symbol<escaped_symbols>(); - return lexy::dsl::delimited(lexy::dsl::lit_c<'"'>, lexy::dsl::not_followed_by(lexy::dsl::lit_c<'"'>, lexy::dsl::lit_c<'"'>))(c, back_escape, quote_escape); - }(); - - static constexpr auto value = lexy::as_string<std::string>; - }; + auto quote_escape = lexy::dsl::escape(lexy::dsl::lit_c<'"'>) // + .template symbol<escaped_quote>(); - template<ParseOptions Options> - struct PlainValue { - static constexpr auto rule = [] { - if constexpr (Options.SupportStrings) { - return lexy::dsl::identifier(Options.character - (lexy::dsl::lit_b<Options.SepChar> / lexy::dsl::ascii::newline)); - } else { - auto escape_check_char = Options.character - (lexy::dsl::lit_b<Options.SepChar> / lexy::dsl::ascii::newline); - auto id_check_char = escape_check_char - lexy::dsl::lit_b<'\\'>; - auto id_segment = lexy::dsl::identifier(id_check_char); - auto escape_segement = lexy::dsl::token(escape_check_char); - auto escape_sym = lexy::dsl::symbol<escaped_symbols>(escape_segement); - auto escape_rule = lexy::dsl::lit_b<'\\'> >> escape_sym; - return lexy::dsl::list(id_segment | escape_rule); - } - }(); - static constexpr auto value = lexy::as_string<std::string>; - }; + return lexy::dsl::delimited(lexy::dsl::lit_c<'"'>, lexy::dsl::not_followed_by(lexy::dsl::lit_c<'"'>, lexy::dsl::lit_c<'"'>))(c, back_escape, quote_escape); + }(); - template<ParseOptions Options> - struct Value { - static constexpr auto rule = [] { - if constexpr (Options.SupportStrings) { - return lexy::dsl::p<StringValue<Options>> | lexy::dsl::p<PlainValue<Options>>; - } else { - return lexy::dsl::p<PlainValue<Options>>; - } - }(); - static constexpr auto value = lexy::forward<std::string>; - }; - - template<ParseOptions Options> - struct SepConst { - static constexpr auto rule = lexy::dsl::lit_b<Options.SepChar>; - static constexpr auto value = lexy::constant(1); - }; + static constexpr auto value = lexy::as_string<std::string>; + }; - template<ParseOptions Options> - struct Seperator { - static constexpr auto rule = lexy::dsl::list(lexy::dsl::p<SepConst<Options>>); - static constexpr auto value = lexy::count; - }; + struct PlainValue { + static constexpr auto rule = [] { + if constexpr (Options.SupportStrings) { + return lexy::dsl::identifier(Options.character - (lexy::dsl::lit_b<Options.SepChar> / lexy::dsl::ascii::newline)); + } else { + auto escape_check_char = Options.character - (lexy::dsl::lit_b<Options.SepChar> / lexy::dsl::ascii::newline); + auto id_check_char = escape_check_char - lexy::dsl::lit_b<'\\'>; + auto id_segment = lexy::dsl::identifier(id_check_char); + auto escape_segement = lexy::dsl::token(escape_check_char); + auto escape_sym = lexy::dsl::symbol<escaped_symbols>(escape_segement); + auto escape_rule = lexy::dsl::lit_b<'\\'> >> escape_sym; + return lexy::dsl::list(id_segment | escape_rule); + } + }(); + static constexpr auto value = lexy::as_string<std::string>; + }; - template<ParseOptions Options> - struct LineEnd { - static constexpr auto rule = lexy::dsl::list(lexy::dsl::p<Value<Options>>, lexy::dsl::trailing_sep(lexy::dsl::p<Seperator<Options>>)); - static constexpr auto value = lexy::fold_inplace<ovdl::csv::LineObject>( - std::initializer_list<ovdl::csv::LineObject::value_type> {}, - [](ovdl::csv::LineObject& result, auto&& arg) { - if constexpr (std::is_same_v<std::decay_t<decltype(arg)>, std::size_t>) { - // Count seperators, adds to previous value, making it a position - using position_type = ovdl::csv::LineObject::position_type; - result.emplace_back(static_cast<position_type>(arg + result.back().first), ""); + struct Value { + static constexpr auto rule = [] { + if constexpr (Options.SupportStrings) { + return lexy::dsl::p<StringValue> | lexy::dsl::p<PlainValue>; } else { - if (result.empty()) result.emplace_back(0u, LEXY_MOV(arg)); - else { - auto& [pos, value] = result.back(); - value = arg; - } + return lexy::dsl::p<PlainValue>; } - }); - }; + }(); + static constexpr auto value = lexy::forward<std::string>; + }; - template<ParseOptions Options> - struct Line { - static constexpr auto suffix_setter(ovdl::csv::LineObject& line) { - auto& [position, value] = line.back(); - if (value.empty()) { - line.set_suffix_end(position); - line.pop_back(); - } else { - line.set_suffix_end(position + 1); - } + struct SepConst { + static constexpr auto rule = lexy::dsl::lit_b<Options.SepChar>; + static constexpr auto value = lexy::constant(1); }; - static constexpr auto rule = lexy::dsl::p<LineEnd<Options>> | lexy::dsl::p<Seperator<Options>> >> lexy::dsl::opt(lexy::dsl::p<LineEnd<Options>>); - static constexpr auto value = - lexy::callback<ovdl::csv::LineObject>( - [](ovdl::csv::LineObject&& line) { - suffix_setter(line); - return LEXY_MOV(line); - }, - [](std::size_t prefix_count, ovdl::csv::LineObject&& line) { - line.set_prefix_end(prefix_count); - // position needs to be adjusted to prefix - for (auto& [position, value] : line) { - position += prefix_count; + struct Seperator { + static constexpr auto rule = lexy::dsl::list(lexy::dsl::p<SepConst>); + static constexpr auto value = lexy::count; + }; + + struct LineEnd { + static constexpr auto rule = lexy::dsl::list(lexy::dsl::p<Value>, lexy::dsl::trailing_sep(lexy::dsl::p<Seperator>)); + static constexpr auto value = lexy::fold_inplace<ovdl::csv::LineObject>( + std::initializer_list<ovdl::csv::LineObject::value_type> {}, + [](ovdl::csv::LineObject& result, auto&& arg) { + if constexpr (std::is_same_v<std::decay_t<decltype(arg)>, std::size_t>) { + // Count seperators, adds to previous value, making it a position + using position_type = ovdl::csv::LineObject::position_type; + result.emplace_back(static_cast<position_type>(arg + result.back().first), ""); + } else { + if (result.empty()) result.emplace_back(0u, LEXY_MOV(arg)); + else { + auto& [pos, value] = result.back(); + value = arg; + } } - suffix_setter(line); - return LEXY_MOV(line); - }, - [](std::size_t suffix_count, lexy::nullopt = {}) { - return ovdl::csv::LineObject(0, {}, suffix_count + 1); }); + }; + + struct Line { + static constexpr auto suffix_setter(ovdl::csv::LineObject& line) { + auto& [position, value] = line.back(); + if (value.empty()) { + line.set_suffix_end(position); + line.pop_back(); + } else { + line.set_suffix_end(position + 1); + } + }; + + static constexpr auto rule = lexy::dsl::p<LineEnd> | lexy::dsl::p<Seperator> >> lexy::dsl::opt(lexy::dsl::p<LineEnd>); + static constexpr auto value = + lexy::callback<ovdl::csv::LineObject>( + [](ovdl::csv::LineObject&& line) { + suffix_setter(line); + return LEXY_MOV(line); + }, + [](std::size_t prefix_count, ovdl::csv::LineObject&& line) { + line.set_prefix_end(prefix_count); + // position needs to be adjusted to prefix + for (auto& [position, value] : line) { + position += prefix_count; + } + suffix_setter(line); + return LEXY_MOV(line); + }, + [](std::size_t suffix_count, lexy::nullopt = {}) { + return ovdl::csv::LineObject(0, {}, suffix_count + 1); + }); + }; }; template<ParseOptions Options> struct File { - static constexpr auto rule = lexy::dsl::terminator(lexy::dsl::eof).opt_list( - lexy::dsl::p<Line<Options>> | lexy::dsl::newline - ); + static constexpr auto rule = lexy::dsl::terminator(lexy::dsl::eof).opt_list(lexy::dsl::p<typename CsvGrammar<Options>::Line> | lexy::dsl::newline); static constexpr auto value = lexy::as_list<std::vector<ovdl::csv::LineObject>>; }; @@ -199,7 +196,7 @@ namespace ovdl::csv::grammar { namespace ovdl::csv::grammar::windows1252 { struct windows1252_t { - static constexpr auto character = detail::lexydsl::make_range<0x01, 0xFF>(); + static constexpr auto character = dsl::make_range<0x01, 0xFF>(); static constexpr auto control = lexy::dsl::ascii::control / lexy::dsl::lit_b<0x81> / lexy::dsl::lit_b<0x8D> / lexy::dsl::lit_b<0x8F> / diff --git a/src/openvic-dataloader/csv/CsvParseState.hpp b/src/openvic-dataloader/csv/CsvParseState.hpp new file mode 100644 index 0000000..2390453 --- /dev/null +++ b/src/openvic-dataloader/csv/CsvParseState.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include <openvic-dataloader/File.hpp> +#include <openvic-dataloader/ParseState.hpp> +#include <openvic-dataloader/csv/LineObject.hpp> +#include <openvic-dataloader/csv/Parser.hpp> + +#include <lexy/encoding.hpp> + +template<ovdl::csv::EncodingType> +struct LexyEncodingFrom { +}; + +template<> +struct LexyEncodingFrom<ovdl::csv::EncodingType::Windows1252> { + using encoding = lexy::default_encoding; +}; + +template<> +struct LexyEncodingFrom<ovdl::csv::EncodingType::Utf8> { + using encoding = lexy::utf8_char_encoding; +}; + +template<ovdl::csv::EncodingType Encoding> +using CsvFile = ovdl::BasicFile<typename LexyEncodingFrom<Encoding>::encoding, std::vector<ovdl::csv::LineObject>>; + +template<ovdl::csv::EncodingType Encoding> +using CsvParseState = ovdl::FileParseState<CsvFile<Encoding>>;
\ No newline at end of file diff --git a/src/openvic-dataloader/csv/Parser.cpp b/src/openvic-dataloader/csv/Parser.cpp index 0ca3402..849ea05 100644 --- a/src/openvic-dataloader/csv/Parser.cpp +++ b/src/openvic-dataloader/csv/Parser.cpp @@ -1,47 +1,34 @@ -#include <memory> #include <vector> +#include <openvic-dataloader/File.hpp> #include <openvic-dataloader/csv/LineObject.hpp> #include <openvic-dataloader/csv/Parser.hpp> -#include <openvic-dataloader/detail/ClassExport.hpp> +#include <openvic-dataloader/detail/LexyReportError.hpp> +#include <openvic-dataloader/detail/OStreamOutputIterator.hpp> +#include <openvic-dataloader/detail/utility/Utility.hpp> #include <lexy/action/parse.hpp> #include <lexy/encoding.hpp> #include <lexy/input/buffer.hpp> #include <lexy/input/file.hpp> -#include "csv/CsvGrammar.hpp" -#include "detail/BasicBufferHandler.hpp" -#include "detail/LexyReportError.hpp" -#include "detail/OStreamOutputIterator.hpp" +#include "CsvGrammar.hpp" +#include "CsvParseState.hpp" +#include "detail/NullBuff.hpp" +#include "detail/ParseHandler.hpp" using namespace ovdl; using namespace ovdl::csv; -/// BufferHandler /// - -template<EncodingType> -struct LexyEncodingFrom { -}; - -template<> -struct LexyEncodingFrom<EncodingType::Windows1252> { - using encoding = lexy::default_encoding; -}; - -template<> -struct LexyEncodingFrom<EncodingType::Utf8> { - using encoding = lexy::utf8_char_encoding; -}; +/// ParseHandler /// template<EncodingType Encoding> -class Parser<Encoding>::BufferHandler final : public detail::BasicBufferHandler<typename LexyEncodingFrom<Encoding>::encoding> { -public: - template<typename Node, typename ErrorCallback> - std::optional<std::vector<ParseError>> parse(const ErrorCallback& callback) { - auto result = lexy::parse<Node>(this->_buffer, callback); +struct Parser<Encoding>::ParseHandler final : detail::BasicFileParseHandler<CsvParseState<Encoding>> { + template<typename Node> + std::optional<DiagnosticLogger::error_range> parse() { + auto result = lexy::parse<Node>(this->buffer(), *this->_parse_state, this->_parse_state->logger().error_callback()); if (!result) { - return result.errors(); + return this->_parse_state->logger().get_errors(); } _lines = std::move(result.value()); return std::nullopt; @@ -59,8 +46,14 @@ private: template<EncodingType Encoding> Parser<Encoding>::Parser() - : _buffer_handler(std::make_unique<BufferHandler>()) { - set_error_log_to_stderr(); + : _parse_handler(std::make_unique<ParseHandler>()) { + set_error_log_to_null(); +} + +template<EncodingType Encoding> +Parser<Encoding>::Parser(std::basic_ostream<char>& error_stream) + : _parse_handler(std::make_unique<ParseHandler>()) { + set_error_log_to(error_stream); } template<EncodingType Encoding> @@ -115,28 +108,31 @@ Parser<Encoding> Parser<Encoding>::from_file(const std::filesystem::path& path) /// template<EncodingType Encoding> template<typename... Args> -constexpr void Parser<Encoding>::_run_load_func(detail::LoadCallback<BufferHandler, Args...> auto func, Args... args) { - _warnings.clear(); - _errors.clear(); +constexpr void Parser<Encoding>::_run_load_func(detail::LoadCallback<ParseHandler, Args...> auto func, Args... args) { _has_fatal_error = false; - if (auto error = func(_buffer_handler.get(), std::forward<Args>(args)...); error) { - _has_fatal_error = error.value().type == ParseError::Type::Fatal; - _errors.push_back(error.value()); - _error_stream.get() << "Error: " << _errors.back().message << '\n'; + auto error = func(_parse_handler.get(), std::forward<Args>(args)...); + auto error_message = _parse_handler->make_error_from(error); + if (!error_message.empty()) { + _has_error = true; + _has_fatal_error = true; + _parse_handler->parse_state().logger().template create_log<error::BufferError>(DiagnosticLogger::DiagnosticKind::error, fmt::runtime(error_message)); + } + if (has_error() && &_error_stream.get() != &detail::cnull) { + print_errors_to(_error_stream.get()); } } template<EncodingType Encoding> constexpr Parser<Encoding>& Parser<Encoding>::load_from_buffer(const char* data, std::size_t size) { // Type can't be deduced? - _run_load_func(std::mem_fn(&BufferHandler::load_buffer_size), data, size); + _run_load_func(std::mem_fn(&ParseHandler::load_buffer_size), data, size); return *this; } template<EncodingType Encoding> constexpr Parser<Encoding>& Parser<Encoding>::load_from_buffer(const char* start, const char* end) { // Type can't be deduced? - _run_load_func(std::mem_fn(&BufferHandler::load_buffer), start, end); + _run_load_func(std::mem_fn(&ParseHandler::load_buffer), start, end); return *this; } @@ -149,7 +145,7 @@ template<EncodingType Encoding> constexpr Parser<Encoding>& Parser<Encoding>::load_from_file(const char* path) { _file_path = path; // Type can be deduced?? - _run_load_func(std::mem_fn(&BufferHandler::load_file), path); + _run_load_func(std::mem_fn(&ParseHandler::load_file), path); return *this; } @@ -159,38 +155,34 @@ Parser<Encoding>& Parser<Encoding>::load_from_file(const std::filesystem::path& } template<EncodingType Encoding> -constexpr Parser<Encoding>& Parser<Encoding>::load_from_file(const detail::Has_c_str auto& path) { - return load_from_file(path.c_str()); -} - -template<EncodingType Encoding> bool Parser<Encoding>::parse_csv(bool handle_strings) { - if (!_buffer_handler->is_valid()) { + if (!_parse_handler->is_valid()) { return false; } - std::optional<std::vector<ParseError>> errors; - auto report_error = ovdl::detail::ReporError.path(_file_path).to(detail::OStreamOutputIterator { _error_stream }); + std::optional<Parser<Encoding>::error_range> errors; + // auto report_error = ovdl::detail::ReporError.path(_file_path).to(detail::OStreamOutputIterator { _error_stream }); if constexpr (Encoding == EncodingType::Windows1252) { if (handle_strings) - errors = _buffer_handler->template parse<csv::grammar::windows1252::strings::SemiColonFile>(report_error); + errors = _parse_handler->template parse<csv::grammar::windows1252::strings::SemiColonFile>(); else - errors = _buffer_handler->template parse<csv::grammar::windows1252::SemiColonFile>(report_error); + errors = _parse_handler->template parse<csv::grammar::windows1252::SemiColonFile>(); } else { if (handle_strings) - errors = _buffer_handler->template parse<csv::grammar::utf8::strings::SemiColonFile>(report_error); + errors = _parse_handler->template parse<csv::grammar::utf8::strings::SemiColonFile>(); else - errors = _buffer_handler->template parse<csv::grammar::utf8::SemiColonFile>(report_error); + errors = _parse_handler->template parse<csv::grammar::utf8::SemiColonFile>(); } - if (errors) { - _errors.reserve(errors->size()); - for (auto& err : errors.value()) { - _has_fatal_error |= err.type == ParseError::Type::Fatal; - _errors.push_back(err); + _has_error = _parse_handler->parse_state().logger().errored(); + _has_warning = _parse_handler->parse_state().logger().warned(); + if (!errors->empty()) { + _has_fatal_error = true; + if (&_error_stream.get() != &detail::cnull) { + print_errors_to(_error_stream); } return false; } - _lines = std::move(_buffer_handler->get_lines()); + _lines = std::move(_parse_handler->get_lines()); return true; } @@ -199,5 +191,60 @@ const std::vector<csv::LineObject>& Parser<Encoding>::get_lines() const { return _lines; } +template<EncodingType Encoding> +typename Parser<Encoding>::error_range Parser<Encoding>::get_errors() const { + return _parse_handler->parse_state().logger().get_errors(); +} + +template<EncodingType Encoding> +const FilePosition Parser<Encoding>::get_error_position(const error::Error* error) const { + if (!error || !error->is_linked_in_tree()) { + return {}; + } + auto err_location = _parse_handler->parse_state().logger().location_of(error); + if (err_location.is_synthesized()) { + return {}; + } + + auto loc_begin = lexy::get_input_location(_parse_handler->buffer(), err_location.begin()); + FilePosition result { loc_begin.line_nr(), loc_begin.line_nr(), loc_begin.column_nr(), loc_begin.column_nr() }; + if (err_location.begin() < err_location.end()) { + auto loc_end = lexy::get_input_location(_parse_handler->buffer(), err_location.end(), loc_begin.anchor()); + result.end_line = loc_end.line_nr(); + result.end_column = loc_end.column_nr(); + } + return result; +} + +template<EncodingType Encoding> +void Parser<Encoding>::print_errors_to(std::basic_ostream<char>& stream) const { + auto errors = get_errors(); + if (errors.empty()) return; + for (const auto error : errors) { + dryad::visit_tree( + error, + [&](const error::BufferError* buffer_error) { + stream << buffer_error->message() << '\n'; + }, + [&](const error::ParseError* parse_error) { + stream << parse_error->message() << '\n'; + }, + [&](dryad::child_visitor<error::ErrorKind> visitor, const error::Semantic* semantic) { + stream << semantic->message() << '\n'; + auto annotations = semantic->annotations(); + if (annotations.empty()) return; + for (auto annotation : annotations) { + visitor(annotation); + } + }, + [&](const error::PrimaryAnnotation* primary) { + stream << primary->message() << '\n'; + }, + [&](const error::SecondaryAnnotation* secondary) { + stream << secondary->message() << '\n'; + }); + } +} + template class ovdl::csv::Parser<EncodingType::Windows1252>; template class ovdl::csv::Parser<EncodingType::Utf8>;
\ No newline at end of file diff --git a/src/openvic-dataloader/detail/BasicBufferHandler.hpp b/src/openvic-dataloader/detail/BasicBufferHandler.hpp deleted file mode 100644 index f26544b..0000000 --- a/src/openvic-dataloader/detail/BasicBufferHandler.hpp +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include <optional> - -#include <openvic-dataloader/ParseError.hpp> -#include <openvic-dataloader/detail/OptionalConstexpr.hpp> - -#include <lexy/encoding.hpp> -#include <lexy/input/buffer.hpp> -#include <lexy/input/file.hpp> - -#include "detail/Errors.hpp" - -namespace ovdl::detail { - template<typename Encoding = lexy::default_encoding, typename MemoryResource = void> - class BasicBufferHandler { - public: - using encoding_type = Encoding; - - OVDL_OPTIONAL_CONSTEXPR bool is_valid() const { - return _buffer.data() != nullptr; - } - - OVDL_OPTIONAL_CONSTEXPR std::optional<ovdl::ParseError> load_buffer_size(const char* data, std::size_t size) { - _buffer = lexy::buffer<Encoding, MemoryResource>(data, size); - return std::nullopt; - } - - OVDL_OPTIONAL_CONSTEXPR std::optional<ovdl::ParseError> load_buffer(const char* start, const char* end) { - _buffer = lexy::buffer<Encoding, MemoryResource>(start, end); - return std::nullopt; - } - - std::optional<ovdl::ParseError> load_file(const char* path) { - auto file = lexy::read_file<Encoding, lexy::encoding_endianness::bom, MemoryResource>(path); - if (!file) { - return ovdl::errors::make_no_file_error(path); - } - - _buffer = file.buffer(); - return std::nullopt; - } - - const auto& get_buffer() const { - return _buffer; - } - - protected: - lexy::buffer<Encoding, MemoryResource> _buffer; - }; -}
\ No newline at end of file diff --git a/src/openvic-dataloader/detail/BufferError.hpp b/src/openvic-dataloader/detail/BufferError.hpp new file mode 100644 index 0000000..1fbb0f4 --- /dev/null +++ b/src/openvic-dataloader/detail/BufferError.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include <cstdint> + +namespace ovdl::detail { + enum class buffer_error : std::uint8_t { + success, + /// An internal OS error, such as failure to read from the file. + os_error, + /// The file was not found. + file_not_found, + /// The file cannot be opened. + permission_denied, + /// The buffer failed to handle the data + buffer_is_null + }; +}
\ No newline at end of file diff --git a/src/openvic-dataloader/detail/DetectUtf8.hpp b/src/openvic-dataloader/detail/DetectUtf8.hpp index 2045b3c..e9d0350 100644 --- a/src/openvic-dataloader/detail/DetectUtf8.hpp +++ b/src/openvic-dataloader/detail/DetectUtf8.hpp @@ -3,7 +3,7 @@ #include <lexy/action/match.hpp> #include <lexy/dsl.hpp> -#include "detail/LexyLitRange.hpp" +#include "detail/dsl.hpp" namespace ovdl::detail { namespace detect_utf8 { @@ -18,15 +18,15 @@ namespace ovdl::detail { constexpr auto is_not_ascii_flag = lexy::dsl::context_flag<DetectUtf8>; // & 0b10000000 == 0b00000000 - constexpr auto ascii_values = lexydsl::make_range<0b00000000, 0b01111111>(); + constexpr auto ascii_values = dsl::make_range<0b00000000, 0b01111111>(); // & 0b11100000 == 0b11000000 - constexpr auto two_byte = lexydsl::make_range<0b11000000, 0b11011111>(); + constexpr auto two_byte = dsl::make_range<0b11000000, 0b11011111>(); // & 0b11110000 == 0b11100000 - constexpr auto three_byte = lexydsl::make_range<0b11100000, 0b11101111>(); + constexpr auto three_byte = dsl::make_range<0b11100000, 0b11101111>(); // & 0b11111000 == 0b11110000 - constexpr auto four_byte = lexydsl::make_range<0b11110000, 0b11110111>(); + constexpr auto four_byte = dsl::make_range<0b11110000, 0b11110111>(); // & 0b11000000 == 0b10000000 - constexpr auto check_bytes = lexydsl::make_range<0b10000000, 0b10111111>(); + constexpr auto check_bytes = dsl::make_range<0b10000000, 0b10111111>(); constexpr auto utf8_check = ((four_byte >> lexy::dsl::times<3>(check_bytes)) | diff --git a/src/openvic-dataloader/detail/LexyLitRange.hpp b/src/openvic-dataloader/detail/LexyLitRange.hpp deleted file mode 100644 index a6761a8..0000000 --- a/src/openvic-dataloader/detail/LexyLitRange.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include <lexy/dsl/literal.hpp> - -namespace ovdl::detail::lexydsl { - template<unsigned char LOW, unsigned char HIGH> - consteval auto make_range() { - if constexpr (LOW == HIGH) { - return lexy::dsl::lit_c<LOW>; - } else if constexpr (LOW == (HIGH - 1)) { - return lexy::dsl::lit_c<LOW> / lexy::dsl::lit_c<HIGH>; - } else { - return lexy::dsl::lit_c<LOW> / make_range<LOW + 1, HIGH>(); - } - } -}
\ No newline at end of file diff --git a/src/openvic-dataloader/detail/NullBuff.hpp b/src/openvic-dataloader/detail/NullBuff.hpp index baf9e1b..e5c6f4a 100644 --- a/src/openvic-dataloader/detail/NullBuff.hpp +++ b/src/openvic-dataloader/detail/NullBuff.hpp @@ -22,9 +22,9 @@ namespace ovdl::detail { basic_nullbuf<cT, traits> m_sbuf; }; - typedef basic_onullstream<char> onullstream; - typedef basic_onullstream<wchar_t> wonullstream; + using onullstream = basic_onullstream<char>; + using wonullstream = basic_onullstream<wchar_t>; - inline onullstream cnull; - inline onullstream wcnull; + static inline onullstream cnull; + static inline onullstream wcnull; }
\ No newline at end of file diff --git a/src/openvic-dataloader/detail/ParseHandler.hpp b/src/openvic-dataloader/detail/ParseHandler.hpp new file mode 100644 index 0000000..fbec0d7 --- /dev/null +++ b/src/openvic-dataloader/detail/ParseHandler.hpp @@ -0,0 +1,145 @@ +#pragma once + +#include <utility> + +#include <openvic-dataloader/ParseState.hpp> +#include <openvic-dataloader/detail/utility/Concepts.hpp> + +#include <lexy/encoding.hpp> +#include <lexy/input/buffer.hpp> +#include <lexy/input/file.hpp> + +#include "detail/BufferError.hpp" + +namespace ovdl::detail { + template<typename Derived> + struct ParseHandler { + std::string make_error_from(buffer_error error) { + switch (error) { + using enum ovdl::detail::buffer_error; + case buffer_is_null: + return "Buffer could not be loaded."; + case os_error: + return "OS file error."; + case file_not_found: + return "File not found."; + case permission_denied: + return "Read Permission denied."; + default: + return ""; + } + } + + template<typename... Args> + constexpr void _run_load_func(detail::LoadCallback<Derived, Args...> auto func, Args... args); + }; + + template<IsFileParseState ParseState, typename MemoryResource = void> + struct BasicFileParseHandler : ParseHandler<BasicFileParseHandler<ParseState, MemoryResource>> { + using parse_state_type = ParseState; + using encoding_type = typename parse_state_type::file_type::encoding_type; + + constexpr bool is_valid() const { + if (!_parse_state) return false; + return buffer().data() != nullptr; + } + + constexpr buffer_error load_buffer_size(const char* data, std::size_t size) { + lexy::buffer<encoding_type, MemoryResource> buffer(data, size); + if (buffer.data() == nullptr) return buffer_error::buffer_is_null; + _parse_state.reset(new parse_state_type { std::move(buffer) }); + return is_valid() ? buffer_error::success : buffer_error::buffer_is_null; + } + + constexpr buffer_error load_buffer(const char* start, const char* end) { + lexy::buffer<encoding_type, MemoryResource> buffer(start, end); + if (buffer.data() == nullptr) return buffer_error::buffer_is_null; + _parse_state.reset(new parse_state_type { std::move(buffer) }); + return is_valid() ? buffer_error::success : buffer_error::buffer_is_null; + } + + buffer_error load_file(const char* path) { + lexy::read_file_result file = lexy::read_file<encoding_type, lexy::encoding_endianness::bom, MemoryResource>(path); + if (!file) { + _parse_state.reset(new parse_state_type { path, lexy::buffer<typename parse_state_type::file_type::encoding_type>() }); + return ovdl::detail::from_underlying<buffer_error>(ovdl::detail::to_underlying(file.error())); + } + _parse_state.reset(new parse_state_type { path, std::move(file).buffer() }); + return is_valid() ? buffer_error::success : buffer_error::buffer_is_null; + } + + const char* path() const { + if (!_parse_state) return ""; + return _parse_state->file().path(); + } + + parse_state_type& parse_state() { + return *_parse_state; + } + + const parse_state_type& parse_state() const { + return *_parse_state; + } + + constexpr const auto& buffer() const { + return _parse_state->file().buffer(); + } + + protected: + std::unique_ptr<parse_state_type> _parse_state; + }; + + template<IsParseState ParseState, typename MemoryResource = void> + struct BasicStateParseHandler : ParseHandler<BasicStateParseHandler<ParseState, MemoryResource>> { + using parse_state_type = ParseState; + using encoding_type = typename parse_state_type::ast_type::file_type::encoding_type; + + constexpr bool is_valid() const { + if (!_parse_state) return false; + return buffer().data() != nullptr; + } + + constexpr buffer_error load_buffer_size(const char* data, std::size_t size) { + lexy::buffer<encoding_type, MemoryResource> buffer(data, size); + _parse_state.reset(new parse_state_type { std::move(buffer) }); + return is_valid() ? buffer_error::success : buffer_error::buffer_is_null; + } + + constexpr buffer_error load_buffer(const char* start, const char* end) { + lexy::buffer<encoding_type, MemoryResource> buffer(start, end); + _parse_state.reset(new parse_state_type { std::move(buffer) }); + return is_valid() ? buffer_error::success : buffer_error::buffer_is_null; + } + + buffer_error load_file(const char* path) { + lexy::read_file_result file = lexy::read_file<encoding_type, lexy::encoding_endianness::bom, MemoryResource>(path); + if (!file) { + _parse_state.reset(new parse_state_type { path, lexy::buffer<typename parse_state_type::ast_type::file_type::encoding_type>() }); + return ovdl::detail::from_underlying<buffer_error>(ovdl::detail::to_underlying(file.error())); + } + + _parse_state.reset(new parse_state_type { path, std::move(file).buffer() }); + return is_valid() ? buffer_error::success : buffer_error::buffer_is_null; + } + + const char* path() const { + if (!_parse_state) return ""; + return _parse_state->ast().file().path(); + } + + parse_state_type& parse_state() { + return *_parse_state; + } + + const parse_state_type& parse_state() const { + return *_parse_state; + } + + constexpr const auto& buffer() const { + return _parse_state->ast().file().buffer(); + } + + protected: + std::unique_ptr<parse_state_type> _parse_state; + }; +}
\ No newline at end of file diff --git a/src/openvic-dataloader/detail/BasicParser.cpp b/src/openvic-dataloader/detail/Parser.cpp index 212bf00..fd87687 100644 --- a/src/openvic-dataloader/detail/BasicParser.cpp +++ b/src/openvic-dataloader/detail/Parser.cpp @@ -1,7 +1,7 @@ #include <iostream> #include <ostream> -#include <openvic-dataloader/detail/BasicParser.hpp> +#include <openvic-dataloader/Parser.hpp> #include "detail/NullBuff.hpp" @@ -27,7 +27,7 @@ void BasicParser::set_error_log_to(std::basic_ostream<char>& stream) { } bool BasicParser::has_error() const { - return !_errors.empty(); + return _has_error; } bool BasicParser::has_fatal_error() const { @@ -35,15 +35,7 @@ bool BasicParser::has_fatal_error() const { } bool BasicParser::has_warning() const { - return !_warnings.empty(); -} - -const std::vector<ovdl::ParseError>& BasicParser::get_errors() const { - return _errors; -} - -const std::vector<ovdl::ParseWarning>& BasicParser::get_warnings() const { - return _warnings; + return _has_warning; } std::string_view BasicParser::get_file_path() const { diff --git a/src/openvic-dataloader/detail/StringLiteral.hpp b/src/openvic-dataloader/detail/StringLiteral.hpp new file mode 100644 index 0000000..985b087 --- /dev/null +++ b/src/openvic-dataloader/detail/StringLiteral.hpp @@ -0,0 +1,232 @@ +#pragma once + +#include <algorithm> +#include <array> +#include <cstddef> +#include <string> +#include <string_view> +#include <type_traits> + +#include <lexy/_detail/config.hpp> +#include <lexy/_detail/integer_sequence.hpp> +#include <lexy/dsl/identifier.hpp> + +namespace ovdl::detail { + + template<std::size_t N, typename CharT> + struct string_literal; + + struct _string_literal { + protected: + static constexpr auto _to_string(const auto& input) { return string_literal(input); } + static constexpr auto _concat(const auto&... input) { return string_literal(_to_string(input)...); } + }; + + template<std::size_t N, typename CharT = char> + struct string_literal : _string_literal { + using value_type = CharT; + using pointer = value_type*; + using const_pointer = const value_type*; + using reference = value_type&; + using const_reference = const value_type&; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using traits_type = std::char_traits<value_type>; + + static constexpr size_type npos = size_type(-1); + + static constexpr auto size = std::integral_constant<std::size_t, N - 1> {}; + static constexpr auto length = size; + + value_type _data[N]; + + constexpr string_literal() noexcept = default; + + constexpr string_literal(const value_type (&literal)[N]) noexcept { + for (auto i = 0u; i != N; i++) + _data[i] = literal[i]; + } + + constexpr string_literal(value_type c) noexcept : _data {} { + _data[0] = c; + } + + constexpr auto begin() noexcept { return std::begin(_data); } + constexpr auto end() noexcept { return std::end(_data); } + constexpr auto cbegin() const noexcept { return std::cbegin(_data); } + constexpr auto cend() const noexcept { return std::cend(_data); } + constexpr auto rbegin() noexcept { return std::rbegin(_data); } + constexpr auto rend() noexcept { return std::rend(_data); } + constexpr auto crbegin() const noexcept { return std::crbegin(_data); } + constexpr auto crend() const noexcept { return std::crend(_data); } + + constexpr auto front() const noexcept { return as_string_view().front(); } + constexpr auto back() const noexcept { return as_string_view().back(); } + constexpr auto at(size_type pos) const { return as_string_view().at(pos); } + + constexpr auto operator[](size_type pos) const noexcept { return as_string_view()[pos]; } + + constexpr bool empty() const { return as_string_view().empty(); } + + template<size_type N2> + constexpr bool operator==(const string_literal<N2, value_type>& other) const { + return as_string_view() == other.as_string_view(); + } + + constexpr bool starts_with(std::basic_string_view<value_type> sv) const noexcept { return as_string_view().starts_with(sv); } + constexpr bool starts_with(value_type ch) const noexcept { return as_string_view().starts_with(ch); } + constexpr bool starts_with(const value_type* s) const { return as_string_view().starts_with(s); } + + template<size_type N2> + constexpr bool starts_with(const string_literal<N2, value_type>& other) const { + return starts_with(other.as_string_view()); + } + + constexpr bool ends_with(std::basic_string_view<value_type> sv) const noexcept { return as_string_view().ends_with(sv); } + constexpr bool ends_with(value_type ch) const noexcept { return as_string_view().ends_with(ch); } + constexpr bool ends_with(const value_type* s) const { return as_string_view().ends_with(s); } + + template<size_type N2> + constexpr bool ends_with(const string_literal<N2, value_type>& other) const { + return ends_with(other.as_string_view()); + } + + constexpr auto clear() const noexcept { + return string_literal<0, value_type> {}; + } + + constexpr auto push_back(value_type c) const noexcept { + return *this + string_literal { c }; + } + + constexpr auto pop_back() const noexcept { + string_literal<N - 1, value_type> result {}; + for (auto i = 0u; i != N - 2; i++) + result._data[i] = _data[i]; + return result; + } + + template<size_type count> + constexpr auto append(value_type ch) const noexcept { + string_literal<N + count, value_type> result {}; + for (auto i = 0u; i != N; i++) + result._data[i] = _data[i]; + + for (auto i = N; i != N + count; i++) + result._data[i] = ch; + + return result; + } + + template<size_type N2> + constexpr auto append(string_literal<N2, value_type> str) const noexcept { + return *this + str; + } + + template<size_type N2> + constexpr auto append(const value_type (&literal)[N2]) const noexcept { + return *this + literal; + } + + template<size_type pos = 0, size_type count = npos> + constexpr auto substr() const noexcept { + static_assert(pos <= N, "pos must be less than or equal to N"); + constexpr size_type result_size = std::min(count - pos, N - pos); + + string_literal<result_size, value_type> result {}; + for (size_type i = 0u, i2 = pos; i != result_size; i++, i2++) + result._data[i] = _data[i2]; + return result; + } + + constexpr auto substr() const noexcept { + return substr<>(); + } + + constexpr std::string_view substr(size_type pos, size_type count = npos) const noexcept { + return as_string_view().substr(pos, count); + } + + constexpr size_type find(std::string_view str, size_type pos = 0) const noexcept { + return as_string_view().find(str, pos); + } + + template<size_type N2> + constexpr size_type find(const value_type (&literal)[N2], size_type pos = 0) const noexcept { + return as_string_view().find(literal, pos, N2 - 2); + } + + constexpr size_type rfind(std::string_view str, size_type pos = 0) const noexcept { + return as_string_view().rfind(str, pos); + } + + template<size_type N2> + constexpr size_type rfind(const value_type (&literal)[N2], size_type pos = 0) const noexcept { + return as_string_view().find(literal, pos, N2 - 2); + } + + constexpr int compare(std::string_view str) const noexcept { + return as_string_view().compare(str); + } + + template<size_type N2> + constexpr int compare(const value_type (&literal)[N2]) const noexcept { + return as_string_view().compare(0, N, literal, N2 - 2); + } + + constexpr operator std::basic_string_view<value_type>() const& { return as_string_view(); } + constexpr operator std::basic_string_view<value_type>() const&& = delete; + + constexpr std::basic_string_view<value_type> as_string_view() const& { return std::basic_string_view<value_type>(_data, size() - 1); } + constexpr std::basic_string_view<value_type> as_string_view() const&& = delete; + + constexpr operator const value_type*() const& { return c_str(); } + constexpr operator const value_type*() const&& = delete; + + constexpr const value_type* c_str() const& { return _data; } + constexpr const value_type* c_str() const&& = delete; + + constexpr const value_type* data() const& { return _data; } + constexpr const value_type* data() const&& = delete; + + template<size_type N2> + constexpr auto operator+(const string_literal<N2, value_type>& other) const noexcept { + string_literal<N + N2 - 1, value_type> result {}; + for (size_type i = 0u; i != N; i++) + result._data[i] = _data[i]; + + for (size_type i = N - 1, i2 = 0; i2 != N2; i++, i2++) + result._data[i] = other._data[i2]; + return result; + } + + template<size_type N2> + constexpr auto operator+(const value_type (&rhs)[N2]) const noexcept { + return *this + _to_string(rhs); + } + + template<size_type N2> + friend constexpr auto operator+(const value_type (&lhs)[N2], string_literal<N, value_type> rhs) noexcept { + return _to_string(lhs) + rhs; + } + }; + template<std::size_t N, typename CharT = char> + string_literal(const CharT (&)[N]) -> string_literal<N, CharT>; + + template<typename CharT = char> + string_literal(CharT) -> string_literal<1, CharT>; + + template<template<typename C, C... Cs> typename T, string_literal Str, std::size_t... Idx> + auto _to_type_string(lexy::_detail::index_sequence<Idx...>) { + return T<typename decltype(Str)::value_type, Str._data[Idx]...> {}; + } + template<template<typename C, C... Cs> typename T, string_literal Str> + using to_type_string = decltype(ovdl::detail::_to_type_string<T, Str>(lexy::_detail::make_index_sequence<decltype(Str)::size()> {})); +} + +namespace ovdl::dsl { + template<ovdl::detail::string_literal Str, typename L, typename T, typename... R> + constexpr auto keyword(lexyd::_id<L, T, R...>) { + return ovdl::detail::to_type_string<lexyd::_keyword<lexyd::_id<L, T>>::template get, Str> {}; + } +}
\ No newline at end of file diff --git a/src/openvic-dataloader/detail/Warnings.hpp b/src/openvic-dataloader/detail/Warnings.hpp index 8fc09bd..ab718bc 100644 --- a/src/openvic-dataloader/detail/Warnings.hpp +++ b/src/openvic-dataloader/detail/Warnings.hpp @@ -5,7 +5,7 @@ #include <openvic-dataloader/ParseWarning.hpp> namespace ovdl::v2script::warnings { - inline const ParseWarning make_utf8_warning(std::string_view file_path) { + inline const std::string make_utf8_warning(std::string_view file_path) { constexpr std::string_view message_suffix = "This may cause problems. Prefer Windows-1252 encoding."; std::string message; @@ -15,7 +15,7 @@ namespace ovdl::v2script::warnings { message = "File '" + std::string(file_path) + "' is a UTF-8 encoded file. " + std::string(message_suffix); } - return ParseWarning { message, 1 }; + return message; } } diff --git a/src/openvic-dataloader/detail/dsl.hpp b/src/openvic-dataloader/detail/dsl.hpp new file mode 100644 index 0000000..9b544bc --- /dev/null +++ b/src/openvic-dataloader/detail/dsl.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include <type_traits> + +#include <openvic-dataloader/NodeLocation.hpp> +#include <openvic-dataloader/ParseState.hpp> + +#include <lexy/callback/adapter.hpp> +#include <lexy/callback/bind.hpp> +#include <lexy/callback/container.hpp> +#include <lexy/callback/fold.hpp> +#include <lexy/dsl.hpp> + +#include "detail/StringLiteral.hpp" + +namespace ovdl::dsl { + template<typename ReturnType, typename... Callback> + constexpr auto callback(Callback... cb) { + return lexy::bind(lexy::callback<ReturnType>(cb...), lexy::parse_state, lexy::values); + } + + template<typename Sink> + constexpr auto sink(Sink sink) { + return lexy::bind_sink(sink, lexy::parse_state); + } + + template<typename Container, typename Callback> + constexpr auto collect(Callback callback) { + return sink(lexy::collect<Container>(callback)); + } + + template<typename Callback> + constexpr auto collect(Callback callback) { + return sink(lexy::collect(callback)); + } + + template<IsParseState StateType, typename T> + constexpr auto construct = callback<T*>( + [](StateType& state, ovdl::NodeLocation loc, auto&& arg) { + if constexpr (std::is_same_v<std::decay_t<decltype(arg)>, lexy::nullopt>) + return state.ast().template create<T>(loc); + else + return state.ast().template create<T>(loc, DRYAD_FWD(arg)); + }, + [](StateType& state, ovdl::NodeLocation loc, auto&&... args) { + return state.ast().template create<T>(loc, DRYAD_FWD(args)...); + }); + + template<IsParseState StateType, typename T, typename ListType, bool DisableEmpty = false> + constexpr auto construct_list = callback<T*>( + [](StateType& state, const char* begin, ListType&& arg, const char* end) { + return state.ast().template create<T>(NodeLocation::make_from(begin, end), DRYAD_FWD(arg)); + }, + [](StateType& state, const char* begin, lexy::nullopt, const char* end) { + return state.ast().template create<T>(NodeLocation::make_from(begin, end)); + }, + [](StateType& state, const char* begin, const char* end) { + return state.ast().template create<T>(NodeLocation::make_from(begin, end)); + }); + + template<IsParseState StateType, typename T, typename ListType> + constexpr auto construct_list<StateType, T, ListType, true> = callback<T*>( + [](StateType& state, const char* begin, ListType&& arg, const char* end) { + return state.ast().template create<T>(NodeLocation::make_from(begin, end), DRYAD_FWD(arg)); + }, + [](StateType& state, const char* begin, lexy::nullopt, const char* end) { + return state.ast().template create<T>(NodeLocation::make_from(begin, end)); + }); + + template<unsigned char LOW, unsigned char HIGH> + consteval auto make_range() { + if constexpr (LOW == HIGH) { + return ::lexy::dsl::lit_c<LOW>; + } else if constexpr (LOW == (HIGH - 1)) { + return ::lexy::dsl::lit_c<LOW> / ::lexy::dsl::lit_c<HIGH>; + } else { + return ::lexy::dsl::lit_c<LOW> / make_range<LOW + 1, HIGH>(); + } + } + + template<auto Open, auto Close> + constexpr auto position_brackets = lexy::dsl::brackets(lexy::dsl::position(lexy::dsl::lit_c<Open>), lexy::dsl::position(lexy::dsl::lit_c<Close>)); + + constexpr auto round_bracketed = position_brackets<'(', ')'>; + constexpr auto square_bracketed = position_brackets<'[', ']'>; + constexpr auto curly_bracketed = position_brackets<'{', '}'>; + constexpr auto angle_bracketed = position_brackets<'<', '>'>; + + template<typename Production> + constexpr auto p = lexy::dsl::position(lexy::dsl::p<Production>); + + template<IsParseState ParseType, typename ReturnType, ovdl::detail::string_literal Keyword> + static constexpr auto default_kw_value = dsl::callback<ReturnType*>( + [](ParseType& state, NodeLocation loc) { + return state.ast().template create<ReturnType>(loc, state.ast().intern(Keyword.data(), Keyword.size())); + }); + + template< + IsParseState ParseType, + typename Identifier, + typename RuleValue, + ovdl::detail::string_literal Keyword, + auto Production, + auto Value> + struct keyword_rule { + struct rule_t { + static constexpr auto keyword = ovdl::dsl::keyword<Keyword>(lexy::dsl::inline_<Identifier>); + static constexpr auto rule = lexy::dsl::position(keyword) >> lexy::dsl::equal_sign; + static constexpr auto value = Value; + }; + static constexpr auto rule = dsl::p<rule_t> >> Production; + static constexpr auto value = construct<ParseType, RuleValue>; + }; + + template< + IsParseState ParseType, + typename Identifier, + typename RuleValue, + ovdl::detail::string_literal Keyword, + auto Production, + auto Value> + struct fkeyword_rule : keyword_rule<ParseType, Identifier, RuleValue, Keyword, Production, Value> { + using base_type = keyword_rule<ParseType, Identifier, RuleValue, Keyword, Production, Value>; + struct context_t; + struct rule_t : base_type::rule_t { + static constexpr auto flag = lexy::dsl::context_flag<context_t>; + struct too_many_error { + static constexpr auto name = "expected event " + Keyword + " to only be found once"; + }; + static constexpr auto must = lexy::dsl::must(rule_t::flag.is_reset()) + . +// See https://stackoverflow.com/questions/77144003/use-of-template-keyword-before-dependent-template-name +// THANKS FOR NOTHING MICROSOFT, CAN'T EVEN GET THE STANDARD RIGHT +#if !defined(_MSC_VER) || defined(__clang__) + template +#endif + error<too_many_error>; + }; + static constexpr auto make_flag = rule_t::flag.create(); + + static constexpr auto rule = dsl::p<rule_t> >> (rule_t::must >> rule_t::flag.set()) >> Production; + static constexpr auto value = construct<ParseType, RuleValue>; + }; + + template<typename... Args> + struct rule_helper { + static constexpr auto flags = (Args::make_flag + ...); + static constexpr auto p = (lexy::dsl::p<Args> | ...); + }; +}
\ No newline at end of file diff --git a/src/openvic-dataloader/v2script/AbstractSyntaxTree.cpp b/src/openvic-dataloader/v2script/AbstractSyntaxTree.cpp index 182d559..4754d8e 100644 --- a/src/openvic-dataloader/v2script/AbstractSyntaxTree.cpp +++ b/src/openvic-dataloader/v2script/AbstractSyntaxTree.cpp @@ -1,9 +1,3 @@ -#include <iomanip> -#include <sstream> -#include <string> -#include <utility> -#include <vector> - #include <stddef.h> #include <openvic-dataloader/v2script/AbstractSyntaxTree.hpp> @@ -11,281 +5,146 @@ #include <lexy/dsl/option.hpp> #include <lexy/input_location.hpp> -using namespace ovdl::v2script::ast; - -static void _handle_string_characters(std::string& string, bool allow_newline) { - size_t position = 0; - for (auto& c : string) { - switch (c) { - case '\r': - case '\n': - if (allow_newline) goto END_LOOP; - c = ' '; - break; - default: break; - } - END_LOOP: - position++; - } -} - -void ovdl::v2script::ast::copy_into_node_ptr_vector(const std::vector<NodePtr>& source, std::vector<NodeUPtr>& dest) { - dest.clear(); - dest.reserve(source.size()); - for (auto&& p : source) { - dest.push_back(NodeUPtr { p }); - } -} - -AbstractStringNode::AbstractStringNode() : Node({}) {} -AbstractStringNode::AbstractStringNode(NodeLocation location, std::string&& name, bool allow_newline) : Node(location), - _name(std::move(name)) { - _handle_string_characters(_name, allow_newline); -} -AbstractStringNode::AbstractStringNode(NodeLocation location) : Node(location) {} -AbstractStringNode::AbstractStringNode(std::string&& name, bool allow_newline) : AbstractStringNode({}, std::move(name), allow_newline) {} - -std::ostream& AbstractStringNode::print(std::ostream& stream, size_t indent) const { - return stream << _name; -} - -#define OVDL_AST_STRING_NODE_DEF(NAME, ...) \ - NAME::NAME() : AbstractStringNode() {} \ - NAME::NAME(std::string&& name, bool allow_newline) : AbstractStringNode(std::move(name), allow_newline) {} \ - NAME::NAME(lexy::nullopt) : AbstractStringNode() {} \ - NAME::NAME(NodeLocation location) : AbstractStringNode(location) {} \ - NAME::NAME(NodeLocation location, std::string&& name, bool allow_newline) : AbstractStringNode(location, std::move(name), allow_newline) {} \ - NAME::NAME(NodeLocation location, lexy::nullopt) : AbstractStringNode(location, {}, true) {} \ - std::ostream& NAME::print(std::ostream& stream, size_t indent) const __VA_ARGS__ - -OVDL_AST_STRING_NODE_DEF(IdentifierNode, { - return stream << _name; -}); - -OVDL_AST_STRING_NODE_DEF(StringNode, { - return stream << '"' << _name << '"'; -}); - -OVDL_AST_STRING_NODE_DEF(FactorNode, { - return stream << "factor = " << _name; -}); - -OVDL_AST_STRING_NODE_DEF(MonthNode, { - return stream << "months = " << _name; -}); - -OVDL_AST_STRING_NODE_DEF(NameNode, { - return stream << "name = " << _name; -}); - -OVDL_AST_STRING_NODE_DEF(FireOnlyNode, { - return stream << "fire_only_once = " << _name; -}); - -OVDL_AST_STRING_NODE_DEF(IdNode, { - return stream << "id = " << _name; -}); - -OVDL_AST_STRING_NODE_DEF(TitleNode, { - return stream << "title = " << _name; -}); - -OVDL_AST_STRING_NODE_DEF(DescNode, { - return stream << "desc = " << _name; -}); - -OVDL_AST_STRING_NODE_DEF(PictureNode, { - return stream << "picture = " << _name; -}); - -OVDL_AST_STRING_NODE_DEF(IsTriggeredNode, { - return stream << "is_triggered_only = " << _name; -}); - -#undef OVDL_AST_STRING_NODE_DEF - -AssignNode::AssignNode(NodeLocation location, NodeCPtr name, NodePtr init) - : Node(location), - _initializer(std::move(init)) { - if (name->is_type<IdentifierNode>()) { - _name = cast_node_cptr<IdentifierNode>(name)._name; - } -} - -std::ostream& Node::print_ptr(std::ostream& stream, NodeCPtr node, size_t indent) { - return node != nullptr ? node->print(stream, indent) : stream << "<NULL>"; -} - -static std::ostream& print_newline_indent(std::ostream& stream, size_t indent) { - return stream << "\n" - << std::setw(indent) << std::setfill('\t') << ""; -} - -/* Starts with a newline and ends at the end of a line, and so - * should be followed by a call to print_newline_indent. - */ -static std::ostream& print_nodeuptr_vector(const std::vector<NodeUPtr>& nodes, - std::ostream& stream, size_t indent) { - for (NodeUPtr const& node : nodes) { - print_newline_indent(stream, indent); - Node::print_ptr(stream, node.get(), indent); - } - return stream; -} - -AbstractListNode::AbstractListNode(NodeLocation location, const std::vector<NodePtr>& statements) : Node(location) { - copy_into_node_ptr_vector(statements, _statements); -} -AbstractListNode::AbstractListNode(const std::vector<NodePtr>& statements) : AbstractListNode({}, statements) {} -std::ostream& AbstractListNode::print(std::ostream& stream, size_t indent) const { - stream << '{'; - if (!_statements.empty()) { - print_nodeuptr_vector(_statements, stream, indent + 1); - print_newline_indent(stream, indent); - } - return stream << "}"; -} - -#define OVDL_AST_LIST_NODE_DEF(NAME, ...) \ - NAME::NAME(const std::vector<NodePtr>& statements) : AbstractListNode(statements) {} \ - NAME::NAME(lexy::nullopt) : AbstractListNode() {} \ - NAME::NAME(NodeLocation location, const std::vector<NodePtr>& statements) : AbstractListNode(location, statements) {} \ - NAME::NAME(NodeLocation location, lexy::nullopt) : AbstractListNode(location, {}) {} \ - std::ostream& NAME::print(std::ostream& stream, size_t indent) const __VA_ARGS__ - -OVDL_AST_LIST_NODE_DEF(FileNode, { - print_nodeuptr_vector(_statements, stream, indent); - return print_newline_indent(stream, indent); -}); +#include <dryad/node.hpp> +#include <dryad/tree.hpp> -OVDL_AST_LIST_NODE_DEF(ListNode, { - stream << '{'; - if (!_statements.empty()) { - print_nodeuptr_vector(_statements, stream, indent + 1); - print_newline_indent(stream, indent); - } - return stream << "}"; -}); - -OVDL_AST_LIST_NODE_DEF(ModifierNode, { - stream << "modifier = {"; - if (!_statements.empty()) { - print_nodeuptr_vector(_statements, stream, indent + 1); - print_newline_indent(stream, indent); - } - return stream << '}'; -}); - -OVDL_AST_LIST_NODE_DEF(MtthNode, { - stream << "mean_time_to_happen = {"; - if (!_statements.empty()) { - print_nodeuptr_vector(_statements, stream, indent + 1); - print_newline_indent(stream, indent); - } - return stream << '}'; -}); +#include <range/v3/view/drop.hpp> -OVDL_AST_LIST_NODE_DEF(EventOptionNode, { - stream << "option = {"; - if (!_statements.empty()) { - print_nodeuptr_vector(_statements, stream, indent + 1); - print_newline_indent(stream, indent); - } - return stream << '}'; -}); - -OVDL_AST_LIST_NODE_DEF(BehaviorListNode, { - stream << "ai_chance = {"; // may be ai_chance or ai_will_do - if (!_statements.empty()) { - print_nodeuptr_vector(_statements, stream, indent + 1); - print_newline_indent(stream, indent); - } - return stream << '}'; -}); - -OVDL_AST_LIST_NODE_DEF(DecisionListNode, { - stream << "political_decisions = {"; - if (!_statements.empty()) { - print_nodeuptr_vector(_statements, stream, indent + 1); - print_newline_indent(stream, indent); - } - return stream << '}'; -}); - -#undef OVDL_AST_LIST_NODE_DEF - -EventNode::EventNode(NodeLocation location, Type type, const std::vector<NodePtr>& statements) : AbstractListNode(location), - _type(type) { - copy_into_node_ptr_vector(statements, _statements); -} -EventNode::EventNode(Type type, const std::vector<NodePtr>& statements) : EventNode({}, type, statements) {} -std::ostream& EventNode::print(std::ostream& stream, size_t indent) const { - switch (_type) { - case Type::Country: stream << "country_event = "; break; - case Type::Province: stream << "province_event = "; break; - } - stream << '{'; - if (!_statements.empty()) { - print_nodeuptr_vector(_statements, stream, indent + 1); - print_newline_indent(stream, indent); - } - return stream << '}'; -} - -DecisionNode::DecisionNode(NodeLocation location, NodePtr name, const std::vector<NodePtr>& statements) : AbstractListNode(location), - _name(std::move(name)) { - copy_into_node_ptr_vector(statements, _statements); -} -DecisionNode::DecisionNode(NodePtr name, const std::vector<NodePtr>& statements) : DecisionNode({}, name, statements) {} -std::ostream& DecisionNode::print(std::ostream& stream, size_t indent) const { - print_ptr(stream, _name.get(), indent) << " = {"; - if (!_statements.empty()) { - print_nodeuptr_vector(_statements, stream, indent + 1); - print_newline_indent(stream, indent); - } - return stream << '}'; -} - -ExecutionNode::ExecutionNode(NodeLocation location, Type type, NodePtr name, NodePtr init) : Node(location), - _type(type), - _name(std::move(name)), - _initializer(std::move(init)) { -} -ExecutionNode::ExecutionNode(Type type, NodePtr name, NodePtr init) : ExecutionNode({}, type, name, init) {} -std::ostream& ExecutionNode::print(std::ostream& stream, size_t indent) const { - print_ptr(stream, _name.get(), indent) << " = "; - if (_initializer) { - Node::print_ptr(stream, _initializer.get(), indent + 1); - } - return stream; -} - -ExecutionListNode::ExecutionListNode(NodeLocation location, ExecutionNode::Type type, const std::vector<NodePtr>& statements) : AbstractListNode(location), - _type(type) { - copy_into_node_ptr_vector(statements, _statements); -} -ExecutionListNode::ExecutionListNode(ExecutionNode::Type type, const std::vector<NodePtr>& statements) : ExecutionListNode({}, type, statements) {} -std::ostream& ExecutionListNode::print(std::ostream& stream, size_t indent) const { - // Only way to make a valid declared parsable file - stream << "{ "; - switch (_type) { - case ExecutionNode::Type::Effect: stream << "effect = {"; break; - case ExecutionNode::Type::Trigger: stream << "trigger = {"; break; - } - if (!_statements.empty()) { - print_nodeuptr_vector(_statements, stream, indent + 1); - print_newline_indent(stream, indent); - } - return stream << "}}"; -} +using namespace ovdl::v2script::ast; -Node::operator std::string() const { - std::stringstream ss; - ss << *this; - return ss.str(); -} +ListValue::ListValue(dryad::node_ctor ctor, StatementList statements) + : node_base(ctor) { + insert_child_list_after(nullptr, statements); + if (statements.empty()) { + _last_statement = nullptr; + } else { + _last_statement = statements.back(); + } +} + +FileTree::FileTree(dryad::node_ctor ctor, StatementList statements) : node_base(ctor) { + insert_child_list_after(nullptr, statements); + if (statements.empty()) { + _last_node = nullptr; + } else { + _last_node = statements.back(); + } +} + +// static void _handle_string_characters(std::string& string, bool allow_newline) { +// size_t position = 0; +// for (auto& c : string) { +// switch (c) { +// case '\r': +// case '\n': +// if (allow_newline) goto END_LOOP; +// c = ' '; +// break; +// default: break; +// } +// END_LOOP: +// position++; +// } +// } + +std::string AbstractSyntaxTree::make_list_visualizer() const { + const int INDENT_SIZE = 2; + + std::string result; + unsigned int level = 0; + + for (auto [event, node] : dryad::traverse(_tree)) { + if (event == dryad::traverse_event::exit) { + --level; + continue; + } -std::ostream& AssignNode::print(std::ostream& stream, size_t indent) const { - stream << _name << " = "; - return Node::print_ptr(stream, _initializer.get(), indent); + result.append(INDENT_SIZE * level, ' '); + result.append(fmt::format("- {}: ", get_kind_name(node->kind()))); + + dryad::visit_node( + node, + [&](const FlatValue* value) { + result.append(value->value(_symbol_interner)); + }, + [&](const ListValue* value) { + }, + [&](const NullValue* value) { + }, + [&](const EventStatement* statement) { + result.append(statement->is_province_event() ? "province_event" : "country_event"); + }, + [&](const AssignStatement* statement) { + }, + [&](const FileTree* tree) { + }); + + result.append(1, '\n'); + + if (event == dryad::traverse_event::enter) + ++level; + } + + return result; +} + +std::string AbstractSyntaxTree::make_native_visualizer() const { + constexpr int INDENT_SIZE = 2; + + std::string result; + unsigned int level = 0; + + dryad::visit_tree( + _tree, + [&](const IdentifierValue* value) { + result.append(value->value(_symbol_interner)); + }, + [&](const StringValue* value) { + result.append(1, '"').append(value->value(_symbol_interner)).append(1, '"'); + }, + [&](dryad::child_visitor<NodeKind> visitor, const ValueStatement* statement) { + visitor(statement->value()); + }, + [&](dryad::child_visitor<NodeKind> visitor, const AssignStatement* statement) { + visitor(statement->left()); + if (statement->right()->kind() != NodeKind::NullValue) { + result.append(" = "); + visitor(statement->right()); + } + }, + [&](dryad::child_visitor<NodeKind> visitor, const ListValue* value) { + result.append(1, '{'); + level++; + for (const auto& statement : value->statements()) { + result + .append(1, '\n') + .append(INDENT_SIZE * level, ' '); + visitor(statement); + } + level--; + result + .append(1, '\n') + .append(INDENT_SIZE * level, ' ') + .append(1, '}'); + }, + [&](dryad::child_visitor<NodeKind> visitor, const EventStatement* statement) { + result.append(statement->is_province_event() ? "province_event" : "country_event"); + if (statement->right()->kind() != NodeKind::NullValue) { + result.append(" = "); + visitor(statement->right()); + } + }, + [&](dryad::child_visitor<NodeKind> visitor, const FileTree* value) { + auto statements = value->statements(); + visitor(*statements.begin()); + + for (const auto& statement : statements | ranges::views::drop(1)) { + result + .append(1, '\n') + .append(INDENT_SIZE * level, ' '); + visitor(statement); + } + }); + + return result; }
\ No newline at end of file diff --git a/src/openvic-dataloader/v2script/AiBehaviorGrammar.hpp b/src/openvic-dataloader/v2script/AiBehaviorGrammar.hpp index e04f447..7fcd13d 100644 --- a/src/openvic-dataloader/v2script/AiBehaviorGrammar.hpp +++ b/src/openvic-dataloader/v2script/AiBehaviorGrammar.hpp @@ -2,6 +2,7 @@ #include <openvic-dataloader/v2script/AbstractSyntaxTree.hpp> +#include <lexy/callback/forward.hpp> #include <lexy/dsl.hpp> #include "ModifierGrammar.hpp" @@ -10,23 +11,12 @@ namespace ovdl::v2script::grammar { struct AiBehaviorList { static constexpr auto rule = lexy::dsl::list(lexy::dsl::p<FactorStatement> | lexy::dsl::p<ModifierStatement>); - static constexpr auto value = - lexy::as_list<std::vector<ast::NodePtr>> >> - lexy::callback<ast::NodePtr>( - [](auto&& list) { - return ast::make_node_ptr<ast::BehaviorListNode>(LEXY_MOV(list)); - }); + static constexpr auto value = lexy::as_list<ast::AssignStatementList>; }; struct AiBehaviorBlock { - static constexpr auto rule = lexy::dsl::curly_bracketed.opt(lexy::dsl::p<AiBehaviorList>); + static constexpr auto rule = dsl::curly_bracketed.opt(lexy::dsl::p<AiBehaviorList>); - static constexpr auto value = lexy::callback<ast::NodePtr>( - [](auto&& list) { - return LEXY_MOV(list); - }, - [](lexy::nullopt = {}) { - return lexy::nullopt {}; - }); + static constexpr auto value = construct_list<ast::ListValue>; }; }
\ No newline at end of file diff --git a/src/openvic-dataloader/v2script/DecisionGrammar.hpp b/src/openvic-dataloader/v2script/DecisionGrammar.hpp index 3bd5cba..05cb8cc 100644 --- a/src/openvic-dataloader/v2script/DecisionGrammar.hpp +++ b/src/openvic-dataloader/v2script/DecisionGrammar.hpp @@ -1,109 +1,49 @@ #pragma once -#include <vector> - #include <openvic-dataloader/v2script/AbstractSyntaxTree.hpp> #include <lexy/callback.hpp> #include <lexy/callback/adapter.hpp> +#include <lexy/callback/constant.hpp> #include <lexy/callback/container.hpp> #include <lexy/dsl.hpp> +#include <lexy/dsl/brackets.hpp> #include <lexy/dsl/option.hpp> +#include <lexy/dsl/position.hpp> +#include <lexy/dsl/production.hpp> -#include "AiBehaviorGrammar.hpp" -#include "SimpleGrammar.hpp" -#include "TriggerGrammar.hpp" +#include "v2script/AiBehaviorGrammar.hpp" +#include "v2script/TriggerGrammar.hpp" // Decision Grammar Definitions // namespace ovdl::v2script::grammar { - ////////////////// - // Macros - ////////////////// -// Produces <KW_NAME>_rule and <KW_NAME>_p -#define OVDL_GRAMMAR_KEYWORD_DEFINE(KW_NAME) \ - struct KW_NAME##_rule { \ - static constexpr auto keyword = LEXY_KEYWORD(#KW_NAME, lexy::dsl::inline_<Identifier<StringEscapeOption>>); \ - static constexpr auto rule = keyword >> lexy::dsl::equal_sign; \ - static constexpr auto value = lexy::noop; \ - }; \ - static constexpr auto KW_NAME##_p = lexy::dsl::p<KW_NAME##_rule> - -// Produces <KW_NAME>_rule and <KW_NAME>_p and <KW_NAME>_rule::flag and <KW_NAME>_rule::too_many_error -#define OVDL_GRAMMAR_KEYWORD_FLAG_DEFINE(KW_NAME) \ - struct KW_NAME##_rule { \ - static constexpr auto keyword = LEXY_KEYWORD(#KW_NAME, lexy::dsl::inline_<Identifier<StringEscapeOption>>); \ - static constexpr auto rule = keyword >> lexy::dsl::equal_sign; \ - static constexpr auto value = lexy::noop; \ - static constexpr auto flag = lexy::dsl::context_flag<struct KW_NAME##_context>; \ - struct too_many_error { \ - static constexpr auto name = "expected left side " #KW_NAME " to be found once"; \ - }; \ - }; \ - static constexpr auto KW_NAME##_p = lexy::dsl::p<KW_NAME##_rule> >> (lexy::dsl::must(KW_NAME##_rule::flag.is_reset()).error<KW_NAME##_rule::too_many_error> + KW_NAME##_rule::flag.set()) - ////////////////// - // Macros - ////////////////// struct DecisionStatement { - template<auto Production, typename AstNode> - struct _StringStatement { - static constexpr auto rule = Production >> (lexy::dsl::p<StringExpression<StringEscapeOption>> | lexy::dsl::p<Identifier<StringEscapeOption>>); - static constexpr auto value = lexy::forward<ast::NodePtr>; - }; - template<auto Production, typename AstNode> - static constexpr auto StringStatement = lexy::dsl::p<_StringStatement<Production, AstNode>>; + using potential = fkeyword_rule<"potential", lexy::dsl::p<TriggerBlock>>; + using allow = fkeyword_rule<"allow", lexy::dsl::p<TriggerBlock>>; + using effect = fkeyword_rule<"effect", lexy::dsl::p<TriggerBlock>>; + using ai_will_do = fkeyword_rule<"ai_will_do", lexy::dsl::p<AiBehaviorBlock>>; - OVDL_GRAMMAR_KEYWORD_FLAG_DEFINE(potential); - OVDL_GRAMMAR_KEYWORD_FLAG_DEFINE(allow); - OVDL_GRAMMAR_KEYWORD_FLAG_DEFINE(effect); - OVDL_GRAMMAR_KEYWORD_FLAG_DEFINE(ai_will_do); + using helper = dsl::rule_helper<potential, allow, effect, ai_will_do>; - static constexpr auto rule = [] { - constexpr auto create_flags = - potential_rule::flag.create() + - allow_rule::flag.create() + - effect_rule::flag.create() + - ai_will_do_rule::flag.create(); + struct List { + static constexpr auto rule = dsl::curly_bracketed.opt_list(helper::p | lexy::dsl::p<SAssignStatement<StringEscapeOption>>); - constexpr auto potential_statement = potential_p >> lexy::dsl::p<TriggerBlock>; - constexpr auto allow_statement = allow_p >> lexy::dsl::p<TriggerBlock>; - constexpr auto effect_statement = effect_p >> lexy::dsl::p<TriggerBlock>; - constexpr auto ai_will_do_statement = ai_will_do_p >> lexy::dsl::p<AiBehaviorBlock>; + static constexpr auto value = lexy::as_list<ast::AssignStatementList> >> construct_list<ast::ListValue>; + }; - return lexy::dsl::p<Identifier<StringEscapeOption>> >> - (create_flags + lexy::dsl::equal_sign + - lexy::dsl::curly_bracketed.list( - potential_statement | - allow_statement | - effect_statement | - ai_will_do_statement | - lexy::dsl::p<SimpleAssignmentStatement<StringEscapeOption>>)); - }(); + static constexpr auto rule = + dsl::p<Identifier<StringEscapeOption>> >> + (helper::flags + lexy::dsl::equal_sign + lexy::dsl::p<List>); - static constexpr auto value = - lexy::as_list<std::vector<ast::NodePtr>> >> - lexy::callback<ast::NodePtr>( - [](auto&& name, auto&& list) { - return ast::make_node_ptr<ast::DecisionNode>(LEXY_MOV(name), LEXY_MOV(list)); - }, - [](auto&& name, lexy::nullopt = {}) { - return ast::make_node_ptr<ast::DecisionNode>(LEXY_MOV(name)); - }); + static constexpr auto value = construct<ast::AssignStatement>; }; struct DecisionList { static constexpr auto rule = - LEXY_KEYWORD("political_decisions", lexy::dsl::inline_<Identifier<StringEscapeOption>>) >> - (lexy::dsl::equal_sign + lexy::dsl::curly_bracketed.opt_list(lexy::dsl::p<DecisionStatement>)); + ovdl::dsl::keyword<"political_decisions">(lexy::dsl::inline_<Identifier<StringEscapeOption>>) >> + (lexy::dsl::equal_sign >> lexy::dsl::curly_bracketed.opt_list(lexy::dsl::p<DecisionStatement>)); - static constexpr auto value = - lexy::as_list<std::vector<ast::NodePtr>> >> - lexy::callback<ast::NodePtr>( - [](auto&& list) { - return ast::make_node_ptr<ast::DecisionListNode>(LEXY_MOV(list)); - }, - [](lexy::nullopt = {}) { - return lexy::nullopt {}; - }); + static constexpr auto value = lexy::as_list<ast::AssignStatementList>; }; struct DecisionFile { @@ -111,15 +51,8 @@ namespace ovdl::v2script::grammar { static constexpr auto whitespace = whitespace_specifier | comment_specifier; static constexpr auto rule = - lexy::dsl::terminator(lexy::dsl::eof).list( // - lexy::dsl::p<DecisionList> | // - lexy::dsl::p<SimpleAssignmentStatement<StringEscapeOption>>); + lexy::dsl::position + lexy::dsl::terminator(lexy::dsl::eof).opt_list(lexy::dsl::p<DecisionList>); - static constexpr auto value = lexy::as_list<std::vector<ast::NodePtr>> >> lexy::new_<ast::FileNode, ast::NodePtr>; + static constexpr auto value = lexy::concat<ast::AssignStatementList> >> construct<ast::FileTree>; }; - -#undef OVDL_GRAMMAR_KEYWORD_DEFINE -#undef OVDL_GRAMMAR_KEYWORD_FLAG_DEFINE -#undef OVDL_GRAMMAR_KEYWORD_STATEMENT -#undef OVDL_GRAMMAR_KEYWORD_FLAG_STATEMENT }
\ No newline at end of file diff --git a/src/openvic-dataloader/v2script/EffectGrammar.hpp b/src/openvic-dataloader/v2script/EffectGrammar.hpp index 1b85382..10f8348 100644 --- a/src/openvic-dataloader/v2script/EffectGrammar.hpp +++ b/src/openvic-dataloader/v2script/EffectGrammar.hpp @@ -6,37 +6,24 @@ #include <lexy/dsl.hpp> #include "SimpleGrammar.hpp" +#include "detail/dsl.hpp" namespace ovdl::v2script::grammar { struct EffectStatement { - static constexpr auto rule = lexy::dsl::inline_<SimpleAssignmentStatement<StringEscapeOption>>; + static constexpr auto rule = lexy::dsl::p<SAssignStatement<StringEscapeOption>>; - static constexpr auto value = lexy::callback<ast::NodePtr>( - [](auto name, auto&& initalizer) { - return ast::make_node_ptr<ast::ExecutionNode>(ast::ExecutionNode::Type::Effect, LEXY_MOV(name), LEXY_MOV(initalizer)); - }); + static constexpr auto value = lexy::forward<ast::AssignStatement*>; }; struct EffectList { - static constexpr auto rule = lexy::dsl::list(lexy::dsl::p<SimpleAssignmentStatement<StringEscapeOption>>); - - static constexpr auto value = - lexy::as_list<std::vector<ast::NodePtr>> >> - lexy::callback<ast::NodePtr>( - [](auto&& list) { - return ast::make_node_ptr<ast::ExecutionListNode>(ast::ExecutionNode::Type::Effect, LEXY_MOV(list)); - }); + static constexpr auto rule = lexy::dsl::list(lexy::dsl::p<EffectStatement>); + + static constexpr auto value = lexy::as_list<ast::AssignStatementList>; }; struct EffectBlock { - static constexpr auto rule = lexy::dsl::curly_bracketed.opt(lexy::dsl::p<EffectList>); - - static constexpr auto value = lexy::callback<ast::NodePtr>( - [](auto&& list) { - return LEXY_MOV(list); - }, - [](lexy::nullopt = {}) { - return lexy::nullopt {}; - }); + static constexpr auto rule = dsl::curly_bracketed.opt(lexy::dsl::p<EffectList>); + + static constexpr auto value = construct_list<ast::ListValue>; }; }
\ No newline at end of file diff --git a/src/openvic-dataloader/v2script/EventGrammar.hpp b/src/openvic-dataloader/v2script/EventGrammar.hpp index 57cd170..c81a173 100644 --- a/src/openvic-dataloader/v2script/EventGrammar.hpp +++ b/src/openvic-dataloader/v2script/EventGrammar.hpp @@ -1,168 +1,109 @@ #pragma once -#include <string> -#include <vector> +#include <cctype> +#include <cstdlib> #include <openvic-dataloader/v2script/AbstractSyntaxTree.hpp> #include <lexy/callback.hpp> #include <lexy/dsl.hpp> +#include <lexy/grammar.hpp> + +#include "openvic-dataloader/NodeLocation.hpp" -#include "AiBehaviorGrammar.hpp" -#include "EffectGrammar.hpp" -#include "ModifierGrammar.hpp" #include "SimpleGrammar.hpp" -#include "TriggerGrammar.hpp" +#include "detail/dsl.hpp" +#include "v2script/AiBehaviorGrammar.hpp" +#include "v2script/EffectGrammar.hpp" +#include "v2script/ModifierGrammar.hpp" // Event Grammar Definitions // namespace ovdl::v2script::grammar { - ////////////////// - // Macros - ////////////////// -// Produces <KW_NAME>_rule and <KW_NAME>_p -#define OVDL_GRAMMAR_KEYWORD_DEFINE(KW_NAME) \ - struct KW_NAME##_rule { \ - static constexpr auto keyword = LEXY_KEYWORD(#KW_NAME, lexy::dsl::inline_<Identifier<StringEscapeOption>>); \ - static constexpr auto rule = keyword >> lexy::dsl::equal_sign; \ - static constexpr auto value = lexy::noop; \ - }; \ - static constexpr auto KW_NAME##_p = lexy::dsl::p<KW_NAME##_rule> - -// Produces <KW_NAME>_rule and <KW_NAME>_p and <KW_NAME>_rule::flag and <KW_NAME>_rule::too_many_error -#define OVDL_GRAMMAR_KEYWORD_FLAG_DEFINE(KW_NAME) \ - struct KW_NAME##_rule { \ - static constexpr auto keyword = LEXY_KEYWORD(#KW_NAME, lexy::dsl::inline_<Identifier<StringEscapeOption>>); \ - static constexpr auto rule = keyword >> lexy::dsl::equal_sign; \ - static constexpr auto value = lexy::noop; \ - static constexpr auto flag = lexy::dsl::context_flag<struct KW_NAME##_context>; \ - struct too_many_error { \ - static constexpr auto name = "expected left side " #KW_NAME " to be found once"; \ - }; \ - }; \ - static constexpr auto KW_NAME##_p = lexy::dsl::p<KW_NAME##_rule> >> (lexy::dsl::must(KW_NAME##_rule::flag.is_reset()).error<KW_NAME##_rule::too_many_error> + KW_NAME##_rule::flag.set()) - ////////////////// - // Macros - ////////////////// - static constexpr auto event_symbols = lexy::symbol_table<ast::EventNode::Type> // - .map<LEXY_SYMBOL("country_event")>(ast::EventNode::Type::Country) - .map<LEXY_SYMBOL("province_event")>(ast::EventNode::Type::Province); + static constexpr auto event_symbols = lexy::symbol_table<bool> // + .map<LEXY_SYMBOL("country_event")>(false) + .map<LEXY_SYMBOL("province_event")>(true); struct EventMtthStatement { - OVDL_GRAMMAR_KEYWORD_DEFINE(months); - struct MonthValue { - static constexpr auto rule = lexy::dsl::inline_<Identifier<StringEscapeOption>>; - static constexpr auto value = lexy::as_string<std::string> | lexy::new_<ast::MonthNode, ast::NodePtr>; + static constexpr auto rule = lexy::dsl::p<Identifier<StringEscapeOption>>; + static constexpr auto value = dsl::callback<ast::IdentifierValue*>( + [](ast::ParseState& state, ast::IdentifierValue* value) { + bool is_number = true; + for (auto* current = value->value(state.ast().symbol_interner()); *current; current++) { + is_number = is_number && std::isdigit(*current); + if (!is_number) break; + } + if (!is_number) { + state.logger().warning("month is not an integer") // + .primary(state.ast().location_of(value), "here") + .finish(); + } + return value; + }); }; - static constexpr auto rule = lexy::dsl::list( - (months_p >> lexy::dsl::p<MonthValue>) | - lexy::dsl::p<ModifierStatement>); + using months = keyword_rule<"months", lexy::dsl::p<MonthValue>>; - static constexpr auto value = - lexy::as_list<std::vector<ast::NodePtr>> >> - lexy::callback<ast::NodePtr>( - [](auto&& list) { - return ast::make_node_ptr<ast::MtthNode>(LEXY_MOV(list)); - }); - }; + static constexpr auto rule = dsl::curly_bracketed(lexy::dsl::p<months> | lexy::dsl::p<ModifierStatement>); - template<auto Production, typename AstNode> - struct _StringStatement { - static constexpr auto rule = Production >> (lexy::dsl::p<StringExpression<StringEscapeOption>> | lexy::dsl::p<Identifier<StringEscapeOption>>); - static constexpr auto value = - lexy::callback<ast::NodePtr>( - [](auto&& value) { - auto result = ast::make_node_ptr<AstNode>(std::move(static_cast<ast::AbstractStringNode*>(value)->_name)); - delete value; - return result; - }); + static constexpr auto value = lexy::as_list<ast::AssignStatementList> >> construct_list<ast::ListValue, true>; }; - template<auto Production, typename AstNode> - static constexpr auto StringStatement = lexy::dsl::p<_StringStatement<Production, AstNode>>; + + static constexpr auto str_or_id = lexy::dsl::p<SimpleGrammar<StringEscapeOption>::ValueExpression>; struct EventOptionList { - OVDL_GRAMMAR_KEYWORD_FLAG_DEFINE(name); - OVDL_GRAMMAR_KEYWORD_FLAG_DEFINE(ai_chance); + using name = fkeyword_rule<"name", str_or_id>; + using ai_chance = fkeyword_rule<"ai_chance", lexy::dsl::p<AiBehaviorBlock>>; static constexpr auto rule = [] { - constexpr auto create_flags = name_rule::flag.create() + ai_chance_rule::flag.create(); - - constexpr auto name_statement = StringStatement<name_p, ast::NameNode>; - constexpr auto ai_chance_statement = ai_chance_p >> lexy::dsl::curly_bracketed(lexy::dsl::p<AiBehaviorList>); + using helper = dsl::rule_helper<name, ai_chance>; - return create_flags + lexy::dsl::list(name_statement | ai_chance_statement | lexy::dsl::p<EffectList>); + return dsl::curly_bracketed(helper::flags + lexy::dsl::list(helper::p | lexy::dsl::p<EffectStatement>)); }(); - static constexpr auto value = - lexy::as_list<std::vector<ast::NodePtr>> >> - lexy::callback<ast::NodePtr>( - [](auto&& list) { - return ast::make_node_ptr<ast::EventOptionNode>(LEXY_MOV(list)); - }); + static constexpr auto value = lexy::as_list<ast::AssignStatementList> >> construct_list<ast::ListValue, true>; }; struct EventStatement { - OVDL_GRAMMAR_KEYWORD_FLAG_DEFINE(id); - OVDL_GRAMMAR_KEYWORD_FLAG_DEFINE(title); - OVDL_GRAMMAR_KEYWORD_FLAG_DEFINE(desc); - OVDL_GRAMMAR_KEYWORD_FLAG_DEFINE(picture); - OVDL_GRAMMAR_KEYWORD_FLAG_DEFINE(is_triggered_only); - OVDL_GRAMMAR_KEYWORD_FLAG_DEFINE(fire_only_once); - OVDL_GRAMMAR_KEYWORD_FLAG_DEFINE(immediate); - OVDL_GRAMMAR_KEYWORD_FLAG_DEFINE(mean_time_to_happen); - OVDL_GRAMMAR_KEYWORD_DEFINE(trigger); - OVDL_GRAMMAR_KEYWORD_DEFINE(option); + using id = fkeyword_rule<"id", str_or_id>; + using title = fkeyword_rule<"title", str_or_id>; + using desc = fkeyword_rule<"desc", str_or_id>; + using picture = fkeyword_rule<"picture", str_or_id>; + using is_triggered_only = fkeyword_rule<"is_triggered_only", str_or_id>; + using fire_only_once = fkeyword_rule<"fire_only_once", str_or_id>; + using immediate = fkeyword_rule<"immediate", lexy::dsl::p<EffectBlock>>; + using mean_time_to_happen = fkeyword_rule<"mean_time_to_happen", lexy::dsl::p<EventMtthStatement>>; + using trigger = keyword_rule<"trigger", lexy::dsl::p<TriggerBlock>>; + using option = keyword_rule<"option", lexy::dsl::p<EventOptionList>>; + + struct EventList { + static constexpr auto rule = [] { + using helper = dsl::rule_helper<id, title, desc, picture, is_triggered_only, fire_only_once, immediate, mean_time_to_happen>; + + return helper::flags + + dsl::curly_bracketed.opt_list( + helper::p | lexy::dsl::p<trigger> | lexy::dsl::p<option> | + lexy::dsl::p<SAssignStatement<StringEscapeOption>>); + }(); + + static constexpr auto value = lexy::as_list<ast::AssignStatementList> >> construct_list<ast::ListValue, true>; + }; - static constexpr auto rule = [] { - constexpr auto symbol_value = lexy::dsl::symbol<event_symbols>(lexy::dsl::inline_<Identifier<StringEscapeOption>>); - - constexpr auto create_flags = - id_rule::flag.create() + - title_rule::flag.create() + - desc_rule::flag.create() + - picture_rule::flag.create() + - is_triggered_only_rule::flag.create() + - fire_only_once_rule::flag.create() + - immediate_rule::flag.create() + - mean_time_to_happen_rule::flag.create(); - - constexpr auto id_statement = StringStatement<id_p, ast::IdNode>; - constexpr auto title_statement = StringStatement<title_p, ast::TitleNode>; - constexpr auto desc_statement = StringStatement<desc_p, ast::DescNode>; - constexpr auto picture_statement = StringStatement<picture_p, ast::PictureNode>; - constexpr auto is_triggered_only_statement = StringStatement<is_triggered_only_p, ast::IsTriggeredNode>; - constexpr auto fire_only_once_statement = StringStatement<fire_only_once_p, ast::FireOnlyNode>; - constexpr auto immediate_statement = immediate_p >> lexy::dsl::p<EffectBlock>; - constexpr auto mean_time_to_happen_statement = mean_time_to_happen_p >> lexy::dsl::curly_bracketed(lexy::dsl::p<EventMtthStatement>); - - constexpr auto trigger_statement = trigger_p >> lexy::dsl::curly_bracketed.opt(lexy::dsl::p<TriggerList>); - constexpr auto option_statement = option_p >> lexy::dsl::curly_bracketed(lexy::dsl::p<EventOptionList>); - - return symbol_value >> - (create_flags + lexy::dsl::equal_sign + - lexy::dsl::curly_bracketed.opt_list( - id_statement | - title_statement | - desc_statement | - picture_statement | - is_triggered_only_statement | - fire_only_once_statement | - immediate_statement | - mean_time_to_happen_statement | - trigger_statement | - option_statement | - lexy::dsl::p<SimpleAssignmentStatement<StringEscapeOption>>)); - }(); + static constexpr auto rule = dsl::p<Identifier<StringEscapeOption>> >> lexy::dsl::equal_sign >> lexy::dsl::p<EventList>; static constexpr auto value = - lexy::as_list<std::vector<ast::NodePtr>> >> - lexy::callback<ast::NodePtr>( - [](auto& type, auto&& list) { - return ast::make_node_ptr<ast::EventNode>(type, LEXY_MOV(list)); - }, - [](auto& type, lexy::nullopt = {}) { - return ast::make_node_ptr<ast::EventNode>(type); + dsl::callback<ast::EventStatement*>( + [](ast::ParseState& state, NodeLocation loc, ast::IdentifierValue* name, ast::ListValue* list) { + static auto country_decl = state.ast().intern_cstr("country_event"); + static auto province_decl = state.ast().intern_cstr("province_event"); + + if (name->value(state.ast().symbol_interner()) != country_decl || name->value(state.ast().symbol_interner()) != province_decl) { + state.logger().warning("event declarator \"{}\" is not {} or {}", name->value(state.ast().symbol_interner()), country_decl, province_decl) // + .primary(loc, "here") + .finish(); + } + + return state.ast().create<ast::EventStatement>(loc, name->value(state.ast().symbol_interner()) == province_decl, list); }); }; @@ -170,13 +111,8 @@ namespace ovdl::v2script::grammar { // Allow arbitrary spaces between individual tokens. static constexpr auto whitespace = whitespace_specifier | comment_specifier; - static constexpr auto rule = lexy::dsl::terminator(lexy::dsl::eof).list(lexy::dsl::p<EventStatement> | lexy::dsl::p<SimpleAssignmentStatement<StringEscapeOption>>); + static constexpr auto rule = lexy::dsl::position + lexy::dsl::terminator(lexy::dsl::eof).list(lexy::dsl::p<EventStatement> | lexy::dsl::p<SAssignStatement<StringEscapeOption>>); - static constexpr auto value = lexy::as_list<std::vector<ast::NodePtr>> >> lexy::new_<ast::FileNode, ast::NodePtr>; + static constexpr auto value = lexy::as_list<ast::StatementList> >> construct<ast::FileTree>; }; - -#undef OVDL_GRAMMAR_KEYWORD_DEFINE -#undef OVDL_GRAMMAR_KEYWORD_FLAG_DEFINE -#undef OVDL_GRAMMAR_KEYWORD_STATEMENT -#undef OVDL_GRAMMAR_KEYWORD_FLAG_STATEMENT }
\ No newline at end of file diff --git a/src/openvic-dataloader/v2script/LuaDefinesGrammar.hpp b/src/openvic-dataloader/v2script/LuaDefinesGrammar.hpp index b64d0f9..4d27d3e 100644 --- a/src/openvic-dataloader/v2script/LuaDefinesGrammar.hpp +++ b/src/openvic-dataloader/v2script/LuaDefinesGrammar.hpp @@ -1,11 +1,25 @@ #pragma once +#include <lexy/_detail/config.hpp> #include <lexy/dsl.hpp> +#include "openvic-dataloader/v2script/AbstractSyntaxTree.hpp" + #include "SimpleGrammar.hpp" -#include "detail/LexyLitRange.hpp" +#include "detail/dsl.hpp" namespace ovdl::v2script::lua::grammar { + template<typename ReturnType, typename... Callback> + constexpr auto callback(Callback... cb) { + return dsl::callback<ReturnType>(cb...); + } + + template<typename T> + constexpr auto construct = v2script::grammar::construct<T>; + + template<typename T> + constexpr auto construct_list = v2script::grammar::construct_list<T>; + struct ParseOptions { }; @@ -17,20 +31,20 @@ namespace ovdl::v2script::lua::grammar { template<ParseOptions Options> struct Identifier { static constexpr auto rule = lexy::dsl::identifier(lexy::dsl::ascii::alpha_underscore, lexy::dsl::ascii::alpha_digit_underscore); - static constexpr auto value = lexy::callback<ast::NodePtr>( - [](auto lexeme) { - std::string str(lexeme.data(), lexeme.size()); - return ast::make_node_ptr<ast::IdentifierNode>(ast::NodeLocation { lexeme.begin(), lexeme.end() }, LEXY_MOV(str)); + static constexpr auto value = callback<ast::IdentifierValue*>( + [](ast::ParseState& state, auto lexeme) { + auto value = state.ast().intern(lexeme.data(), lexeme.size()); + return state.ast().create<ast::IdentifierValue>(lexeme.begin(), lexeme.end(), value); }); }; template<ParseOptions Options> struct Value { static constexpr auto rule = lexy::dsl::identifier(lexy::dsl::ascii::digit / lexy::dsl::lit_c<'.'> / lexy::dsl::lit_c<'-'>); - static constexpr auto value = lexy::callback<ast::NodePtr>( - [](auto lexeme) { - std::string str(lexeme.data(), lexeme.size()); - return ast::make_node_ptr<ast::IdentifierNode>(ast::NodeLocation { lexeme.begin(), lexeme.end() }, LEXY_MOV(str)); + static constexpr auto value = callback<ast::IdentifierValue*>( + [](ast::ParseState& state, auto lexeme) { + auto value = state.ast().intern(lexeme.data(), lexeme.size()); + return state.ast().create<ast::IdentifierValue>(lexeme.begin(), lexeme.end(), value); }); }; @@ -38,66 +52,50 @@ namespace ovdl::v2script::lua::grammar { struct String { static constexpr auto rule = [] { // Arbitrary code points that aren't control characters. - auto c = ovdl::detail::lexydsl::make_range<0x20, 0xFF>() - lexy::dsl::ascii::control; + auto c = dsl::make_range<0x20, 0xFF>() - lexy::dsl::ascii::control; return lexy::dsl::delimited(lexy::dsl::position(lexy::dsl::lit_b<'"'>))(c) | lexy::dsl::delimited(lexy::dsl::position(lexy::dsl::lit_b<'\''>))(c); }(); static constexpr auto value = lexy::as_string<std::string> >> - lexy::callback<ast::NodePtr>( - [](const char* begin, auto&& str, const char* end) { - return ast::make_node_ptr<ast::StringNode>(ast::NodeLocation::make_from(begin, end), LEXY_MOV(str)); + callback<ast::StringValue*>( + [](ast::ParseState& state, const char* begin, const std::string& str, const char* end) { + auto value = state.ast().intern(str.data(), str.length()); + return state.ast().create<ast::StringValue>(begin, end, value); }); }; template<ParseOptions Options> struct Expression { static constexpr auto rule = lexy::dsl::p<Value<Options>> | lexy::dsl::p<String<Options>>; - static constexpr auto value = lexy::forward<ast::NodePtr>; + static constexpr auto value = lexy::forward<ast::Value*>; }; template<ParseOptions Options> struct AssignmentStatement { static constexpr auto rule = - lexy::dsl::position(lexy::dsl::p<Identifier<Options>>) >> + dsl::p<Identifier<Options>> >> lexy::dsl::equal_sign >> (lexy::dsl::p<Expression<Options>> | lexy::dsl::recurse_branch<StatementListBlock<Options>>); - static constexpr auto value = lexy::callback<ast::NodePtr>( - [](const char* pos, auto name, lexy::nullopt = {}) { - return LEXY_MOV(name); - }, - [](auto name, lexy::nullopt = {}) { - return LEXY_MOV(name); - }, - [](const char* pos, auto name, auto&& initalizer) { - return ast::make_node_ptr<ast::AssignNode>(pos, LEXY_MOV(name), LEXY_MOV(initalizer)); + static constexpr auto value = callback<ast::AssignStatement*>( + [](ast::ParseState& state, const char* pos, ast::IdentifierValue* name, ast::Value* initializer) { + return state.ast().create<ast::AssignStatement>(pos, name, initializer); }); }; template<ParseOptions Options> struct StatementListBlock { static constexpr auto rule = - lexy::dsl::position(lexy::dsl::curly_bracketed.open()) >> - lexy::dsl::opt( - lexy::dsl::list( - lexy::dsl::recurse_branch<AssignmentStatement<Options>>, - lexy::dsl::trailing_sep(lexy::dsl::lit_c<','>))) >> - lexy::dsl::position(lexy::dsl::curly_bracketed.close()); + dsl::curly_bracketed( + lexy::dsl::opt( + lexy::dsl::list( + lexy::dsl::recurse_branch<AssignmentStatement<Options>>, + lexy::dsl::trailing_sep(lexy::dsl::lit_c<','>)))); static constexpr auto value = - lexy::as_list<std::vector<ast::NodePtr>> >> - lexy::callback<ast::NodePtr>( - [](const char* begin, lexy::nullopt, const char* end) { - return ast::make_node_ptr<ast::ListNode>(ast::NodeLocation::make_from(begin, end)); - }, - [](const char* begin, auto&& list, const char* end) { - return ast::make_node_ptr<ast::ListNode>(ast::NodeLocation::make_from(begin, end), LEXY_MOV(list)); - }, - [](const char* begin, auto& list, const char* end) { - return ast::make_node_ptr<ast::ListNode>(ast::NodeLocation::make_from(begin, end), list); - }); + lexy::as_list<ast::AssignStatementList> >> construct_list<ast::ListValue>; }; template<ParseOptions Options = ParseOptions {}> @@ -107,6 +105,6 @@ namespace ovdl::v2script::lua::grammar { static constexpr auto rule = lexy::dsl::position + lexy::dsl::terminator(lexy::dsl::eof).opt_list(lexy::dsl::p<AssignmentStatement<Options>>); - static constexpr auto value = lexy::as_list<std::vector<ast::NodePtr>> >> lexy::new_<ast::FileNode, ast::NodePtr>; + static constexpr auto value = lexy::as_list<ast::AssignStatementList> >> construct<ast::FileTree>; }; }
\ No newline at end of file diff --git a/src/openvic-dataloader/v2script/ModifierGrammar.hpp b/src/openvic-dataloader/v2script/ModifierGrammar.hpp index 96e928c..5b937d5 100644 --- a/src/openvic-dataloader/v2script/ModifierGrammar.hpp +++ b/src/openvic-dataloader/v2script/ModifierGrammar.hpp @@ -2,32 +2,55 @@ #include <openvic-dataloader/v2script/AbstractSyntaxTree.hpp> +#include <lexy/callback/container.hpp> #include <lexy/dsl.hpp> +#include <dryad/node.hpp> +#include <dryad/tree.hpp> + +#include "openvic-dataloader/NodeLocation.hpp" + #include "SimpleGrammar.hpp" #include "TriggerGrammar.hpp" +#include "detail/dsl.hpp" namespace ovdl::v2script::grammar { constexpr auto modifier_keyword = LEXY_KEYWORD("modifier", lexy::dsl::inline_<Identifier<StringEscapeOption>>); constexpr auto factor_keyword = LEXY_KEYWORD("factor", lexy::dsl::inline_<Identifier<StringEscapeOption>>); struct FactorStatement { - static constexpr auto rule = factor_keyword >> lexy::dsl::equal_sign + lexy::dsl::inline_<Identifier<StringEscapeOption>>; - static constexpr auto value = lexy::as_string<std::string> | lexy::new_<ast::FactorNode, ast::NodePtr>; + static constexpr auto rule = lexy::dsl::position(factor_keyword) >> (lexy::dsl::equal_sign + lexy::dsl::p<Identifier<StringEscapeOption>>); + static constexpr auto value = dsl::callback<ast::AssignStatement*>( + [](ast::ParseState& state, NodeLocation loc, ast::IdentifierValue* value) { + auto* factor = state.ast().create<ast::IdentifierValue>(loc, state.ast().intern("factor")); + return state.ast().create<ast::AssignStatement>(loc, factor, value); + }); + }; + + struct ModifierList { + struct expected_factor { + static constexpr auto name = "expected factor in modifier"; + }; + + static constexpr auto rule = [] { + auto factor_flag = lexy::dsl::context_flag<ModifierList>; + + auto element = (lexy::dsl::p<FactorStatement> >> factor_flag.set()) | lexy::dsl::p<TriggerStatement>; + + return dsl::curly_bracketed.list(factor_flag.create() + element) >> lexy::dsl::must(factor_flag.is_reset()).error<expected_factor>; + }(); + + static constexpr auto value = lexy::as_list<ast::AssignStatementList> >> construct_list<ast::ListValue>; }; struct ModifierStatement { static constexpr auto rule = - modifier_keyword >> - lexy::dsl::curly_bracketed.list( - lexy::dsl::p<FactorStatement> | - lexy::dsl::p<TriggerList>); - - static constexpr auto value = - lexy::as_list<std::vector<ast::NodePtr>> >> - lexy::callback<ast::NodePtr>( - [](auto&& list) { - return ast::make_node_ptr<ast::ModifierNode>(LEXY_MOV(list)); - }); + lexy::dsl::position(modifier_keyword) >> lexy::dsl::equal_sign >> lexy::dsl::p<ModifierList>; + + static constexpr auto value = dsl::callback<ast::AssignStatement*>( + [](ast::ParseState& state, NodeLocation loc, ast::ListValue* list) { + auto* factor = state.ast().create<ast::IdentifierValue>(loc, state.ast().intern("modifier")); + return state.ast().create<ast::AssignStatement>(loc, factor, list); + }); }; }
\ No newline at end of file diff --git a/src/openvic-dataloader/v2script/Parser.cpp b/src/openvic-dataloader/v2script/Parser.cpp index 3141550..29e9e80 100644 --- a/src/openvic-dataloader/v2script/Parser.cpp +++ b/src/openvic-dataloader/v2script/Parser.cpp @@ -1,29 +1,38 @@ #include "openvic-dataloader/v2script/Parser.hpp" -#include <functional> -#include <memory> +#include <iostream> #include <optional> #include <string> #include <utility> -#include <vector> +#include <openvic-dataloader/DiagnosticLogger.hpp> +#include <openvic-dataloader/NodeLocation.hpp> #include <openvic-dataloader/ParseError.hpp> #include <openvic-dataloader/ParseWarning.hpp> -#include <openvic-dataloader/detail/Concepts.hpp> +#include <openvic-dataloader/detail/LexyReportError.hpp> +#include <openvic-dataloader/detail/OStreamOutputIterator.hpp> +#include <openvic-dataloader/detail/utility/Concepts.hpp> +#include <openvic-dataloader/detail/utility/Utility.hpp> #include <openvic-dataloader/v2script/AbstractSyntaxTree.hpp> -#include <openvic-dataloader/v2script/NodeLocationMap.hpp> #include <lexy/action/parse.hpp> #include <lexy/encoding.hpp> #include <lexy/input/buffer.hpp> #include <lexy/input/file.hpp> +#include <lexy/input_location.hpp> #include <lexy/lexeme.hpp> #include <lexy/visualize.hpp> -#include "detail/BasicBufferHandler.hpp" +#include <dryad/node.hpp> +#include <dryad/tree.hpp> + +#include <fmt/core.h> + +#include "openvic-dataloader/Error.hpp" + #include "detail/DetectUtf8.hpp" -#include "detail/LexyReportError.hpp" -#include "detail/OStreamOutputIterator.hpp" +#include "detail/NullBuff.hpp" +#include "detail/ParseHandler.hpp" #include "detail/Warnings.hpp" #include "v2script/DecisionGrammar.hpp" #include "v2script/EventGrammar.hpp" @@ -35,42 +44,36 @@ using namespace ovdl::v2script; /// BufferHandler /// -class Parser::BufferHandler final : public detail::BasicBufferHandler<> { -public: +struct Parser::ParseHandler final : detail::BasicStateParseHandler<v2script::ast::ParseState> { constexpr bool is_exclusive_utf8() const { - return detail::is_utf8_no_ascii(_buffer); + return detail::is_utf8_no_ascii(buffer()); } - template<typename Node, typename ErrorCallback> - std::optional<std::vector<ParseError>> parse(const ErrorCallback& callback) { - auto result = lexy::parse<Node>(_buffer, callback); + template<typename Node> + std::optional<DiagnosticLogger::error_range> parse() { + auto result = lexy::parse<Node>(buffer(), *_parse_state, _parse_state->logger().error_callback()); if (!result) { - return result.errors(); + return _parse_state->logger().get_errors(); } - // This is mighty frustrating - _root = std::unique_ptr<ast::Node>(result.value()); + _parse_state->ast().set_root(result.value()); return std::nullopt; } - std::unique_ptr<ast::Node>& get_root() { - return _root; + ast::FileTree* root() { + return _parse_state->ast().root(); } - - ast::NodeLocationMap<decltype(_buffer)>& get_location_map() { - return _location_map; - } - -private: - friend class ::ovdl::v2script::ast::Node; - std::unique_ptr<ast::Node> _root; - ast::NodeLocationMap<decltype(_buffer)> _location_map; }; /// BufferHandler /// Parser::Parser() - : _buffer_handler(std::make_unique<BufferHandler>()) { - set_error_log_to_stderr(); + : _parse_handler(std::make_unique<ParseHandler>()) { + set_error_log_to_null(); +} + +Parser::Parser(std::basic_ostream<char>& error_stream) + : _parse_handler(std::make_unique<ParseHandler>()) { + set_error_log_to(error_stream); } Parser::Parser(Parser&&) = default; @@ -116,26 +119,29 @@ Parser Parser::from_file(const std::filesystem::path& path) { /// @param args /// template<typename... Args> -constexpr void Parser::_run_load_func(detail::LoadCallback<BufferHandler, Args...> auto func, Args... args) { - _warnings.clear(); - _errors.clear(); +constexpr void Parser::_run_load_func(detail::LoadCallback<Parser::ParseHandler*, Args...> auto func, Args... args) { _has_fatal_error = false; - if (auto error = func(_buffer_handler.get(), std::forward<Args>(args)...); error) { - _has_fatal_error = error.value().type == ParseError::Type::Fatal; - _errors.push_back(error.value()); - _error_stream.get() << "Error: " << _errors.back().message << '\n'; + auto error = func(_parse_handler.get(), std::forward<Args>(args)...); + auto error_message = _parse_handler->make_error_from(error); + if (!error_message.empty()) { + _has_error = true; + _has_fatal_error = true; + _parse_handler->parse_state().logger().create_log<error::BufferError>(DiagnosticLogger::DiagnosticKind::error, fmt::runtime(error_message)); + } + if (has_error() && &_error_stream.get() != &detail::cnull) { + print_errors_to(_error_stream.get()); } } constexpr Parser& Parser::load_from_buffer(const char* data, std::size_t size) { // Type can't be deduced? - _run_load_func(std::mem_fn(&BufferHandler::load_buffer_size), data, size); + _run_load_func(std::mem_fn(&ParseHandler::load_buffer_size), data, size); return *this; } constexpr Parser& Parser::load_from_buffer(const char* start, const char* end) { // Type can't be deduced? - _run_load_func(std::mem_fn(&BufferHandler::load_buffer), start, end); + _run_load_func(std::mem_fn(&ParseHandler::load_buffer), start, end); return *this; } @@ -146,7 +152,7 @@ constexpr Parser& Parser::load_from_string(const std::string_view string) { constexpr Parser& Parser::load_from_file(const char* path) { _file_path = path; // Type can be deduced?? - _run_load_func(std::mem_fn(&BufferHandler::load_file), path); + _run_load_func(std::mem_fn(&ParseHandler::load_file), path); return *this; } @@ -154,10 +160,6 @@ Parser& Parser::load_from_file(const std::filesystem::path& path) { return load_from_file(path.string().c_str()); } -constexpr Parser& Parser::load_from_file(const detail::Has_c_str auto& path) { - return load_from_file(path.c_str()); -} - /* REQUIREMENTS: * DAT-23 * DAT-26 @@ -165,149 +167,175 @@ constexpr Parser& Parser::load_from_file(const detail::Has_c_str auto& path) { * DAT-29 */ bool Parser::simple_parse() { - if (!_buffer_handler->is_valid()) { + if (!_parse_handler->is_valid()) { return false; } - if (_buffer_handler->is_exclusive_utf8()) { - _warnings.push_back(warnings::make_utf8_warning(_file_path)); + if (_parse_handler->is_exclusive_utf8()) { + _parse_handler->parse_state().logger().warning(warnings::make_utf8_warning(_file_path)); } - auto errors = _buffer_handler->parse<grammar::File<grammar::NoStringEscapeOption>>(ovdl::detail::ReporError.path(_file_path).to(detail::OStreamOutputIterator { _error_stream })); - if (errors) { - _errors.reserve(errors->size()); - for (auto& err : errors.value()) { - _has_fatal_error |= err.type == ParseError::Type::Fatal; - _errors.push_back(err); + auto errors = _parse_handler->parse<grammar::File<grammar::NoStringEscapeOption>>(); + _has_error = _parse_handler->parse_state().logger().errored(); + _has_warning = _parse_handler->parse_state().logger().warned(); + if (!_parse_handler->root()) { + _has_fatal_error = true; + if (&_error_stream.get() != &detail::cnull) { + print_errors_to(_error_stream); } return false; } - _file_node.reset(static_cast<ast::FileNode*>(_buffer_handler->get_root().release())); return true; } bool Parser::event_parse() { - if (!_buffer_handler->is_valid()) { + if (!_parse_handler->is_valid()) { return false; } - if (_buffer_handler->is_exclusive_utf8()) { - _warnings.push_back(warnings::make_utf8_warning(_file_path)); + if (_parse_handler->is_exclusive_utf8()) { + _parse_handler->parse_state().logger().warning(warnings::make_utf8_warning(_file_path)); } - auto errors = _buffer_handler->parse<grammar::EventFile>(ovdl::detail::ReporError.path(_file_path).to(detail::OStreamOutputIterator { _error_stream })); - if (errors) { - _errors.reserve(errors->size()); - for (auto& err : errors.value()) { - _has_fatal_error |= err.type == ParseError::Type::Fatal; - _errors.push_back(err); + auto errors = _parse_handler->parse<grammar::EventFile>(); + _has_error = _parse_handler->parse_state().logger().errored(); + _has_warning = _parse_handler->parse_state().logger().warned(); + if (!_parse_handler->root()) { + _has_fatal_error = true; + if (&_error_stream.get() != &detail::cnull) { + print_errors_to(_error_stream); } return false; } - _file_node.reset(static_cast<ast::FileNode*>(_buffer_handler->get_root().release())); return true; } bool Parser::decision_parse() { - if (!_buffer_handler->is_valid()) { + if (!_parse_handler->is_valid()) { return false; } - if (_buffer_handler->is_exclusive_utf8()) { - _warnings.push_back(warnings::make_utf8_warning(_file_path)); + if (_parse_handler->is_exclusive_utf8()) { + _parse_handler->parse_state().logger().warning(warnings::make_utf8_warning(_file_path)); } - auto errors = _buffer_handler->parse<grammar::DecisionFile>(ovdl::detail::ReporError.path(_file_path).to(detail::OStreamOutputIterator { _error_stream })); - if (errors) { - _errors.reserve(errors->size()); - for (auto& err : errors.value()) { - _has_fatal_error |= err.type == ParseError::Type::Fatal; - _errors.push_back(err); + auto errors = _parse_handler->parse<grammar::DecisionFile>(); + _has_error = _parse_handler->parse_state().logger().errored(); + _has_warning = _parse_handler->parse_state().logger().warned(); + if (!_parse_handler->root()) { + _has_fatal_error = true; + if (&_error_stream.get() != &detail::cnull) { + print_errors_to(_error_stream); } return false; } - _file_node.reset(static_cast<ast::FileNode*>(_buffer_handler->get_root().release())); return true; } bool Parser::lua_defines_parse() { - if (!_buffer_handler->is_valid()) { + if (!_parse_handler->is_valid()) { return false; } - if (_buffer_handler->is_exclusive_utf8()) { - _warnings.push_back(warnings::make_utf8_warning(_file_path)); + if (_parse_handler->is_exclusive_utf8()) { + _parse_handler->parse_state().logger().warning(warnings::make_utf8_warning(_file_path)); } - auto errors = _buffer_handler->parse<lua::grammar::File<>>(ovdl::detail::ReporError.path(_file_path).to(detail::OStreamOutputIterator { _error_stream })); - if (errors) { - _errors.reserve(errors->size()); - for (auto& err : errors.value()) { - _has_fatal_error |= err.type == ParseError::Type::Fatal; - _errors.push_back(err); + auto errors = _parse_handler->parse<lua::grammar::File<>>(); + _has_error = _parse_handler->parse_state().logger().errored(); + _has_warning = _parse_handler->parse_state().logger().warned(); + if (!_parse_handler->root()) { + _has_fatal_error = true; + if (&_error_stream.get() != &detail::cnull) { + print_errors_to(_error_stream); } return false; } - _file_node.reset(static_cast<ast::FileNode*>(_buffer_handler->get_root().release())); return true; } -const FileNode* Parser::get_file_node() const { - return _file_node.get(); +const FileTree* Parser::get_file_node() const { + return _parse_handler->root(); } -void Parser::generate_node_location_map() { - _buffer_handler->get_location_map().clear(); - _buffer_handler->get_location_map().generate_location_map(_buffer_handler->get_buffer(), get_file_node()); +std::string_view Parser::value(const ovdl::v2script::ast::FlatValue& node) const { + return node.value(_parse_handler->parse_state().ast().symbol_interner()); } -const ast::Node::line_col Parser::get_node_begin(const ast::NodeCPtr node) const { - if (!node) return { 0, 0 }; - return node->get_begin_line_col(*this); +std::string Parser::make_native_string() const { + return _parse_handler->parse_state().ast().make_native_visualizer(); } -const ast::Node::line_col Parser::get_node_end(const ast::NodeCPtr node) const { - if (!node) return { 0, 0 }; - return node->get_end_line_col(*this); +std::string Parser::make_list_string() const { + return _parse_handler->parse_state().ast().make_list_visualizer(); } -const ast::Node::line_col ast::Node::get_begin_line_col(const Parser& parser) const { - if (!parser._buffer_handler->is_valid() || parser._buffer_handler->_location_map.empty()) return {}; - line_col result {}; - auto [itr, range_end] = parser._buffer_handler->_location_map.equal_range(this); - if (itr != range_end) { - result.line = itr->second.line_nr(); - result.column = itr->second.column_nr(); +const FilePosition Parser::get_position(const ast::Node* node) const { + if (!node || !node->is_linked_in_tree()) { + return {}; } - // Standard doesn't really guarantee the direction of the range's sequence, but only GCC goes backwards - // TODO: DON'T USE STANDARD UNORDERED_MULTIMAP -#if defined(__GNUC__) && !defined(__clang__) - itr++; - if (itr != range_end) { - result.line = itr->second.line_nr(); - result.column = itr->second.column_nr(); + auto node_location = _parse_handler->parse_state().ast().location_of(node); + if (node_location.is_synthesized()) { + return {}; + } + + auto loc_begin = lexy::get_input_location(_parse_handler->buffer(), node_location.begin()); + FilePosition result { loc_begin.line_nr(), loc_begin.line_nr(), loc_begin.column_nr(), loc_begin.column_nr() }; + if (node_location.begin() < node_location.end()) { + auto loc_end = lexy::get_input_location(_parse_handler->buffer(), node_location.end(), loc_begin.anchor()); + result.end_line = loc_end.line_nr(); + result.end_column = loc_end.column_nr(); } -#endif return result; } -const ast::Node::line_col ast::Node::get_end_line_col(const Parser& parser) const { - if (!parser._buffer_handler->is_valid() || parser._buffer_handler->_location_map.empty()) return {}; - line_col result {}; - auto [itr, range_end] = parser._buffer_handler->_location_map.equal_range(this); - if (itr != range_end) { - result.line = itr->second.line_nr(); - result.column = itr->second.column_nr(); +Parser::error_range Parser::get_errors() const { + return _parse_handler->parse_state().logger().get_errors(); +} + +const FilePosition Parser::get_error_position(const error::Error* error) const { + if (!error || !error->is_linked_in_tree()) { + return {}; } - // Standard doesn't really guarantee the direction of the range's sequence, but only GCC goes backwards - // TODO: DON'T USE STANDARD UNORDERED_MULTIMAP -#if defined(__GNUC__) && !defined(__clang__) - return result; -#endif - itr++; - if (itr != range_end) { - result.line = itr->second.line_nr(); - result.column = itr->second.column_nr(); + auto err_location = _parse_handler->parse_state().logger().location_of(error); + if (err_location.is_synthesized()) { + return {}; + } + + auto loc_begin = lexy::get_input_location(_parse_handler->buffer(), err_location.begin()); + FilePosition result { loc_begin.line_nr(), loc_begin.line_nr(), loc_begin.column_nr(), loc_begin.column_nr() }; + if (err_location.begin() < err_location.end()) { + auto loc_end = lexy::get_input_location(_parse_handler->buffer(), err_location.end(), loc_begin.anchor()); + result.end_line = loc_end.line_nr(); + result.end_column = loc_end.column_nr(); } return result; +} + +void Parser::print_errors_to(std::basic_ostream<char>& stream) const { + auto errors = get_errors(); + if (errors.empty()) return; + for (const auto error : errors) { + dryad::visit_tree( + error, + [&](const error::BufferError* buffer_error) { + stream << buffer_error->message() << '\n'; + }, + [&](const error::ParseError* parse_error) { + stream << parse_error->message() << '\n'; + }, + [&](dryad::child_visitor<error::ErrorKind> visitor, const error::Semantic* semantic) { + stream << semantic->message() << '\n'; + auto annotations = semantic->annotations(); + for (auto annotation : annotations) { + visitor(annotation); + } + }, + [&](const error::PrimaryAnnotation* primary) { + stream << primary->message() << '\n'; + }, + [&](const error::SecondaryAnnotation* secondary) { + stream << secondary->message() << '\n'; + }); + } }
\ No newline at end of file diff --git a/src/openvic-dataloader/v2script/SimpleGrammar.hpp b/src/openvic-dataloader/v2script/SimpleGrammar.hpp index 7a59123..bd4adaa 100644 --- a/src/openvic-dataloader/v2script/SimpleGrammar.hpp +++ b/src/openvic-dataloader/v2script/SimpleGrammar.hpp @@ -1,14 +1,12 @@ #pragma once -#include <string> -#include <vector> - +#include <openvic-dataloader/NodeLocation.hpp> #include <openvic-dataloader/v2script/AbstractSyntaxTree.hpp> #include <lexy/callback.hpp> #include <lexy/dsl.hpp> -#include "detail/LexyLitRange.hpp" +#include "detail/dsl.hpp" // Grammar Definitions // /* REQUIREMENTS: @@ -21,6 +19,11 @@ * DAT-643 */ namespace ovdl::v2script::grammar { + template<typename T> + constexpr auto construct = dsl::construct<ast::ParseState, T>; + template<typename T, bool DisableEmpty = false, typename ListType = ast::AssignStatementList> + constexpr auto construct_list = dsl::construct_list<ast::ParseState, T, ListType, DisableEmpty>; + struct ParseOptions { /// @brief Makes string parsing avoid string escapes bool NoStringEscape; @@ -29,11 +32,6 @@ namespace ovdl::v2script::grammar { static constexpr ParseOptions NoStringEscapeOption = ParseOptions { true }; static constexpr ParseOptions StringEscapeOption = ParseOptions { false }; - template<ParseOptions Options> - struct StatementListBlock; - template<ParseOptions Options> - struct AssignmentStatement; - /* REQUIREMENTS: DAT-630 */ static constexpr auto whitespace_specifier = lexy::dsl::ascii::blank / lexy::dsl::ascii::newline; /* REQUIREMENTS: DAT-631 */ @@ -47,18 +45,18 @@ namespace ovdl::v2script::grammar { lexy::dsl::ascii::alpha_digit_underscore / LEXY_ASCII_ONE_OF("+:@%&'-.") / lexy::dsl::lit_b<0x8A> / lexy::dsl::lit_b<0x8C> / lexy::dsl::lit_b<0x8E> / lexy::dsl::lit_b<0x92> / lexy::dsl::lit_b<0x97> / lexy::dsl::lit_b<0x9A> / lexy::dsl::lit_b<0x9C> / - detail::lexydsl::make_range<0x9E, 0x9F>() / - detail::lexydsl::make_range<0xC0, 0xD6>() / - detail::lexydsl::make_range<0xD8, 0xF6>() / - detail::lexydsl::make_range<0xF8, 0xFF>(); + dsl::make_range<0x9E, 0x9F>() / + dsl::make_range<0xC0, 0xD6>() / + dsl::make_range<0xD8, 0xF6>() / + dsl::make_range<0xF8, 0xFF>(); static constexpr auto windows_1251_data_specifier_additions = - detail::lexydsl::make_range<0x80, 0x81>() / lexy::dsl::lit_b<0x83> / lexy::dsl::lit_b<0x8D> / lexy::dsl::lit_b<0x8F> / + dsl::make_range<0x80, 0x81>() / lexy::dsl::lit_b<0x83> / lexy::dsl::lit_b<0x8D> / lexy::dsl::lit_b<0x8F> / lexy::dsl::lit_b<0x90> / lexy::dsl::lit_b<0x9D> / lexy::dsl::lit_b<0x9F> / - detail::lexydsl::make_range<0xA1, 0xA3>() / lexy::dsl::lit_b<0xA5> / lexy::dsl::lit_b<0xA8> / lexy::dsl::lit_b<0xAA> / + dsl::make_range<0xA1, 0xA3>() / lexy::dsl::lit_b<0xA5> / lexy::dsl::lit_b<0xA8> / lexy::dsl::lit_b<0xAA> / lexy::dsl::lit_b<0xAF> / - detail::lexydsl::make_range<0xB2, 0xB4>() / lexy::dsl::lit_b<0xB8> / lexy::dsl::lit_b<0xBA> / - detail::lexydsl::make_range<0xBC, 0xBF>() / + dsl::make_range<0xB2, 0xB4>() / lexy::dsl::lit_b<0xB8> / lexy::dsl::lit_b<0xBA> / + dsl::make_range<0xBC, 0xBF>() / lexy::dsl::lit_b<0xD7> / lexy::dsl::lit_b<0xF7>; static constexpr auto data_specifier = windows_1252_data_specifier / windows_1251_data_specifier_additions; @@ -77,125 +75,145 @@ namespace ovdl::v2script::grammar { .map<'t'>('\t'); template<ParseOptions Options> - struct Identifier { - static constexpr auto rule = lexy::dsl::identifier(data_char_class); - static constexpr auto value = lexy::callback<ast::NodePtr>( - [](auto lexeme) { - std::string str(lexeme.data(), lexeme.size()); - return ast::make_node_ptr<ast::IdentifierNode>(ast::NodeLocation { lexeme.begin(), lexeme.end() }, LEXY_MOV(str)); - }); - }; - - /* REQUIREMENTS: - * DAT-633 - * DAT-634 - */ - template<ParseOptions Options> - struct StringExpression { - static constexpr auto rule = [] { - if constexpr (Options.NoStringEscape) { - auto c = ovdl::detail::lexydsl::make_range<0x20, 0xFF>() / lexy::dsl::lit_b<0x07> / lexy::dsl::lit_b<0x09> / lexy::dsl::lit_b<0x0A> / lexy::dsl::lit_b<0x0D>; - return lexy::dsl::delimited(lexy::dsl::position(lexy::dsl::lit_b<'"'>))(c); - } else { - // Arbitrary code points that aren't control characters. - auto c = ovdl::detail::lexydsl::make_range<0x20, 0xFF>() - lexy::dsl::ascii::control; - - // Escape sequences start with a backlash. - // They either map one of the symbols, - // or a Unicode code point of the form uXXXX. - auto escape = lexy::dsl::backslash_escape // - .symbol<escaped_symbols>(); - return lexy::dsl::delimited(lexy::dsl::position(lexy::dsl::lit_b<'"'>))(c, escape); - } - }(); - - static constexpr auto value = - lexy::as_string<std::string> >> - lexy::callback<ast::NodePtr>( - [](const char* begin, auto&& str, const char* end) { - return ast::make_node_ptr<ast::StringNode>(ast::NodeLocation::make_from(begin, end), LEXY_MOV(str), Options.NoStringEscape); + struct SimpleGrammar { + struct StatementListBlock; + + struct Identifier { + static constexpr auto rule = lexy::dsl::identifier(data_char_class); + static constexpr auto value = dsl::callback<ast::IdentifierValue*>( + [](ast::ParseState& state, auto lexeme) { + auto value = state.ast().intern(lexeme.data(), lexeme.size()); + return state.ast().create<ast::IdentifierValue>(ovdl::NodeLocation::make_from(lexeme.begin(), lexeme.end()), value); }); - }; - - /* REQUIREMENTS: DAT-638 */ - template<ParseOptions Options> - struct ValueExpression { - static constexpr auto rule = lexy::dsl::p<Identifier<Options>> | lexy::dsl::p<StringExpression<Options>>; - static constexpr auto value = lexy::forward<ast::NodePtr>; + }; + + /* REQUIREMENTS: + * DAT-633 + * DAT-634 + */ + struct StringExpression { + static constexpr auto rule = [] { + if constexpr (Options.NoStringEscape) { + auto c = dsl::make_range<0x20, 0xFF>() / lexy::dsl::lit_b<0x07> / lexy::dsl::lit_b<0x09> / lexy::dsl::lit_b<0x0A> / lexy::dsl::lit_b<0x0D>; + return lexy::dsl::delimited(lexy::dsl::position(lexy::dsl::lit_b<'"'>))(c); + } else { + // Arbitrary code points that aren't control characters. + auto c = dsl::make_range<0x20, 0xFF>() - lexy::dsl::ascii::control; + + // Escape sequences start with a backlash. + // They either map one of the symbols, + // or a Unicode code point of the form uXXXX. + auto escape = lexy::dsl::backslash_escape // + .symbol<escaped_symbols>(); + return lexy::dsl::delimited(lexy::dsl::position(lexy::dsl::lit_b<'"'>))(c, escape); + } + }(); + + static constexpr auto value = + lexy::as_string<std::string> >> + dsl::callback<ast::StringValue*>( + [](ast::ParseState& state, const char* begin, auto&& str, const char* end) { + auto value = state.ast().intern(str.data(), str.length()); + return state.ast().create<ast::StringValue>(ovdl::NodeLocation::make_from(begin, end), value); + }); + }; + + /* REQUIREMENTS: DAT-638 */ + struct ValueExpression { + static constexpr auto rule = lexy::dsl::p<Identifier> | lexy::dsl::p<StringExpression>; + static constexpr auto value = lexy::forward<ast::Value*>; + }; + + struct SimpleAssignmentStatement { + static constexpr auto rule = + dsl::p<Identifier> >> + (lexy::dsl::equal_sign >> + (lexy::dsl::p<ValueExpression> | lexy::dsl::recurse_branch<StatementListBlock>)); + + static constexpr auto value = construct<ast::AssignStatement>; + }; + + /* REQUIREMENTS: DAT-639 */ + struct AssignmentStatement { + static constexpr auto rule = + dsl::p<Identifier> >> + (lexy::dsl::equal_sign >> + (lexy::dsl::p<ValueExpression> | lexy::dsl::recurse_branch<StatementListBlock>) | + lexy::dsl::else_ >> lexy::dsl::return_) | + dsl::p<StringExpression> | + lexy::dsl::recurse_branch<StatementListBlock>; + + static constexpr auto value = dsl::callback<ast::Statement*>( + [](ast::ParseState& state, const char* pos, ast::IdentifierValue* name, ast::Value* initializer) { + return state.ast().create<ast::AssignStatement>(pos, name, initializer); + }, + [](ast::ParseState& state, const char* pos, ast::Value* left, lexy::nullopt = {}) { + return state.ast().create<ast::ValueStatement>(pos, left); + }, + [](ast::ParseState& state, ast::Value* left) { + return state.ast().create<ast::ValueStatement>(state.ast().location_of(left), left); + }); + }; + + /* REQUIREMENTS: DAT-640 */ + struct StatementListBlock { + static constexpr auto rule = + dsl::curly_bracketed( + (lexy::dsl::opt(lexy::dsl::list(lexy::dsl::recurse_branch<AssignmentStatement>)) + + lexy::dsl::opt(lexy::dsl::semicolon))); + + static constexpr auto value = + lexy::as_list<ast::StatementList> >> + dsl::callback<ast::ListValue*>( + [](ast::ParseState& state, const char* begin, auto&& list, const char* end) { + if constexpr (std::is_same_v<std::decay_t<decltype(list)>, lexy::nullopt>) { + return state.ast().create<ast::ListValue>(ovdl::NodeLocation::make_from(begin, end)); + } else { + return state.ast().create<ast::ListValue>(ovdl::NodeLocation::make_from(begin, end), LEXY_MOV(list)); + } + }, + [](ast::ParseState& state, const char* begin, auto&& list, auto&& semicolon, const char* end) { + if constexpr (std::is_same_v<std::decay_t<decltype(list)>, lexy::nullopt>) { + return state.ast().create<ast::ListValue>(ovdl::NodeLocation::make_from(begin, end)); + } else { + return state.ast().create<ast::ListValue>(ovdl::NodeLocation::make_from(begin, end), LEXY_MOV(list)); + } + }); + }; }; template<ParseOptions Options> - struct SimpleAssignmentStatement { - static constexpr auto rule = - lexy::dsl::position(lexy::dsl::p<Identifier<Options>>) >> - (lexy::dsl::equal_sign + - (lexy::dsl::p<ValueExpression<Options>> | lexy::dsl::recurse_branch<StatementListBlock<Options>>)); - - static constexpr auto value = lexy::callback<ast::NodePtr>( - [](const char* pos, auto name, auto&& initalizer) { - return ast::make_node_ptr<ast::AssignNode>(pos, LEXY_MOV(name), LEXY_MOV(initalizer)); - }); - }; + using StringExpression = typename SimpleGrammar<Options>::StringExpression; - /* REQUIREMENTS: DAT-639 */ template<ParseOptions Options> - struct AssignmentStatement { - static constexpr auto rule = - lexy::dsl::position(lexy::dsl::p<Identifier<Options>>) >> - (lexy::dsl::equal_sign >> - (lexy::dsl::p<ValueExpression<Options>> | lexy::dsl::recurse_branch<StatementListBlock<Options>>) | - lexy::dsl::else_ >> lexy::dsl::return_) | - lexy::dsl::p<StringExpression<Options>> | - lexy::dsl::recurse_branch<StatementListBlock<Options>>; - - static constexpr auto value = lexy::callback<ast::NodePtr>( - [](const char* pos, auto name, lexy::nullopt = {}) { - return LEXY_MOV(name); - }, - [](auto name, lexy::nullopt = {}) { - return LEXY_MOV(name); - }, - [](const char* pos, auto name, auto&& initalizer) { - return ast::make_node_ptr<ast::AssignNode>(pos, LEXY_MOV(name), LEXY_MOV(initalizer)); - }); - }; + using Identifier = typename SimpleGrammar<Options>::Identifier; - /* REQUIREMENTS: DAT-640 */ template<ParseOptions Options> - struct StatementListBlock { - static constexpr auto rule = - lexy::dsl::position(lexy::dsl::curly_bracketed.open()) >> - (lexy::dsl::opt(lexy::dsl::list(lexy::dsl::recurse_branch<AssignmentStatement<Options>>)) + - lexy::dsl::opt(lexy::dsl::semicolon)) >> - lexy::dsl::position(lexy::dsl::curly_bracketed.close()); - - static constexpr auto value = - lexy::as_list<std::vector<ast::NodePtr>> >> - lexy::callback<ast::NodePtr>( - [](const char* begin, lexy::nullopt, const char* end) { - return ast::make_node_ptr<ast::ListNode>(ast::NodeLocation::make_from(begin, end)); - }, - [](const char* begin, auto&& list, const char* end) { - return ast::make_node_ptr<ast::ListNode>(ast::NodeLocation::make_from(begin, end), LEXY_MOV(list)); - }, - [](const char* begin, lexy::nullopt, lexy::nullopt, const char* end) { - return ast::make_node_ptr<ast::ListNode>(ast::NodeLocation::make_from(begin, end)); - }, - [](const char* begin, auto&& list, lexy::nullopt, const char* end) { - return ast::make_node_ptr<ast::ListNode>(ast::NodeLocation::make_from(begin, end), LEXY_MOV(list)); - }, - [](const char* begin, auto& list, const char* end) { - return ast::make_node_ptr<ast::ListNode>(ast::NodeLocation::make_from(begin, end), list); - }); - }; + using SAssignStatement = typename SimpleGrammar<Options>::SimpleAssignmentStatement; + + template<ovdl::detail::string_literal Keyword, auto Production, auto Value = dsl::default_kw_value<ast::ParseState, ast::IdentifierValue, Keyword>> + using keyword_rule = dsl::keyword_rule< + ast::ParseState, + Identifier<StringEscapeOption>, + ast::AssignStatement, + Keyword, Production, Value>; + + template<ovdl::detail::string_literal Keyword, auto Production, auto Value = dsl::default_kw_value<ast::ParseState, ast::IdentifierValue, Keyword>> + using fkeyword_rule = dsl::fkeyword_rule< + ast::ParseState, + Identifier<StringEscapeOption>, + ast::AssignStatement, + Keyword, Production, Value>; template<ParseOptions Options> struct File { // Allow arbitrary spaces between individual tokens. static constexpr auto whitespace = whitespace_specifier | comment_specifier; - static constexpr auto rule = lexy::dsl::position + lexy::dsl::terminator(lexy::dsl::eof).opt_list(lexy::dsl::p<AssignmentStatement<Options>>); + static constexpr auto rule = lexy::dsl::position( + lexy::dsl::terminator(lexy::dsl::eof) + .opt_list(lexy::dsl::p<typename SimpleGrammar<Options>::AssignmentStatement>)); - static constexpr auto value = lexy::as_list<std::vector<ast::NodePtr>> >> lexy::new_<ast::FileNode, ast::NodePtr>; + static constexpr auto value = lexy::as_list<ast::StatementList> >> construct<ast::FileTree>; }; } diff --git a/src/openvic-dataloader/v2script/TriggerGrammar.hpp b/src/openvic-dataloader/v2script/TriggerGrammar.hpp index 3849b12..095d115 100644 --- a/src/openvic-dataloader/v2script/TriggerGrammar.hpp +++ b/src/openvic-dataloader/v2script/TriggerGrammar.hpp @@ -6,37 +6,24 @@ #include <lexy/dsl.hpp> #include "SimpleGrammar.hpp" +#include "detail/dsl.hpp" namespace ovdl::v2script::grammar { struct TriggerStatement { - static constexpr auto rule = lexy::dsl::inline_<SimpleAssignmentStatement<StringEscapeOption>>; + static constexpr auto rule = lexy::dsl::p<SAssignStatement<StringEscapeOption>>; - static constexpr auto value = lexy::callback<ast::NodePtr>( - [](auto name, auto&& initalizer) { - return ast::make_node_ptr<ast::ExecutionNode>({}, ast::ExecutionNode::Type::Trigger, LEXY_MOV(name), LEXY_MOV(initalizer)); - }); + static constexpr auto value = lexy::forward<ast::AssignStatement*>; }; struct TriggerList { - static constexpr auto rule = lexy::dsl::list(lexy::dsl::p<SimpleAssignmentStatement<StringEscapeOption>>); - - static constexpr auto value = - lexy::as_list<std::vector<ast::NodePtr>> >> - lexy::callback<ast::NodePtr>( - [](auto&& list) { - return ast::make_node_ptr<ast::ExecutionListNode>(ast::ExecutionNode::Type::Trigger, LEXY_MOV(list)); - }); + static constexpr auto rule = lexy::dsl::list(lexy::dsl::p<TriggerStatement>); + + static constexpr auto value = lexy::as_list<ast::AssignStatementList>; }; struct TriggerBlock { - static constexpr auto rule = lexy::dsl::curly_bracketed.opt(lexy::dsl::p<TriggerList>); - - static constexpr auto value = lexy::callback<ast::NodePtr>( - [](auto&& list) { - return LEXY_MOV(list); - }, - [](lexy::nullopt = {}) { - return lexy::nullopt {}; - }); + static constexpr auto rule = dsl::curly_bracketed.opt(lexy::dsl::p<TriggerList>); + + static constexpr auto value = construct_list<ast::ListValue>; }; }
\ No newline at end of file |