Add conditional operator to simple expressions

This commit is contained in:
Jan 2021-11-26 17:39:46 +01:00
parent 5b087e0d31
commit 2a4768e5b0
7 changed files with 327 additions and 38 deletions

View File

@ -12,7 +12,7 @@ static constexpr int CAPTURE_FUNCTION_NAME = SimpleExpressionMatchers::CAPTURE_O
MenuExpressionMatchers::MenuExpressionMatchers()
: SimpleExpressionMatchers(true, true, true, true)
: SimpleExpressionMatchers(true, true, true, true, true)
{
}

View File

@ -8,7 +8,7 @@ DefinesIfExpressionMatchers::DefinesIfExpressionMatchers()
}
DefinesIfExpressionMatchers::DefinesIfExpressionMatchers(const DefinesIfDirectiveParsingState* state)
: SimpleExpressionMatchers(false, false, true, true),
: SimpleExpressionMatchers(false, false, true, true, false),
m_state(state)
{
}

View File

@ -0,0 +1,22 @@
#include "SimpleExpressionConditionalOperator.h"
SimpleExpressionConditionalOperator::SimpleExpressionConditionalOperator()
= default;
SimpleExpressionConditionalOperator::SimpleExpressionConditionalOperator(std::unique_ptr<ISimpleExpression> condition, std::unique_ptr<ISimpleExpression> trueExpression,
std::unique_ptr<ISimpleExpression> falseExpression)
: m_condition(std::move(condition)),
m_true_value(std::move(trueExpression)),
m_false_value(std::move(falseExpression))
{
}
bool SimpleExpressionConditionalOperator::IsStatic()
{
return m_condition->IsStatic() && m_true_value->IsStatic() && m_false_value->IsStatic();
}
SimpleExpressionValue SimpleExpressionConditionalOperator::Evaluate()
{
return m_condition->Evaluate().IsTruthy() ? m_true_value->Evaluate() : m_false_value->Evaluate();
}

View File

@ -0,0 +1,16 @@
#pragma once
#include "ISimpleExpression.h"
class SimpleExpressionConditionalOperator final : public ISimpleExpression
{
public:
std::unique_ptr<ISimpleExpression> m_condition;
std::unique_ptr<ISimpleExpression> m_true_value;
std::unique_ptr<ISimpleExpression> m_false_value;
bool IsStatic() override;
SimpleExpressionValue Evaluate() override;
SimpleExpressionConditionalOperator();
SimpleExpressionConditionalOperator(std::unique_ptr<ISimpleExpression> condition, std::unique_ptr<ISimpleExpression> trueExpression, std::unique_ptr<ISimpleExpression> falseExpression);
};

View File

