diff --git a/src/ObjLoading/Parsing/Menu/Matcher/MenuMatcherFactory.cpp b/src/ObjLoading/Parsing/Menu/Matcher/MenuMatcherFactory.cpp index 1219311b..bb985ba9 100644 --- a/src/ObjLoading/Parsing/Menu/Matcher/MenuMatcherFactory.cpp +++ b/src/ObjLoading/Parsing/Menu/Matcher/MenuMatcherFactory.cpp @@ -1,5 +1,7 @@ #include "MenuMatcherFactory.h" +#include "MenuExpressionMatchers.h" + using namespace menu; MenuMatcherFactory::MenuMatcherFactory(const IMatcherForLabelSupplier* labelSupplier) @@ -9,17 +11,41 @@ MenuMatcherFactory::MenuMatcherFactory(const IMatcherForLabelSupplier MenuMatcherFactory::Text() const { - return MatcherFactoryWrapper(Or({String(), Identifier()})); + return MatcherFactoryWrapper(Or({String(), Identifier()})); } MatcherFactoryWrapper MenuMatcherFactory::Numeric() const { - return MatcherFactoryWrapper(Or({FloatingPoint(), Integer()})); + return MatcherFactoryWrapper(Or({FloatingPoint(), Integer()})); +} + +MatcherFactoryWrapper MenuMatcherFactory::IntExpression() const +{ + return MatcherFactoryWrapper(Or({ + Integer().Tag(TAG_INT).Capture(CAPTURE_INT), + And({ + Char('(').Capture(CAPTURE_FIRST_TOKEN), + Label(MenuExpressionMatchers::LABEL_EXPRESSION), + Char(')'), + }).Tag(TAG_EXPRESSION) + })); +} + +MatcherFactoryWrapper MenuMatcherFactory::NumericExpression() const +{ + return MatcherFactoryWrapper(Or({ + Numeric().Tag(TAG_NUMERIC).Capture(CAPTURE_NUMERIC), + And({ + Char('(').Capture(CAPTURE_FIRST_TOKEN), + Label(MenuExpressionMatchers::LABEL_EXPRESSION), + Char(')'), + }).Tag(TAG_EXPRESSION) + })); } int MenuMatcherFactory::TokenNumericIntValue(const SimpleParserValue& value) { - if(value.m_type == SimpleParserValueType::FLOATING_POINT) + if (value.m_type == SimpleParserValueType::FLOATING_POINT) { return static_cast(value.FloatingPointValue()); } @@ -31,7 +57,7 @@ double MenuMatcherFactory::TokenNumericFloatingPointValue(const SimpleParserValu { if (value.m_type == SimpleParserValueType::INTEGER) { - return static_cast(value.IntegerValue()); + return value.IntegerValue(); } return value.FloatingPointValue(); @@ -39,10 +65,72 @@ double MenuMatcherFactory::TokenNumericFloatingPointValue(const SimpleParserValu std::string& MenuMatcherFactory::TokenTextValue(const SimpleParserValue& value) { - if(value.m_type == SimpleParserValueType::IDENTIFIER) + if (value.m_type == SimpleParserValueType::IDENTIFIER) { return value.IdentifierValue(); } return value.StringValue(); } + +int MenuMatcherFactory::TokenIntExpressionValue(SequenceResult& result) +{ + const auto nextTag = result.PeekTag(); + + assert(nextTag == TAG_INT || nextTag == TAG_EXPRESSION); + if (nextTag == TAG_INT) + { + result.NextTag(); + return result.NextCapture(CAPTURE_INT).IntegerValue(); + } + + if (nextTag == TAG_EXPRESSION) + { + result.NextTag(); + const auto expression = MenuExpressionMatchers().ProcessExpression(result); + + if (!expression || !expression->IsStatic()) + throw ParsingException(result.NextCapture(CAPTURE_FIRST_TOKEN).GetPos(), "Not a valid static expression"); + + const auto value = expression->Evaluate(); + + if (value.m_type != SimpleExpressionValue::Type::INT) + throw ParsingException(result.NextCapture(CAPTURE_FIRST_TOKEN).GetPos(), "Expression MUST be int type"); + + return value.m_int_value; + } + + throw ParsingException(TokenPos(), "TokenIntExpressionValue must be expression or int"); +} + +double MenuMatcherFactory::TokenNumericExpressionValue(SequenceResult& result) +{ + const auto nextTag = result.PeekTag(); + + assert(nextTag == TAG_NUMERIC || nextTag == TAG_EXPRESSION); + if (nextTag == TAG_NUMERIC) + { + result.NextTag(); + return TokenNumericFloatingPointValue(result.NextCapture(CAPTURE_NUMERIC)); + } + + if (nextTag == TAG_EXPRESSION) + { + result.NextTag(); + const auto expression = MenuExpressionMatchers().ProcessExpression(result); + + if (!expression || !expression->IsStatic()) + throw ParsingException(result.NextCapture(CAPTURE_FIRST_TOKEN).GetPos(), "Not a valid static expression"); + + const auto value = expression->Evaluate(); + + if (value.m_type == SimpleExpressionValue::Type::INT) + return value.m_int_value; + if (value.m_type == SimpleExpressionValue::Type::DOUBLE) + return value.m_double_value; + + throw ParsingException(result.NextCapture(CAPTURE_FIRST_TOKEN).GetPos(), "Expression MUST be numeric type"); + } + + throw ParsingException(TokenPos(), "TokenNumericExpressionValue must be expression or numeric"); +} diff --git a/src/ObjLoading/Parsing/Menu/Matcher/MenuMatcherFactory.h b/src/ObjLoading/Parsing/Menu/Matcher/MenuMatcherFactory.h index 58baff2a..ceefb06b 100644 --- a/src/ObjLoading/Parsing/Menu/Matcher/MenuMatcherFactory.h +++ b/src/ObjLoading/Parsing/Menu/Matcher/MenuMatcherFactory.h @@ -1,19 +1,34 @@ #pragma once +#include "Parsing/Sequence/SequenceResult.h" #include "Parsing/Simple/Matcher/SimpleMatcherFactory.h" namespace menu { class MenuMatcherFactory : public SimpleMatcherFactory { + static constexpr auto TAG_INT = 1420; + static constexpr auto TAG_NUMERIC = 1421; + static constexpr auto TAG_EXPRESSION = 1422; + + static constexpr auto CAPTURE_FIRST_TOKEN = 1420; + static constexpr auto CAPTURE_INT = 1421; + static constexpr auto CAPTURE_NUMERIC = 1422; + public: explicit MenuMatcherFactory(const IMatcherForLabelSupplier* labelSupplier); _NODISCARD MatcherFactoryWrapper Text() const; _NODISCARD MatcherFactoryWrapper Numeric() const; + _NODISCARD MatcherFactoryWrapper IntExpression() const; + _NODISCARD MatcherFactoryWrapper NumericExpression() const; + _NODISCARD static int TokenNumericIntValue(const SimpleParserValue& value); _NODISCARD static double TokenNumericFloatingPointValue(const SimpleParserValue& value); _NODISCARD static std::string& TokenTextValue(const SimpleParserValue& value); + + _NODISCARD static int TokenIntExpressionValue(SequenceResult& result); + _NODISCARD static double TokenNumericExpressionValue(SequenceResult& result); }; } diff --git a/src/ObjLoading/Parsing/Menu/Sequence/EventHandlerSetScopeSequences.cpp b/src/ObjLoading/Parsing/Menu/Sequence/EventHandlerSetScopeSequences.cpp index c65ce61f..8d387b6a 100644 --- a/src/ObjLoading/Parsing/Menu/Sequence/EventHandlerSetScopeSequences.cpp +++ b/src/ObjLoading/Parsing/Menu/Sequence/EventHandlerSetScopeSequences.cpp @@ -270,6 +270,29 @@ namespace menu::event_handler_set_scope_sequences } }; + class SequenceUiScriptStatement final : public SequenceGenericScriptStatement + { + public: + explicit SequenceUiScriptStatement(std::initializer_list>>> matchers) + : SequenceGenericScriptStatement() + { + const ScriptMatcherFactory create(this); + + AddMatchers({ + create.And({ + create.ScriptKeyword("uiScript"), + create.And(matchers) + }).Capture(CAPTURE_SCRIPT_TOKEN), + create.Optional(create.Char(';')) + }); + } + + static std::unique_ptr Create(std::initializer_list>>> matchers) + { + return std::make_unique(matchers); + } + }; + class SequenceSetPlayerData final : public SequenceGenericScriptStatement { public: @@ -653,6 +676,30 @@ void EventHandlerSetScopeSequences::AddSequences(FeatureLevel featureLevel) AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("resolveError")})); AddSequence(std::make_unique()); + AddSequence(SequenceUiScriptStatement::Create({create.ScriptKeyword("StartServer")})); + AddSequence(SequenceUiScriptStatement::Create({create.ScriptKeyword("loadArenas")})); + AddSequence(SequenceUiScriptStatement::Create({create.ScriptKeyword("loadGameInfo")})); + AddSequence(SequenceUiScriptStatement::Create({create.ScriptKeyword("clearError")})); + AddSequence(SequenceUiScriptStatement::Create({create.ScriptKeyword("Quit")})); + AddSequence(SequenceUiScriptStatement::Create({create.ScriptKeyword("Controls")})); + AddSequence(SequenceUiScriptStatement::Create({create.ScriptKeyword("Leave")})); + AddSequence(SequenceUiScriptStatement::Create({create.ScriptKeyword("closeingame")})); + AddSequence(SequenceUiScriptStatement::Create({create.ScriptKeyword("update"), create.ScriptText()})); + AddSequence(SequenceUiScriptStatement::Create({create.ScriptKeyword("startSingleplayer")})); + AddSequence(SequenceUiScriptStatement::Create({create.ScriptKeyword("getLanguage")})); + AddSequence(SequenceUiScriptStatement::Create({create.ScriptKeyword("verifyLanguage")})); + AddSequence(SequenceUiScriptStatement::Create({create.ScriptKeyword("updateLanguage")})); + AddSequence(SequenceUiScriptStatement::Create({create.ScriptKeyword("mutePlayer")})); + AddSequence(SequenceUiScriptStatement::Create({create.ScriptKeyword("openMenuOnDvar"), create.ScriptText(), create.Or({create.ScriptStrictNumeric(), create.ScriptText()}), create.ScriptText()})); + AddSequence( + SequenceUiScriptStatement::Create({create.ScriptKeyword("openMenuOnDvarNot"), create.ScriptText(), create.Or({create.ScriptStrictNumeric(), create.ScriptText()}), create.ScriptText()})); + AddSequence(SequenceUiScriptStatement::Create({create.ScriptKeyword("closeMenuOnDvar"), create.ScriptText(), create.Or({create.ScriptStrictNumeric(), create.ScriptText()}), create.ScriptText()})); + AddSequence(SequenceUiScriptStatement::Create( + {create.ScriptKeyword("closeMenuOnDvarNot"), create.ScriptText(), create.Or({create.ScriptStrictNumeric(), create.ScriptText()}), create.ScriptText()})); + AddSequence(SequenceUiScriptStatement::Create({create.ScriptKeyword("setRecommended")})); + AddSequence(SequenceUiScriptStatement::Create({create.ScriptKeyword("clearLoadErrorsSummary")})); + AddSequence(SequenceUiScriptStatement::Create({create.ScriptKeyword("clearClientMatchData")})); + AddSequence(std::make_unique()); AddSequence(std::make_unique()); AddSequence(std::make_unique()); diff --git a/src/ObjLoading/Parsing/Menu/Sequence/ItemScopeSequences.cpp b/src/ObjLoading/Parsing/Menu/Sequence/ItemScopeSequences.cpp index 28d5126b..ebc426f8 100644 --- a/src/ObjLoading/Parsing/Menu/Sequence/ItemScopeSequences.cpp +++ b/src/ObjLoading/Parsing/Menu/Sequence/ItemScopeSequences.cpp @@ -10,6 +10,7 @@ #include "Generic/GenericKeywordPropertySequence.h" #include "Generic/GenericMenuEventHandlerSetPropertySequence.h" #include "Generic/GenericStringPropertySequence.h" +#include "Parsing/Menu/Matcher/MenuExpressionMatchers.h" #include "Parsing/Menu/Matcher/MenuMatcherFactory.h" using namespace menu; @@ -142,24 +143,21 @@ namespace menu::item_scope_sequences class SequenceRect final : public MenuFileParser::sequence_t { - static constexpr auto CAPTURE_X = 1; - static constexpr auto CAPTURE_Y = 2; - static constexpr auto CAPTURE_W = 3; - static constexpr auto CAPTURE_H = 4; - static constexpr auto CAPTURE_ALIGN_HORIZONTAL = 5; - static constexpr auto CAPTURE_ALIGN_VERTICAL = 6; + static constexpr auto CAPTURE_ALIGN_HORIZONTAL = 1; + static constexpr auto CAPTURE_ALIGN_VERTICAL = 2; public: SequenceRect() { const MenuMatcherFactory create(this); + AddLabeledMatchers(MenuExpressionMatchers().Expression(this), MenuExpressionMatchers::LABEL_EXPRESSION); AddMatchers({ create.KeywordIgnoreCase("rect"), - create.Numeric().Capture(CAPTURE_X), - create.Numeric().Capture(CAPTURE_Y), - create.Numeric().Capture(CAPTURE_W), - create.Numeric().Capture(CAPTURE_H), + create.NumericExpression(), // x + create.NumericExpression(), // y + create.NumericExpression(), // w + create.NumericExpression(), // h create.Optional(create.And({ create.Integer().Capture(CAPTURE_ALIGN_HORIZONTAL), create.Integer().Capture(CAPTURE_ALIGN_VERTICAL) @@ -172,12 +170,16 @@ namespace menu::item_scope_sequences { assert(state->m_current_item); + const auto x = MenuMatcherFactory::TokenNumericExpressionValue(result); + const auto y = MenuMatcherFactory::TokenNumericExpressionValue(result); + const auto w = MenuMatcherFactory::TokenNumericExpressionValue(result); + const auto h = MenuMatcherFactory::TokenNumericExpressionValue(result); CommonRect rect { - MenuMatcherFactory::TokenNumericFloatingPointValue(result.NextCapture(CAPTURE_X)), - MenuMatcherFactory::TokenNumericFloatingPointValue(result.NextCapture(CAPTURE_Y)), - MenuMatcherFactory::TokenNumericFloatingPointValue(result.NextCapture(CAPTURE_W)), - MenuMatcherFactory::TokenNumericFloatingPointValue(result.NextCapture(CAPTURE_H)), + x, + y, + w, + h, 0, 0 }; diff --git a/src/ObjLoading/Parsing/Menu/Sequence/MenuScopeSequences.cpp b/src/ObjLoading/Parsing/Menu/Sequence/MenuScopeSequences.cpp index edfb2bd0..325e7fb7 100644 --- a/src/ObjLoading/Parsing/Menu/Sequence/MenuScopeSequences.cpp +++ b/src/ObjLoading/Parsing/Menu/Sequence/MenuScopeSequences.cpp @@ -79,10 +79,6 @@ namespace menu::menu_scope_sequences class SequenceRect final : public MenuFileParser::sequence_t { - static constexpr auto CAPTURE_X = 1; - static constexpr auto CAPTURE_Y = 2; - static constexpr auto CAPTURE_W = 3; - static constexpr auto CAPTURE_H = 4; static constexpr auto CAPTURE_ALIGN_HORIZONTAL = 5; static constexpr auto CAPTURE_ALIGN_VERTICAL = 6; @@ -91,12 +87,13 @@ namespace menu::menu_scope_sequences { const MenuMatcherFactory create(this); + AddLabeledMatchers(MenuExpressionMatchers().Expression(this), MenuExpressionMatchers::LABEL_EXPRESSION); AddMatchers({ create.KeywordIgnoreCase("rect"), - create.Numeric().Capture(CAPTURE_X), - create.Numeric().Capture(CAPTURE_Y), - create.Numeric().Capture(CAPTURE_W), - create.Numeric().Capture(CAPTURE_H), + create.NumericExpression(), // x + create.NumericExpression(), // y + create.NumericExpression(), // w + create.NumericExpression(), // h create.Optional(create.And({ create.Integer().Capture(CAPTURE_ALIGN_HORIZONTAL), create.Integer().Capture(CAPTURE_ALIGN_VERTICAL) @@ -109,12 +106,16 @@ namespace menu::menu_scope_sequences { assert(state->m_current_menu); + const auto x = MenuMatcherFactory::TokenNumericExpressionValue(result); + const auto y = MenuMatcherFactory::TokenNumericExpressionValue(result); + const auto w = MenuMatcherFactory::TokenNumericExpressionValue(result); + const auto h = MenuMatcherFactory::TokenNumericExpressionValue(result); CommonRect rect { - MenuMatcherFactory::TokenNumericFloatingPointValue(result.NextCapture(CAPTURE_X)), - MenuMatcherFactory::TokenNumericFloatingPointValue(result.NextCapture(CAPTURE_Y)), - MenuMatcherFactory::TokenNumericFloatingPointValue(result.NextCapture(CAPTURE_W)), - MenuMatcherFactory::TokenNumericFloatingPointValue(result.NextCapture(CAPTURE_H)), + x, + y, + w, + h, 0, 0 };