mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2025-04-19 15:52:53 +00:00
253 lines
9.0 KiB
C++
253 lines
9.0 KiB
C++
#include "StateMapParser.h"
|
|
|
|
#include "Matcher/StateMapExpressionMatchers.h"
|
|
#include "Parsing/Simple/Matcher/SimpleMatcherFactory.h"
|
|
|
|
using namespace state_map;
|
|
|
|
namespace state_map
|
|
{
|
|
class SequenceStateMapEntry final : public StateMapParser::sequence_t
|
|
{
|
|
static constexpr auto CAPTURE_ENTRY_NAME = 1;
|
|
|
|
public:
|
|
SequenceStateMapEntry()
|
|
{
|
|
const SimpleMatcherFactory create(this);
|
|
|
|
AddMatchers({
|
|
create.Identifier().Capture(CAPTURE_ENTRY_NAME),
|
|
create.Char('{'),
|
|
});
|
|
}
|
|
|
|
protected:
|
|
void ProcessMatch(StateMapParserState* state, SequenceResult<SimpleParserValue>& result) const override
|
|
{
|
|
const auto& entryNameToken = result.NextCapture(CAPTURE_ENTRY_NAME);
|
|
|
|
const auto foundEntry = state->m_valid_state_map_entry_names.find(entryNameToken.IdentifierValue());
|
|
if (foundEntry == state->m_valid_state_map_entry_names.end())
|
|
throw ParsingException(entryNameToken.GetPos(), "Unknown entry name");
|
|
|
|
state->m_in_entry = true;
|
|
state->m_current_entry_index = foundEntry->second;
|
|
}
|
|
};
|
|
|
|
class SequenceStateMapEntryClose final : public StateMapParser::sequence_t
|
|
{
|
|
static constexpr auto CAPTURE_FIRST_TOKEN = 1;
|
|
|
|
public:
|
|
SequenceStateMapEntryClose()
|
|
{
|
|
const SimpleMatcherFactory create(this);
|
|
|
|
AddMatchers({
|
|
create.Char('}').Capture(CAPTURE_FIRST_TOKEN),
|
|
});
|
|
}
|
|
|
|
protected:
|
|
void ProcessMatch(StateMapParserState* state, SequenceResult<SimpleParserValue>& result) const override
|
|
{
|
|
if (!state->m_entry_has_default)
|
|
throw ParsingException(result.NextCapture(CAPTURE_FIRST_TOKEN).GetPos(), "Entry must have a default case");
|
|
|
|
state->m_in_entry = false;
|
|
state->m_entry_has_default = false;
|
|
}
|
|
};
|
|
|
|
class SequenceCondition final : public StateMapParser::sequence_t
|
|
{
|
|
static constexpr auto TAG_DEFAULT = 1;
|
|
static constexpr auto TAG_EXPRESSION = 2;
|
|
|
|
public:
|
|
SequenceCondition()
|
|
{
|
|
AddLabeledMatchers(StateMapExpressionMatchers().Expression(this), StateMapExpressionMatchers::LABEL_EXPRESSION);
|
|
const SimpleMatcherFactory create(this);
|
|
|
|
AddMatchers({
|
|
create.Or({
|
|
create.Keyword("default").Tag(TAG_DEFAULT),
|
|
create.Label(StateMapExpressionMatchers::LABEL_EXPRESSION).Tag(TAG_EXPRESSION),
|
|
}),
|
|
create.Char(':'),
|
|
});
|
|
}
|
|
|
|
protected:
|
|
void ProcessMatch(StateMapParserState* state, SequenceResult<SimpleParserValue>& result) const override
|
|
{
|
|
assert(state->m_definition);
|
|
assert(state->m_in_entry);
|
|
|
|
if (!state->m_current_rule)
|
|
{
|
|
auto newRule = std::make_unique<StateMapRule>();
|
|
state->m_current_rule = newRule.get();
|
|
state->m_definition->m_state_map_entries[state->m_current_entry_index].m_rules.emplace_back(std::move(newRule));
|
|
}
|
|
|
|
if (result.PeekAndRemoveIfTag(TAG_EXPRESSION) == TAG_EXPRESSION)
|
|
{
|
|
auto expression = StateMapExpressionMatchers(state).ProcessExpression(result);
|
|
|
|
state->m_current_rule->m_conditions.emplace_back(std::move(expression));
|
|
}
|
|
else
|
|
{
|
|
assert(result.PeekAndRemoveIfTag(TAG_DEFAULT) == TAG_DEFAULT);
|
|
auto& entry = state->m_definition->m_state_map_entries[state->m_current_entry_index];
|
|
state->m_entry_has_default = true;
|
|
entry.m_default_index = entry.m_rules.size() - 1;
|
|
}
|
|
}
|
|
};
|
|
|
|
class SequenceValue final : public StateMapParser::sequence_t
|
|
{
|
|
static constexpr auto TAG_PASSTHROUGH = 1;
|
|
static constexpr auto TAG_VALUE_LIST = 2;
|
|
|
|
static constexpr auto CAPTURE_VALUE = 1;
|
|
static constexpr auto CAPTURE_VALUE_END = 2;
|
|
|
|
static constexpr auto LABEL_VALUE_LIST = 1;
|
|
static constexpr auto LABEL_VALUE = 2;
|
|
|
|
public:
|
|
SequenceValue()
|
|
{
|
|
const SimpleMatcherFactory create(this);
|
|
|
|
AddLabeledMatchers(create.Or({
|
|
create.Identifier(),
|
|
create.Integer(),
|
|
}),
|
|
LABEL_VALUE);
|
|
|
|
AddLabeledMatchers(
|
|
{
|
|
create.Label(LABEL_VALUE).Capture(CAPTURE_VALUE),
|
|
create.OptionalLoop(create.And({
|
|
create.Char(','),
|
|
create.Label(LABEL_VALUE).Capture(CAPTURE_VALUE),
|
|
})),
|
|
},
|
|
LABEL_VALUE_LIST);
|
|
|
|
AddMatchers({
|
|
create.Or({
|
|
create.Keyword("passthrough").Tag(TAG_PASSTHROUGH),
|
|
create.Label(LABEL_VALUE_LIST).Tag(TAG_VALUE_LIST),
|
|
}),
|
|
create.Char(';').Capture(CAPTURE_VALUE_END),
|
|
});
|
|
}
|
|
|
|
protected:
|
|
void ProcessMatch(StateMapParserState* state, SequenceResult<SimpleParserValue>& result) const override
|
|
{
|
|
assert(state->m_in_entry);
|
|
assert(state->m_current_rule);
|
|
|
|
const auto& layoutEntry = state->m_layout.m_entry_layout.m_entries[state->m_current_entry_index];
|
|
|
|
if (result.PeekAndRemoveIfTag(TAG_VALUE_LIST) == TAG_VALUE_LIST)
|
|
{
|
|
auto resultIndex = 0u;
|
|
|
|
while (result.HasNextCapture(CAPTURE_VALUE))
|
|
{
|
|
const auto& valueToken = result.NextCapture(CAPTURE_VALUE);
|
|
|
|
if (resultIndex >= layoutEntry.m_result_vars.size())
|
|
throw ParsingException(valueToken.GetPos(), "Too many results");
|
|
|
|
const auto& resultVar = layoutEntry.m_result_vars[resultIndex];
|
|
const auto varForResult = state->m_valid_vars.find(resultVar);
|
|
|
|
if (varForResult != state->m_valid_vars.end())
|
|
{
|
|
const auto& var = state->m_layout.m_var_layout.m_vars[varForResult->second];
|
|
const auto tokenValue =
|
|
valueToken.m_type == SimpleParserValueType::IDENTIFIER ? valueToken.IdentifierValue() : std::to_string(valueToken.IntegerValue());
|
|
|
|
const auto referencedValue = std::find_if(var.m_values.begin(),
|
|
var.m_values.end(),
|
|
[&tokenValue](const StateMapLayoutVarValue& value)
|
|
{
|
|
return value.m_name == tokenValue;
|
|
});
|
|
|
|
if (referencedValue == var.m_values.end())
|
|
throw ParsingException(valueToken.GetPos(), "Not part of the valid values for this var");
|
|
|
|
state->m_current_rule->m_value |= referencedValue->m_state_bits_mask;
|
|
}
|
|
else
|
|
throw ParsingException(valueToken.GetPos(), "Unknown var for result?");
|
|
|
|
resultIndex++;
|
|
}
|
|
|
|
if (resultIndex < layoutEntry.m_result_vars.size())
|
|
throw ParsingException(result.NextCapture(CAPTURE_VALUE_END).GetPos(), "Not enough results");
|
|
}
|
|
else
|
|
{
|
|
// A rule without values is considered passthrough therefore just don't add values
|
|
assert(result.PeekAndRemoveIfTag(TAG_PASSTHROUGH) == TAG_PASSTHROUGH);
|
|
state->m_current_rule->m_passthrough = true;
|
|
}
|
|
|
|
state->m_current_rule = nullptr;
|
|
}
|
|
};
|
|
} // namespace state_map
|
|
|
|
StateMapParser::StateMapParser(SimpleLexer* lexer, std::string stateMapName, const StateMapLayout& layout)
|
|
: AbstractParser(lexer, std::make_unique<StateMapParserState>(std::move(stateMapName), layout))
|
|
{
|
|
}
|
|
|
|
const std::vector<StateMapParser::sequence_t*>& StateMapParser::GetTestsForState()
|
|
{
|
|
static std::vector<sequence_t*> rootSequences({
|
|
new SequenceStateMapEntry(),
|
|
});
|
|
|
|
static std::vector<sequence_t*> entrySequences({
|
|
new SequenceStateMapEntryClose(),
|
|
new SequenceCondition(),
|
|
});
|
|
|
|
static std::vector<sequence_t*> ruleSequences({
|
|
new SequenceCondition(),
|
|
new SequenceValue(),
|
|
});
|
|
|
|
if (m_state->m_in_entry)
|
|
{
|
|
return m_state->m_current_rule ? ruleSequences : entrySequences;
|
|
}
|
|
|
|
return rootSequences;
|
|
}
|
|
|
|
std::unique_ptr<StateMapDefinition> StateMapParser::GetStateMapDefinition() const
|
|
{
|
|
return std::move(m_state->m_definition);
|
|
}
|
|
|
|
StateMapParserState* StateMapParser::GetState() const
|
|
{
|
|
return m_state.get();
|
|
}
|