From b082e471e7c74a82a98933636341c26a8abfe7f9 Mon Sep 17 00:00:00 2001 From: Jan Date: Sun, 28 Nov 2021 17:55:26 +0100 Subject: [PATCH] Add permissive mode for menu parsing that accepts unknown script tokens as long as they can be put into the script --- src/Linker/LinkerArgs.cpp | 13 ++- .../IW4/AssetLoaders/AssetLoaderMenuList.cpp | 2 + src/ObjLoading/ObjLoading.h | 1 + .../Parsing/Menu/MenuFileParser.cpp | 21 ++-- src/ObjLoading/Parsing/Menu/MenuFileParser.h | 4 +- .../Parsing/Menu/MenuFileParserState.cpp | 8 +- .../Parsing/Menu/MenuFileParserState.h | 6 +- .../Parsing/Menu/MenuFileReader.cpp | 13 ++- src/ObjLoading/Parsing/Menu/MenuFileReader.h | 2 + .../Sequence/AbstractScopeSequenceHolder.h | 4 +- .../EventHandlerSetScopeSequences.cpp | 60 +++++++++++- .../Sequence/EventHandlerSetScopeSequences.h | 2 +- .../Menu/Sequence/FunctionScopeSequences.cpp | 2 +- .../Menu/Sequence/FunctionScopeSequences.h | 2 +- .../Menu/Sequence/GlobalScopeSequences.cpp | 2 +- .../Menu/Sequence/GlobalScopeSequences.h | 2 +- .../Menu/Sequence/ItemScopeSequences.cpp | 2 +- .../Menu/Sequence/ItemScopeSequences.h | 2 +- .../Menu/Sequence/MenuScopeSequences.cpp | 2 +- .../Menu/Sequence/MenuScopeSequences.h | 2 +- .../Menu/Sequence/NoScopeSequences.cpp | 2 +- .../Parsing/Menu/Sequence/NoScopeSequences.h | 2 +- .../EventHandlerSetScopeSequencesTests.cpp | 96 +++++++++++++------ test/ParserTestUtils/Parsing/Mock/MockLexer.h | 20 ++-- 24 files changed, 200 insertions(+), 72 deletions(-) diff --git a/src/Linker/LinkerArgs.cpp b/src/Linker/LinkerArgs.cpp index e19f33d7..5c4aa7ee 100644 --- a/src/Linker/LinkerArgs.cpp +++ b/src/Linker/LinkerArgs.cpp @@ -70,6 +70,12 @@ const CommandLineOption* const OPTION_LOAD = .Reusable() .Build(); +const CommandLineOption* const OPTION_MENU_PERMISSIVE = + CommandLineOption::Builder::Create() + .WithLongName("menu-permissive") + .WithDescription("Allows the usage of unknown script commands that can be compiled.") + .Build(); + const CommandLineOption* const COMMAND_LINE_OPTIONS[] { OPTION_HELP, @@ -79,7 +85,8 @@ const CommandLineOption* const COMMAND_LINE_OPTIONS[] OPTION_ASSET_SEARCH_PATH, OPTION_GDT_SEARCH_PATH, OPTION_SOURCE_SEARCH_PATH, - OPTION_LOAD + OPTION_LOAD, + OPTION_MENU_PERMISSIVE }; LinkerArgs::LinkerArgs() @@ -257,6 +264,10 @@ bool LinkerArgs::ParseArgs(const int argc, const char** argv) if (m_argument_parser.IsOptionSpecified(OPTION_LOAD)) m_zones_to_load = m_argument_parser.GetParametersForOption(OPTION_LOAD); + // --menu-permissive + if (m_argument_parser.IsOptionSpecified(OPTION_MENU_PERMISSIVE)) + ObjLoading::Configuration.PermissiveMenuParsing = true; + return true; } diff --git a/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderMenuList.cpp b/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderMenuList.cpp index 7661178b..e8f885de 100644 --- a/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderMenuList.cpp +++ b/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderMenuList.cpp @@ -3,6 +3,7 @@ #include #include +#include "ObjLoading.h" #include "Game/IW4/IW4.h" #include "Parsing/Menu/MenuFileReader.h" #include "Pool/GlobalAssetPool.h" @@ -101,6 +102,7 @@ bool AssetLoaderMenuList::LoadFromRaw(const std::string& assetName, ISearchPath* }); reader.IncludeZoneState(zoneState); + reader.SetPermissiveMode(ObjLoading::Configuration.PermissiveMenuParsing); const auto menuFileResult = reader.ReadMenuFile(); if (menuFileResult) diff --git a/src/ObjLoading/ObjLoading.h b/src/ObjLoading/ObjLoading.h index e0735cd2..68bf0e8c 100644 --- a/src/ObjLoading/ObjLoading.h +++ b/src/ObjLoading/ObjLoading.h @@ -12,6 +12,7 @@ public: { public: bool Verbose = false; + bool PermissiveMenuParsing = false; } Configuration; /** diff --git a/src/ObjLoading/Parsing/Menu/MenuFileParser.cpp b/src/ObjLoading/Parsing/Menu/MenuFileParser.cpp index cb3606c0..c0e0668d 100644 --- a/src/ObjLoading/Parsing/Menu/MenuFileParser.cpp +++ b/src/ObjLoading/Parsing/Menu/MenuFileParser.cpp @@ -9,14 +9,14 @@ using namespace menu; -MenuFileParser::MenuFileParser(SimpleLexer* lexer, const FeatureLevel featureLevel) - : AbstractParser(lexer, std::make_unique(featureLevel)) +MenuFileParser::MenuFileParser(SimpleLexer* lexer, const FeatureLevel featureLevel, bool permissiveMode) + : AbstractParser(lexer, std::make_unique(featureLevel, permissiveMode)) { CreateSequenceCollections(); } -MenuFileParser::MenuFileParser(SimpleLexer* lexer, FeatureLevel featureLevel, const MenuAssetZoneState* zoneState) - : AbstractParser(lexer, std::make_unique(featureLevel, zoneState)) +MenuFileParser::MenuFileParser(SimpleLexer* lexer, FeatureLevel featureLevel, bool permissiveMode, const MenuAssetZoneState* zoneState) + : AbstractParser(lexer, std::make_unique(featureLevel, permissiveMode, zoneState)) { CreateSequenceCollections(); } @@ -38,24 +38,25 @@ void MenuFileParser::CreateSequenceCollections() m_event_handler_set_scope_tests.clear(); const auto featureLevel = m_state->m_feature_level; + const auto permissive = m_state->m_permissive_mode; NoScopeSequences noScopeSequences(m_all_tests, m_no_scope_tests); - noScopeSequences.AddSequences(featureLevel); + noScopeSequences.AddSequences(featureLevel, permissive); GlobalScopeSequences globalScopeSequences(m_all_tests, m_global_scope_tests); - globalScopeSequences.AddSequences(featureLevel); + globalScopeSequences.AddSequences(featureLevel, permissive); MenuScopeSequences menuPropertySequences(m_all_tests, m_menu_scope_tests); - menuPropertySequences.AddSequences(featureLevel); + menuPropertySequences.AddSequences(featureLevel, permissive); ItemScopeSequences itemPropertySequences(m_all_tests, m_item_scope_tests); - itemPropertySequences.AddSequences(featureLevel); + itemPropertySequences.AddSequences(featureLevel, permissive); FunctionScopeSequences functionPropertySequences(m_all_tests, m_function_scope_tests); - functionPropertySequences.AddSequences(featureLevel); + functionPropertySequences.AddSequences(featureLevel, permissive); EventHandlerSetScopeSequences eventHandlerSetScopeSequences(m_all_tests, m_event_handler_set_scope_tests); - eventHandlerSetScopeSequences.AddSequences(featureLevel); + eventHandlerSetScopeSequences.AddSequences(featureLevel, permissive); } const std::vector& MenuFileParser::GetTestsForState() diff --git a/src/ObjLoading/Parsing/Menu/MenuFileParser.h b/src/ObjLoading/Parsing/Menu/MenuFileParser.h index 64aaf40f..7e911f7d 100644 --- a/src/ObjLoading/Parsing/Menu/MenuFileParser.h +++ b/src/ObjLoading/Parsing/Menu/MenuFileParser.h @@ -26,8 +26,8 @@ namespace menu const std::vector& GetTestsForState() override; public: - MenuFileParser(SimpleLexer* lexer, FeatureLevel featureLevel); - MenuFileParser(SimpleLexer* lexer, FeatureLevel featureLevel, const MenuAssetZoneState* zoneState); + MenuFileParser(SimpleLexer* lexer, FeatureLevel featureLevel, bool permissiveMode); + MenuFileParser(SimpleLexer* lexer, FeatureLevel featureLevel, bool permissiveMode, const MenuAssetZoneState* zoneState); _NODISCARD MenuFileParserState* GetState() const; }; } diff --git a/src/ObjLoading/Parsing/Menu/MenuFileParserState.cpp b/src/ObjLoading/Parsing/Menu/MenuFileParserState.cpp index cc1ad48b..64fe0ffa 100644 --- a/src/ObjLoading/Parsing/Menu/MenuFileParserState.cpp +++ b/src/ObjLoading/Parsing/Menu/MenuFileParserState.cpp @@ -14,19 +14,21 @@ MenuFileParserState::EventHandlerConditionState::EventHandlerConditionState(Comm { } -MenuFileParserState::MenuFileParserState(const FeatureLevel featureLevel) +MenuFileParserState::MenuFileParserState(const FeatureLevel featureLevel, const bool permissiveMode) : m_feature_level(featureLevel), + m_permissive_mode(permissiveMode), m_in_global_scope(false), m_current_function(nullptr), m_current_menu(nullptr), m_current_item(nullptr), m_current_event_handler_set(nullptr), + m_current_script_statement_terminated(true), m_current_nested_event_handler_set(nullptr) { } -MenuFileParserState::MenuFileParserState(const FeatureLevel featureLevel, const MenuAssetZoneState* zoneState) - : MenuFileParserState(featureLevel) +MenuFileParserState::MenuFileParserState(const FeatureLevel featureLevel, const bool permissiveMode, const MenuAssetZoneState* zoneState) + : MenuFileParserState(featureLevel, permissiveMode) { for (const auto& function : zoneState->m_functions) { diff --git a/src/ObjLoading/Parsing/Menu/MenuFileParserState.h b/src/ObjLoading/Parsing/Menu/MenuFileParserState.h index 3bba0eaa..b01294eb 100644 --- a/src/ObjLoading/Parsing/Menu/MenuFileParserState.h +++ b/src/ObjLoading/Parsing/Menu/MenuFileParserState.h @@ -30,6 +30,7 @@ namespace menu }; const FeatureLevel m_feature_level; + const bool m_permissive_mode; std::vector m_menus_to_load; std::vector> m_functions; @@ -45,10 +46,11 @@ namespace menu CommonEventHandlerSet* m_current_event_handler_set; std::ostringstream m_current_script; + bool m_current_script_statement_terminated; std::stack m_condition_stack; CommonEventHandlerSet* m_current_nested_event_handler_set; - explicit MenuFileParserState(FeatureLevel featureLevel); - MenuFileParserState(FeatureLevel featureLevel, const MenuAssetZoneState* zoneState); + explicit MenuFileParserState(FeatureLevel featureLevel, bool permissiveMode); + MenuFileParserState(FeatureLevel featureLevel, bool permissiveMode, const MenuAssetZoneState* zoneState); }; } diff --git a/src/ObjLoading/Parsing/Menu/MenuFileReader.cpp b/src/ObjLoading/Parsing/Menu/MenuFileReader.cpp index daf61c62..2d354d2c 100644 --- a/src/ObjLoading/Parsing/Menu/MenuFileReader.cpp +++ b/src/ObjLoading/Parsing/Menu/MenuFileReader.cpp @@ -15,7 +15,8 @@ MenuFileReader::MenuFileReader(std::istream& stream, std::string fileName, const : m_feature_level(featureLevel), m_file_name(std::move(fileName)), m_stream(nullptr), - m_zone_state(nullptr) + m_zone_state(nullptr), + m_permissive_mode(false) { OpenBaseStream(stream, std::move(includeCallback)); SetupStreamProxies(); @@ -26,7 +27,8 @@ MenuFileReader::MenuFileReader(std::istream& stream, std::string fileName, const : m_feature_level(featureLevel), m_file_name(std::move(fileName)), m_stream(nullptr), - m_zone_state(nullptr) + m_zone_state(nullptr), + m_permissive_mode(false) { OpenBaseStream(stream, nullptr); SetupStreamProxies(); @@ -117,6 +119,11 @@ void MenuFileReader::IncludeZoneState(const MenuAssetZoneState* zoneState) m_zone_state = zoneState; } +void MenuFileReader::SetPermissiveMode(const bool usePermissiveMode) +{ + m_permissive_mode = usePermissiveMode; +} + std::unique_ptr MenuFileReader::ReadMenuFile() { SimpleLexer::Config lexerConfig; @@ -126,7 +133,7 @@ std::unique_ptr MenuFileReader::ReadMenuFile() MenuExpressionMatchers().ApplyTokensToLexerConfig(lexerConfig); const auto lexer = std::make_unique(m_stream, std::move(lexerConfig)); - const auto parser = std::make_unique(lexer.get(), m_feature_level); + const auto parser = std::make_unique(lexer.get(), m_feature_level, m_permissive_mode, m_zone_state); if (!parser->Parse()) { diff --git a/src/ObjLoading/Parsing/Menu/MenuFileReader.h b/src/ObjLoading/Parsing/Menu/MenuFileReader.h index 98dc82cd..990e5c31 100644 --- a/src/ObjLoading/Parsing/Menu/MenuFileReader.h +++ b/src/ObjLoading/Parsing/Menu/MenuFileReader.h @@ -25,6 +25,7 @@ namespace menu std::vector> m_open_streams; const MenuAssetZoneState* m_zone_state; + bool m_permissive_mode; bool OpenBaseStream(std::istream& stream, include_callback_t includeCallback); void SetupDefinesProxy(); @@ -38,6 +39,7 @@ namespace menu MenuFileReader(std::istream& stream, std::string fileName, FeatureLevel featureLevel, include_callback_t includeCallback); void IncludeZoneState(const MenuAssetZoneState* zoneState); + void SetPermissiveMode(bool usePermissiveMode); std::unique_ptr ReadMenuFile(); }; diff --git a/src/ObjLoading/Parsing/Menu/Sequence/AbstractScopeSequenceHolder.h b/src/ObjLoading/Parsing/Menu/Sequence/AbstractScopeSequenceHolder.h index ced55ec6..8132b042 100644 --- a/src/ObjLoading/Parsing/Menu/Sequence/AbstractScopeSequenceHolder.h +++ b/src/ObjLoading/Parsing/Menu/Sequence/AbstractScopeSequenceHolder.h @@ -22,8 +22,8 @@ namespace menu AbstractScopeSequenceHolder(const AbstractScopeSequenceHolder& other) = delete; AbstractScopeSequenceHolder(AbstractScopeSequenceHolder&& other) noexcept = default; AbstractScopeSequenceHolder& operator=(const AbstractScopeSequenceHolder& other) = delete; - AbstractScopeSequenceHolder& operator=(AbstractScopeSequenceHolder&& other) noexcept = default; + AbstractScopeSequenceHolder& operator=(AbstractScopeSequenceHolder&& other) noexcept = delete; - virtual void AddSequences(FeatureLevel featureLevel) = 0; + virtual void AddSequences(FeatureLevel featureLevel, bool permissive) = 0; }; } diff --git a/src/ObjLoading/Parsing/Menu/Sequence/EventHandlerSetScopeSequences.cpp b/src/ObjLoading/Parsing/Menu/Sequence/EventHandlerSetScopeSequences.cpp index 8d387b6a..29cb29dc 100644 --- a/src/ObjLoading/Parsing/Menu/Sequence/EventHandlerSetScopeSequences.cpp +++ b/src/ObjLoading/Parsing/Menu/Sequence/EventHandlerSetScopeSequences.cpp @@ -195,6 +195,59 @@ namespace menu::event_handler_set_scope_sequences protected: void ProcessMatch(MenuFileParserState* state, SequenceResult& result) const override { + if (!state->m_current_script_statement_terminated) + { + state->m_current_script << "; "; + state->m_current_script_statement_terminated = true; + } + } + }; + + class SequenceSkipScriptToken final : public MenuFileParser::sequence_t + { + static constexpr auto CAPTURE_SCRIPT_TOKEN = 1; + + public: + SequenceSkipScriptToken() + { + const ScriptMatcherFactory create(this); + + AddMatchers({ + create.Or({ + create.Text(), + create.Numeric() + }).Capture(CAPTURE_SCRIPT_TOKEN) + }); + } + + protected: + void ProcessMatch(MenuFileParserState* state, SequenceResult& result) const override + { + const auto& capture = result.NextCapture(CAPTURE_SCRIPT_TOKEN); + + switch(capture.m_type) + { + case SimpleParserValueType::STRING: + state->m_current_script << "\"" << capture.StringValue() << "\" "; + break; + + case SimpleParserValueType::IDENTIFIER: + state->m_current_script << "\"" << capture.IdentifierValue() << "\" "; + break; + + case SimpleParserValueType::INTEGER: + state->m_current_script << "\"" << capture.IntegerValue() << "\" "; + break; + + case SimpleParserValueType::FLOATING_POINT: + state->m_current_script << "\"" << capture.FloatingPointValue() << "\" "; + break; + + default: + throw ParsingException(capture.GetPos(), "Invalid script token type for skipping"); + } + + state->m_current_script_statement_terminated = false; } }; @@ -611,7 +664,7 @@ EventHandlerSetScopeSequences::EventHandlerSetScopeSequences(std::vector()); // If else and stuff @@ -704,4 +757,9 @@ void EventHandlerSetScopeSequences::AddSequences(FeatureLevel featureLevel) AddSequence(std::make_unique()); AddSequence(std::make_unique()); AddSequence(std::make_unique()); + + if (permissive) + { + AddSequence(std::make_unique()); + } } diff --git a/src/ObjLoading/Parsing/Menu/Sequence/EventHandlerSetScopeSequences.h b/src/ObjLoading/Parsing/Menu/Sequence/EventHandlerSetScopeSequences.h index 60c421a6..2bd34622 100644 --- a/src/ObjLoading/Parsing/Menu/Sequence/EventHandlerSetScopeSequences.h +++ b/src/ObjLoading/Parsing/Menu/Sequence/EventHandlerSetScopeSequences.h @@ -9,6 +9,6 @@ namespace menu public: EventHandlerSetScopeSequences(std::vector>& allSequences, std::vector& scopeSequences); - void AddSequences(FeatureLevel featureLevel) override; + void AddSequences(FeatureLevel featureLevel, bool permissive) override; }; } diff --git a/src/ObjLoading/Parsing/Menu/Sequence/FunctionScopeSequences.cpp b/src/ObjLoading/Parsing/Menu/Sequence/FunctionScopeSequences.cpp index f00158d7..1834102e 100644 --- a/src/ObjLoading/Parsing/Menu/Sequence/FunctionScopeSequences.cpp +++ b/src/ObjLoading/Parsing/Menu/Sequence/FunctionScopeSequences.cpp @@ -49,7 +49,7 @@ FunctionScopeSequences::FunctionScopeSequences(std::vector()); AddSequence(std::make_unique("name", [](const MenuFileParserState* state, const TokenPos&, const std::string& value) diff --git a/src/ObjLoading/Parsing/Menu/Sequence/FunctionScopeSequences.h b/src/ObjLoading/Parsing/Menu/Sequence/FunctionScopeSequences.h index 612610c8..5b35550c 100644 --- a/src/ObjLoading/Parsing/Menu/Sequence/FunctionScopeSequences.h +++ b/src/ObjLoading/Parsing/Menu/Sequence/FunctionScopeSequences.h @@ -9,6 +9,6 @@ namespace menu public: FunctionScopeSequences(std::vector>& allSequences, std::vector& scopeSequences); - void AddSequences(FeatureLevel featureLevel) override; + void AddSequences(FeatureLevel featureLevel, bool permissive) override; }; } diff --git a/src/ObjLoading/Parsing/Menu/Sequence/GlobalScopeSequences.cpp b/src/ObjLoading/Parsing/Menu/Sequence/GlobalScopeSequences.cpp index 66b6f491..fa230397 100644 --- a/src/ObjLoading/Parsing/Menu/Sequence/GlobalScopeSequences.cpp +++ b/src/ObjLoading/Parsing/Menu/Sequence/GlobalScopeSequences.cpp @@ -107,7 +107,7 @@ GlobalScopeSequences::GlobalScopeSequences(std::vector()); AddSequence(std::make_unique()); diff --git a/src/ObjLoading/Parsing/Menu/Sequence/GlobalScopeSequences.h b/src/ObjLoading/Parsing/Menu/Sequence/GlobalScopeSequences.h index 1521baae..d940ff68 100644 --- a/src/ObjLoading/Parsing/Menu/Sequence/GlobalScopeSequences.h +++ b/src/ObjLoading/Parsing/Menu/Sequence/GlobalScopeSequences.h @@ -9,6 +9,6 @@ namespace menu public: GlobalScopeSequences(std::vector>& allSequences, std::vector& scopeSequences); - void AddSequences(FeatureLevel featureLevel) override; + void AddSequences(FeatureLevel featureLevel, bool permissive) override; }; } diff --git a/src/ObjLoading/Parsing/Menu/Sequence/ItemScopeSequences.cpp b/src/ObjLoading/Parsing/Menu/Sequence/ItemScopeSequences.cpp index d8863bec..aacbc1ff 100644 --- a/src/ObjLoading/Parsing/Menu/Sequence/ItemScopeSequences.cpp +++ b/src/ObjLoading/Parsing/Menu/Sequence/ItemScopeSequences.cpp @@ -554,7 +554,7 @@ ItemScopeSequences::ItemScopeSequences(std::vector()); AddSequence(std::make_unique("name", [](const MenuFileParserState* state, const TokenPos&, const std::string& value) diff --git a/src/ObjLoading/Parsing/Menu/Sequence/ItemScopeSequences.h b/src/ObjLoading/Parsing/Menu/Sequence/ItemScopeSequences.h index 0ecb0882..c6cbf638 100644 --- a/src/ObjLoading/Parsing/Menu/Sequence/ItemScopeSequences.h +++ b/src/ObjLoading/Parsing/Menu/Sequence/ItemScopeSequences.h @@ -9,6 +9,6 @@ namespace menu public: ItemScopeSequences(std::vector>& allSequences, std::vector& scopeSequences); - void AddSequences(FeatureLevel featureLevel) override; + void AddSequences(FeatureLevel featureLevel, bool permissive) override; }; } diff --git a/src/ObjLoading/Parsing/Menu/Sequence/MenuScopeSequences.cpp b/src/ObjLoading/Parsing/Menu/Sequence/MenuScopeSequences.cpp index bf2b3353..e3059f42 100644 --- a/src/ObjLoading/Parsing/Menu/Sequence/MenuScopeSequences.cpp +++ b/src/ObjLoading/Parsing/Menu/Sequence/MenuScopeSequences.cpp @@ -225,7 +225,7 @@ MenuScopeSequences::MenuScopeSequences(std::vector()); AddSequence(std::make_unique()); diff --git a/src/ObjLoading/Parsing/Menu/Sequence/MenuScopeSequences.h b/src/ObjLoading/Parsing/Menu/Sequence/MenuScopeSequences.h index d3f6484c..18a22423 100644 --- a/src/ObjLoading/Parsing/Menu/Sequence/MenuScopeSequences.h +++ b/src/ObjLoading/Parsing/Menu/Sequence/MenuScopeSequences.h @@ -9,6 +9,6 @@ namespace menu public: MenuScopeSequences(std::vector>& allSequences, std::vector& scopeSequences); - void AddSequences(FeatureLevel featureLevel) override; + void AddSequences(FeatureLevel featureLevel, bool permissive) override; }; } diff --git a/src/ObjLoading/Parsing/Menu/Sequence/NoScopeSequences.cpp b/src/ObjLoading/Parsing/Menu/Sequence/NoScopeSequences.cpp index cb4dc0d9..3de59d04 100644 --- a/src/ObjLoading/Parsing/Menu/Sequence/NoScopeSequences.cpp +++ b/src/ObjLoading/Parsing/Menu/Sequence/NoScopeSequences.cpp @@ -34,7 +34,7 @@ NoScopeSequences::NoScopeSequences(std::vector()); } diff --git a/src/ObjLoading/Parsing/Menu/Sequence/NoScopeSequences.h b/src/ObjLoading/Parsing/Menu/Sequence/NoScopeSequences.h index f0469e81..8ca90e04 100644 --- a/src/ObjLoading/Parsing/Menu/Sequence/NoScopeSequences.h +++ b/src/ObjLoading/Parsing/Menu/Sequence/NoScopeSequences.h @@ -9,6 +9,6 @@ namespace menu public: NoScopeSequences(std::vector>& allSequences, std::vector& scopeSequences); - void AddSequences(FeatureLevel featureLevel) override; + void AddSequences(FeatureLevel featureLevel, bool permissive) override; }; } diff --git a/test/ObjLoadingTests/Parsing/Menu/Sequence/EventHandlerSetScopeSequencesTests.cpp b/test/ObjLoadingTests/Parsing/Menu/Sequence/EventHandlerSetScopeSequencesTests.cpp index 90d5b3e9..3fbdeeee 100644 --- a/test/ObjLoadingTests/Parsing/Menu/Sequence/EventHandlerSetScopeSequencesTests.cpp +++ b/test/ObjLoadingTests/Parsing/Menu/Sequence/EventHandlerSetScopeSequencesTests.cpp @@ -22,13 +22,13 @@ namespace test::parsing::menu::sequence::event_handler_set unsigned m_consumed_token_count; - explicit EventHandlerSetSequenceTestsHelper(FeatureLevel featureLevel) - : m_state(std::make_unique(featureLevel)), + explicit EventHandlerSetSequenceTestsHelper(FeatureLevel featureLevel, bool permissive) + : m_state(std::make_unique(featureLevel, false)), m_event_handler_set(std::make_unique()), m_consumed_token_count(0u) { EventHandlerSetScopeSequences scopeSequences(m_all_sequences, m_scope_sequences); - scopeSequences.AddSequences(m_state->m_feature_level); + scopeSequences.AddSequences(m_state->m_feature_level, permissive); m_state->m_current_menu = m_state->m_menus.emplace_back(std::make_unique()).get(); m_state->m_current_event_handler_set = m_event_handler_set.get(); @@ -54,7 +54,10 @@ namespace test::parsing::menu::sequence::event_handler_set { const auto couldMatch = sequence->MatchSequence(m_lexer.get(), m_state.get(), m_consumed_token_count); if (couldMatch) + { + m_lexer->PopTokens(static_cast(m_consumed_token_count)); return couldMatch; + } } return false; @@ -64,7 +67,7 @@ namespace test::parsing::menu::sequence::event_handler_set #pragma region General TEST_CASE("EventHandlerSetScopeSequences: Keyword casing doesnt matter", "[parsing][sequence][menu]") { - EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4); + EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4, false); const TokenPos pos; helper.Tokens({ SimpleParserValue::Identifier(pos, new std::string("fadein")), @@ -84,7 +87,7 @@ namespace test::parsing::menu::sequence::event_handler_set TEST_CASE("EventHandlerSetScopeSequences: Invalid keywords are not recognized", "[parsing][sequence][menu]") { - EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4); + EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4, false); const TokenPos pos; helper.Tokens({ SimpleParserValue::Identifier(pos, new std::string("noScriptCommand")), @@ -99,6 +102,43 @@ namespace test::parsing::menu::sequence::event_handler_set REQUIRE(helper.m_consumed_token_count == 0); } + TEST_CASE("EventHandlerSetScopeSequences: Permissive mode ignores unknown script tokens and adds them to script", "[parsing][sequence][menu]") + { + EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4, true); + const TokenPos pos; + helper.Tokens({ + SimpleParserValue::Identifier(pos, new std::string("uiScript")), + SimpleParserValue::Identifier(pos, new std::string("somethingUnknown")), + SimpleParserValue::String(pos, new std::string("anArgumentForTheUnknownScript")), + SimpleParserValue::Character(pos, ';'), + SimpleParserValue::EndOfFile(pos) + }); + + auto result = helper.PerformTest(); + REQUIRE(result); + REQUIRE(helper.m_consumed_token_count == 1); + auto script = helper.m_state->m_current_script.str(); + REQUIRE(script == R"("uiScript" )"); + + result = helper.PerformTest(); + REQUIRE(result); + REQUIRE(helper.m_consumed_token_count == 1); + script = helper.m_state->m_current_script.str(); + REQUIRE(script == R"("uiScript" "somethingUnknown" )"); + + result = helper.PerformTest(); + REQUIRE(result); + REQUIRE(helper.m_consumed_token_count == 1); + script = helper.m_state->m_current_script.str(); + REQUIRE(script == R"("uiScript" "somethingUnknown" "anArgumentForTheUnknownScript" )"); + + result = helper.PerformTest(); + REQUIRE(result); + REQUIRE(helper.m_consumed_token_count == 1); + script = helper.m_state->m_current_script.str(); + REQUIRE(script == R"("uiScript" "somethingUnknown" "anArgumentForTheUnknownScript" ; )"); + } + #pragma endregion void TestGenericScriptStatement(const std::initializer_list> tokens, const std::string& expectedScript) @@ -109,7 +149,7 @@ namespace test::parsing::menu::sequence::event_handler_set tokenList.emplace_back(SimpleParserValue::Character(TokenPos(), ';')); tokenList.emplace_back(SimpleParserValue::EndOfFile(TokenPos())); - EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4); + EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4, false); helper.Tokens(std::move(tokenList)); const auto result = helper.PerformTest(); @@ -220,7 +260,7 @@ namespace test::parsing::menu::sequence::event_handler_set TEST_CASE("EventHandlerSetScopeSequences: Ensure cannot use setColor with no color", "[parsing][sequence][menu]") { - EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4); + EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4, false); const TokenPos pos; helper.Tokens({ SimpleParserValue::Identifier(pos, new std::string("setColor")), @@ -761,7 +801,7 @@ namespace test::parsing::menu::sequence::event_handler_set TEST_CASE("EventHandlerSetScopeSequences: Ensure setLocalVarBool is setLocalVar handler on non-static value", "[parsing][sequence][menu]") { - EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4); + EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4, false); const TokenPos pos; helper.Tokens({ SimpleParserValue::Identifier(TokenPos(), new std::string("setLocalVarBool")), @@ -796,7 +836,7 @@ namespace test::parsing::menu::sequence::event_handler_set TEST_CASE("EventHandlerSetScopeSequences: Ensure setLocalVarInt is setLocalVar handler on non-static value", "[parsing][sequence][menu]") { - EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4); + EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4, false); const TokenPos pos; helper.Tokens({ SimpleParserValue::Identifier(TokenPos(), new std::string("setLocalVarInt")), @@ -831,7 +871,7 @@ namespace test::parsing::menu::sequence::event_handler_set TEST_CASE("EventHandlerSetScopeSequences: Ensure setLocalVarFloat is setLocalVar handler on non-static value", "[parsing][sequence][menu]") { - EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4); + EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4, false); const TokenPos pos; helper.Tokens({ SimpleParserValue::Identifier(TokenPos(), new std::string("setLocalVarFloat")), @@ -866,7 +906,7 @@ namespace test::parsing::menu::sequence::event_handler_set TEST_CASE("EventHandlerSetScopeSequences: Ensure setLocalVarString is setLocalVar handler on non-static value", "[parsing][sequence][menu]") { - EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4); + EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4, false); const TokenPos pos; helper.Tokens({ SimpleParserValue::Identifier(TokenPos(), new std::string("setLocalVarString")), @@ -905,7 +945,7 @@ namespace test::parsing::menu::sequence::event_handler_set TEST_CASE("EventHandlerSetScopeSequences: Closing block terminates EventHandlerSet", "[parsing][sequence][menu]") { - EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4); + EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4, false); const TokenPos pos; helper.Tokens({ SimpleParserValue::Character(pos, '}'), @@ -926,7 +966,7 @@ namespace test::parsing::menu::sequence::event_handler_set TEST_CASE("EventHandlerSetScopeSequences: Closing block finishes current script", "[parsing][sequence][menu]") { - EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4); + EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4, false); const TokenPos pos; helper.Tokens({ SimpleParserValue::Character(pos, '}'), @@ -955,7 +995,7 @@ namespace test::parsing::menu::sequence::event_handler_set TEST_CASE("EventHandlerSetScopeSequences: If opens new condition", "[parsing][sequence][menu]") { - EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4); + EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4, false); const TokenPos pos; helper.Tokens({ SimpleParserValue::Identifier(pos, new std::string("if")), @@ -1001,7 +1041,7 @@ namespace test::parsing::menu::sequence::event_handler_set TEST_CASE("EventHandlerSetScopeSequences: If applies current script", "[parsing][sequence][menu]") { - EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4); + EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4, false); const TokenPos pos; helper.Tokens({ SimpleParserValue::Identifier(pos, new std::string("if")), @@ -1040,7 +1080,7 @@ namespace test::parsing::menu::sequence::event_handler_set TEST_CASE("EventHandlerSetScopeSequences: ElseIf opens new condition", "[parsing][sequence][menu]") { - EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4); + EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4, false); const TokenPos pos; helper.Tokens({ SimpleParserValue::Character(pos, '}'), @@ -1094,7 +1134,7 @@ namespace test::parsing::menu::sequence::event_handler_set TEST_CASE("EventHandlerSetScopeSequences: ElseIf applies current script", "[parsing][sequence][menu]") { - EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4); + EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4, false); const TokenPos pos; helper.Tokens({ SimpleParserValue::Character(pos, '}'), @@ -1138,7 +1178,7 @@ namespace test::parsing::menu::sequence::event_handler_set TEST_CASE("EventHandlerSetScopeSequences: ElseIf cannot be specified when not in if", "[parsing][sequence][menu]") { - EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4); + EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4, false); const TokenPos pos; helper.Tokens({ SimpleParserValue::Character(pos, '}'), @@ -1155,7 +1195,7 @@ namespace test::parsing::menu::sequence::event_handler_set TEST_CASE("EventHandlerSetScopeSequences: ElseIf cannot be specified when else was already used", "[parsing][sequence][menu]") { - EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4); + EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4, false); const TokenPos pos; helper.Tokens({ SimpleParserValue::Character(pos, '}'), @@ -1177,7 +1217,7 @@ namespace test::parsing::menu::sequence::event_handler_set TEST_CASE("EventHandlerSetScopeSequences: Else switches to else element", "[parsing][sequence][menu]") { - EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4); + EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4, false); const TokenPos pos; helper.Tokens({ SimpleParserValue::Character(pos, '}'), @@ -1209,7 +1249,7 @@ namespace test::parsing::menu::sequence::event_handler_set TEST_CASE("EventHandlerSetScopeSequences: Else keeps auto_skip value", "[parsing][sequence][menu]") { - EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4); + EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4, false); const TokenPos pos; helper.Tokens({ SimpleParserValue::Character(pos, '}'), @@ -1241,7 +1281,7 @@ namespace test::parsing::menu::sequence::event_handler_set TEST_CASE("EventHandlerSetScopeSequences: Else cannot be specified when not in if", "[parsing][sequence][menu]") { - EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4); + EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4, false); const TokenPos pos; helper.Tokens({ SimpleParserValue::Character(pos, '}'), @@ -1255,7 +1295,7 @@ namespace test::parsing::menu::sequence::event_handler_set TEST_CASE("EventHandlerSetScopeSequences: Else cannot be specified when else was already used", "[parsing][sequence][menu]") { - EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4); + EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4, false); const TokenPos pos; helper.Tokens({ SimpleParserValue::Character(pos, '}'), @@ -1274,7 +1314,7 @@ namespace test::parsing::menu::sequence::event_handler_set TEST_CASE("EventHandlerSetScopeSequences: CloseBlock closes if statements", "[parsing][sequence][menu]") { - EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4); + EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4, false); const TokenPos pos; helper.Tokens({ SimpleParserValue::Character(pos, '}'), @@ -1299,7 +1339,7 @@ namespace test::parsing::menu::sequence::event_handler_set TEST_CASE("EventHandlerSetScopeSequences: CloseBlock closes nested if statements to parent if statement", "[parsing][sequence][menu]") { - EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4); + EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4, false); const TokenPos pos; helper.Tokens({ SimpleParserValue::Character(pos, '}'), @@ -1328,7 +1368,7 @@ namespace test::parsing::menu::sequence::event_handler_set TEST_CASE("EventHandlerSetScopeSequences: CloseBlock closes nested if statements to parent else statement", "[parsing][sequence][menu]") { - EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4); + EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4, false); const TokenPos pos; helper.Tokens({ SimpleParserValue::Character(pos, '}'), @@ -1358,7 +1398,7 @@ namespace test::parsing::menu::sequence::event_handler_set TEST_CASE("EventHandlerSetScopeSequences: CloseBlock closes all autoskip conditions to parent if statement", "[parsing][sequence][menu]") { - EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4); + EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4, false); const TokenPos pos; helper.Tokens({ SimpleParserValue::Character(pos, '}'), @@ -1391,7 +1431,7 @@ namespace test::parsing::menu::sequence::event_handler_set TEST_CASE("EventHandlerSetScopeSequences: CloseBlock closes all autoskip conditions to base", "[parsing][sequence][menu]") { - EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4); + EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4, false); const TokenPos pos; helper.Tokens({ SimpleParserValue::Character(pos, '}'), diff --git a/test/ParserTestUtils/Parsing/Mock/MockLexer.h b/test/ParserTestUtils/Parsing/Mock/MockLexer.h index 9101793c..78be4b34 100644 --- a/test/ParserTestUtils/Parsing/Mock/MockLexer.h +++ b/test/ParserTestUtils/Parsing/Mock/MockLexer.h @@ -14,20 +14,20 @@ class MockLexer final : public ILexer std::vector m_tokens; TokenType m_eof; - int m_pop_count; + size_t m_pop_count; public: MockLexer(std::initializer_list> tokens, TokenType eof) : m_tokens(std::make_move_iterator(tokens.begin()), std::make_move_iterator(tokens.end())), m_eof(std::move(eof)), - m_pop_count(0) + m_pop_count(0u) { } MockLexer(std::vector tokens, TokenType eof) : m_tokens(std::move(tokens)), m_eof(std::move(eof)), - m_pop_count(0) + m_pop_count(0u) { } @@ -39,28 +39,30 @@ public: const TokenType& GetToken(const unsigned index) override { - if (index < m_tokens.size()) - return m_tokens[index]; + const auto absoluteIndex = m_pop_count + index; + if (absoluteIndex < m_tokens.size()) + return m_tokens[absoluteIndex]; return m_eof; } void PopTokens(const int amount) override { - m_pop_count += amount; + assert(amount >= 0); + m_pop_count += static_cast(amount); } bool IsEof() override { - return !m_tokens.empty(); + return !m_tokens.empty() || m_pop_count >= m_tokens.size(); } const TokenPos& GetPos() override { - return !m_tokens.empty() ? m_tokens[0].GetPos() : m_eof.GetPos(); + return !m_tokens.empty() && m_pop_count < m_tokens.size() ? m_tokens[m_pop_count].GetPos() : m_eof.GetPos(); } - _NODISCARD int GetPopCount() const + _NODISCARD size_t GetPopCount() const { return m_pop_count; }