Make use of custom functions when converting menus

This commit is contained in:
Jan
2021-12-28 23:52:42 +01:00
parent 338de302d9
commit 7188b0946d
27 changed files with 459 additions and 143 deletions

View File

@ -0,0 +1,43 @@
#include "CommonExpressionBaseFunctionCall.h"
using namespace menu;
CommonExpressionBaseFunctionCall::CommonExpressionBaseFunctionCall(std::string functionName, size_t functionIndex)
: m_function_name(std::move(functionName)),
m_function_index(functionIndex)
{
}
bool CommonExpressionBaseFunctionCall::Equals(const ISimpleExpression* other) const
{
const auto otherFunctionCall = dynamic_cast<const CommonExpressionBaseFunctionCall*>(other);
if (!otherFunctionCall
|| m_function_name != otherFunctionCall->m_function_name
|| m_function_index != otherFunctionCall->m_function_index
|| m_args.size() != otherFunctionCall->m_args.size())
{
return false;
}
for (auto i = 0u; i < m_args.size(); i++)
{
const auto* arg = m_args[i].get();
const auto* otherArg = otherFunctionCall->m_args[i].get();
if (!arg->Equals(otherArg))
return false;
}
return true;
}
bool CommonExpressionBaseFunctionCall::IsStatic() const
{
return false;
}
SimpleExpressionValue CommonExpressionBaseFunctionCall::Evaluate() const
{
return SimpleExpressionValue(0);
}

View File

