#include "MenuExpressionMatchers.h" #include "Game/IW4/IW4.h" #include "Game/IW4/MenuConstantsIW4.h" #include "Game/IW5/IW5.h" #include "Game/IW5/MenuConstantsIW5.h" #include "MenuMatcherFactory.h" #include "Parsing/Menu/Domain/Expression/CommonExpressionBaseFunctionCall.h" #include "Parsing/Menu/Domain/Expression/CommonExpressionCustomFunctionCall.h" using namespace menu; static constexpr int TAG_EXPRESSION_FUNCTION_CALL = SimpleExpressionMatchers::TAG_OFFSET_EXPRESSION_EXT + 1; static constexpr int TAG_EXPRESSION_FUNCTION_CALL_END = SimpleExpressionMatchers::TAG_OFFSET_EXPRESSION_EXT + 2; static constexpr int CAPTURE_FUNCTION_NAME = SimpleExpressionMatchers::CAPTURE_OFFSET_EXPRESSION_EXT + 1; MenuExpressionMatchers::MenuExpressionMatchers(const MenuFileParserState* state) : SimpleExpressionMatchers(true, true, true, true, true), m_state(state) { } MenuExpressionMatchers::MenuExpressionMatchers() : MenuExpressionMatchers(nullptr) { } std::unique_ptr MenuExpressionMatchers::ParseOperandExtension(const supplier_t* labelSupplier) const { const MenuMatcherFactory create(labelSupplier); return create.Or({ create .And({ create.Identifier().Capture(CAPTURE_FUNCTION_NAME), create.Char('('), create.Optional(create.And({ create.Label(LABEL_EXPRESSION), create.OptionalLoop(create.And({ create.Char(','), create.Label(LABEL_EXPRESSION), })), })), create.Char(')').Tag(TAG_EXPRESSION_FUNCTION_CALL_END), }) .Tag(TAG_EXPRESSION_FUNCTION_CALL), }); } const std::map& MenuExpressionMatchers::GetBaseFunctionMapForFeatureLevel(const FeatureLevel featureLevel) { if (featureLevel == FeatureLevel::IW4) { static std::map iw4FunctionMap; static bool iw4FunctionMapInitialized = false; if (!iw4FunctionMapInitialized) { for (size_t i = IW4::expressionFunction_e::EXP_FUNC_DYN_START; i < std::extent_v; i++) { std::string functionName(IW4::g_expFunctionNames[i]); utils::MakeStringLowerCase(functionName); iw4FunctionMap.emplace(std::make_pair(functionName, i)); } } return iw4FunctionMap; } if (featureLevel == FeatureLevel::IW5) { static std::map iw5FunctionMap; static bool iw5FunctionMapInitialized = false; if (!iw5FunctionMapInitialized) { for (size_t i = IW5::expressionFunction_e::EXP_FUNC_DYN_START; i < std::extent_v; i++) { std::string functionName(IW5::g_expFunctionNames[i]); utils::MakeStringLowerCase(functionName); iw5FunctionMap.emplace(std::make_pair(std::move(functionName), i)); } } return iw5FunctionMap; } assert(false); throw ParsingException(TokenPos(), "Feature level has no functions registered!!"); } std::unique_ptr MenuExpressionMatchers::ProcessOperandExtension(SequenceResult& result) const { assert(m_state); if (m_state == nullptr) throw ParsingException(TokenPos(), "No state when processing menu operand extension!!"); if (result.PeekAndRemoveIfTag(TAG_EXPRESSION_FUNCTION_CALL) != TAG_EXPRESSION_FUNCTION_CALL) throw ParsingException(TokenPos(), "Menu Operand Extension must be function call"); const auto& functionCallToken = result.NextCapture(CAPTURE_FUNCTION_NAME); auto functionCallName = functionCallToken.IdentifierValue(); utils::MakeStringLowerCase(functionCallName); const auto& baseFunctionMap = GetBaseFunctionMapForFeatureLevel(m_state->m_feature_level); const auto foundBaseFunction = baseFunctionMap.find(functionCallName); if (foundBaseFunction != baseFunctionMap.end()) { auto functionCall = std::make_unique(std::move(functionCallName), foundBaseFunction->second); while (result.PeekAndRemoveIfTag(TAG_EXPRESSION_FUNCTION_CALL_END) != TAG_EXPRESSION_FUNCTION_CALL_END) { functionCall->m_args.emplace_back(ProcessExpression(result)); } return std::move(functionCall); } const auto foundCustomFunction = m_state->m_functions_by_name.find(functionCallName); if (foundCustomFunction != m_state->m_functions_by_name.end()) { auto functionCall = std::make_unique(std::move(functionCallName)); if (result.PeekAndRemoveIfTag(TAG_EXPRESSION_FUNCTION_CALL_END) != TAG_EXPRESSION_FUNCTION_CALL_END) throw ParsingException(functionCallToken.GetPos(), "Custom functions cannot be called with arguments"); return std::move(functionCall); } throw ParsingException(functionCallToken.GetPos(), "Unknown function"); }