diff --git a/ecsact/detail/grammar.hh b/ecsact/detail/grammar.hh index a9fd5f7..0d51569 100644 --- a/ecsact/detail/grammar.hh +++ b/ecsact/detail/grammar.hh @@ -668,17 +668,25 @@ struct system_component_statement { static constexpr auto value = lexy::noop; }; + struct with_fields : lexy::token_production { + static constexpr auto rule = lexy::dsl::list( + lexy::dsl::p, + lexy::dsl::sep(lexy::dsl::comma) + ); + static constexpr auto value = lexy::as_list>; + }; + static constexpr auto capability = lexy::dsl::p | lexy::dsl::p | lexy::dsl::p; static constexpr auto rule = capability >> lexy::dsl::p >> - lexy::dsl::opt(lexy::dsl::p >> lexy::dsl::p); + lexy::dsl::opt(lexy::dsl::p >> lexy::dsl::p); static constexpr auto value = lexy::callback( []( - ecsact_system_capability cap, - std::string_view component_name, - std::optional with_entity_field_name + ecsact_system_capability cap, + std::string_view component_name, + std::optional> with_entity_field_names ) { ecsact_statement statement{ .type = ECSACT_STATEMENT_SYSTEM_COMPONENT, @@ -691,10 +699,15 @@ struct system_component_statement { data.component_name.data = component_name.data(); data.component_name.length = static_cast(component_name.size()); - if(with_entity_field_name) { - data.with_entity_field_name.data = with_entity_field_name->data(); - data.with_entity_field_name.length = - static_cast(with_entity_field_name->size()); + if(with_entity_field_names) { + data.with_field_name_list_count = + static_cast(with_entity_field_names->size()); + for(int i = 0; data.with_field_name_list_count > i; ++i) { + data.with_field_name_list[i] = ecsact_statement_sv{ + .data = (*with_entity_field_names)[i].data(), + .length = static_cast((*with_entity_field_names)[i].size()), + }; + } } return statement; @@ -758,21 +771,35 @@ struct with_statement { static constexpr auto value = lexy::noop; }; + struct with_fields : lexy::token_production { + static constexpr auto rule = lexy::dsl::list( + lexy::dsl::p, + lexy::dsl::sep(lexy::dsl::comma) + ); + static constexpr auto value = lexy::as_list>; + }; + static constexpr auto rule = lexy::dsl::p >> - lexy::dsl::p; + lexy::dsl::p; + + static constexpr auto value = lexy::callback( + [](std::vector field_names) { + auto data = ecsact_system_with_statement{}; + data.with_field_name_list_count = field_names.size(); + + for(int i = 0; data.with_field_name_list_count > i; ++i) { + data.with_field_name_list[i] = ecsact_statement_sv{ + .data = field_names[i].data(), + .length = static_cast(field_names[i].size()), + }; + } - static constexpr auto value = - lexy::callback([](std::string_view field_name) { return ecsact_statement{ - .type = ECSACT_STATEMENT_SYSTEM_WITH_ENTITY, - .data{.system_with_entity_statement{ - .with_entity_field_name{ - .data = field_name.data(), - .length = static_cast(field_name.size()), - }, - }}, + .type = ECSACT_STATEMENT_SYSTEM_WITH, + .data{.system_with_statement{data}}, }; - }); + } + ); }; struct system_notify_statement { @@ -1003,13 +1030,13 @@ using system_component_level_statement = statement< system_component_statement, with_statement>; -constexpr char system_with_entity_level_statement_name[] = +constexpr char system_with_level_statement_name[] = "system with entity level statement"; -constexpr char system_with_entity_level_statement_expected_message[] = +constexpr char system_with_level_statement_expected_message[] = "expected system with entity level statement"; -using system_with_entity_level_statement = statement< - system_with_entity_level_statement_name, - system_with_entity_level_statement_expected_message, +using system_with_level_statement = statement< + system_with_level_statement_name, + system_with_level_statement_expected_message, system_component_statement, with_statement>; diff --git a/ecsact/ecsact_parse_statement.cc b/ecsact/ecsact_parse_statement.cc index ae5a4f2..5683c37 100644 --- a/ecsact/ecsact_parse_statement.cc +++ b/ecsact/ecsact_parse_statement.cc @@ -31,8 +31,8 @@ static auto context_grammar(ecsact_statement_type context_type, Fn&& fn) { return fn(grammar::entity_field_level_statement{}); case ECSACT_STATEMENT_SYSTEM_COMPONENT: return fn(grammar::system_component_level_statement{}); - case ECSACT_STATEMENT_SYSTEM_WITH_ENTITY: - return fn(grammar::system_with_entity_level_statement{}); + case ECSACT_STATEMENT_SYSTEM_WITH: + return fn(grammar::system_with_level_statement{}); case ECSACT_STATEMENT_PACKAGE: return fn(grammar::package_level_statement{}); case ECSACT_STATEMENT_IMPORT: diff --git a/ecsact/parse/statements.h b/ecsact/parse/statements.h index eff9302..cc0b850 100644 --- a/ecsact/parse/statements.h +++ b/ecsact/parse/statements.h @@ -6,6 +6,9 @@ #include "ecsact/runtime/common.h" #include "ecsact/runtime/definitions.h" +/** Maximum number of 'with' fields that may be parsed */ +#define ECSACT_MAX_WITH_FIELDS 32 + typedef enum { ECSACT_STATEMENT_NONE, ECSACT_STATEMENT_UNKNOWN, @@ -22,7 +25,7 @@ typedef enum { ECSACT_STATEMENT_ENTITY_FIELD, ECSACT_STATEMENT_SYSTEM_COMPONENT, ECSACT_STATEMENT_SYSTEM_GENERATES, - ECSACT_STATEMENT_SYSTEM_WITH_ENTITY, + ECSACT_STATEMENT_SYSTEM_WITH, ECSACT_STATEMENT_ENTITY_CONSTRAINT, ECSACT_STATEMENT_SYSTEM_NOTIFY, ECSACT_STATEMENT_SYSTEM_NOTIFY_COMPONENT, @@ -83,12 +86,14 @@ typedef struct { typedef struct { ecsact_system_capability capability; ecsact_statement_sv component_name; - ecsact_statement_sv with_entity_field_name; + int with_field_name_list_count; + ecsact_statement_sv with_field_name_list[ECSACT_MAX_WITH_FIELDS]; } ecsact_system_component_statement; typedef struct { - ecsact_statement_sv with_entity_field_name; -} ecsact_system_with_entity_statement; + int with_field_name_list_count; + ecsact_statement_sv with_field_name_list[ECSACT_MAX_WITH_FIELDS]; +} ecsact_system_with_statement; typedef struct { bool optional; @@ -143,7 +148,7 @@ typedef union { ecsact_field_statement field_statement; ecsact_user_type_field_statement user_type_field_statement; ecsact_system_component_statement system_component_statement; - ecsact_system_with_entity_statement system_with_entity_statement; + ecsact_system_with_statement system_with_statement; ecsact_entity_constraint_statement entity_constraint_statement; ecsact_system_notify_statement system_notify_statement; ecsact_system_notify_component_statement system_notify_component_statement; diff --git a/test/BUILD.bazel b/test/BUILD.bazel index be4f144..0d07ccb 100644 --- a/test/BUILD.bazel +++ b/test/BUILD.bazel @@ -1,5 +1,5 @@ -load("@rules_cc//cc:defs.bzl", "cc_test") load("@ecsact_parse//bazel:copts.bzl", "copts") +load("@rules_cc//cc:defs.bzl", "cc_test") # keep sorted _TESTS = [ @@ -36,6 +36,7 @@ cc_library( deps = [ ":parse_test_util", "@ecsact_parse", + "@magic_enum", "@googletest//:gtest", "@googletest//:gtest_main", ], diff --git a/test/MODULE.bazel b/test/MODULE.bazel index dc62342..d8c3fa6 100644 --- a/test/MODULE.bazel +++ b/test/MODULE.bazel @@ -2,12 +2,16 @@ module(name = "ecsact_parse_test") bazel_dep(name = "rules_cc", version = "0.0.9") bazel_dep(name = "googletest", version = "1.14.0") - +bazel_dep(name = "magic_enum", version = "0.9.3") bazel_dep(name = "ecsact_parse") -local_path_override(module_name = "ecsact_parse", path = "..") +local_path_override( + module_name = "ecsact_parse", + path = "..", +) bazel_dep(name = "toolchains_llvm", version = "1.0.0", dev_dependency = True) bazel_dep(name = "hedron_compile_commands", dev_dependency = True) + git_override( module_name = "hedron_compile_commands", commit = "204aa593e002cbd177d30f11f54cff3559110bb9", diff --git a/test/parse_test_system_component_statement.cc b/test/parse_test_system_component_statement.cc index 3cd3c96..8ecad58 100644 --- a/test/parse_test_system_component_statement.cc +++ b/test/parse_test_system_component_statement.cc @@ -2,18 +2,28 @@ #include #include +#include +#include +#include #include "ecsact/parse.h" using namespace std::string_literals; +using namespace std::string_view_literals; + +template +concept RangeOf = + std::ranges::range && std::same_as, V>; + +constexpr auto empty_list = std::array{}; void TestValidSystemComponent( - std::string_view statement_str, - ecsact_system_capability expected_capability, - std::string_view expected_component_name, - std::string_view expected_with_entity_field_name = "" + std::string_view statement_str, + ecsact_system_capability expected_capability, + std::string_view expected_component_name, + RangeOf auto expected_with_field_names = empty_list ) { - auto system_name = "ExampleSystem"s; - ecsact_statement component_statement{ + auto system_name = "ExampleSystem"s; + auto context_statement = ecsact_statement{ .type = ECSACT_STATEMENT_SYSTEM, .data{.system_statement{.system_name{ .data = system_name.data(), @@ -27,13 +37,13 @@ void TestValidSystemComponent( auto read_amount = ecsact_parse_statement( statement_str.data(), statement_str.size(), - &component_statement, + &context_statement, &statement, &status ); + ASSERT_EQ(status.code, ECSACT_PARSE_STATUS_OK); ASSERT_EQ(statement.type, ECSACT_STATEMENT_SYSTEM_COMPONENT); - EXPECT_EQ(status.code, ECSACT_PARSE_STATUS_OK); EXPECT_EQ( expected_capability, @@ -47,14 +57,21 @@ void TestValidSystemComponent( ) ); - EXPECT_EQ( - expected_with_entity_field_name, - std::string_view( - statement.data.system_component_statement.with_entity_field_name.data, - statement.data.system_component_statement.with_entity_field_name.length - ) + ASSERT_EQ( + statement.data.system_component_statement.with_field_name_list_count, + expected_with_field_names.size() ); + for(int i = 0; std::size(expected_with_field_names) > i; ++i) { + EXPECT_EQ( + expected_with_field_names[i], + std::string_view( + statement.data.system_component_statement.with_field_name_list[i].data, + statement.data.system_component_statement.with_field_name_list[i].length + ) + ); + } + EXPECT_EQ(read_amount, statement_str.size()); } @@ -62,7 +79,8 @@ TEST(Parse, ReadonlySystemComponent) { TestValidSystemComponent( "readonly ExampleComponent;"s, ECSACT_SYS_CAP_READONLY, - "ExampleComponent" + "ExampleComponent", + empty_list ); } @@ -70,7 +88,8 @@ TEST(Parse, WriteonlySystemComponent) { TestValidSystemComponent( "writeonly ExampleComponent;"s, ECSACT_SYS_CAP_WRITEONLY, - "ExampleComponent" + "ExampleComponent", + empty_list ); } @@ -78,7 +97,8 @@ TEST(Parse, ReadwriteSystemComponent) { TestValidSystemComponent( "readwrite ExampleComponent;"s, ECSACT_SYS_CAP_READWRITE, - "ExampleComponent" + "ExampleComponent", + empty_list ); } @@ -86,7 +106,8 @@ TEST(Parse, OptionalReadonlySystemComponent) { TestValidSystemComponent( "optional readonly ExampleComponent;"s, ECSACT_SYS_CAP_OPTIONAL_READONLY, - "ExampleComponent" + "ExampleComponent", + empty_list ); } @@ -94,7 +115,8 @@ TEST(Parse, OptionalWriteonlySystemComponent) { TestValidSystemComponent( "optional writeonly ExampleComponent;"s, ECSACT_SYS_CAP_OPTIONAL_WRITEONLY, - "ExampleComponent" + "ExampleComponent", + empty_list ); } @@ -102,7 +124,8 @@ TEST(Parse, OptionalReadwriteSystemComponent) { TestValidSystemComponent( "optional readwrite ExampleComponent;"s, ECSACT_SYS_CAP_OPTIONAL_READWRITE, - "ExampleComponent" + "ExampleComponent", + empty_list ); } @@ -110,7 +133,8 @@ TEST(Parse, IncludeSystemComponent) { TestValidSystemComponent( "include ExampleComponent;"s, ECSACT_SYS_CAP_INCLUDE, - "ExampleComponent" + "ExampleComponent", + empty_list ); } @@ -118,7 +142,8 @@ TEST(Parse, ExcludeSystemComponent) { TestValidSystemComponent( "exclude ExampleComponent;"s, ECSACT_SYS_CAP_EXCLUDE, - "ExampleComponent" + "ExampleComponent", + empty_list ); } @@ -126,7 +151,8 @@ TEST(Parse, AddsSystemComponent) { TestValidSystemComponent( "adds ExampleComponent;"s, ECSACT_SYS_CAP_ADDS, - "ExampleComponent" + "ExampleComponent", + empty_list ); } @@ -134,7 +160,8 @@ TEST(Parse, RemovesSystemComponent) { TestValidSystemComponent( "removes ExampleComponent;"s, ECSACT_SYS_CAP_REMOVES, - "ExampleComponent" + "ExampleComponent", + empty_list ); } @@ -147,7 +174,7 @@ TEST(Parse, ReadonlySystemComponentWithEntity) { "readonly ExampleComponent with example_entity;"s, ECSACT_SYS_CAP_READONLY, "ExampleComponent", - "example_entity" + std::array{"example_entity"sv} ); } @@ -156,7 +183,7 @@ TEST(Parse, WriteonlySystemComponentWithEntity) { "writeonly ExampleComponent with example_entity;"s, ECSACT_SYS_CAP_WRITEONLY, "ExampleComponent", - "example_entity" + std::array{"example_entity"sv} ); } @@ -165,7 +192,7 @@ TEST(Parse, ReadwriteSystemComponentWithEntity) { "readwrite ExampleComponent with example_entity;"s, ECSACT_SYS_CAP_READWRITE, "ExampleComponent", - "example_entity" + std::array{"example_entity"sv} ); } @@ -174,7 +201,7 @@ TEST(Parse, OptionalReadonlySystemComponentWithEntity) { "optional readonly ExampleComponent with example_entity;"s, ECSACT_SYS_CAP_OPTIONAL_READONLY, "ExampleComponent", - "example_entity" + std::array{"example_entity"sv} ); } @@ -183,7 +210,7 @@ TEST(Parse, OptionalWriteonlySystemComponentWithEntity) { "optional writeonly ExampleComponent with example_entity;"s, ECSACT_SYS_CAP_OPTIONAL_WRITEONLY, "ExampleComponent", - "example_entity" + std::array{"example_entity"sv} ); } @@ -192,7 +219,7 @@ TEST(Parse, OptionalReadwriteSystemComponentWithEntity) { "optional readwrite ExampleComponent with example_entity;"s, ECSACT_SYS_CAP_OPTIONAL_READWRITE, "ExampleComponent", - "example_entity" + std::array{"example_entity"sv} ); } @@ -201,7 +228,7 @@ TEST(Parse, IncludeSystemComponentWithEntity) { "include ExampleComponent with example_entity;"s, ECSACT_SYS_CAP_INCLUDE, "ExampleComponent", - "example_entity" + std::array{"example_entity"sv} ); } @@ -213,7 +240,8 @@ TEST(Parse, OtherPackageFullyQualified) { TestValidSystemComponent( "readwrite other.pkg.ExampleComponent;"s, ECSACT_SYS_CAP_READWRITE, - "other.pkg.ExampleComponent" + "other.pkg.ExampleComponent", + empty_list ); } @@ -222,6 +250,6 @@ TEST(Parse, IncludeSystemComponentWithEntityFullQualified) { "include other.pkg.ExampleComponent with example_entity;"s, ECSACT_SYS_CAP_INCLUDE, "other.pkg.ExampleComponent", - "example_entity" + std::array{"example_entity"sv} ); } diff --git a/test/parse_test_system_with_entity_statement.cc b/test/parse_test_system_with_entity_statement.cc index b4fa602..885de8a 100644 --- a/test/parse_test_system_with_entity_statement.cc +++ b/test/parse_test_system_with_entity_statement.cc @@ -2,6 +2,7 @@ #include #include +#include "magic_enum.hpp" #include "ecsact/parse.h" using namespace std::string_literals; @@ -9,10 +10,7 @@ using namespace std::string_literals; TEST(Parse, WithEntityStatement) { ecsact_statement context; context.type = ECSACT_STATEMENT_SYSTEM_COMPONENT; - context.data.system_component_statement = { - .component_name = {}, - .with_entity_field_name = {}, - }; + context.data.system_component_statement = {}; auto statement_str = "with test_entity;"s; ecsact_statement statement; @@ -26,14 +24,16 @@ TEST(Parse, WithEntityStatement) { &status ); - ASSERT_EQ(statement.type, ECSACT_STATEMENT_SYSTEM_WITH_ENTITY); - EXPECT_EQ(status.code, ECSACT_PARSE_STATUS_OK); + ASSERT_EQ(status.code, ECSACT_PARSE_STATUS_OK) + << "status.code=" << magic_enum::enum_name(status.code) << "\n"; + ASSERT_EQ(statement.type, ECSACT_STATEMENT_SYSTEM_WITH); + ASSERT_EQ(statement.data.system_with_statement.with_field_name_list_count, 1); EXPECT_EQ( "test_entity", std::string_view( - statement.data.system_with_entity_statement.with_entity_field_name.data, - statement.data.system_with_entity_statement.with_entity_field_name.length + statement.data.system_with_statement.with_field_name_list[0].data, + statement.data.system_with_statement.with_field_name_list[0].length ) );