@ -2,6 +2,7 @@
#include <list>
#include "SimpleExpressionConditionalOperator.h"
#include "Parsing/Simple/Matcher/SimpleMatcherFactory.h"
#include "Parsing/Simple/Expression/SimpleExpressionBinaryOperation.h"
#include "Parsing/Simple/Expression/SimpleExpressionUnaryOperation.h"
@ -14,21 +15,26 @@ static constexpr int TAG_PARENTHESIS_END = SimpleExpressionMatchers::TAG_OFFSET_
static constexpr int TAG_BINARY_OPERATION = SimpleExpressionMatchers::TAG_OFFSET_EXPRESSION + 6;
static constexpr int TAG_OPERAND_EXT = SimpleExpressionMatchers::TAG_OFFSET_EXPRESSION + 7;
static constexpr int TAG_OPERAND_EXT_END = SimpleExpressionMatchers::TAG_OFFSET_EXPRESSION + 8;
static constexpr int TAG_CONDITIONAL_OPERATOR = SimpleExpressionMatchers::TAG_OFFSET_EXPRESSION + 9;
static constexpr int TAG_CONDITIONAL_OPERATOR_SEPARATOR = SimpleExpressionMatchers::TAG_OFFSET_EXPRESSION + 10;
static constexpr int TAG_CONDITIONAL_OPERATOR_END = SimpleExpressionMatchers::TAG_OFFSET_EXPRESSION + 11;
static constexpr int CAPTURE_OPERAND = SimpleExpressionMatchers::CAPTURE_OFFSET_EXPRESSION + 1;
static constexpr int CAPTURE_UNARY_OPERATION_TYPE = SimpleExpressionMatchers::CAPTURE_OFFSET_EXPRESSION + 2;
static constexpr int CAPTURE_BINARY_OPERATION_TYPE = SimpleExpressionMatchers::CAPTURE_OFFSET_EXPRESSION + 3;
SimpleExpressionMatchers::SimpleExpressionMatchers()
: SimpleExpressionMatchers(true, true, true, true)
: SimpleExpressionMatchers(true, true, true, true, false)
{
}
SimpleExpressionMatchers::SimpleExpressionMatchers(const bool enableStringOperands, const bool enableIdentifierOperands, const bool enableFloatingPointOperands, const bool enableIntOperands)
SimpleExpressionMatchers::SimpleExpressionMatchers(const bool enableStringOperands, const bool enableIdentifierOperands, const bool enableFloatingPointOperands, const bool enableIntOperands,
const bool enableConditionalOperator)
: m_enable_string_operands(enableStringOperands),
m_enable_identifier_operands(enableIdentifierOperands),
m_enable_floating_point_operands(enableFloatingPointOperands),
m_enable_int_operands(enableIntOperands)
m_enable_int_operands(enableIntOperands),
m_enable_conditional_operator(enableConditionalOperator)
{
}
@ -76,6 +82,21 @@ std::unique_ptr<ISimpleExpression> SimpleExpressionMatchers::ProcessExpressionIn
return processedEvaluation;
}
std::unique_ptr<ISimpleExpression> SimpleExpressionMatchers::ProcessConditionalOperation(std::unique_ptr<ISimpleExpression> condition, SequenceResult<SimpleParserValue>& result) const
{
auto trueExpression = ProcessExpression(result);
if (result.PeekAndRemoveIfTag(TAG_CONDITIONAL_OPERATOR_SEPARATOR) != TAG_CONDITIONAL_OPERATOR_SEPARATOR)
throw ParsingException(TokenPos(), "Expected conditional separator tag @ ProcessConditionalOperation");
auto falseExpression = ProcessExpression(result);
if (result.PeekAndRemoveIfTag(TAG_CONDITIONAL_OPERATOR_END) != TAG_CONDITIONAL_OPERATOR_END)
throw ParsingException(TokenPos(), "Expected conditional end tag @ ProcessConditionalOperation");
return std::make_unique<SimpleExpressionConditionalOperator>(std::move(condition), std::move(trueExpression), std::move(falseExpression));
}
std::unique_ptr<ISimpleExpression> SimpleExpressionMatchers::ProcessOperand(SequenceResult<SimpleParserValue>& result) const
{
const auto& operandToken = result.NextCapture(CAPTURE_OPERAND);
@ -149,6 +170,12 @@ std::unique_ptr<ISimpleExpression> SimpleExpressionMatchers::ProcessExpression(S
std::move(firstStatementPart));
}
if (result.PeekAndRemoveIfTag(TAG_CONDITIONAL_OPERATOR) == TAG_CONDITIONAL_OPERATOR)
{
operands.emplace_back(ProcessConditionalOperation(std::move(firstStatementPart), result));
break;
}
operands.emplace_back(std::move(firstStatementPart));
if (result.PeekAndRemoveIfTag(TAG_BINARY_OPERATION) == TAG_BINARY_OPERATION)
@ -270,25 +297,41 @@ std::unique_ptr<SimpleExpressionMatchers::matcher_t> SimpleExpressionMatchers::P
{
unaryOperationsMatchers.emplace_back(
create.MultiChar(MULTI_TOKEN_OFFSET_UNARY + static_cast<int>(enabledUnaryOperation->m_id))
.Transform([enabledUnaryOperation](const SimpleMatcherFactory::token_list_t& values)
{
return SimpleParserValue::Integer(values[0].get().GetPos(), static_cast<int>(enabledUnaryOperation->m_id));
}));
.Transform([enabledUnaryOperation](const SimpleMatcherFactory::token_list_t& values)
{
return SimpleParserValue::Integer(values[0].get().GetPos(), static_cast<int>(enabledUnaryOperation->m_id));
}));
}
else if (!enabledUnaryOperation->m_syntax.empty())
{
unaryOperationsMatchers.emplace_back(
create.Char(enabledUnaryOperation->m_syntax[0])
.Transform([enabledUnaryOperation](const SimpleMatcherFactory::token_list_t& values)
{
return SimpleParserValue::Integer(values[0].get().GetPos(), static_cast<int>(enabledUnaryOperation->m_id));
}));
.Transform([enabledUnaryOperation](const SimpleMatcherFactory::token_list_t& values)
{
return SimpleParserValue::Integer(values[0].get().GetPos(), static_cast<int>(enabledUnaryOperation->m_id));
}));
}
}
return create.Or(std::move(unaryOperationsMatchers)).Tag(TAG_UNARY_OPERATION).Capture(CAPTURE_UNARY_OPERATION_TYPE);
}
std::unique_ptr<SimpleExpressionMatchers::matcher_t> SimpleExpressionMatchers::ParseConditionalOperator(const supplier_t* labelSupplier) const
{
const SimpleMatcherFactory create(labelSupplier);
if (!m_enable_conditional_operator)
return create.False();
return create.And({
create.Char('?').Tag(TAG_CONDITIONAL_OPERATOR),
create.Label(LABEL_EXPRESSION),
create.Char(':').Tag(TAG_CONDITIONAL_OPERATOR_SEPARATOR),
create.Label(LABEL_EXPRESSION),
create.True().Tag(TAG_CONDITIONAL_OPERATOR_END)
});
}
std::unique_ptr<SimpleExpressionMatchers::matcher_t> SimpleExpressionMatchers::Expression(const supplier_t* labelSupplier) const
{
const SimpleMatcherFactory create(labelSupplier);
@ -308,9 +351,12 @@ std::unique_ptr<SimpleExpressionMatchers::matcher_t> SimpleExpressionMatchers::E
}),
ParseOperand(labelSupplier)
}),
create.Optional(create.And({
ParseBinaryOperationType(labelSupplier),
create.Label(LABEL_EXPRESSION)
}).Tag(TAG_BINARY_OPERATION))
create.Optional(create.Or({
ParseConditionalOperator(labelSupplier),
create.And({
ParseBinaryOperationType(labelSupplier),
create.Label(LABEL_EXPRESSION)
}).Tag(TAG_BINARY_OPERATION)
}))
}).Tag(TAG_EXPRESSION);
}

