diff --git a/src/ObjLoading/Game/IW4/Menu/MenuConverterIW4.cpp b/src/ObjLoading/Game/IW4/Menu/MenuConverterIW4.cpp index 2914194f..ecd91299 100644 --- a/src/ObjLoading/Game/IW4/Menu/MenuConverterIW4.cpp +++ b/src/ObjLoading/Game/IW4/Menu/MenuConverterIW4.cpp @@ -751,7 +751,8 @@ namespace IW4 continue; } - assert(false); + // Do not consider this a mistake since the games menus do this by mistake and it should be able to compile them anyway + // But the game should also not know what to do with this i guess expressionIsStatic = false; } diff --git a/src/ObjLoading/Game/IW5/Menu/MenuConverterIW5.cpp b/src/ObjLoading/Game/IW5/Menu/MenuConverterIW5.cpp index 1a2ff51d..3da84150 100644 --- a/src/ObjLoading/Game/IW5/Menu/MenuConverterIW5.cpp +++ b/src/ObjLoading/Game/IW5/Menu/MenuConverterIW5.cpp @@ -753,7 +753,8 @@ namespace IW5 continue; } - assert(false); + // Do not consider this a mistake since the games menus do this by mistake and it should be able to compile them anyway + // But the game should also not know what to do with this i guess expressionIsStatic = false; } diff --git a/src/ObjLoading/Parsing/Menu/MenuFileCommonOperations.cpp b/src/ObjLoading/Parsing/Menu/MenuFileCommonOperations.cpp new file mode 100644 index 00000000..48abf663 --- /dev/null +++ b/src/ObjLoading/Parsing/Menu/MenuFileCommonOperations.cpp @@ -0,0 +1,27 @@ +#include "MenuFileCommonOperations.h" + +#include "Parsing/ParsingException.h" + +using namespace menu; + +void MenuFileCommonOperations::EnsureIsNumericExpression(const MenuFileParserState* state, const TokenPos& pos, const ISimpleExpression& expression) +{ + if (!state->m_permissive_mode && expression.IsStatic()) + { + const auto staticValue = expression.EvaluateStatic(); + + if (staticValue.m_type != SimpleExpressionValue::Type::INT && staticValue.m_type != SimpleExpressionValue::Type::DOUBLE) + throw ParsingException(pos, "Expression is expected to be numeric. Use permissive mode to compile anyway."); + } +} + +void MenuFileCommonOperations::EnsureIsStringExpression(const MenuFileParserState* state, const TokenPos& pos, const ISimpleExpression& expression) +{ + if (!state->m_permissive_mode && expression.IsStatic()) + { + const auto staticValue = expression.EvaluateStatic(); + + if (staticValue.m_type != SimpleExpressionValue::Type::STRING) + throw ParsingException(pos, "Expression is expected to be string. Use permissive mode to compile anyway."); + } +} \ No newline at end of file diff --git a/src/ObjLoading/Parsing/Menu/MenuFileCommonOperations.h b/src/ObjLoading/Parsing/Menu/MenuFileCommonOperations.h new file mode 100644 index 00000000..b56c83b5 --- /dev/null +++ b/src/ObjLoading/Parsing/Menu/MenuFileCommonOperations.h @@ -0,0 +1,14 @@ +#pragma once +#include "MenuFileParserState.h" +#include "Parsing/TokenPos.h" +#include "Parsing/Simple/Expression/ISimpleExpression.h" + +namespace menu +{ + class MenuFileCommonOperations + { + public: + static void EnsureIsNumericExpression(const MenuFileParserState* state, const TokenPos& pos, const ISimpleExpression& expression); + static void EnsureIsStringExpression(const MenuFileParserState* state, const TokenPos& pos, const ISimpleExpression& expression); + }; +} diff --git a/src/ObjLoading/Parsing/Menu/Sequence/ItemScopeSequences.cpp b/src/ObjLoading/Parsing/Menu/Sequence/ItemScopeSequences.cpp index 336b1182..cb94bf8c 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/MenuFileCommonOperations.h" #include "Parsing/Menu/Matcher/MenuExpressionMatchers.h" #include "Parsing/Menu/Matcher/MenuMatcherFactory.h" @@ -663,12 +664,14 @@ void ItemScopeSequences::AddSequences(FeatureLevel featureLevel, bool permissive { state->m_current_item->m_border_size = value; })); - AddSequence(GenericExpressionPropertySequence::WithKeywordAndBool("visible", [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr value) + AddSequence(GenericExpressionPropertySequence::WithKeywordAndBool("visible", [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr value) { + MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value); state->m_current_item->m_visible_expression = std::move(value); })); - AddSequence(GenericExpressionPropertySequence::WithKeywordAndBool("disabled", [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr value) + AddSequence(GenericExpressionPropertySequence::WithKeywordAndBool("disabled", [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr value) { + MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value); state->m_current_item->m_disabled_expression = std::move(value); })); AddSequence(std::make_unique("ownerdraw", [](const MenuFileParserState* state, const TokenPos&, const int value) @@ -803,96 +806,114 @@ void ItemScopeSequences::AddSequences(FeatureLevel featureLevel, bool permissive state->m_current_item->m_game_message_window_mode = value; })); AddSequence(std::make_unique()); - AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "disabled"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr value) + AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "disabled"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr value) { + MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value); state->m_current_item->m_disabled_expression = std::move(value); })); - AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "text"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr value) + AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "text"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr value) { + MenuFileCommonOperations::EnsureIsStringExpression(state, pos, *value); state->m_current_item->m_text_expression = std::move(value); })); - AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "material"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr value) + AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "material"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr value) { + MenuFileCommonOperations::EnsureIsStringExpression(state, pos, *value); state->m_current_item->m_material_expression = std::move(value); })); - AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "material"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr value) - { - state->m_current_item->m_material_expression = std::move(value); - })); - AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "rect", "X"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr value) + AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "rect", "X"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr value) { + MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value); state->m_current_item->m_rect_x_exp = std::move(value); })); - AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "rect", "Y"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr value) + AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "rect", "Y"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr value) { + MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value); state->m_current_item->m_rect_y_exp = std::move(value); })); - AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "rect", "W"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr value) + AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "rect", "W"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr value) { + MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value); state->m_current_item->m_rect_w_exp = std::move(value); })); - AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "rect", "H"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr value) + AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "rect", "H"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr value) { + MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value); state->m_current_item->m_rect_h_exp = std::move(value); })); - AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "forecolor", "R"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr value) + AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "forecolor", "R"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr value) { + MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value); state->m_current_item->m_forecolor_expressions.m_r_exp = std::move(value); })); - AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "forecolor", "G"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr value) + AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "forecolor", "G"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr value) { + MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value); state->m_current_item->m_forecolor_expressions.m_g_exp = std::move(value); })); - AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "forecolor", "B"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr value) + AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "forecolor", "B"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr value) { + MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value); state->m_current_item->m_forecolor_expressions.m_b_exp = std::move(value); })); - AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "forecolor", "A"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr value) + AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "forecolor", "A"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr value) { + MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value); state->m_current_item->m_forecolor_expressions.m_a_exp = std::move(value); })); - AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "forecolor", "RGB"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr value) + AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "forecolor", "RGB"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr value) { + MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value); state->m_current_item->m_forecolor_expressions.m_rgb_exp = std::move(value); })); - AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "glowcolor", "R"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr value) + AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "glowcolor", "R"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr value) { + MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value); state->m_current_item->m_glowcolor_expressions.m_r_exp = std::move(value); })); - AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "glowcolor", "G"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr value) + AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "glowcolor", "G"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr value) { + MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value); state->m_current_item->m_glowcolor_expressions.m_g_exp = std::move(value); })); - AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "glowcolor", "B"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr value) + AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "glowcolor", "B"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr value) { + MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value); state->m_current_item->m_glowcolor_expressions.m_b_exp = std::move(value); })); - AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "glowcolor", "A"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr value) + AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "glowcolor", "A"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr value) { + MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value); state->m_current_item->m_glowcolor_expressions.m_a_exp = std::move(value); })); - AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "glowcolor", "RGB"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr value) + AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "glowcolor", "RGB"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr value) { + MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value); state->m_current_item->m_glowcolor_expressions.m_rgb_exp = std::move(value); })); - AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "backcolor", "R"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr value) + AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "backcolor", "R"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr value) { + MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value); state->m_current_item->m_backcolor_expressions.m_r_exp = std::move(value); })); - AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "backcolor", "G"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr value) + AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "backcolor", "G"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr value) { + MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value); state->m_current_item->m_backcolor_expressions.m_g_exp = std::move(value); })); - AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "backcolor", "B"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr value) + AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "backcolor", "B"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr value) { + MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value); state->m_current_item->m_backcolor_expressions.m_b_exp = std::move(value); })); - AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "backcolor", "A"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr value) + AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "backcolor", "A"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr value) { + MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value); state->m_current_item->m_backcolor_expressions.m_a_exp = std::move(value); })); - AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "backcolor", "RGB"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr value) + AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "backcolor", "RGB"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr value) { + MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value); state->m_current_item->m_backcolor_expressions.m_rgb_exp = std::move(value); })); @@ -901,8 +922,9 @@ void ItemScopeSequences::AddSequences(FeatureLevel featureLevel, bool permissive AddSequence(std::make_unique("hasFocus", [](const MenuFileParserState* state, const TokenPos&) -> std::unique_ptr& { return state->m_current_item->m_has_focus; })); - AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "textaligny"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr value) + AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "textaligny"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr value) { + MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value); state->m_current_item->m_text_align_y_expression = std::move(value); })); } @@ -964,6 +986,7 @@ void ItemScopeSequences::AddSequences(FeatureLevel featureLevel, bool permissive AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "elementheight"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr value) { ItemScopeOperations::EnsureHasListboxFeatures(*state->m_current_item, pos); + MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value); state->m_current_item->m_list_box_features->m_element_height_expression = std::move(value); })); } diff --git a/src/ObjLoading/Parsing/Menu/Sequence/MenuScopeSequences.cpp b/src/ObjLoading/Parsing/Menu/Sequence/MenuScopeSequences.cpp index 22a6a20e..cbfbbed6 100644 --- a/src/ObjLoading/Parsing/Menu/Sequence/MenuScopeSequences.cpp +++ b/src/ObjLoading/Parsing/Menu/Sequence/MenuScopeSequences.cpp @@ -10,6 +10,7 @@ #include "Generic/GenericKeywordPropertySequence.h" #include "Generic/GenericMenuEventHandlerSetPropertySequence.h" #include "Generic/GenericStringPropertySequence.h" +#include "Parsing/Menu/MenuFileCommonOperations.h" #include "Parsing/Menu/Matcher/MenuMatcherFactory.h" #include "Parsing/Menu/Domain/CommonMenuTypes.h" #include "Parsing/Menu/Matcher/MenuExpressionMatchers.h" @@ -254,8 +255,9 @@ void MenuScopeSequences::AddSequences(FeatureLevel featureLevel, bool permissive { state->m_current_menu->m_style = value; })); - AddSequence(GenericExpressionPropertySequence::WithKeywordAndBool("visible", [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr value) + AddSequence(GenericExpressionPropertySequence::WithKeywordAndBool("visible", [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr value) { + MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value); state->m_current_menu->m_visible_expression = std::move(value); })); AddSequence(std::make_unique("onOpen", [](const MenuFileParserState* state, const TokenPos&) -> std::unique_ptr& { @@ -318,28 +320,34 @@ void MenuScopeSequences::AddSequences(FeatureLevel featureLevel, bool permissive { state->m_current_menu->m_sound_loop = value; })); - AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "rect", "X"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr value) + AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "rect", "X"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr value) { + MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value); state->m_current_menu->m_rect_x_exp = std::move(value); })); - AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "rect", "Y"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr value) + AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "rect", "Y"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr value) { + MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value); state->m_current_menu->m_rect_y_exp = std::move(value); })); - AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "rect", "W"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr value) + AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "rect", "W"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr value) { + MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value); state->m_current_menu->m_rect_w_exp = std::move(value); })); - AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "rect", "H"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr value) + AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "rect", "H"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr value) { + MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value); state->m_current_menu->m_rect_h_exp = std::move(value); })); - AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "openSound"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr value) + AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "openSound"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr value) { + MenuFileCommonOperations::EnsureIsStringExpression(state, pos, *value); state->m_current_menu->m_open_sound_exp = std::move(value); })); - AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "closeSound"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr value) + AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "closeSound"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr value) { + MenuFileCommonOperations::EnsureIsStringExpression(state, pos, *value); state->m_current_menu->m_close_sound_exp = std::move(value); })); AddSequence(std::make_unique("popup", [](const MenuFileParserState* state, const TokenPos&) @@ -396,8 +404,9 @@ void MenuScopeSequences::AddSequences(FeatureLevel featureLevel, bool permissive if (featureLevel == FeatureLevel::IW5) { - AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "soundLoop"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr value) + AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "soundLoop"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr value) { + MenuFileCommonOperations::EnsureIsStringExpression(state, pos, *value); state->m_current_menu->m_sound_loop_exp = std::move(value); })); AddSequence(std::make_unique("onFocusDueToClose", [](const MenuFileParserState* state, const TokenPos&) -> std::unique_ptr& {