From 69c08def7f3b29a61aa1f028d5a44c98a69a8453 Mon Sep 17 00:00:00 2001 From: Jan Date: Wed, 3 Nov 2021 23:46:18 +0100 Subject: [PATCH] Add expression parsing for menu parser --- .../CommonExpressionBinaryOperation.cpp | 31 +- .../CommonExpressionBinaryOperation.h | 34 +- .../CommonExpressionUnaryOperation.cpp | 12 +- .../CommonExpressionUnaryOperation.h | 16 +- .../Menu/Matcher/MenuCommonMatchers.cpp | 302 ++++++++++++++++++ .../Parsing/Menu/Matcher/MenuCommonMatchers.h | 37 +++ src/ObjLoading/Parsing/Menu/MenuFileLexing.h | 22 ++ .../Parsing/Menu/MenuFileReader.cpp | 21 +- .../Properties/MenuPropertySequences.cpp | 49 +++ .../Matcher/CommandsCommonMatchers.cpp | 6 +- 10 files changed, 505 insertions(+), 25 deletions(-) create mode 100644 src/ObjLoading/Parsing/Menu/Matcher/MenuCommonMatchers.cpp create mode 100644 src/ObjLoading/Parsing/Menu/Matcher/MenuCommonMatchers.h create mode 100644 src/ObjLoading/Parsing/Menu/MenuFileLexing.h diff --git a/src/ObjLoading/Parsing/Menu/Domain/Expression/CommonExpressionBinaryOperation.cpp b/src/ObjLoading/Parsing/Menu/Domain/Expression/CommonExpressionBinaryOperation.cpp index 390abe40..5ba24b65 100644 --- a/src/ObjLoading/Parsing/Menu/Domain/Expression/CommonExpressionBinaryOperation.cpp +++ b/src/ObjLoading/Parsing/Menu/Domain/Expression/CommonExpressionBinaryOperation.cpp @@ -385,11 +385,32 @@ const CommonExpressionBinaryOperationType CommonExpressionBinaryOperationType::O } ); -CommonExpressionBinaryOperation::CommonExpressionBinaryOperation(std::unique_ptr operand1, std::unique_ptr operand2, - const CommonExpressionBinaryOperationType* operationType) - : m_operand1(std::move(operand1)), - m_operand2(std::move(operand2)), - m_operation_type(operationType) +const CommonExpressionBinaryOperationType* const CommonExpressionBinaryOperationType::ALL_OPERATION_TYPES[static_cast(BinaryOperationId::COUNT)] +{ + &OPERATION_ADD, + &OPERATION_SUBTRACT, + &OPERATION_MULTIPLY, + &OPERATION_DIVIDE, + &OPERATION_REMAINDER, + &OPERATION_BITWISE_AND, + &OPERATION_BITWISE_OR, + &OPERATION_SHIFT_LEFT, + &OPERATION_SHIFT_RIGHT, + &OPERATION_GREATER_THAN, + &OPERATION_GREATER_EQUAL_THAN, + &OPERATION_LESS_THAN, + &OPERATION_LESS_EQUAL_THAN, + &OPERATION_EQUALS, + &OPERATION_NOT_EQUAL, + &OPERATION_AND, + &OPERATION_OR +}; + +CommonExpressionBinaryOperation::CommonExpressionBinaryOperation(const CommonExpressionBinaryOperationType* operationType, std::unique_ptr operand1, + std::unique_ptr operand2) + : m_operation_type(operationType), + m_operand1(std::move(operand1)), + m_operand2(std::move(operand2)) { } diff --git a/src/ObjLoading/Parsing/Menu/Domain/Expression/CommonExpressionBinaryOperation.h b/src/ObjLoading/Parsing/Menu/Domain/Expression/CommonExpressionBinaryOperation.h index 9393a67a..1a88a8bc 100644 --- a/src/ObjLoading/Parsing/Menu/Domain/Expression/CommonExpressionBinaryOperation.h +++ b/src/ObjLoading/Parsing/Menu/Domain/Expression/CommonExpressionBinaryOperation.h @@ -23,6 +23,29 @@ namespace menu LOGICAL_OR = 10 }; + enum class BinaryOperationId + { + ADD, + SUBTRACT, + MULTIPLY, + DIVIDE, + REMAINDER, + BITWISE_AND, + BITWISE_OR, + SHIFT_LEFT, + SHIFT_RIGHT, + GREATER_THAN, + GREATER_EQUAL_THAN, + LESS_THAN, + LESS_EQUAL_THAN, + EQUALS, + NOT_EQUAL, + AND, + OR, + + COUNT + }; + class CommonExpressionBinaryOperationType { public: @@ -35,6 +58,7 @@ namespace menu private: CommonExpressionBinaryOperationType(std::string syntax, OperationPrecedence precedence, evaluation_function_t evaluationFunction); + public: static const CommonExpressionBinaryOperationType OPERATION_ADD; static const CommonExpressionBinaryOperationType OPERATION_SUBTRACT; static const CommonExpressionBinaryOperationType OPERATION_MULTIPLY; @@ -53,19 +77,19 @@ namespace menu static const CommonExpressionBinaryOperationType OPERATION_AND; static const CommonExpressionBinaryOperationType OPERATION_OR; - static const CommonExpressionBinaryOperationType* const ALL_OPERATION_TYPES[]; + static const CommonExpressionBinaryOperationType* const ALL_OPERATION_TYPES[static_cast(BinaryOperationId::COUNT)]; }; class CommonExpressionBinaryOperation final : public ICommonExpression { public: + const CommonExpressionBinaryOperationType* m_operation_type; std::unique_ptr m_operand1; std::unique_ptr m_operand2; - const CommonExpressionBinaryOperationType* m_operation_type; - CommonExpressionBinaryOperation(std::unique_ptr operand1, - std::unique_ptr operand2, - const CommonExpressionBinaryOperationType* operationType); + CommonExpressionBinaryOperation(const CommonExpressionBinaryOperationType* operationType, + std::unique_ptr operand1, + std::unique_ptr operand2); _NODISCARD bool Operand1NeedsParenthesis() const; _NODISCARD bool Operand2NeedsParenthesis() const; diff --git a/src/ObjLoading/Parsing/Menu/Domain/Expression/CommonExpressionUnaryOperation.cpp b/src/ObjLoading/Parsing/Menu/Domain/Expression/CommonExpressionUnaryOperation.cpp index 67275a7d..275b7c00 100644 --- a/src/ObjLoading/Parsing/Menu/Domain/Expression/CommonExpressionUnaryOperation.cpp +++ b/src/ObjLoading/Parsing/Menu/Domain/Expression/CommonExpressionUnaryOperation.cpp @@ -31,9 +31,15 @@ const CommonExpressionUnaryOperationType CommonExpressionUnaryOperationType::OPE } ); -CommonExpressionUnaryOperation::CommonExpressionUnaryOperation(std::unique_ptr operand, const CommonExpressionUnaryOperationType* operationType) - : m_operand(std::move(operand)), - m_operation_type(operationType) +const CommonExpressionUnaryOperationType* const CommonExpressionUnaryOperationType::ALL_OPERATION_TYPES[static_cast(UnaryOperationId::COUNT)] +{ + &OPERATION_NOT, + &OPERATION_BITWISE_NOT, +}; + +CommonExpressionUnaryOperation::CommonExpressionUnaryOperation(const CommonExpressionUnaryOperationType* operationType, std::unique_ptr operand) + : m_operation_type(operationType), + m_operand(std::move(operand)) { } diff --git a/src/ObjLoading/Parsing/Menu/Domain/Expression/CommonExpressionUnaryOperation.h b/src/ObjLoading/Parsing/Menu/Domain/Expression/CommonExpressionUnaryOperation.h index 40288045..1debd289 100644 --- a/src/ObjLoading/Parsing/Menu/Domain/Expression/CommonExpressionUnaryOperation.h +++ b/src/ObjLoading/Parsing/Menu/Domain/Expression/CommonExpressionUnaryOperation.h @@ -9,6 +9,14 @@ namespace menu { + enum class UnaryOperationId + { + NOT, + BITWISE_NOT, + + COUNT + }; + class CommonExpressionUnaryOperationType { public: @@ -20,20 +28,20 @@ namespace menu private: CommonExpressionUnaryOperationType(std::string syntax, evaluation_function_t evaluationFunction); + public: static const CommonExpressionUnaryOperationType OPERATION_NOT; static const CommonExpressionUnaryOperationType OPERATION_BITWISE_NOT; - static const CommonExpressionUnaryOperationType* const ALL_OPERATION_TYPES[]; + static const CommonExpressionUnaryOperationType* const ALL_OPERATION_TYPES[static_cast(UnaryOperationId::COUNT)]; }; class CommonExpressionUnaryOperation final : public ICommonExpression { public: - std::unique_ptr m_operand; const CommonExpressionUnaryOperationType* m_operation_type; + std::unique_ptr m_operand; - CommonExpressionUnaryOperation(std::unique_ptr operand, - const CommonExpressionUnaryOperationType* operationType); + CommonExpressionUnaryOperation(const CommonExpressionUnaryOperationType* operationType, std::unique_ptr operand); _NODISCARD bool OperandNeedsParenthesis() const; diff --git a/src/ObjLoading/Parsing/Menu/Matcher/MenuCommonMatchers.cpp b/src/ObjLoading/Parsing/Menu/Matcher/MenuCommonMatchers.cpp new file mode 100644 index 00000000..2afa715a --- /dev/null +++ b/src/ObjLoading/Parsing/Menu/Matcher/MenuCommonMatchers.cpp @@ -0,0 +1,302 @@ +#include "MenuCommonMatchers.h" + +#include + +#include "MenuMatcherFactory.h" +#include "Parsing/Menu/MenuFileLexing.h" +#include "Parsing/Menu/Domain/Expression/CommonExpressionBinaryOperation.h" +#include "Parsing/Menu/Domain/Expression/CommonExpressionFunctionCall.h" +#include "Parsing/Menu/Domain/Expression/CommonExpressionUnaryOperation.h" + +using namespace menu; + +static constexpr int TAG_OPERAND = std::numeric_limits::max() - 1; +static constexpr int TAG_EXPRESSION_UNARY_OPERATION = std::numeric_limits::max() - 2; +static constexpr int TAG_EXPRESSION_FUNCTION_CALL = std::numeric_limits::max() - 3; +static constexpr int TAG_EXPRESSION_FUNCTION_CALL_END = std::numeric_limits::max() - 4; +static constexpr int TAG_EXPRESSION_PARENTHESIS = std::numeric_limits::max() - 5; +static constexpr int TAG_EXPRESSION_PARENTHESIS_END = std::numeric_limits::max() - 6; +static constexpr int TAG_EXPRESSION = std::numeric_limits::max() - 7; +static constexpr int TAG_EXPRESSION_BINARY_OPERATION = std::numeric_limits::max() - 8; + +static constexpr int CAPTURE_OPERAND = std::numeric_limits::max() - 1; +static constexpr int CAPTURE_UNARY_OPERATION_TYPE = std::numeric_limits::max() - 2; +static constexpr int CAPTURE_BINARY_OPERATION_TYPE = std::numeric_limits::max() - 3; +static constexpr int CAPTURE_FUNCTION_NAME = std::numeric_limits::max() - 4; + +std::unique_ptr MenuCommonMatchers::ProcessExpressionInParenthesis(MenuFileParserState* state, SequenceResult& result) +{ + auto processedEvaluation = ProcessExpression(state, result); + + if (result.PeekAndRemoveIfTag(TAG_EXPRESSION_PARENTHESIS_END) != TAG_EXPRESSION_PARENTHESIS_END) + throw ParsingException(TokenPos(), "Expected parenthesis end tag @ ExpressionInParenthesis"); + + return processedEvaluation; +} + +std::unique_ptr MenuCommonMatchers::ProcessOperand(MenuFileParserState* state, SequenceResult& result) +{ + const auto& operandToken = result.NextCapture(CAPTURE_OPERAND); + + switch (operandToken.m_type) + { + case SimpleParserValueType::INTEGER: + return std::make_unique(operandToken.IntegerValue()); + case SimpleParserValueType::FLOATING_POINT: + return std::make_unique(operandToken.FloatingPointValue()); + case SimpleParserValueType::STRING: + return std::make_unique(operandToken.StringValue()); + case SimpleParserValueType::IDENTIFIER: + return std::make_unique(operandToken.IdentifierValue()); + default: + throw ParsingException(TokenPos(), "Unknown operand type @ Operand"); + } +} + +std::unique_ptr MenuCommonMatchers::ProcessFunctionCall(MenuFileParserState* state, SequenceResult& result) +{ + auto functionCall = std::make_unique(result.NextCapture(CAPTURE_FUNCTION_NAME).IdentifierValue()); + + while (result.PeekAndRemoveIfTag(TAG_EXPRESSION_FUNCTION_CALL_END) != TAG_EXPRESSION_FUNCTION_CALL_END) + { + functionCall->m_args.emplace_back(ProcessExpression(state, result)); + } + + return std::move(functionCall); +} + +std::unique_ptr MenuCommonMatchers::ProcessExpression(MenuFileParserState* state, SequenceResult& result) +{ + if (result.PeekAndRemoveIfTag(TAG_EXPRESSION) != TAG_EXPRESSION) + return nullptr; + + std::vector> operands; + std::list> operators; + + while (true) + { + std::unique_ptr firstStatementPart; + std::vector unaryOperations; + auto nextTag = result.NextTag(); + + while (nextTag == TAG_EXPRESSION_UNARY_OPERATION) + { + unaryOperations.push_back(result.NextCapture(CAPTURE_UNARY_OPERATION_TYPE).IntegerValue()); + nextTag = result.NextTag(); + } + + switch (nextTag) + { + case TAG_EXPRESSION_PARENTHESIS: + firstStatementPart = ProcessExpressionInParenthesis(state, result); + break; + + case TAG_EXPRESSION_FUNCTION_CALL: + firstStatementPart = ProcessFunctionCall(state, result); + break; + + case TAG_OPERAND: + firstStatementPart = ProcessOperand(state, result); + break; + + default: + throw ParsingException(TokenPos(), "Invalid followup tag @ Expression"); + } + + for (auto i = unaryOperations.size(); i > 0; i--) + { + const auto operationIndex = unaryOperations[i - 1]; + if (operationIndex < 0 || operationIndex >= static_cast(UnaryOperationId::COUNT)) + throw ParsingException(TokenPos(), "Invalid unary operation id @ Expression"); + firstStatementPart = std::make_unique(CommonExpressionUnaryOperationType::ALL_OPERATION_TYPES[operationIndex], + std::move(firstStatementPart)); + } + + operands.emplace_back(std::move(firstStatementPart)); + + if (result.PeekAndRemoveIfTag(TAG_EXPRESSION_BINARY_OPERATION) == TAG_EXPRESSION_BINARY_OPERATION) + { + const auto operationIndex = result.NextCapture(CAPTURE_BINARY_OPERATION_TYPE).IntegerValue(); + if (operationIndex < 0 || operationIndex >= static_cast(BinaryOperationId::COUNT)) + throw ParsingException(TokenPos(), "Invalid binary operation id @ Expression"); + + operators.emplace_back(operators.size(), CommonExpressionBinaryOperationType::ALL_OPERATION_TYPES[operationIndex]); + } + else + break; + + if (result.PeekAndRemoveIfTag(TAG_EXPRESSION) != TAG_EXPRESSION) + throw ParsingException(TokenPos(), "Expected EvaluationTag @ Evaluation"); + } + + operators.sort([](const std::pair& p1, const std::pair& p2) + { + if (p1.second->m_precedence != p2.second->m_precedence) + return p1.second->m_precedence > p2.second->m_precedence; + + return p1.first > p2.first; + }); + + while (!operators.empty()) + { + const auto [operatorIndex, operatorType] = operators.back(); + + auto operation = std::make_unique(operatorType, std::move(operands[operatorIndex]), std::move(operands[operatorIndex + 1])); + operands.erase(operands.begin() + static_cast(operatorIndex)); + operands[operatorIndex] = std::move(operation); + + operators.pop_back(); + + for (auto& [opIndex, _] : operators) + { + if (opIndex > operatorIndex) + opIndex--; + } + } + + return std::move(operands.front()); +} + +std::unique_ptr MenuCommonMatchers::ParseOperand(const supplier_t* labelSupplier) +{ + const MenuMatcherFactory create(labelSupplier); + + return create.Or({ + create.Integer(), + create.FloatingPoint(), + create.String(), + create.Identifier(), + }).Tag(TAG_OPERAND).Capture(CAPTURE_OPERAND); +} + +std::unique_ptr MenuCommonMatchers::ParseBinaryOperationType(const supplier_t* labelSupplier) +{ + const MenuMatcherFactory create(labelSupplier); + + return create.Or({ + create.Char('+').Transform([](const MenuMatcherFactory::token_list_t& values) + { + return SimpleParserValue::Integer(values[0].get().GetPos(), static_cast(BinaryOperationId::ADD)); + }), + create.Char('-').Transform([](const MenuMatcherFactory::token_list_t& values) + { + return SimpleParserValue::Integer(values[0].get().GetPos(), static_cast(BinaryOperationId::SUBTRACT)); + }), + create.Char('*').Transform([](const MenuMatcherFactory::token_list_t& values) + { + return SimpleParserValue::Integer(values[0].get().GetPos(), static_cast(BinaryOperationId::MULTIPLY)); + }), + create.Char('/').Transform([](const MenuMatcherFactory::token_list_t& values) + { + return SimpleParserValue::Integer(values[0].get().GetPos(), static_cast(BinaryOperationId::DIVIDE)); + }), + create.Char('%').Transform([](const MenuMatcherFactory::token_list_t& values) + { + return SimpleParserValue::Integer(values[0].get().GetPos(), static_cast(BinaryOperationId::REMAINDER)); + }), + create.Char('&').Transform([](const MenuMatcherFactory::token_list_t& values) + { + return SimpleParserValue::Integer(values[0].get().GetPos(), static_cast(BinaryOperationId::BITWISE_AND)); + }), + create.Char('|').Transform([](const MenuMatcherFactory::token_list_t& values) + { + return SimpleParserValue::Integer(values[0].get().GetPos(), static_cast(BinaryOperationId::BITWISE_OR)); + }), + create.MultiChar(static_cast(MenuFileLexing::MultiChar::SHIFT_LEFT)).Transform([](const MenuMatcherFactory::token_list_t& values) + { + return SimpleParserValue::Integer(values[0].get().GetPos(), static_cast(BinaryOperationId::SHIFT_LEFT)); + }), + create.MultiChar(static_cast(MenuFileLexing::MultiChar::SHIFT_RIGHT)).Transform([](const MenuMatcherFactory::token_list_t& values) + { + return SimpleParserValue::Integer(values[0].get().GetPos(), static_cast(BinaryOperationId::SHIFT_RIGHT)); + }), + create.Char('>').Transform([](const MenuMatcherFactory::token_list_t& values) + { + return SimpleParserValue::Integer(values[0].get().GetPos(), static_cast(BinaryOperationId::GREATER_THAN)); + }), + create.MultiChar(static_cast(MenuFileLexing::MultiChar::GREATER_EQUAL)).Transform([](const MenuMatcherFactory::token_list_t& values) + { + return SimpleParserValue::Integer(values[0].get().GetPos(), static_cast(BinaryOperationId::GREATER_EQUAL_THAN)); + }), + create.Char('<').Transform([](const MenuMatcherFactory::token_list_t& values) + { + return SimpleParserValue::Integer(values[0].get().GetPos(), static_cast(BinaryOperationId::LESS_THAN)); + }), + create.MultiChar(static_cast(MenuFileLexing::MultiChar::LESS_EQUAL)).Transform([](const MenuMatcherFactory::token_list_t& values) + { + return SimpleParserValue::Integer(values[0].get().GetPos(), static_cast(BinaryOperationId::LESS_EQUAL_THAN)); + }), + create.MultiChar(static_cast(MenuFileLexing::MultiChar::EQUALS)).Transform([](const MenuMatcherFactory::token_list_t& values) + { + return SimpleParserValue::Integer(values[0].get().GetPos(), static_cast(BinaryOperationId::EQUALS)); + }), + create.MultiChar(static_cast(MenuFileLexing::MultiChar::NOT_EQUAL)).Transform([](const MenuMatcherFactory::token_list_t& values) + { + return SimpleParserValue::Integer(values[0].get().GetPos(), static_cast(BinaryOperationId::NOT_EQUAL)); + }), + create.MultiChar(static_cast(MenuFileLexing::MultiChar::LOGICAL_AND)).Transform([](const MenuMatcherFactory::token_list_t& values) + { + return SimpleParserValue::Integer(values[0].get().GetPos(), static_cast(BinaryOperationId::AND)); + }), + create.MultiChar(static_cast(MenuFileLexing::MultiChar::LOGICAL_OR)).Transform([](const MenuMatcherFactory::token_list_t& values) + { + return SimpleParserValue::Integer(values[0].get().GetPos(), static_cast(BinaryOperationId::OR)); + }), + }).Capture(CAPTURE_BINARY_OPERATION_TYPE); +} + +std::unique_ptr MenuCommonMatchers::ParseFunctionCall(const supplier_t* labelSupplier) +{ + const MenuMatcherFactory create(labelSupplier); + + return 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); +} + +std::unique_ptr MenuCommonMatchers::ParseUnaryOperationType(const supplier_t* labelSupplier) +{ + const MenuMatcherFactory create(labelSupplier); + + return create.Or({ + create.Char('!').Transform([](const MenuMatcherFactory::token_list_t& values) + { + return SimpleParserValue::Integer(values[0].get().GetPos(), static_cast(UnaryOperationId::NOT)); + }), + create.Char('~').Transform([](const MenuMatcherFactory::token_list_t& values) + { + return SimpleParserValue::Integer(values[0].get().GetPos(), static_cast(UnaryOperationId::BITWISE_NOT)); + }), + }).Tag(TAG_EXPRESSION_UNARY_OPERATION).Capture(CAPTURE_UNARY_OPERATION_TYPE); +} + +std::unique_ptr MenuCommonMatchers::Expression(const supplier_t* labelSupplier) +{ + const MenuMatcherFactory create(labelSupplier); + + return create.And({ + create.OptionalLoop(ParseUnaryOperationType(labelSupplier)), + create.Or({ + create.And({ + create.Char('('), + create.Label(LABEL_EXPRESSION), + create.Char(')').Tag(TAG_EXPRESSION_PARENTHESIS_END) + }).Tag(TAG_EXPRESSION_PARENTHESIS), + ParseFunctionCall(labelSupplier), + ParseOperand(labelSupplier) + }), + create.Optional(create.And({ + ParseBinaryOperationType(labelSupplier), + create.Label(LABEL_EXPRESSION) + }).Tag(TAG_EXPRESSION_BINARY_OPERATION)) + }).Tag(TAG_EXPRESSION); +} diff --git a/src/ObjLoading/Parsing/Menu/Matcher/MenuCommonMatchers.h b/src/ObjLoading/Parsing/Menu/Matcher/MenuCommonMatchers.h new file mode 100644 index 00000000..d95433a4 --- /dev/null +++ b/src/ObjLoading/Parsing/Menu/Matcher/MenuCommonMatchers.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +#include "Parsing/Matcher/AbstractMatcher.h" +#include "Parsing/Matcher/MatcherLabel.h" +#include "Parsing/Menu/MenuFileParser.h" +#include "Parsing/Menu/Domain/Expression/ICommonExpression.h" +#include "Parsing/Sequence/SequenceResult.h" +#include "Parsing/Simple/SimpleParserValue.h" + +namespace menu +{ + class MenuCommonMatchers + { + public: + using matcher_t = MenuFileParser::sequence_t::matcher_t; + typedef IMatcherForLabelSupplier supplier_t; + + static constexpr int LABEL_EXPRESSION = std::numeric_limits::max() - 1; + + private: + static std::unique_ptr ParseBinaryOperationType(const supplier_t* labelSupplier); + static std::unique_ptr ParseOperand(const supplier_t* labelSupplier); + static std::unique_ptr ParseFunctionCall(const supplier_t* labelSupplier); + static std::unique_ptr ParseUnaryOperationType(const supplier_t* labelSupplier); + + static std::unique_ptr ProcessExpressionInParenthesis(MenuFileParserState* state, SequenceResult& result); + static std::unique_ptr ProcessOperand(MenuFileParserState* state, SequenceResult& result); + static std::unique_ptr ProcessFunctionCall(MenuFileParserState* state, SequenceResult& result); + + public: + static std::unique_ptr Expression(const supplier_t* labelSupplier); + static std::unique_ptr ProcessExpression(MenuFileParserState* state, SequenceResult& result); + }; +} diff --git a/src/ObjLoading/Parsing/Menu/MenuFileLexing.h b/src/ObjLoading/Parsing/Menu/MenuFileLexing.h new file mode 100644 index 00000000..cd925301 --- /dev/null +++ b/src/ObjLoading/Parsing/Menu/MenuFileLexing.h @@ -0,0 +1,22 @@ +#pragma once + +namespace menu +{ + class MenuFileLexing + { + public: + enum class MultiChar + { + SHIFT_LEFT, + SHIFT_RIGHT, + GREATER_EQUAL, + LESS_EQUAL, + EQUALS, + NOT_EQUAL, + LOGICAL_AND, + LOGICAL_OR + }; + + MenuFileLexing() = delete; + }; +} \ No newline at end of file diff --git a/src/ObjLoading/Parsing/Menu/MenuFileReader.cpp b/src/ObjLoading/Parsing/Menu/MenuFileReader.cpp index dabf469c..a70b7185 100644 --- a/src/ObjLoading/Parsing/Menu/MenuFileReader.cpp +++ b/src/ObjLoading/Parsing/Menu/MenuFileReader.cpp @@ -1,5 +1,6 @@ #include "MenuFileReader.h" +#include "MenuFileLexing.h" #include "MenuFileParser.h" #include "Parsing/Impl/CommentRemovingStreamProxy.h" #include "Parsing/Impl/DefinesStreamProxy.h" @@ -45,7 +46,7 @@ void MenuFileReader::SetupDefinesProxy() auto defines = std::make_unique(m_open_streams.back().get()); defines->AddDefine(DefinesStreamProxy::Define("PC", "1")); - switch(m_feature_level) + switch (m_feature_level) { case FeatureLevel::IW4: defines->AddDefine(DefinesStreamProxy::Define("FEATURE_LEVEL_IW4", "1")); @@ -72,25 +73,25 @@ void MenuFileReader::SetupStreamProxies() bool MenuFileReader::IsValidEndState(const MenuFileParserState* state) const { - if(state->m_current_item) + if (state->m_current_item) { std::cout << "In \"" << m_file_name << "\": Unclosed item at end of file!\n"; return false; } - if(state->m_current_menu) + if (state->m_current_menu) { std::cout << "In \"" << m_file_name << "\": Unclosed menu at end of file!\n"; return false; } - if(state->m_current_function) + if (state->m_current_function) { std::cout << "In \"" << m_file_name << "\": Unclosed function at end of file!\n"; return false; } - if(state->m_in_global_scope) + if (state->m_in_global_scope) { std::cout << "In \"" << m_file_name << "\": Did not close global scope!\n"; return false; @@ -115,6 +116,16 @@ std::unique_ptr MenuFileReader::ReadMenuFile() lexerConfig.m_emit_new_line_tokens = false; lexerConfig.m_read_strings = true; lexerConfig.m_read_numbers = true; + lexerConfig.m_multi_character_tokens = std::vector({ + {static_cast(MenuFileLexing::MultiChar::SHIFT_LEFT), "<<"}, + {static_cast(MenuFileLexing::MultiChar::SHIFT_RIGHT), ">>"}, + {static_cast(MenuFileLexing::MultiChar::GREATER_EQUAL), ">="}, + {static_cast(MenuFileLexing::MultiChar::LESS_EQUAL), "<="}, + {static_cast(MenuFileLexing::MultiChar::EQUALS), "=="}, + {static_cast(MenuFileLexing::MultiChar::NOT_EQUAL), "!="}, + {static_cast(MenuFileLexing::MultiChar::LOGICAL_AND), "&&"}, + {static_cast(MenuFileLexing::MultiChar::LOGICAL_OR), "||"}, + }); const auto lexer = std::make_unique(m_stream, std::move(lexerConfig)); const auto parser = std::make_unique(lexer.get(), m_feature_level); diff --git a/src/ObjLoading/Parsing/Menu/Sequence/Properties/MenuPropertySequences.cpp b/src/ObjLoading/Parsing/Menu/Sequence/Properties/MenuPropertySequences.cpp index 0bf8f137..70e7ed21 100644 --- a/src/ObjLoading/Parsing/Menu/Sequence/Properties/MenuPropertySequences.cpp +++ b/src/ObjLoading/Parsing/Menu/Sequence/Properties/MenuPropertySequences.cpp @@ -8,6 +8,7 @@ #include "GenericStringPropertySequence.h" #include "Parsing/Menu/Matcher/MenuMatcherFactory.h" #include "Parsing/Menu/Domain/CommonMenuTypes.h" +#include "Parsing/Menu/Matcher/MenuCommonMatchers.h" using namespace menu; @@ -63,6 +64,52 @@ namespace menu::menu_properties state->m_current_menu->m_rect = rect; } }; + + class SequenceBooleanExpression final : public MenuFileParser::sequence_t + { + static constexpr auto CAPTURE_EXPRESSION = 1; + + public: + explicit SequenceBooleanExpression(std::string keyword) + { + const MenuMatcherFactory create(this); + + AddLabeledMatchers(MenuCommonMatchers::Expression(this), MenuCommonMatchers::LABEL_EXPRESSION); + + AddMatchers({ + create.KeywordIgnoreCase(std::move(keyword)), + create.Or({ + create.And({ + create.KeywordIgnoreCase("when"), + create.Char('('), + create.Label(MenuCommonMatchers::LABEL_EXPRESSION).Capture(CAPTURE_EXPRESSION), + create.Char(')') + }), + create.Label(MenuCommonMatchers::LABEL_EXPRESSION) + }), + create.Char(';') + }); + } + + protected: + void ProcessMatch(MenuFileParserState* state, SequenceResult& result) const override + { + assert(state->m_current_menu); + + const auto expression = MenuCommonMatchers::ProcessExpression(state, result); + + std::cout << "Evaluated expression!\n"; + std::cout << " IsStatic: " << expression->IsStatic() << "\n"; + + const auto value = expression->Evaluate(); + if(value.m_type == CommonExpressionValue::Type::DOUBLE) + std::cout << " Value: " << value.m_double_value << "\n"; + else if(value.m_type == CommonExpressionValue::Type::INT) + std::cout << " Value: " << value.m_int_value << "\n"; + else if (value.m_type == CommonExpressionValue::Type::STRING) + std::cout << " Value: \"" << *value.m_string_value << "\"\n"; + } + }; } using namespace menu_properties; @@ -187,4 +234,6 @@ void MenuPropertySequences::AddSequences(FeatureLevel featureLevel) { state->m_current_menu->m_text_only_focus = true; })); + + AddSequence(std::make_unique("visible")); } diff --git a/src/ZoneCodeGeneratorLib/Parsing/Commands/Matcher/CommandsCommonMatchers.cpp b/src/ZoneCodeGeneratorLib/Parsing/Commands/Matcher/CommandsCommonMatchers.cpp index c1cebeae..d95a6fa0 100644 --- a/src/ZoneCodeGeneratorLib/Parsing/Commands/Matcher/CommandsCommonMatchers.cpp +++ b/src/ZoneCodeGeneratorLib/Parsing/Commands/Matcher/CommandsCommonMatchers.cpp @@ -106,7 +106,7 @@ static constexpr int TAG_EVALUATION_OPERATION = std::numeric_limits::max() static constexpr int CAPTURE_OPERAND_TYPENAME = std::numeric_limits::max() - 1; static constexpr int CAPTURE_OPERAND_ARRAY = std::numeric_limits::max() - 2; static constexpr int CAPTURE_OPERAND_INTEGER = std::numeric_limits::max() - 3; -static constexpr int CAPTURE_OPERATION_TYPE = std::numeric_limits::max() - 4; +static constexpr int CAPTURE_BINARY_OPERATION_TYPE = std::numeric_limits::max() - 4; std::unique_ptr CommandsCommonMatchers::ParseOperandArray(const supplier_t* labelSupplier) { @@ -209,7 +209,7 @@ std::unique_ptr CommandsCommonMatchers::Parse { return CommandsParserValue::OpType(values[0].get().GetPos(), OperationType::OPERATION_OR); }) - }).Capture(CAPTURE_OPERATION_TYPE); + }).Capture(CAPTURE_BINARY_OPERATION_TYPE); } std::unique_ptr CommandsCommonMatchers::Evaluation(const supplier_t* labelSupplier) @@ -324,7 +324,7 @@ std::unique_ptr CommandsCommonMatchers::ProcessEvaluation(CommandsP operands.emplace_back(std::move(firstStatementPart)); if (result.PeekAndRemoveIfTag(TAG_EVALUATION_OPERATION) == TAG_EVALUATION_OPERATION) - operators.emplace_back(operators.size(), result.NextCapture(CAPTURE_OPERATION_TYPE).OpTypeValue()); + operators.emplace_back(operators.size(), result.NextCapture(CAPTURE_BINARY_OPERATION_TYPE).OpTypeValue()); else break;