Add simple script parsing for menus

This commit is contained in:
Jan 2021-11-07 00:45:39 +01:00
parent 81203e523e
commit 7025fca81c
3 changed files with 405 additions and 5 deletions

View File

@ -3,10 +3,13 @@
using namespace menu; using namespace menu;
CommonEventHandlerSetLocalVar::CommonEventHandlerSetLocalVar() CommonEventHandlerSetLocalVar::CommonEventHandlerSetLocalVar()
= default; : m_type(SetLocalVarType::UNKNOWN)
{
}
CommonEventHandlerSetLocalVar::CommonEventHandlerSetLocalVar(std::string varName, std::unique_ptr<ICommonExpression> value) CommonEventHandlerSetLocalVar::CommonEventHandlerSetLocalVar(SetLocalVarType type, std::string varName, std::unique_ptr<ICommonExpression> value)
: m_var_name(std::move(varName)), : m_type(type),
m_var_name(std::move(varName)),
m_value(std::move(value)) m_value(std::move(value))
{ {
} }

View File

@ -7,14 +7,24 @@
namespace menu namespace menu
{ {
enum class SetLocalVarType
{
UNKNOWN,
BOOL,
INT,
FLOAT,
STRING
};
class CommonEventHandlerSetLocalVar final : public ICommonEventHandlerElement class CommonEventHandlerSetLocalVar final : public ICommonEventHandlerElement
{ {
public: public:
SetLocalVarType m_type;
std::string m_var_name; std::string m_var_name;
std::unique_ptr<ICommonExpression> m_value; std::unique_ptr<ICommonExpression> m_value;
CommonEventHandlerSetLocalVar(); CommonEventHandlerSetLocalVar();
CommonEventHandlerSetLocalVar(std::string varName, std::unique_ptr<ICommonExpression> value); CommonEventHandlerSetLocalVar(SetLocalVarType type, std::string varName, std::unique_ptr<ICommonExpression> value);
CommonEventHandlerElementType GetType() override; CommonEventHandlerElementType GetType() override;
}; };

View File

@ -1,13 +1,111 @@
#include "EventHandlerSetScopeSequences.h" #include "EventHandlerSetScopeSequences.h"
#include <sstream> #include <sstream>
#include <memory>
#include <initializer_list>
#include "Generic/GenericStringPropertySequence.h" #include "Generic/GenericStringPropertySequence.h"
#include "Parsing/Menu/Domain/EventHandler/CommonEventHandlerScript.h" #include "Parsing/Menu/Domain/EventHandler/CommonEventHandlerScript.h"
#include "Parsing/Menu/Domain/EventHandler/CommonEventHandlerSetLocalVar.h"
#include "Parsing/Menu/Matcher/MenuCommonMatchers.h"
#include "Parsing/Menu/Matcher/MenuMatcherFactory.h" #include "Parsing/Menu/Matcher/MenuMatcherFactory.h"
using namespace menu; using namespace menu;
namespace menu
{
enum class ExpectedScriptToken
{
NUMERIC,
INT,
TEXT
};
class ScriptMatcherFactory final : public MenuMatcherFactory
{
public:
explicit ScriptMatcherFactory(const IMatcherForLabelSupplier<SimpleParserValue>* labelSupplier)
: MenuMatcherFactory(labelSupplier)
{
}
_NODISCARD MatcherFactoryWrapper<SimpleParserValue> ScriptNumeric() const
{
return Or({
Type(SimpleParserValueType::INTEGER).Transform([](const token_list_t& tokens)-> SimpleParserValue
{
const auto& firstToken = tokens[0].get();
return SimpleParserValue::String(firstToken.GetPos(), new std::string(std::to_string(firstToken.IntegerValue())));
}),
Type(SimpleParserValueType::FLOATING_POINT).Transform([](const token_list_t& tokens)-> SimpleParserValue
{
const auto& firstToken = tokens[0].get();
return SimpleParserValue::String(firstToken.GetPos(), new std::string(std::to_string(firstToken.FloatingPointValue())));
}),
Or({
Type(SimpleParserValueType::CHARACTER),
Type(SimpleParserValueType::STRING),
Type(SimpleParserValueType::IDENTIFIER),
}).Transform([](const token_list_t& tokens) -> SimpleParserValue
{
return SimpleParserValue::Integer(tokens[0].get().GetPos(), static_cast<int>(ExpectedScriptToken::INT));
})
});
}
_NODISCARD MatcherFactoryWrapper<SimpleParserValue> ScriptInt() const
{
return Or({
Type(SimpleParserValueType::INTEGER).Transform([](const token_list_t& tokens)-> SimpleParserValue
{
const auto& firstToken = tokens[0].get();
return SimpleParserValue::String(firstToken.GetPos(), new std::string(std::to_string(firstToken.IntegerValue())));
}),
Or({
Type(SimpleParserValueType::CHARACTER),
Type(SimpleParserValueType::FLOATING_POINT),
Type(SimpleParserValueType::STRING),
Type(SimpleParserValueType::IDENTIFIER),
}).Transform([](const token_list_t& tokens) -> SimpleParserValue
{
return SimpleParserValue::Integer(tokens[0].get().GetPos(), static_cast<int>(ExpectedScriptToken::INT));
})
});
}
_NODISCARD MatcherFactoryWrapper<SimpleParserValue> ScriptText() const
{
return Or({
Type(SimpleParserValueType::STRING),
Type(SimpleParserValueType::IDENTIFIER),
Or({
Type(SimpleParserValueType::CHARACTER),
Type(SimpleParserValueType::FLOATING_POINT),
Type(SimpleParserValueType::INTEGER),
}).Transform([](const token_list_t& tokens) -> SimpleParserValue
{
return SimpleParserValue::Integer(tokens[0].get().GetPos(), static_cast<int>(ExpectedScriptToken::TEXT));
})
});
}
_NODISCARD MatcherFactoryWrapper<SimpleParserValue> ScriptKeyword(std::string keyword) const
{
return KeywordIgnoreCase(std::move(keyword));
}
_NODISCARD MatcherFactoryWrapper<SimpleParserValue> ScriptColor() const
{
return And({
Optional(ScriptNumeric()),
Optional(ScriptNumeric()),
Optional(ScriptNumeric()),
Optional(ScriptNumeric())
});
}
};
}
namespace menu::event_handler_set_scope_sequences namespace menu::event_handler_set_scope_sequences
{ {
class SequenceCloseBlock final : public MenuFileParser::sequence_t class SequenceCloseBlock final : public MenuFileParser::sequence_t
@ -54,6 +152,233 @@ namespace menu::event_handler_set_scope_sequences
} }
} }
}; };
class SequenceSkipEmptyStatements final : public MenuFileParser::sequence_t
{
public:
SequenceSkipEmptyStatements()
{
const MenuMatcherFactory create(this);
AddMatchers({
create.Char(';')
});
}
protected:
void ProcessMatch(MenuFileParserState* state, SequenceResult<SimpleParserValue>& result) const override
{
}
};
class SequenceGenericScriptStatement : public MenuFileParser::sequence_t
{
protected:
static constexpr auto CAPTURE_SCRIPT_TOKEN = 1;
SequenceGenericScriptStatement() = default;
public:
explicit SequenceGenericScriptStatement(std::initializer_list<Movable<std::unique_ptr<AbstractMatcher<SimpleParserValue>>>> matchers)
{
const MenuMatcherFactory create(this);
AddMatchers({
create.And(matchers).Capture(CAPTURE_SCRIPT_TOKEN),
create.Optional(create.Char(';'))
});
}
static std::unique_ptr<SequenceGenericScriptStatement> Create(std::initializer_list<Movable<std::unique_ptr<AbstractMatcher<SimpleParserValue>>>> matchers)
{
return std::make_unique<SequenceGenericScriptStatement>(matchers);
}
private:
static std::string ScriptTokenTypeToString(int tokenType)
{
switch (static_cast<ExpectedScriptToken>(tokenType))
{
case ExpectedScriptToken::INT:
return "INT";
case ExpectedScriptToken::NUMERIC:
return "NUMERIC";
case ExpectedScriptToken::TEXT:
return "TEXT";
default:
return "UNKNOWN";
}
}
protected:
void ProcessMatch(MenuFileParserState* state, SequenceResult<SimpleParserValue>& result) const override
{
while (result.HasNextCapture(CAPTURE_SCRIPT_TOKEN))
{
const auto& capture = result.NextCapture(CAPTURE_SCRIPT_TOKEN);
if (capture.m_type == SimpleParserValueType::IDENTIFIER)
{
state->m_current_script << "\"" << capture.IdentifierValue() << "\" ";
}
else if (capture.m_type == SimpleParserValueType::STRING)
{
state->m_current_script << "\"" << capture.StringValue() << "\" ";
}
else if (capture.m_type == SimpleParserValueType::INTEGER)
{
std::ostringstream ss;
ss << "Invalid script token. Expected " << ScriptTokenTypeToString(capture.IntegerValue()) << ".";
throw ParsingException(capture.GetPos(), ss.str());
}
else
throw ParsingException(capture.GetPos(), "Invalid script capture");
}
state->m_current_script << "; ";
}
};
class SequenceSetPlayerData final : public SequenceGenericScriptStatement
{
public:
explicit SequenceSetPlayerData()
{
const MenuMatcherFactory create(this);
AddMatchers({
create.And({
}).Capture(CAPTURE_SCRIPT_TOKEN),
create.Optional(create.Char(';'))
});
}
};
class SequenceSetLocalVar final : public MenuFileParser::sequence_t
{
static constexpr auto TAG_BOOL = static_cast<int>(SetLocalVarType::BOOL);
static constexpr auto TAG_INT = static_cast<int>(SetLocalVarType::INT);
static constexpr auto TAG_FLOAT = static_cast<int>(SetLocalVarType::FLOAT);
static constexpr auto TAG_STRING = static_cast<int>(SetLocalVarType::STRING);
static constexpr auto CAPTURE_VAR_NAME = 1;
public:
SequenceSetLocalVar()
{
const ScriptMatcherFactory create(this);
AddLabeledMatchers(MenuCommonMatchers::Expression(this), MenuCommonMatchers::LABEL_EXPRESSION);
AddMatchers({
create.Or({
create.ScriptKeyword("setLocalVarBool").Tag(TAG_BOOL),
create.ScriptKeyword("setLocalVarInt").Tag(TAG_INT),
create.ScriptKeyword("setLocalVarFloat").Tag(TAG_FLOAT),
create.ScriptKeyword("setLocalVarString").Tag(TAG_STRING)
}),
create.ScriptText().Capture(CAPTURE_VAR_NAME),
create.Label(MenuCommonMatchers::LABEL_EXPRESSION),
create.Optional(create.Char(';'))
});
}
private:
static std::string ScriptKeywordForType(const SetLocalVarType type)
{
switch (type)
{
case SetLocalVarType::BOOL:
return "setLocalVarBool";
case SetLocalVarType::INT:
return "setLocalVarInt";
case SetLocalVarType::FLOAT:
return "setLocalVarFloat";
case SetLocalVarType::STRING:
return "setLocalVarString";
default:
return "unknown";
}
}
static void EmitStaticValue(MenuFileParserState* state, const CommonExpressionValue& value)
{
switch (value.m_type)
{
case CommonExpressionValue::Type::DOUBLE:
state->m_current_script << value.m_double_value;
break;
case CommonExpressionValue::Type::INT:
state->m_current_script << value.m_int_value;
break;
case CommonExpressionValue::Type::STRING:
state->m_current_script << *value.m_string_value;
break;
}
}
static void CheckStaticValueType(const TokenPos& pos, const SetLocalVarType type, const CommonExpressionValue& staticValue)
{
switch (type)
{
case SetLocalVarType::BOOL:
if (staticValue.m_type != CommonExpressionValue::Type::INT)
throw ParsingException(pos, "Static value must be BOOL");
break;
case SetLocalVarType::INT:
if (staticValue.m_type != CommonExpressionValue::Type::INT)
throw ParsingException(pos, "Static value must be INT");
break;
case SetLocalVarType::FLOAT:
if (staticValue.m_type != CommonExpressionValue::Type::DOUBLE && staticValue.m_type != CommonExpressionValue::Type::INT)
throw ParsingException(pos, "Static value must be FLOAT");
break;
case SetLocalVarType::STRING:
default:
break;
}
}
static void EmitStaticSetLocalVar(MenuFileParserState* state, const TokenPos& pos, const SetLocalVarType type, const std::string& varName, std::unique_ptr<ICommonExpression> expression)
{
state->m_current_script << "\"" << ScriptKeywordForType(type) << "\" \"" << varName << "\" \"";
const auto staticValue = expression->Evaluate();
CheckStaticValueType(pos, type, staticValue);
EmitStaticValue(state, staticValue);
state->m_current_script << "\" ; ";
}
static void EmitDynamicSetLocalVar(const MenuFileParserState* state, const SetLocalVarType type, const std::string& varName, std::unique_ptr<ICommonExpression> expression)
{
state->m_current_nested_event_handler_set->m_elements.emplace_back(std::make_unique<CommonEventHandlerSetLocalVar>(type, varName, std::move(expression)));
}
protected:
void ProcessMatch(MenuFileParserState* state, SequenceResult<SimpleParserValue>& result) const override
{
const auto typeTag = static_cast<SetLocalVarType>(result.NextTag());
const auto& varNameToken = result.NextCapture(CAPTURE_VAR_NAME);
const auto& varName = MenuMatcherFactory::TokenTextValue(varNameToken);
auto expression = MenuCommonMatchers::ProcessExpression(state, result);
if (!expression)
throw ParsingException(varNameToken.GetPos(), "No expression");
if (expression && expression->IsStatic())
EmitStaticSetLocalVar(state, varNameToken.GetPos(), typeTag, varName, std::move(expression));
else
EmitDynamicSetLocalVar(state, typeTag, varName, std::move(expression));
}
};
} }
using namespace event_handler_set_scope_sequences; using namespace event_handler_set_scope_sequences;
@ -66,4 +391,66 @@ EventHandlerSetScopeSequences::EventHandlerSetScopeSequences(std::vector<std::un
void EventHandlerSetScopeSequences::AddSequences(FeatureLevel featureLevel) void EventHandlerSetScopeSequences::AddSequences(FeatureLevel featureLevel)
{ {
AddSequence(std::make_unique<SequenceCloseBlock>()); AddSequence(std::make_unique<SequenceCloseBlock>());
AddSequence(std::make_unique<SequenceSkipEmptyStatements>());
// If else and stuff
// Creating factory with no label supplier. Cannot use labels with it.
const ScriptMatcherFactory create(nullptr);
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("fadeIn"), create.ScriptText()}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("fadeOut"), create.ScriptText()}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("show"), create.ScriptText()}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("hide"), create.ScriptText()}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("showMenu"), create.ScriptText()}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("hideMenu"), create.ScriptText()}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("setColor"), create.ScriptColor()}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("open"), create.ScriptText()}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("close"), create.ScriptText()}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("escape"), create.ScriptText()}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("closeForAllPlayers"), create.ScriptText()}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("ingameOpen"), create.ScriptText()}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("ingameClose"), create.ScriptText()}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("setBackground"), create.ScriptText()}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("setItemColor"), create.ScriptText(), create.ScriptText(), create.ScriptColor()}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("focusFirst")}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("setFocus"), create.ScriptText()}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("setFocusByDvar"), create.ScriptText()}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("setDvar"), create.ScriptText(), create.ScriptText()}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("exec"), create.ScriptText()}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("execNow"), create.ScriptText()}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("execOnDvarStringValue"), create.ScriptText(), create.ScriptText(), create.ScriptText()}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("execOnDvarIntValue"), create.ScriptText(), create.ScriptInt(), create.ScriptText()}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("execOnDvarFloatValue"), create.ScriptText(), create.ScriptNumeric(), create.ScriptText()}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("execNowOnDvarStringValue"), create.ScriptText(), create.ScriptText(), create.ScriptText()}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("execNowOnDvarIntValue"), create.ScriptText(), create.ScriptInt(), create.ScriptText()}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("execNowOnDvarFloatValue"), create.ScriptText(), create.ScriptNumeric(), create.ScriptText()}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("play"), create.ScriptText()}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("scriptMenuResponse"), create.ScriptText()}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("respondOnDvarStringValue"), create.ScriptText(), create.ScriptText(), create.ScriptText()}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("respondOnDvarIntValue"), create.ScriptText(), create.ScriptInt(), create.ScriptText()}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("respondOnDvarFloatValue"), create.ScriptText(), create.ScriptNumeric(), create.ScriptText()}));
AddSequence(std::make_unique<SequenceSetPlayerData>());
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("setPlayerDataSp")}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("updateMail")}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("openMail")}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("deleteMail")}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("doMailLottery")}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("resetStatsConfirm")}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("resetStatsCancel")}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("setGameMode"), create.ScriptText()}));
AddSequence(std::make_unique<SequenceSetLocalVar>());
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("feederTop")}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("feederBottom")}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("showGamerCard")}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("openForGameType"), create.ScriptText()}));
AddSequence(SequenceGenericScriptStatement::Create({create.ScriptKeyword("closeForGameType"), create.ScriptText()}));
// statClearPerkNew
// statSetUsingTable
// statClearBitMask
AddSequence(SequenceGenericScriptStatement::Create({ create.ScriptKeyword("kickPlayer") }));
AddSequence(SequenceGenericScriptStatement::Create({ create.ScriptKeyword("getKickPlayerQuestion") }));
AddSequence(SequenceGenericScriptStatement::Create({ create.ScriptKeyword("partyUpdateMissingMapPackDvar") }));
AddSequence(SequenceGenericScriptStatement::Create({ create.ScriptKeyword("togglePlayerMute") }));
AddSequence(SequenceGenericScriptStatement::Create({ create.ScriptKeyword("resolveError") }));
// lerp
} }