View File

@ -31,10 +31,11 @@ private:
bool m_enable_identifier_operands;
bool m_enable_floating_point_operands;
bool m_enable_int_operands;
bool m_enable_conditional_operator;
public:
SimpleExpressionMatchers();
SimpleExpressionMatchers(bool enableStringOperands, bool enableIdentifierOperands, bool enableFloatingPointOperands, bool enableIntOperands);
SimpleExpressionMatchers(bool enableStringOperands, bool enableIdentifierOperands, bool enableFloatingPointOperands, bool enableIntOperands, bool enableConditionalOperator);
virtual ~SimpleExpressionMatchers();
SimpleExpressionMatchers(const SimpleExpressionMatchers& other) = default;
SimpleExpressionMatchers(SimpleExpressionMatchers&& other) noexcept = default;
@ -52,8 +53,10 @@ private:
std::unique_ptr<matcher_t> ParseBinaryOperationType(const supplier_t* labelSupplier) const;
std::unique_ptr<matcher_t> ParseOperand(const supplier_t* labelSupplier) const;
std::unique_ptr<matcher_t> ParseUnaryOperationType(const supplier_t* labelSupplier) const;
std::unique_ptr<matcher_t> ParseConditionalOperator(const supplier_t* labelSupplier) const;
std::unique_ptr<ISimpleExpression> ProcessExpressionInParenthesis(SequenceResult<SimpleParserValue>& result) const;
std::unique_ptr<ISimpleExpression> ProcessConditionalOperation(std::unique_ptr<ISimpleExpression> condition, SequenceResult<SimpleParserValue>& result) const;
std::unique_ptr<ISimpleExpression> ProcessOperand(SequenceResult<SimpleParserValue>& result) const;
public:

View File

