diff options
Diffstat (limited to 'src/openvic-dataloader/v2script')
-rw-r--r-- | src/openvic-dataloader/v2script/AbstractSyntaxTree.cpp | 53 | ||||
-rw-r--r-- | src/openvic-dataloader/v2script/EventGrammar.hpp | 8 | ||||
-rw-r--r-- | src/openvic-dataloader/v2script/LuaDefinesGrammar.hpp | 133 | ||||
-rw-r--r-- | src/openvic-dataloader/v2script/ModifierGrammar.hpp | 14 | ||||
-rw-r--r-- | src/openvic-dataloader/v2script/ParseState.hpp | 19 | ||||
-rw-r--r-- | src/openvic-dataloader/v2script/Parser.cpp | 195 | ||||
-rw-r--r-- | src/openvic-dataloader/v2script/SimpleGrammar.hpp | 307 |
7 files changed, 461 insertions, 268 deletions
diff --git a/src/openvic-dataloader/v2script/AbstractSyntaxTree.cpp b/src/openvic-dataloader/v2script/AbstractSyntaxTree.cpp index abade40..5a98b40 100644 --- a/src/openvic-dataloader/v2script/AbstractSyntaxTree.cpp +++ b/src/openvic-dataloader/v2script/AbstractSyntaxTree.cpp @@ -1,8 +1,7 @@ -#include <stddef.h> - -#include <openvic-dataloader/v2script/AbstractSyntaxTree.hpp> +#include "openvic-dataloader/v2script/AbstractSyntaxTree.hpp" #include <lexy/dsl/option.hpp> +#include <lexy/encoding.hpp> #include <lexy/input_location.hpp> #include <dryad/node.hpp> @@ -23,6 +22,15 @@ ListValue::ListValue(dryad::node_ctor ctor, StatementList statements) } } +ListValue::ListValue(dryad::node_ctor ctor, AssignStatementList 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()) { @@ -32,29 +40,22 @@ FileTree::FileTree(dryad::node_ctor ctor, StatementList statements) : node_base( } } -// 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 { +FileTree::FileTree(dryad::node_ctor ctor, AssignStatementList statements) : node_base(ctor) { + insert_child_list_after(nullptr, statements); + if (statements.empty()) { + _last_node = nullptr; + } else { + _last_node = statements.back(); + } +} + +std::string FileAbstractSyntaxTree::make_list_visualizer() const { const int INDENT_SIZE = 2; std::string result; unsigned int level = 0; - for (auto [event, node] : dryad::traverse(_tree)) { + for (auto [event, node] : dryad::traverse(this->_tree)) { if (event == dryad::traverse_event::exit) { --level; continue; @@ -66,7 +67,7 @@ std::string AbstractSyntaxTree::make_list_visualizer() const { dryad::visit_node( node, [&](const FlatValue* value) { - result.append(value->value(_symbol_interner)); + result.append(value->value(this->_symbol_interner)); }, [&](const ListValue* value) { }, @@ -89,19 +90,19 @@ std::string AbstractSyntaxTree::make_list_visualizer() const { return result; } -std::string AbstractSyntaxTree::make_native_visualizer() const { +std::string FileAbstractSyntaxTree::make_native_visualizer() const { constexpr int INDENT_SIZE = 2; std::string result; unsigned int level = 0; dryad::visit_tree( - _tree, + this->_tree, [&](const IdentifierValue* value) { - result.append(value->value(_symbol_interner)); + result.append(value->value(this->_symbol_interner)); }, [&](const StringValue* value) { - result.append(1, '"').append(value->value(_symbol_interner)).append(1, '"'); + result.append(1, '"').append(value->value(this->_symbol_interner)).append(1, '"'); }, [&](dryad::child_visitor<NodeKind> visitor, const ValueStatement* statement) { visitor(statement->value()); diff --git a/src/openvic-dataloader/v2script/EventGrammar.hpp b/src/openvic-dataloader/v2script/EventGrammar.hpp index 27f6459..130a233 100644 --- a/src/openvic-dataloader/v2script/EventGrammar.hpp +++ b/src/openvic-dataloader/v2script/EventGrammar.hpp @@ -11,8 +11,8 @@ #include "openvic-dataloader/NodeLocation.hpp" -#include "ParseState.hpp" #include "SimpleGrammar.hpp" +#include "detail/InternalConcepts.hpp" #include "detail/dsl.hpp" #include "v2script/AiBehaviorGrammar.hpp" #include "v2script/EffectGrammar.hpp" @@ -28,7 +28,7 @@ namespace ovdl::v2script::grammar { struct MonthValue { static constexpr auto rule = lexy::dsl::p<Identifier<StringEscapeOption>>; static constexpr auto value = dsl::callback<ast::IdentifierValue*>( - [](ast::ParseState& state, ast::IdentifierValue* value) { + [](detail::IsParseState auto& 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); @@ -94,7 +94,7 @@ namespace ovdl::v2script::grammar { static constexpr auto value = dsl::callback<ast::EventStatement*>( - [](ast::ParseState& state, NodeLocation loc, ast::IdentifierValue* name, ast::ListValue* list) { + [](detail::IsParseState auto& 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"); @@ -104,7 +104,7 @@ namespace ovdl::v2script::grammar { .finish(); } - return state.ast().create<ast::EventStatement>(loc, name->value(state.ast().symbol_interner()) == province_decl, list); + return state.ast().template create<ast::EventStatement>(loc, name->value(state.ast().symbol_interner()) == province_decl, list); }); }; diff --git a/src/openvic-dataloader/v2script/LuaDefinesGrammar.hpp b/src/openvic-dataloader/v2script/LuaDefinesGrammar.hpp index 96cce99..885413c 100644 --- a/src/openvic-dataloader/v2script/LuaDefinesGrammar.hpp +++ b/src/openvic-dataloader/v2script/LuaDefinesGrammar.hpp @@ -4,9 +4,12 @@ #include <lexy/_detail/config.hpp> #include <lexy/dsl.hpp> +#include <lexy/dsl/delimited.hpp> +#include <lexy/dsl/recover.hpp> +#include <lexy/dsl/unicode.hpp> -#include "ParseState.hpp" #include "SimpleGrammar.hpp" +#include "detail/InternalConcepts.hpp" #include "detail/dsl.hpp" namespace ovdl::v2script::lua::grammar { @@ -21,90 +24,118 @@ namespace ovdl::v2script::lua::grammar { template<typename T> constexpr auto construct_list = v2script::grammar::construct_list<T>; - struct ParseOptions { - }; - - template<ParseOptions Options> struct StatementListBlock; static constexpr auto comment_specifier = LEXY_LIT("--") >> lexy::dsl::until(lexy::dsl::newline).or_eof(); - 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 = 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); - }); + static constexpr auto value = + callback<ast::IdentifierValue*>( + [](detail::IsParseState auto& state, auto lexeme) { + auto value = state.ast().intern(lexeme.data(), lexeme.size()); + return state.ast().template 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 = 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 String { - static constexpr auto rule = [] { - // Arbitrary code points that aren't control characters. - 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> >> - 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); + callback<ast::IdentifierValue*>( + [](detail::IsParseState auto& state, auto lexeme) { + auto value = state.ast().intern(lexeme.data(), lexeme.size()); + return state.ast().template create<ast::IdentifierValue>(lexeme.begin(), lexeme.end(), value); }); }; - template<ParseOptions Options> + struct String : lexy::scan_production<ast::StringValue*>, + lexy::token_production { + template<typename Context, typename Reader> + static constexpr scan_result scan(lexy::rule_scanner<Context, Reader>& scanner, detail::IsParseState auto& state) { + using encoding = typename Reader::encoding; + + constexpr auto c = [] { + if constexpr (std::same_as<encoding, lexy::default_encoding> || std::same_as<encoding, lexy::byte_encoding>) { + // Arbitrary code points that aren't control characters. + return dsl::lit_b_range<0x20, 0xFF> - lexy::dsl::ascii::control; + } else { + return -lexy::dsl::unicode::control; + } + }(); + auto rule = lexy::dsl::quoted(c) | lexy::dsl::single_quoted(c); + auto begin = scanner.position(); + lexy::scan_result<std::string> str_result; + scanner.parse(str_result, rule); + if (!scanner || !str_result) + return lexy::scan_failed; + auto end = scanner.position(); + auto str = str_result.value(); + auto value = state.ast().intern(str.data(), str.size()); + return state.ast().template create<ast::StringValue>(begin, end, value); + } + + static constexpr auto rule = lexy::dsl::peek(lexy::dsl::quoted.open() | lexy::dsl::single_quoted.open()) >> lexy::dsl::scan; + static constexpr auto value = ovdl::v2script::grammar::convert_as_string<std::string> >> lexy::forward<ast::StringValue*>; + }; + struct Expression { - static constexpr auto rule = lexy::dsl::p<Value<Options>> | lexy::dsl::p<String<Options>>; + static constexpr auto rule = lexy::dsl::p<Value> | lexy::dsl::p<String>; static constexpr auto value = lexy::forward<ast::Value*>; }; - template<ParseOptions Options> struct AssignmentStatement { - static constexpr auto rule = - dsl::p<Identifier<Options>> >> - lexy::dsl::equal_sign >> - (lexy::dsl::p<Expression<Options>> | lexy::dsl::recurse_branch<StatementListBlock<Options>>); + static constexpr auto rule = [] { + auto right_brace = lexy::dsl::lit_c<'}'>; + + auto expression = lexy::dsl::p<Expression>; + auto statement_list = lexy::dsl::recurse_branch<StatementListBlock>; + + auto rhs_recover = lexy::dsl::recover(expression, statement_list).limit(right_brace); + auto rhs_try = lexy::dsl::try_(expression | statement_list, rhs_recover); + + auto identifier = dsl::p<Identifier> >> lexy::dsl::equal_sign + rhs_try; + + auto recover = lexy::dsl::recover(identifier).limit(right_brace); + return lexy::dsl::try_(identifier, recover); + }(); 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); + [](detail::IsParseState auto& state, const char* pos, ast::IdentifierValue* name, ast::Value* initializer) -> ast::AssignStatement* { + if (initializer == nullptr) return nullptr; + return state.ast().template create<ast::AssignStatement>(pos, name, initializer); + }, + [](detail::IsParseState auto& state, ast::Value*) { + return nullptr; + }, + [](detail::IsParseState auto& state) { + return nullptr; }); }; - template<ParseOptions Options> struct StatementListBlock { - static constexpr auto rule = - 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 rule = [] { + auto right_brace = lexy::dsl::lit_c<'}'>; + auto comma = lexy::dsl::lit_c<','>; + + auto assign_statement = lexy::dsl::recurse_branch<AssignmentStatement>; + auto assign_try = lexy::dsl::try_(assign_statement); + + auto curly_bracket = dsl::curly_bracketed.opt_list( + assign_try, + lexy::dsl::trailing_sep(comma)); + + return lexy::dsl::try_(curly_bracket, lexy::dsl::find(right_brace)); + }(); static constexpr auto value = lexy::as_list<ast::AssignStatementList> >> construct_list<ast::ListValue>; }; - template<ParseOptions Options = ParseOptions {}> struct File { // Allow arbitrary spaces between individual tokens. static constexpr auto whitespace = ovdl::v2script::grammar::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<AssignmentStatement>); static constexpr auto value = lexy::as_list<ast::AssignStatementList> >> construct<ast::FileTree>; }; diff --git a/src/openvic-dataloader/v2script/ModifierGrammar.hpp b/src/openvic-dataloader/v2script/ModifierGrammar.hpp index 22592d4..122a8c7 100644 --- a/src/openvic-dataloader/v2script/ModifierGrammar.hpp +++ b/src/openvic-dataloader/v2script/ModifierGrammar.hpp @@ -10,9 +10,9 @@ #include "openvic-dataloader/NodeLocation.hpp" -#include "ParseState.hpp" #include "SimpleGrammar.hpp" #include "TriggerGrammar.hpp" +#include "detail/InternalConcepts.hpp" #include "detail/dsl.hpp" namespace ovdl::v2script::grammar { @@ -22,9 +22,9 @@ namespace ovdl::v2script::grammar { struct FactorStatement { 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); + [](detail::IsParseState auto& state, NodeLocation loc, ast::IdentifierValue* value) { + auto* factor = state.ast().template create<ast::IdentifierValue>(loc, state.ast().intern("factor")); + return state.ast().template create<ast::AssignStatement>(loc, factor, value); }); }; @@ -49,9 +49,9 @@ namespace ovdl::v2script::grammar { 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); + [](detail::IsParseState auto& state, NodeLocation loc, ast::ListValue* list) { + auto* factor = state.ast().template create<ast::IdentifierValue>(loc, state.ast().intern("modifier")); + return state.ast().template create<ast::AssignStatement>(loc, factor, list); }); }; }
\ No newline at end of file diff --git a/src/openvic-dataloader/v2script/ParseState.hpp b/src/openvic-dataloader/v2script/ParseState.hpp index 8e29bf5..954e39d 100644 --- a/src/openvic-dataloader/v2script/ParseState.hpp +++ b/src/openvic-dataloader/v2script/ParseState.hpp @@ -1,23 +1,24 @@ #pragma once -#include <openvic-dataloader/File.hpp> -#include <openvic-dataloader/ParseState.hpp> #include <openvic-dataloader/v2script/AbstractSyntaxTree.hpp> #include <lexy/encoding.hpp> +#include "../openvic-dataloader/ParseState.hpp" +#include "AbstractSyntaxTree.hpp" +#include "File.hpp" +#include "detail/InternalConcepts.hpp" + namespace ovdl::v2script::ast { - using File = ovdl::BasicFile<lexy::default_encoding, Node>; - struct AbstractSyntaxTree : ovdl::BasicAbstractSyntaxTree<File, FileTree> { - using BasicAbstractSyntaxTree::BasicAbstractSyntaxTree; + + struct FileAbstractSyntaxTree : ovdl::BasicAbstractSyntaxTree<ovdl::BasicFile<Node>, FileTree> { + using ovdl::BasicAbstractSyntaxTree<ovdl::BasicFile<Node>, FileTree>::BasicAbstractSyntaxTree; std::string make_list_visualizer() const; std::string make_native_visualizer() const; }; - using ParseState = ovdl::ParseState<AbstractSyntaxTree>; + using ParseState = ovdl::ParseState<FileAbstractSyntaxTree>; - 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"); + static_assert(detail::IsParseState<ast::ParseState>, "ParseState failed IsParseState concept"); }
\ No newline at end of file diff --git a/src/openvic-dataloader/v2script/Parser.cpp b/src/openvic-dataloader/v2script/Parser.cpp index eb491d5..23dada7 100644 --- a/src/openvic-dataloader/v2script/Parser.cpp +++ b/src/openvic-dataloader/v2script/Parser.cpp @@ -4,16 +4,15 @@ #include <iostream> #include <optional> #include <string> +#include <type_traits> #include <utility> -#include <openvic-dataloader/DiagnosticLogger.hpp> +#include <openvic-dataloader/Error.hpp> #include <openvic-dataloader/NodeLocation.hpp> -#include <openvic-dataloader/ParseError.hpp> -#include <openvic-dataloader/ParseWarning.hpp> -#include <openvic-dataloader/detail/LexyReportError.hpp> +#include <openvic-dataloader/detail/Concepts.hpp> +#include <openvic-dataloader/detail/Encoding.hpp> #include <openvic-dataloader/detail/OStreamOutputIterator.hpp> -#include <openvic-dataloader/detail/utility/Concepts.hpp> -#include <openvic-dataloader/detail/utility/Utility.hpp> +#include <openvic-dataloader/detail/Utility.hpp> #include <openvic-dataloader/v2script/AbstractSyntaxTree.hpp> #include <lexy/action/parse.hpp> @@ -29,10 +28,8 @@ #include <fmt/core.h> -#include "openvic-dataloader/Error.hpp" - +#include "DiagnosticLogger.hpp" #include "ParseState.hpp" -#include "detail/DetectUtf8.hpp" #include "detail/NullBuff.hpp" #include "detail/ParseHandler.hpp" #include "detail/Warnings.hpp" @@ -44,29 +41,46 @@ using namespace ovdl; using namespace ovdl::v2script; -/// BufferHandler /// +/// ParseHandler /// struct Parser::ParseHandler final : detail::BasicStateParseHandler<v2script::ast::ParseState> { - constexpr bool is_exclusive_utf8() const { - return detail::is_utf8_no_ascii(buffer()); - } - template<typename Node> std::optional<DiagnosticLogger::error_range> parse() { - auto result = lexy::parse<Node>(buffer(), *_parse_state, _parse_state->logger().error_callback()); + if (parse_state().encoding() == ovdl::detail::Encoding::Utf8) { + parse_state().logger().warning(warnings::make_utf8_warning(path())); + } + + auto result = [&] { + switch (parse_state().encoding()) { + using enum detail::Encoding; + case Ascii: + case Utf8: + return lexy::parse<Node>(buffer<lexy::utf8_char_encoding>(), parse_state(), parse_state().logger().error_callback()); + case Unknown: + case Windows1251: + case Windows1252: + return lexy::parse<Node>(buffer<lexy::default_encoding>(), parse_state(), parse_state().logger().error_callback()); + default: + ovdl::detail::unreachable(); + } + }(); if (!result) { - return _parse_state->logger().get_errors(); + return parse_state().logger().get_errors(); } - _parse_state->ast().set_root(result.value()); + parse_state().ast().set_root(result.value()); return std::nullopt; } ast::FileTree* root() { - return _parse_state->ast().root(); + return parse_state().ast().root(); + } + + Parser::error_range get_errors() { + return parse_state().logger().get_errors(); } }; -/// BufferHandler /// +/// ParseHandler /// Parser::Parser() : _parse_handler(std::make_unique<ParseHandler>()) { @@ -82,29 +96,29 @@ Parser::Parser(Parser&&) = default; Parser& Parser::operator=(Parser&&) = default; Parser::~Parser() = default; -Parser Parser::from_buffer(const char* data, std::size_t size) { +Parser Parser::from_buffer(const char* data, std::size_t size, std::optional<detail::Encoding> encoding_fallback) { Parser result; - return std::move(result.load_from_buffer(data, size)); + return std::move(result.load_from_buffer(data, size, encoding_fallback)); } -Parser Parser::from_buffer(const char* start, const char* end) { +Parser Parser::from_buffer(const char* start, const char* end, std::optional<detail::Encoding> encoding_fallback) { Parser result; - return std::move(result.load_from_buffer(start, end)); + return std::move(result.load_from_buffer(start, end, encoding_fallback)); } -Parser Parser::from_string(const std::string_view string) { +Parser Parser::from_string(const std::string_view string, std::optional<detail::Encoding> encoding_fallback) { Parser result; - return std::move(result.load_from_string(string)); + return std::move(result.load_from_string(string, encoding_fallback)); } -Parser Parser::from_file(const char* path) { +Parser Parser::from_file(const char* path, std::optional<detail::Encoding> encoding_fallback) { Parser result; - return std::move(result.load_from_file(path)); + return std::move(result.load_from_file(path, encoding_fallback)); } -Parser Parser::from_file(const std::filesystem::path& path) { +Parser Parser::from_file(const std::filesystem::path& path, std::optional<detail::Encoding> encoding_fallback) { Parser result; - return std::move(result.load_from_file(path)); + return std::move(result.load_from_file(path, encoding_fallback)); } /// @@ -128,38 +142,38 @@ constexpr void Parser::_run_load_func(detail::LoadCallback<Parser::ParseHandler* 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)); + _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()); } } -constexpr Parser& Parser::load_from_buffer(const char* data, std::size_t size) { +constexpr Parser& Parser::load_from_buffer(const char* data, std::size_t size, std::optional<detail::Encoding> encoding_fallback) { // Type can't be deduced? - _run_load_func(std::mem_fn(&ParseHandler::load_buffer_size), data, size); + _run_load_func(std::mem_fn(&ParseHandler::load_buffer_size), data, size, encoding_fallback); return *this; } -constexpr Parser& Parser::load_from_buffer(const char* start, const char* end) { +constexpr Parser& Parser::load_from_buffer(const char* start, const char* end, std::optional<detail::Encoding> encoding_fallback) { // Type can't be deduced? - _run_load_func(std::mem_fn(&ParseHandler::load_buffer), start, end); + _run_load_func(std::mem_fn(&ParseHandler::load_buffer), start, end, encoding_fallback); return *this; } -constexpr Parser& Parser::load_from_string(const std::string_view string) { - return load_from_buffer(string.data(), string.size()); +constexpr Parser& Parser::load_from_string(const std::string_view string, std::optional<detail::Encoding> encoding_fallback) { + return load_from_buffer(string.data(), string.size(), encoding_fallback); } -Parser& Parser::load_from_file(const char* path) { +Parser& Parser::load_from_file(const char* path, std::optional<detail::Encoding> encoding_fallback) { set_file_path(path); // Type can be deduced?? - _run_load_func(std::mem_fn(&ParseHandler::load_file), path); + _run_load_func(std::mem_fn(&ParseHandler::load_file), get_file_path().data(), encoding_fallback); return *this; } -Parser& Parser::load_from_file(const std::filesystem::path& path) { - return load_from_file(path.string().c_str()); +Parser& Parser::load_from_file(const std::filesystem::path& path, std::optional<detail::Encoding> encoding_fallback) { + return load_from_file(path.string().c_str(), encoding_fallback); } /* REQUIREMENTS: @@ -173,11 +187,7 @@ bool Parser::simple_parse() { return false; } - if (_parse_handler->is_exclusive_utf8()) { - _parse_handler->parse_state().logger().warning(warnings::make_utf8_warning(_file_path)); - } - - auto errors = _parse_handler->parse<grammar::File<grammar::NoStringEscapeOption>>(); + std::optional<DiagnosticLogger::error_range> errors = _parse_handler->parse<grammar::File>(); _has_error = _parse_handler->parse_state().logger().errored(); _has_warning = _parse_handler->parse_state().logger().warned(); if (!_parse_handler->root()) { @@ -196,14 +206,11 @@ bool Parser::event_parse() { return false; } - if (_parse_handler->is_exclusive_utf8()) { - _parse_handler->parse_state().logger().warning(warnings::make_utf8_warning(_file_path)); - } - - auto errors = _parse_handler->parse<grammar::EventFile>(); + std::optional<DiagnosticLogger::error_range> 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_error = true; _has_fatal_error = true; if (&_error_stream.get() != &detail::cnull) { print_errors_to(_error_stream); @@ -218,14 +225,11 @@ bool Parser::decision_parse() { return false; } - if (_parse_handler->is_exclusive_utf8()) { - _parse_handler->parse_state().logger().warning(warnings::make_utf8_warning(_file_path)); - } - - auto errors = _parse_handler->parse<grammar::DecisionFile>(); + std::optional<DiagnosticLogger::error_range> 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_error = true; _has_fatal_error = true; if (&_error_stream.get() != &detail::cnull) { print_errors_to(_error_stream); @@ -240,14 +244,11 @@ bool Parser::lua_defines_parse() { return false; } - if (_parse_handler->is_exclusive_utf8()) { - _parse_handler->parse_state().logger().warning(warnings::make_utf8_warning(_file_path)); - } - - auto errors = _parse_handler->parse<lua::grammar::File<>>(); + std::optional<DiagnosticLogger::error_range> 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_error = true; _has_fatal_error = true; if (&_error_stream.get() != &detail::cnull) { print_errors_to(_error_stream); @@ -273,48 +274,66 @@ std::string Parser::make_list_string() const { return _parse_handler->parse_state().ast().make_list_visualizer(); } +// TODO: Remove reinterpret_cast +// WARNING: This almost certainly breaks on utf16 and utf32 encodings, luckily we don't parse in that format +// This is purely to silence the node_location errors because char8_t is useless +#define REINTERPRET_IT(IT) reinterpret_cast<const std::decay_t<decltype(buffer)>::encoding::char_type*>((IT)) + const FilePosition Parser::get_position(const ast::Node* node) const { if (!node || !node->is_linked_in_tree()) { return {}; } - auto node_location = _parse_handler->parse_state().ast().location_of(node); + + NodeLocation node_location; + + node_location = _parse_handler->parse_state().ast().location_of(node); + if (node_location.is_synthesized()) { - return {}; + return FilePosition {}; } - 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(); - } - return result; + return _parse_handler->parse_state().ast().file().visit_buffer( + [&](auto&& buffer) -> FilePosition { + auto loc_begin = lexy::get_input_location(buffer, REINTERPRET_IT(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(buffer, REINTERPRET_IT(node_location.end()), loc_begin.anchor()); + result.end_line = loc_end.line_nr(); + result.end_column = loc_end.column_nr(); + } + return result; + }); } Parser::error_range Parser::get_errors() const { - return _parse_handler->parse_state().logger().get_errors(); + return _parse_handler->get_errors(); } const FilePosition Parser::get_error_position(const error::Error* error) const { if (!error || !error->is_linked_in_tree()) { return {}; } + auto err_location = _parse_handler->parse_state().logger().location_of(error); if (err_location.is_synthesized()) { - return {}; + return FilePosition {}; } - 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; + return _parse_handler->parse_state().ast().file().visit_buffer( + [&](auto&& buffer) -> FilePosition { + auto loc_begin = lexy::get_input_location(buffer, REINTERPRET_IT(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(buffer, REINTERPRET_IT(err_location.end()), loc_begin.anchor()); + result.end_line = loc_end.line_nr(); + result.end_column = loc_end.column_nr(); + } + return result; + }); } +#undef REINTERPRET_IT + void Parser::print_errors_to(std::basic_ostream<char>& stream) const { auto errors = get_errors(); if (errors.empty()) return; @@ -324,19 +343,9 @@ void Parser::print_errors_to(std::basic_ostream<char>& stream) const { [&](const error::BufferError* buffer_error) { stream << "buffer error: " << buffer_error->message() << '\n'; }, - [&](const error::ParseError* parse_error) { - auto position = get_error_position(parse_error); - std::string pos_str = fmt::format(":{}:{}: ", position.start_line, position.start_column); - stream << _file_path << pos_str << "parse error for '" << parse_error->production_name() << "': " << parse_error->message() << '\n'; - }, - [&](dryad::child_visitor<error::ErrorKind> visitor, const error::Semantic* semantic) { - auto position = get_error_position(semantic); - std::string pos_str = ": "; - if (!position.is_empty()) { - pos_str = fmt::format(":{}:{}: ", position.start_line, position.start_column); - } - stream << _file_path << pos_str << semantic->message() << '\n'; - auto annotations = semantic->annotations(); + [&](dryad::child_visitor<error::ErrorKind> visitor, const error::AnnotatedError* annotated_error) { + stream << annotated_error->message() << '\n'; + auto annotations = annotated_error->annotations(); for (auto annotation : annotations) { visitor(annotation); } diff --git a/src/openvic-dataloader/v2script/SimpleGrammar.hpp b/src/openvic-dataloader/v2script/SimpleGrammar.hpp index 37e295f..d42ce07 100644 --- a/src/openvic-dataloader/v2script/SimpleGrammar.hpp +++ b/src/openvic-dataloader/v2script/SimpleGrammar.hpp @@ -5,10 +5,22 @@ #include <lexy/callback.hpp> #include <lexy/dsl.hpp> +#include <lexy/dsl/any.hpp> #include <lexy/dsl/identifier.hpp> +#include <lexy/dsl/option.hpp> +#include <lexy/dsl/peek.hpp> +#include <lexy/dsl/punctuator.hpp> +#include <lexy/dsl/recover.hpp> +#include <lexy/dsl/scan.hpp> #include <lexy/dsl/symbol.hpp> - -#include "ParseState.hpp" +#include <lexy/dsl/unicode.hpp> +#include <lexy/encoding.hpp> +#include <lexy/input/base.hpp> +#include <lexy/input/buffer.hpp> +#include <lexy/lexeme.hpp> + +#include "detail/Convert.hpp" +#include "detail/InternalConcepts.hpp" #include "detail/dsl.hpp" // Grammar Definitions // @@ -23,17 +35,28 @@ */ namespace ovdl::v2script::grammar { template<typename T> - constexpr auto construct = dsl::construct<ast::ParseState, T>; + constexpr auto construct = dsl::construct<T>; template<typename T, bool DisableEmpty = false, typename ListType = ast::AssignStatementList> - constexpr auto construct_list = dsl::construct_list<ast::ParseState, T, ListType, DisableEmpty>; + constexpr auto construct_list = dsl::construct_list<T, ListType, DisableEmpty>; + + struct ConvertErrorHandler { + static constexpr void on_invalid_character(detail::IsStateType auto& state, auto reader) { + state.logger().warning("invalid character value '{}' found.", static_cast<int>(reader.peek())) // + .primary(BasicNodeLocation { reader.position() }, "here") + .finish(); + } + }; + + template<typename String> + constexpr auto convert_as_string = convert::convert_as_string<String, ConvertErrorHandler>; struct ParseOptions { /// @brief Makes string parsing avoid string escapes bool NoStringEscape; }; - static constexpr ParseOptions NoStringEscapeOption = ParseOptions { true }; - static constexpr ParseOptions StringEscapeOption = ParseOptions { false }; + static constexpr auto NoStringEscapeOption = ParseOptions { true }; + static constexpr auto StringEscapeOption = ParseOptions { false }; /* REQUIREMENTS: DAT-630 */ static constexpr auto whitespace_specifier = lexy::dsl::ascii::blank / lexy::dsl::ascii::newline; @@ -50,24 +73,28 @@ namespace ovdl::v2script::grammar { ascii / 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> / - dsl::make_range<0x9E, 0x9F>() / - dsl::make_range<0xC0, 0xD6>() / - dsl::make_range<0xD8, 0xF6>() / - dsl::make_range<0xF8, 0xFF>(); + dsl::lit_b_range<0x9E, 0x9F> / + dsl::lit_b_range<0xC0, 0xD6> / + dsl::lit_b_range<0xD8, 0xF6> / + dsl::lit_b_range<0xF8, 0xFF>; static constexpr auto windows_1251_data_specifier_additions = - dsl::make_range<0x80, 0x81>() / lexy::dsl::lit_b<0x83> / lexy::dsl::lit_b<0x8D> / lexy::dsl::lit_b<0x8F> / + dsl::lit_b_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> / - dsl::make_range<0xA1, 0xA3>() / lexy::dsl::lit_b<0xA5> / lexy::dsl::lit_b<0xA8> / lexy::dsl::lit_b<0xAA> / + dsl::lit_b_range<0xA1, 0xA3> / lexy::dsl::lit_b<0xA5> / lexy::dsl::lit_b<0xA8> / lexy::dsl::lit_b<0xAA> / lexy::dsl::lit_b<0xAF> / - dsl::make_range<0xB2, 0xB4>() / lexy::dsl::lit_b<0xB8> / lexy::dsl::lit_b<0xBA> / - dsl::make_range<0xBC, 0xBF>() / + dsl::lit_b_range<0xB2, 0xB4> / lexy::dsl::lit_b<0xB8> / lexy::dsl::lit_b<0xBA> / + dsl::lit_b_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; static constexpr auto data_char_class = LEXY_CHAR_CLASS("DataSpecifier", data_specifier); + static constexpr auto utf_data_specifier = lexy::dsl::unicode::xid_continue / LEXY_ASCII_ONE_OF("+:@%&'-."); + + static constexpr auto utf_char_class = LEXY_CHAR_CLASS("DataSpecifier", utf_data_specifier); + static constexpr auto escaped_symbols = lexy::symbol_table<char> // .map<'"'>('"') .map<'\''>('\'') @@ -79,50 +106,121 @@ namespace ovdl::v2script::grammar { .map<'r'>('\r') .map<'t'>('\t'); - static constexpr auto id = lexy::dsl::identifier(data_char_class); + static constexpr auto id = lexy::dsl::identifier(ascii); template<ParseOptions Options> 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); - }); + struct Identifier : lexy::scan_production<ast::IdentifierValue*>, + lexy::token_production { + + template<typename Context, typename Reader> + static constexpr scan_result scan(lexy::rule_scanner<Context, Reader>& scanner, detail::IsParseState auto& state) { + using encoding = typename Reader::encoding; + using char_type = typename encoding::char_type; + + std::basic_string<char_type> value_result; + + auto content_begin = scanner.position(); + do { + if constexpr (std::same_as<encoding, lexy::default_encoding> || std::same_as<encoding, lexy::byte_encoding>) { + if (lexy::scan_result<lexy::lexeme<Reader>> ascii_result; scanner.branch(ascii_result, lexy::dsl::identifier(ascii))) { + value_result.append(ascii_result.value().begin(), ascii_result.value().end()); + continue; + } + + char_type char_array[] { *scanner.position(), char_type {} }; + auto input = lexy::range_input(&char_array[0], &char_array[1]); + auto reader = input.reader(); + convert::map_value val = convert::try_parse_map(state.encoding(), reader); + + if (val.is_invalid()) { + ConvertErrorHandler::on_invalid_character(state, reader); + continue; + } + + if (!val.is_pass()) { + // non-pass characters are not valid ascii and are mapped to utf8 values + value_result.append(val._value); + scanner.parse(data_char_class); + } else { + break; + } + } else { + auto lexeme_result = scanner.template parse<lexy::lexeme<Reader>>(lexy::dsl::identifier(utf_char_class)); + if (lexeme_result) { + value_result.append(lexeme_result.value().begin(), lexeme_result.value().size()); + break; + } + } + } while (scanner); + auto content_end = scanner.position(); + + if (value_result.empty()) { + return lexy::scan_failed; + } + + auto value = state.ast().intern(value_result); + return state.ast().template create<ast::IdentifierValue>(ovdl::NodeLocation::make_from(content_begin, content_end), value); + } + + static constexpr auto rule = dsl::peek(data_char_class, utf_char_class) >> lexy::dsl::scan; }; /* 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); - }); + struct StringExpression : lexy::scan_production<ast::StringValue*>, + lexy::token_production { + + template<typename Context, typename Reader> + static constexpr scan_result scan(lexy::rule_scanner<Context, Reader>& scanner, detail::IsParseState auto& state) { + using encoding = typename Reader::encoding; + + constexpr auto rule = [] { + if constexpr (Options.NoStringEscape) { + auto c = [] { + if constexpr (std::same_as<encoding, lexy::default_encoding> || std::same_as<encoding, lexy::byte_encoding>) { + return dsl::lit_b_range<0x20, 0xFF> / lexy::dsl::lit_b<0x07> / lexy::dsl::lit_b<0x09> / lexy::dsl::lit_b<0x0A> / lexy::dsl::lit_b<0x0D>; + } else { + return -lexy::dsl::unicode::control; + } + }(); + return lexy::dsl::quoted(c); + } else { + // Arbitrary code points that aren't control characters. + auto c = [] { + if constexpr (std::same_as<encoding, lexy::default_encoding> || std::same_as<encoding, lexy::byte_encoding>) { + return dsl::lit_b_range<0x20, 0xFF> - lexy::dsl::ascii::control; + } else { + return -lexy::dsl::unicode::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::quoted(c, escape); + } + }(); + + auto begin = scanner.position(); + lexy::scan_result<std::string> str_result; + scanner.parse(str_result, rule); + if (!scanner || !str_result) + return lexy::scan_failed; + auto end = scanner.position(); + auto str = str_result.value(); + auto value = state.ast().intern(str.data(), str.size()); + return state.ast().template create<ast::StringValue>(ovdl::NodeLocation::make_from(begin, end), value); + } + + static constexpr auto rule = lexy::dsl::peek(lexy::dsl::quoted.open()) >> lexy::dsl::scan; + static constexpr auto value = convert_as_string<std::string> >> lexy::forward<ast::StringValue*>; }; /* REQUIREMENTS: DAT-638 */ @@ -132,59 +230,112 @@ namespace ovdl::v2script::grammar { }; 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 rule = [] { + auto right_brace = lexy::dsl::lit_c<'}'>; + + auto value_expression = lexy::dsl::p<ValueExpression>; + auto statement_list_expression = lexy::dsl::recurse_branch<StatementListBlock>; + + auto rhs_recover = lexy::dsl::recover(value_expression, statement_list_expression).limit(right_brace); + auto rhs_try = lexy::dsl::try_(value_expression | statement_list_expression, rhs_recover); + + auto identifier = + dsl::p<Identifier> >> + (lexy::dsl::equal_sign >> rhs_try); + + auto recover = lexy::dsl::recover(identifier).limit(right_brace); + return lexy::dsl::try_(identifier, recover); + }(); static constexpr auto value = construct<ast::AssignStatement>; }; /* REQUIREMENTS: DAT-639 */ struct AssignmentStatement { - static constexpr auto rule = - dsl::p<Identifier> >> + static constexpr auto rule = [] { + auto right_brace = lexy::dsl::lit_c<'}'>; + + auto value_expression = lexy::dsl::p<ValueExpression>; + auto statement_list_expression = lexy::dsl::recurse_branch<StatementListBlock>; + + auto rhs_recover = lexy::dsl::recover(value_expression, statement_list_expression).limit(right_brace); + auto rhs_try = lexy::dsl::try_(value_expression | statement_list_expression, rhs_recover); + + auto identifier = + 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>; + rhs_try | + lexy::dsl::else_ >> lexy::dsl::return_); + + auto string_expression = dsl::p<StringExpression>; + auto statement_list = lexy::dsl::recurse_branch<StatementListBlock>; + + return identifier | string_expression | statement_list; + }(); 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); + [](detail::IsParseState auto& state, const char* pos, ast::IdentifierValue* name, ast::Value* initializer) { + return state.ast().template 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); + [](detail::IsParseState auto& state, bool&, const char* pos, ast::IdentifierValue* name, ast::Value* initializer) { + return state.ast().template create<ast::AssignStatement>(pos, name, initializer); }, - [](ast::ParseState& state, ast::Value* left) { - return state.ast().create<ast::ValueStatement>(state.ast().location_of(left), left); + [](detail::IsParseState auto& state, bool&, bool&, const char* pos, ast::IdentifierValue* name, ast::Value* initializer) { + return state.ast().template create<ast::AssignStatement>(pos, name, initializer); + }, + [](detail::IsParseState auto& state, bool&, bool&, const char* pos, ast::Value* name) { + return state.ast().template create<ast::ValueStatement>(pos, name); + }, + [](detail::IsParseState auto& state, const char* pos, ast::Value* left, lexy::nullopt = {}) { + return state.ast().template create<ast::ValueStatement>(pos, left); + }, + [](detail::IsParseState auto& state, bool&, const char* pos, ast::Value* left, lexy::nullopt = {}) { + return state.ast().template create<ast::ValueStatement>(pos, left); + }, + [](detail::IsParseState auto& state, ast::Value* left) -> ast::ValueStatement* { + if (left == nullptr) return nullptr; + return state.ast().template create<ast::ValueStatement>(state.ast().location_of(left), left); + }, + [](detail::IsParseState auto& state, bool&, ast::Value* left) -> ast::ValueStatement* { + if (left == nullptr) return nullptr; + return state.ast().template 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 rule = [] { + auto right_brace = lexy::dsl::lit_c<'}'>; + + auto assign_statement = lexy::dsl::recurse_branch<AssignmentStatement>; + + auto assign_try = lexy::dsl::try_(assign_statement); + auto assign_opt = lexy::dsl::opt(lexy::dsl::list(assign_try)); + + auto curly_bracket = dsl::curly_bracketed(assign_opt + lexy::dsl::opt(lexy::dsl::semicolon)); + + return lexy::dsl::try_(curly_bracket, lexy::dsl::find(right_brace)); + }(); static constexpr auto value = lexy::as_list<ast::StatementList> >> dsl::callback<ast::ListValue*>( - [](ast::ParseState& state, const char* begin, auto&& list, const char* end) { + [](detail::IsParseState auto& 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)); + return state.ast().template 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)); + return state.ast().template 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) { + [](detail::IsParseState auto& 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)); + return state.ast().template 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)); + return state.ast().template create<ast::ListValue>(ovdl::NodeLocation::make_from(begin, end), LEXY_MOV(list)); } + }, + [](detail::IsParseState auto& state, lexy::nullopt fail = {}) { + return fail; }); }; }; @@ -198,22 +349,20 @@ namespace ovdl::v2script::grammar { template<ParseOptions Options> 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>> + template<ovdl::detail::string_literal Keyword, auto Production, auto Value = dsl::default_kw_value<ast::IdentifierValue, Keyword>> using keyword_rule = dsl::keyword_rule< - ast::ParseState, id, ast::AssignStatement, Keyword, Production, Value>; - template<ovdl::detail::string_literal Keyword, auto Production, auto Value = dsl::default_kw_value<ast::ParseState, ast::IdentifierValue, Keyword>> + template<ovdl::detail::string_literal Keyword, auto Production, auto Value = dsl::default_kw_value<ast::IdentifierValue, Keyword>> using fkeyword_rule = dsl::fkeyword_rule< - ast::ParseState, id, ast::AssignStatement, Keyword, Production, Value>; template<ParseOptions Options> - struct File { + struct BasicFile { // Allow arbitrary spaces between individual tokens. static constexpr auto whitespace = whitespace_specifier | comment_specifier; @@ -223,4 +372,6 @@ namespace ovdl::v2script::grammar { static constexpr auto value = lexy::as_list<ast::StatementList> >> construct<ast::FileTree>; }; + + using File = BasicFile<NoStringEscapeOption>; } |