diff options
Diffstat (limited to 'src/openvic-dataloader/v2script')
-rw-r--r-- | src/openvic-dataloader/v2script/AbstractSyntaxTree.cpp | 417 | ||||
-rw-r--r-- | src/openvic-dataloader/v2script/AiBehaviorGrammar.hpp | 18 | ||||
-rw-r--r-- | src/openvic-dataloader/v2script/DecisionGrammar.hpp | 115 | ||||
-rw-r--r-- | src/openvic-dataloader/v2script/EffectGrammar.hpp | 31 | ||||
-rw-r--r-- | src/openvic-dataloader/v2script/EventGrammar.hpp | 214 | ||||
-rw-r--r-- | src/openvic-dataloader/v2script/LuaDefinesGrammar.hpp | 82 | ||||
-rw-r--r-- | src/openvic-dataloader/v2script/ModifierGrammar.hpp | 49 | ||||
-rw-r--r-- | src/openvic-dataloader/v2script/Parser.cpp | 280 | ||||
-rw-r--r-- | src/openvic-dataloader/v2script/SimpleGrammar.hpp | 262 | ||||
-rw-r--r-- | src/openvic-dataloader/v2script/TriggerGrammar.hpp | 31 |
10 files changed, 629 insertions, 870 deletions
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 |