@ -5,13 +5,14 @@
namespace menu
{
class CommonExpressionFunctionCall final : public ISimpleExpression
class CommonExpressionBaseFunctionCall final : public ISimpleExpression
{
public:
std::string m_function_name;
size_t m_function_index;
std::vector<std::unique_ptr<ISimpleExpression>> m_args;
explicit CommonExpressionFunctionCall(std::string functionName);
CommonExpressionBaseFunctionCall(std::string functionName, size_t functionIndex);
_NODISCARD bool Equals(const ISimpleExpression* other) const override;
_NODISCARD bool IsStatic() const override;

View File

@ -0,0 +1,24 @@
#include "CommonExpressionCustomFunctionCall.h"
using namespace menu;
CommonExpressionCustomFunctionCall::CommonExpressionCustomFunctionCall(std::string functionName)
: m_function_name(std::move(functionName))
{
}
bool CommonExpressionCustomFunctionCall::Equals(const ISimpleExpression* other) const
{
const auto otherFunctionCall = dynamic_cast<const CommonExpressionCustomFunctionCall*>(other);
return otherFunctionCall && m_function_name == otherFunctionCall->m_function_name;
}
bool CommonExpressionCustomFunctionCall::IsStatic() const
{
return false;
}
SimpleExpressionValue CommonExpressionCustomFunctionCall::Evaluate() const
{
return SimpleExpressionValue(0);
}

View File

@ -0,0 +1,18 @@
#pragma once
#include "Parsing/Simple/Expression/ISimpleExpression.h"
namespace menu
{
class CommonExpressionCustomFunctionCall final : public ISimpleExpression
{
public:
std::string m_function_name;
explicit CommonExpressionCustomFunctionCall(std::string functionName);
_NODISCARD bool Equals(const ISimpleExpression* other) const override;
_NODISCARD bool IsStatic() const override;
_NODISCARD SimpleExpressionValue Evaluate() const override;
};
}

View File

@ -1,41 +0,0 @@
#include "CommonExpressionFunctionCall.h"
using namespace menu;
CommonExpressionFunctionCall::CommonExpressionFunctionCall(std::string functionName)
: m_function_name(std::move(functionName))
{
}
bool CommonExpressionFunctionCall::Equals(const ISimpleExpression* other) const
{
const auto otherFunctionCall = dynamic_cast<const CommonExpressionFunctionCall*>(other);
if (!otherFunctionCall
|| m_function_name != otherFunctionCall->m_function_name
|| m_args.size() != otherFunctionCall->m_args.size())
{
return false;
}
for(auto i = 0u; i < m_args.size(); i++)
{
const auto* arg = m_args[i].get();
const auto* otherArg = otherFunctionCall->m_args[i].get();
if (!arg->Equals(otherArg))
return false;
}
return true;
}
bool CommonExpressionFunctionCall::IsStatic() const
{
return false;
}
SimpleExpressionValue CommonExpressionFunctionCall::Evaluate() const
{
return SimpleExpressionValue(0);
}

View File

@ -1,7 +1,12 @@
#include "MenuExpressionMatchers.h"
#include "MenuMatcherFactory.h"
#include "Parsing/Menu/Domain/Expression/CommonExpressionFunctionCall.h"
#include "Game/IW4/IW4.h"
#include "Game/IW5/IW5.h"
#include "Game/IW4/MenuConstantsIW4.h"
#include "Game/IW5/MenuConstantsIW5.h"
#include "Parsing/Menu/Domain/Expression/CommonExpressionBaseFunctionCall.h"
#include "Parsing/Menu/Domain/Expression/CommonExpressionCustomFunctionCall.h"
using namespace menu;
@ -10,9 +15,14 @@ static constexpr int TAG_EXPRESSION_FUNCTION_CALL_END = SimpleExpressionMatchers
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()
: SimpleExpressionMatchers(true, true, true, true, true)
: MenuExpressionMatchers(nullptr)
{
}
@ -36,17 +46,73 @@ std::unique_ptr<SimpleExpressionMatchers::matcher_t> MenuExpressionMatchers::Par
});
}
const std::map<std::string, size_t>& MenuExpressionMatchers::GetBaseFunctionMapForFeatureLevel(const FeatureLevel featureLevel)
{
if(featureLevel == FeatureLevel::IW4)
{
static std::map<std::string, size_t> iw4FunctionMap;
static bool iw4FunctionMapInitialized = false;
if(!iw4FunctionMapInitialized)
{
for(size_t i = IW4::expressionFunction_e::EXP_FUNC_DYN_START; i < std::extent_v<decltype(IW4::g_expFunctionNames)>; i++)
iw4FunctionMap.emplace(std::make_pair(IW4::g_expFunctionNames[i], i));
}
return iw4FunctionMap;
}
if(featureLevel == FeatureLevel::IW5)
{
static std::map<std::string, size_t> iw5FunctionMap;
static bool iw5FunctionMapInitialized = false;
if(!iw5FunctionMapInitialized)
{
for(size_t i = IW5::expressionFunction_e::EXP_FUNC_DYN_START; i < std::extent_v<decltype(IW5::g_expFunctionNames)>; i++)
iw5FunctionMap.emplace(std::make_pair(IW5::g_expFunctionNames[i], i));
}
return iw5FunctionMap;
}
assert(false);
throw ParsingException(TokenPos(), "Feature level has no functions registered!!");
}
std::unique_ptr<ISimpleExpression> MenuExpressionMatchers::ProcessOperandExtension(SequenceResult<SimpleParserValue>& 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");
auto functionCall = std::make_unique<CommonExpressionFunctionCall>(result.NextCapture(CAPTURE_FUNCTION_NAME).IdentifierValue());
const auto& functionCallToken = result.NextCapture(CAPTURE_FUNCTION_NAME);
auto functionCallName = functionCallToken.IdentifierValue();
while (result.PeekAndRemoveIfTag(TAG_EXPRESSION_FUNCTION_CALL_END) != TAG_EXPRESSION_FUNCTION_CALL_END)
const auto& baseFunctionMap = GetBaseFunctionMapForFeatureLevel(m_state->m_feature_level);
const auto foundBaseFunction = baseFunctionMap.find(functionCallName);
if(foundBaseFunction != baseFunctionMap.end())
{
functionCall->m_args.emplace_back(ProcessExpression(result));
auto functionCall = std::make_unique<CommonExpressionBaseFunctionCall>(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);
}
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<CommonExpressionCustomFunctionCall>(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");
}

View File

@ -2,14 +2,20 @@
#include <memory>
#include "Parsing/Menu/MenuFileParserState.h"
#include "Parsing/Simple/Expression/SimpleExpressionMatchers.h"
namespace menu
{
class MenuExpressionMatchers final : public SimpleExpressionMatchers
{
const MenuFileParserState* m_state;
static const std::map<std::string, size_t>& GetBaseFunctionMapForFeatureLevel(FeatureLevel featureLevel);
public:
MenuExpressionMatchers();
explicit MenuExpressionMatchers(const MenuFileParserState* state);
protected:
std::unique_ptr<matcher_t> ParseOperandExtension(const supplier_t* labelSupplier) const override;

View File

@ -101,7 +101,7 @@ std::string& MenuMatcherFactory::TokenTextValue(const SimpleParserValue& value)
return value.StringValue();
}
int MenuMatcherFactory::TokenIntExpressionValue(SequenceResult<SimpleParserValue>& result)
int MenuMatcherFactory::TokenIntExpressionValue(MenuFileParserState* state, SequenceResult<SimpleParserValue>& result)
{
const auto nextTag = result.PeekTag();
@ -115,7 +115,7 @@ int MenuMatcherFactory::TokenIntExpressionValue(SequenceResult<SimpleParserValue
if (nextTag == TAG_EXPRESSION)
{
result.NextTag();
const auto expression = MenuExpressionMatchers().ProcessExpression(result);
const auto expression = MenuExpressionMatchers(state).ProcessExpression(result);
if (!expression || !expression->IsStatic())
throw ParsingException(result.NextCapture(CAPTURE_FIRST_TOKEN).GetPos(), "Not a valid static expression");
@ -131,7 +131,7 @@ int MenuMatcherFactory::TokenIntExpressionValue(SequenceResult<SimpleParserValue
throw ParsingException(TokenPos(), "TokenIntExpressionValue must be expression or int");
}
double MenuMatcherFactory::TokenNumericExpressionValue(SequenceResult<SimpleParserValue>& result)
double MenuMatcherFactory::TokenNumericExpressionValue(MenuFileParserState* state, SequenceResult<SimpleParserValue>& result)
{
const auto nextTag = result.PeekTag();
@ -145,7 +145,7 @@ double MenuMatcherFactory::TokenNumericExpressionValue(SequenceResult<SimplePars
if (nextTag == TAG_EXPRESSION)
{
result.NextTag();
const auto expression = MenuExpressionMatchers().ProcessExpression(result);
const auto expression = MenuExpressionMatchers(state).ProcessExpression(result);
if (!expression || !expression->IsStatic())
throw ParsingException(result.NextCapture(CAPTURE_FIRST_TOKEN).GetPos(), "Not a valid static expression");

View File

@ -2,6 +2,7 @@
#include "Parsing/Sequence/SequenceResult.h"
#include "Parsing/Simple/Matcher/SimpleMatcherFactory.h"
#include "Parsing/Menu/MenuFileParserState.h"
namespace menu
{
@ -30,7 +31,7 @@ namespace menu
_NODISCARD static double TokenNumericFloatingPointValue(const SimpleParserValue& value);
_NODISCARD static std::string& TokenTextValue(const SimpleParserValue& value);
_NODISCARD static int TokenIntExpressionValue(SequenceResult<SimpleParserValue>& result);
_NODISCARD static double TokenNumericExpressionValue(SequenceResult<SimpleParserValue>& result);
_NODISCARD static int TokenIntExpressionValue(MenuFileParserState* state, SequenceResult<SimpleParserValue>& result);
_NODISCARD static double TokenNumericExpressionValue(MenuFileParserState* state, SequenceResult<SimpleParserValue>& result);
};
}

View File

@ -0,0 +1,19 @@
#include "MenuAssetZoneState.h"
using namespace menu;
void MenuAssetZoneState::AddLoadedFile(std::string loadedFileName)
{
m_loaded_files.emplace(std::move(loadedFileName));
}
void MenuAssetZoneState::AddFunction(std::unique_ptr<CommonFunctionDef> function)
{
m_functions_by_name.emplace(std::make_pair(function->m_name, function.get()));
m_functions.emplace_back(std::move(function));
}
void MenuAssetZoneState::AddMenu(std::unique_ptr<CommonMenuDef> menu)
{
m_menus.emplace_back(std::move(menu));
}

View File

@ -16,6 +16,12 @@ namespace menu
std::vector<std::unique_ptr<CommonFunctionDef>> m_functions;
std::vector<std::unique_ptr<CommonMenuDef>> m_menus;
std::map<std::string, CommonFunctionDef*> m_functions_by_name;
MenuAssetZoneState() = default;
void AddLoadedFile(std::string loadedFileName);
void AddFunction(std::unique_ptr<CommonFunctionDef> function);
void AddMenu(std::unique_ptr<CommonMenuDef> menu);
};
}

View File

@ -582,7 +582,7 @@ namespace menu::event_handler_set_scope_sequences
protected:
void ProcessMatch(MenuFileParserState* state, SequenceResult<SimpleParserValue>& result) const override
{
const MenuExpressionMatchers expressionMatchers;
const MenuExpressionMatchers expressionMatchers(state);
const auto typeTag = static_cast<SetLocalVarType>(result.NextTag());
const auto& varNameToken = result.NextCapture(CAPTURE_VAR_NAME);
@ -623,7 +623,7 @@ namespace menu::event_handler_set_scope_sequences
protected:
void ProcessMatch(MenuFileParserState* state, SequenceResult<SimpleParserValue>& result) const override
{
const MenuExpressionMatchers expressionMatchers;
const MenuExpressionMatchers expressionMatchers(state);
auto expression = expressionMatchers.ProcessExpression(result);
if (!expression)
@ -668,7 +668,7 @@ namespace menu::event_handler_set_scope_sequences
protected:
void ProcessMatch(MenuFileParserState* state, SequenceResult<SimpleParserValue>& result) const override
{
const MenuExpressionMatchers expressionMatchers;
const MenuExpressionMatchers expressionMatchers(state);
auto expression = expressionMatchers.ProcessExpression(result);
if (!expression)
@ -708,9 +708,7 @@ namespace menu::event_handler_set_scope_sequences
{
const ScriptMatcherFactory create(this);
const MenuExpressionMatchers expressionMatchers;
AddLabeledMatchers(expressionMatchers.Expression(this), MenuExpressionMatchers::LABEL_EXPRESSION);
AddMatchers({
create.Char('}'),
create.Keyword("else").Capture(CAPTURE_KEYWORD),

View File

@ -22,10 +22,10 @@ GenericColorPropertySequence::GenericColorPropertySequence(std::string keywordNa
});
}
double GenericColorPropertySequence::ReadColorValue(SequenceResult<SimpleParserValue>& result)
double GenericColorPropertySequence::ReadColorValue(MenuFileParserState* state, SequenceResult<SimpleParserValue>& result)
{
if (result.PeekAndRemoveIfTag(TAG_COLOR) == TAG_COLOR)
return MenuMatcherFactory::TokenNumericExpressionValue(result);
return MenuMatcherFactory::TokenNumericExpressionValue(state, result);
return 0.0;
}
@ -35,10 +35,10 @@ void GenericColorPropertySequence::ProcessMatch(MenuFileParserState* state, Sequ
if (m_set_callback)
{
CommonColor color{};
color.r = ReadColorValue(result);
color.g = ReadColorValue(result);
color.b = ReadColorValue(result);
color.a = ReadColorValue(result);
color.r = ReadColorValue(state, result);
color.g = ReadColorValue(state, result);
color.b = ReadColorValue(state, result);
color.a = ReadColorValue(state, result);
m_set_callback(state, result.NextCapture(CAPTURE_FIRST_TOKEN).GetPos(), color);
}

View File

@ -20,7 +20,7 @@ namespace menu
const callback_t m_set_callback;
static double ReadColorValue(SequenceResult<SimpleParserValue>& result);
static double ReadColorValue(MenuFileParserState* state, SequenceResult<SimpleParserValue>& result);
protected:
void ProcessMatch(MenuFileParserState* state, SequenceResult<SimpleParserValue>& result) const override;

View File

@ -65,7 +65,7 @@ void GenericExpressionPropertySequence::ProcessMatch(MenuFileParserState* state,
{
if (m_set_callback)
{
const MenuExpressionMatchers expressionMatchers;
const MenuExpressionMatchers expressionMatchers(state);
auto expression = expressionMatchers.ProcessExpression(result);
m_set_callback(state, result.NextCapture(CAPTURE_FIRST_TOKEN).GetPos(), std::move(expression));
}

View File

@ -23,7 +23,7 @@ void GenericFloatingPointPropertySequence::ProcessMatch(MenuFileParserState* sta
{
if (m_set_callback)
{
const auto value = MenuMatcherFactory::TokenNumericExpressionValue(result);
const auto value = MenuMatcherFactory::TokenNumericExpressionValue(state, result);
m_set_callback(state, result.NextCapture(CAPTURE_FIRST_TOKEN).GetPos(), value);
}
}

View File

@ -23,7 +23,7 @@ void GenericIntPropertySequence::ProcessMatch(MenuFileParserState* state, Sequen
{
if (m_set_callback)
{
const auto value = MenuMatcherFactory::TokenIntExpressionValue(result);
const auto value = MenuMatcherFactory::TokenIntExpressionValue(state, result);
m_set_callback(state, result.NextCapture(CAPTURE_FIRST_TOKEN).GetPos(), value);
}
}

View File

@ -188,10 +188,10 @@ 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);
const auto x = MenuMatcherFactory::TokenNumericExpressionValue(state, result);
const auto y = MenuMatcherFactory::TokenNumericExpressionValue(state, result);
const auto w = MenuMatcherFactory::TokenNumericExpressionValue(state, result);
const auto h = MenuMatcherFactory::TokenNumericExpressionValue(state, result);
CommonRect rect
{
x,
@ -231,8 +231,8 @@ namespace menu::item_scope_sequences
{
assert(state->m_current_item);
state->m_current_item->m_rect.x = MenuMatcherFactory::TokenNumericExpressionValue(result);
state->m_current_item->m_rect.y = MenuMatcherFactory::TokenNumericExpressionValue(result);
state->m_current_item->m_rect.x = MenuMatcherFactory::TokenNumericExpressionValue(state, result);
state->m_current_item->m_rect.y = MenuMatcherFactory::TokenNumericExpressionValue(state, result);
}
};
@ -261,9 +261,9 @@ namespace menu::item_scope_sequences
{
assert(state->m_current_item);
state->m_current_item->m_fx_letter_time = MenuMatcherFactory::TokenIntExpressionValue(result);
state->m_current_item->m_fx_decay_start_time = MenuMatcherFactory::TokenIntExpressionValue(result);
state->m_current_item->m_fx_decay_duration = MenuMatcherFactory::TokenIntExpressionValue(result);
state->m_current_item->m_fx_letter_time = MenuMatcherFactory::TokenIntExpressionValue(state, result);
state->m_current_item->m_fx_decay_start_time = MenuMatcherFactory::TokenIntExpressionValue(state, result);
state->m_current_item->m_fx_decay_duration = MenuMatcherFactory::TokenIntExpressionValue(state, result);
}
};
@ -341,9 +341,9 @@ namespace menu::item_scope_sequences
ItemScopeOperations::EnsureHasEditFieldFeatures(*state->m_current_item, result.NextCapture(CAPTURE_FIRST_TOKEN).GetPos());
state->m_current_item->m_dvar = MenuMatcherFactory::TokenTextValue(result.NextCapture(CAPTURE_DVAR_NAME));
state->m_current_item->m_edit_field_features->m_def_val = MenuMatcherFactory::TokenNumericExpressionValue(result);
state->m_current_item->m_edit_field_features->m_min_val = MenuMatcherFactory::TokenNumericExpressionValue(result);
state->m_current_item->m_edit_field_features->m_max_val = MenuMatcherFactory::TokenNumericExpressionValue(result);
state->m_current_item->m_edit_field_features->m_def_val = MenuMatcherFactory::TokenNumericExpressionValue(state, result);
state->m_current_item->m_edit_field_features->m_min_val = MenuMatcherFactory::TokenNumericExpressionValue(state, result);
state->m_current_item->m_edit_field_features->m_max_val = MenuMatcherFactory::TokenNumericExpressionValue(state, result);
}
};
@ -422,7 +422,7 @@ namespace menu::item_scope_sequences
while (result.HasNextCapture(CAPTURE_STEP_NAME))
{
multiValueFeatures->m_step_names.emplace_back(MenuMatcherFactory::TokenTextValue(result.NextCapture(CAPTURE_STEP_NAME)));
multiValueFeatures->m_double_values.emplace_back(MenuMatcherFactory::TokenNumericExpressionValue(result));
multiValueFeatures->m_double_values.emplace_back(MenuMatcherFactory::TokenNumericExpressionValue(state, result));
}
}
};

View File

@ -124,10 +124,10 @@ 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);
const auto x = MenuMatcherFactory::TokenNumericExpressionValue(state, result);
const auto y = MenuMatcherFactory::TokenNumericExpressionValue(state, result);
const auto w = MenuMatcherFactory::TokenNumericExpressionValue(state, result);
const auto h = MenuMatcherFactory::TokenNumericExpressionValue(state, result);
CommonRect rect
{
x,