@ -22,7 +22,7 @@ namespace test::parsing::simple::expression
public:
SimpleExpressionSequence()
: m_expression_matchers(true, true, true, true)
: m_expression_matchers(true, true, true, true, true)
{
const SimpleMatcherFactory create(this);
@ -494,7 +494,7 @@ namespace test::parsing::simple::expression
SimpleParserValue::Character(pos, '+'),
SimpleParserValue::Integer(pos, 220),
SimpleParserValue::EndOfFile(pos)
});
});
const auto result = helper.PerformTest();
@ -522,7 +522,7 @@ namespace test::parsing::simple::expression
SimpleParserValue::Character(pos, '*'),
SimpleParserValue::Integer(pos, 10),
SimpleParserValue::EndOfFile(pos)
});
});
const auto result = helper.PerformTest();
@ -536,4 +536,206 @@ namespace test::parsing::simple::expression
REQUIRE(value.m_type == SimpleExpressionValue::Type::INT);
REQUIRE(value.m_int_value == 420);
}
TEST_CASE("SimpleExpressions: Simple conditional operator can be used with true value", "[parsing][simple][expression]")
{
SimpleExpressionTestsHelper helper;
const TokenPos pos;
helper.Tokens({
SimpleParserValue::Integer(pos, 1),
SimpleParserValue::Character(pos, '?'),
SimpleParserValue::Integer(pos, 420),
SimpleParserValue::Character(pos, ':'),
SimpleParserValue::Integer(pos, 1337),
SimpleParserValue::EndOfFile(pos)
});
const auto result = helper.PerformTest();
REQUIRE(result);
REQUIRE(helper.m_consumed_token_count == 5);
const auto& expression = helper.m_state->m_expression;
REQUIRE(expression->IsStatic());
const auto value = expression->Evaluate();
REQUIRE(value.m_type == SimpleExpressionValue::Type::INT);
REQUIRE(value.m_int_value == 420);
}
TEST_CASE("SimpleExpressions: Simple conditional operator can be used with false value", "[parsing][simple][expression]")
{
SimpleExpressionTestsHelper helper;
const TokenPos pos;
helper.Tokens({
SimpleParserValue::Integer(pos, 0),
SimpleParserValue::Character(pos, '?'),
SimpleParserValue::Integer(pos, 420),
SimpleParserValue::Character(pos, ':'),
SimpleParserValue::Integer(pos, 1337),
SimpleParserValue::EndOfFile(pos)
});
const auto result = helper.PerformTest();
REQUIRE(result);
REQUIRE(helper.m_consumed_token_count == 5);
const auto& expression = helper.m_state->m_expression;
REQUIRE(expression->IsStatic());
const auto value = expression->Evaluate();
REQUIRE(value.m_type == SimpleExpressionValue::Type::INT);
REQUIRE(value.m_int_value == 1337);
}
TEST_CASE("SimpleExpressions: Simple conditional operator can be used within parenthesis with true value", "[parsing][simple][expression]")
{
SimpleExpressionTestsHelper helper;
const TokenPos pos;
helper.Tokens({
SimpleParserValue::Character(pos, '('),
SimpleParserValue::Integer(pos, 1),
SimpleParserValue::Character(pos, '?'),
SimpleParserValue::Integer(pos, 420),
SimpleParserValue::Character(pos, ':'),
SimpleParserValue::Integer(pos, 1337),
SimpleParserValue::Character(pos, ')'),
SimpleParserValue::Character(pos, '+'),
SimpleParserValue::Integer(pos, 1),
SimpleParserValue::EndOfFile(pos)
});
const auto result = helper.PerformTest();
REQUIRE(result);
REQUIRE(helper.m_consumed_token_count == 9);
const auto& expression = helper.m_state->m_expression;
REQUIRE(expression->IsStatic());
const auto value = expression->Evaluate();
REQUIRE(value.m_type == SimpleExpressionValue::Type::INT);
REQUIRE(value.m_int_value == 421);
}
TEST_CASE("SimpleExpressions: Simple conditional operator can be used within parenthesis with false value", "[parsing][simple][expression]")
{
SimpleExpressionTestsHelper helper;
const TokenPos pos;
helper.Tokens({
SimpleParserValue::Character(pos, '('),
SimpleParserValue::Integer(pos, 0),
SimpleParserValue::Character(pos, '?'),
SimpleParserValue::Integer(pos, 420),
SimpleParserValue::Character(pos, ':'),
SimpleParserValue::Integer(pos, 1337),
SimpleParserValue::Character(pos, ')'),
SimpleParserValue::Character(pos, '+'),
SimpleParserValue::Integer(pos, 1),
SimpleParserValue::EndOfFile(pos)
});
const auto result = helper.PerformTest();
REQUIRE(result);
REQUIRE(helper.m_consumed_token_count == 9);
const auto& expression = helper.m_state->m_expression;
REQUIRE(expression->IsStatic());
const auto value = expression->Evaluate();
REQUIRE(value.m_type == SimpleExpressionValue::Type::INT);
REQUIRE(value.m_int_value == 1338);
}
TEST_CASE("SimpleExpressions: Simple conditional operator can have an expression as condition", "[parsing][simple][expression]")
{
SimpleExpressionTestsHelper helper;
const TokenPos pos;
helper.Tokens({
SimpleParserValue::Character(pos, '('),
SimpleParserValue::Integer(pos, -1),
SimpleParserValue::Character(pos, '+'),
SimpleParserValue::Integer(pos, 2),
SimpleParserValue::Character(pos, ')'),
SimpleParserValue::Character(pos, '?'),
SimpleParserValue::Integer(pos, 420),
SimpleParserValue::Character(pos, ':'),
SimpleParserValue::Integer(pos, 1337),
SimpleParserValue::EndOfFile(pos)
});
const auto result = helper.PerformTest();
REQUIRE(result);
REQUIRE(helper.m_consumed_token_count == 9);
const auto& expression = helper.m_state->m_expression;
REQUIRE(expression->IsStatic());
const auto value = expression->Evaluate();
REQUIRE(value.m_type == SimpleExpressionValue::Type::INT);
REQUIRE(value.m_int_value == 420);
}
TEST_CASE("SimpleExpressions: Simple conditional operator can have an expression as true value", "[parsing][simple][expression]")
{
SimpleExpressionTestsHelper helper;
const TokenPos pos;
helper.Tokens({
SimpleParserValue::Integer(pos, 1),
SimpleParserValue::Character(pos, '?'),
SimpleParserValue::Character(pos, '('),
SimpleParserValue::Integer(pos, 210),
SimpleParserValue::Character(pos, '*'),
SimpleParserValue::Integer(pos, 2),
SimpleParserValue::Character(pos, ')'),
SimpleParserValue::Character(pos, ':'),
SimpleParserValue::Integer(pos, 1337),
SimpleParserValue::EndOfFile(pos)
});
const auto result = helper.PerformTest();
REQUIRE(result);
REQUIRE(helper.m_consumed_token_count == 9);
const auto& expression = helper.m_state->m_expression;
REQUIRE(expression->IsStatic());
const auto value = expression->Evaluate();
REQUIRE(value.m_type == SimpleExpressionValue::Type::INT);
REQUIRE(value.m_int_value == 420);
}
TEST_CASE("SimpleExpressions: Simple conditional operator can have an expression as false value", "[parsing][simple][expression]")
{
SimpleExpressionTestsHelper helper;
const TokenPos pos;
helper.Tokens({
SimpleParserValue::Integer(pos, 0),
SimpleParserValue::Character(pos, '?'),
SimpleParserValue::Integer(pos, 420),
SimpleParserValue::Character(pos, ':'),
SimpleParserValue::Character(pos, '('),
SimpleParserValue::Integer(pos, 1336),
SimpleParserValue::Character(pos, '+'),
SimpleParserValue::Integer(pos, 1),
SimpleParserValue::Character(pos, ')'),
SimpleParserValue::EndOfFile(pos)
});
const auto result = helper.PerformTest();
REQUIRE(result);
REQUIRE(helper.m_consumed_token_count == 9);
const auto& expression = helper.m_state->m_expression;
REQUIRE(expression->IsStatic());
const auto value = expression->Evaluate();
REQUIRE(value.m_type == SimpleExpressionValue::Type::INT);
REQUIRE(value.m_int_value == 1337);
}
}