From 4b2f001e7c23029f7c098dca6ad6849311e4b08e Mon Sep 17 00:00:00 2001 From: Jan Date: Thu, 25 Nov 2021 12:33:30 +0100 Subject: [PATCH] Add more unit tests for simple expressions and fix code --- .../Expression/SimpleExpressionMatchers.cpp | 4 +- .../Parsing/Simple/SimpleExpressionTests.cpp | 439 ++++++++++++++++++ 2 files changed, 441 insertions(+), 2 deletions(-) diff --git a/src/Parser/Parsing/Simple/Expression/SimpleExpressionMatchers.cpp b/src/Parser/Parsing/Simple/Expression/SimpleExpressionMatchers.cpp index 5ade655a..1b7a5855 100644 --- a/src/Parser/Parsing/Simple/Expression/SimpleExpressionMatchers.cpp +++ b/src/Parser/Parsing/Simple/Expression/SimpleExpressionMatchers.cpp @@ -171,12 +171,12 @@ std::unique_ptr SimpleExpressionMatchers::ProcessExpression(S if (p1.second->m_precedence != p2.second->m_precedence) return p1.second->m_precedence > p2.second->m_precedence; - return p1.first < p2.first; + return p1.first > p2.first; }); while (!operators.empty()) { - const auto& [operatorIndex, operatorType] = operators.back(); + const auto [operatorIndex, operatorType] = operators.back(); // This must not be a reference auto operation = std::make_unique(operatorType, std::move(operands[operatorIndex]), std::move(operands[operatorIndex + 1])); operands.erase(operands.begin() + static_cast(operatorIndex)); diff --git a/test/ParserTests/Parsing/Simple/SimpleExpressionTests.cpp b/test/ParserTests/Parsing/Simple/SimpleExpressionTests.cpp index 8ee8ee74..eab155f0 100644 --- a/test/ParserTests/Parsing/Simple/SimpleExpressionTests.cpp +++ b/test/ParserTests/Parsing/Simple/SimpleExpressionTests.cpp @@ -97,4 +97,443 @@ namespace test::parsing::simple::expression REQUIRE(value.m_int_value == 1337); } + TEST_CASE("SimpleExpressions: Can parse expression with subtract operation", "[parsing][simple][expression]") + { + SimpleExpressionTestsHelper helper; + const TokenPos pos; + helper.Tokens({ + SimpleParserValue::Integer(pos, 428), + SimpleParserValue::Character(pos, '-'), + SimpleParserValue::Integer(pos, 8), + SimpleParserValue::EndOfFile(pos) + }); + + const auto result = helper.PerformTest(); + + REQUIRE(result); + REQUIRE(helper.m_consumed_token_count == 3); + + 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: Can parse expression with multiply operation", "[parsing][simple][expression]") + { + SimpleExpressionTestsHelper helper; + const TokenPos pos; + helper.Tokens({ + SimpleParserValue::Integer(pos, 105), + SimpleParserValue::Character(pos, '*'), + SimpleParserValue::Integer(pos, 4), + SimpleParserValue::EndOfFile(pos) + }); + + const auto result = helper.PerformTest(); + + REQUIRE(result); + REQUIRE(helper.m_consumed_token_count == 3); + + 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: Can parse expression with divide operation", "[parsing][simple][expression]") + { + SimpleExpressionTestsHelper helper; + const TokenPos pos; + helper.Tokens({ + SimpleParserValue::Integer(pos, 561540), + SimpleParserValue::Character(pos, '/'), + SimpleParserValue::Integer(pos, 420), + SimpleParserValue::EndOfFile(pos) + }); + + const auto result = helper.PerformTest(); + + REQUIRE(result); + REQUIRE(helper.m_consumed_token_count == 3); + + 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: Can parse expression with remainder operation", "[parsing][simple][expression]") + { + SimpleExpressionTestsHelper helper; + const TokenPos pos; + helper.Tokens({ + SimpleParserValue::Integer(pos, 92673), + SimpleParserValue::Character(pos, '%'), + SimpleParserValue::Integer(pos, 1337), + SimpleParserValue::EndOfFile(pos) + }); + + const auto result = helper.PerformTest(); + + REQUIRE(result); + REQUIRE(helper.m_consumed_token_count == 3); + + 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: Can parse expression with bitwise and operation", "[parsing][simple][expression]") + { + SimpleExpressionTestsHelper helper; + const TokenPos pos; + helper.Tokens({ + SimpleParserValue::Integer(pos, 0x7FFFFFF0), + SimpleParserValue::Character(pos, '&'), + SimpleParserValue::Integer(pos, 0x2AAAAAAA), + SimpleParserValue::EndOfFile(pos) + }); + + const auto result = helper.PerformTest(); + + REQUIRE(result); + REQUIRE(helper.m_consumed_token_count == 3); + + 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 == 0x2AAAAAA0); + } + + TEST_CASE("SimpleExpressions: Can parse expression with bitwise or operation", "[parsing][simple][expression]") + { + SimpleExpressionTestsHelper helper; + const TokenPos pos; + helper.Tokens({ + SimpleParserValue::Integer(pos, 5), + SimpleParserValue::Character(pos, '|'), + SimpleParserValue::Integer(pos, 3), + SimpleParserValue::EndOfFile(pos) + }); + + const auto result = helper.PerformTest(); + + REQUIRE(result); + REQUIRE(helper.m_consumed_token_count == 3); + + 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 == 7); + } + + TEST_CASE("SimpleExpressions: Can parse expression with shift left operation", "[parsing][simple][expression]") + { + SimpleExpressionTestsHelper helper; + const TokenPos pos; + helper.Tokens({ + SimpleParserValue::Integer(pos, 105), + SimpleParserValue::MultiCharacter(pos, SimpleExpressionMatchers::MULTI_TOKEN_OFFSET_BINARY + static_cast(SimpleBinaryOperationId::SHIFT_LEFT)), + SimpleParserValue::Integer(pos, 2), + SimpleParserValue::EndOfFile(pos) + }); + + const auto result = helper.PerformTest(); + + REQUIRE(result); + REQUIRE(helper.m_consumed_token_count == 3); + + 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: Can parse expression with shift right operation", "[parsing][simple][expression]") + { + SimpleExpressionTestsHelper helper; + const TokenPos pos; + helper.Tokens({ + SimpleParserValue::Integer(pos, 42784), + SimpleParserValue::MultiCharacter(pos, SimpleExpressionMatchers::MULTI_TOKEN_OFFSET_BINARY + static_cast(SimpleBinaryOperationId::SHIFT_RIGHT)), + SimpleParserValue::Integer(pos, 5), + SimpleParserValue::EndOfFile(pos) + }); + + const auto result = helper.PerformTest(); + + REQUIRE(result); + REQUIRE(helper.m_consumed_token_count == 3); + + 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: Can parse expression with greater operation", "[parsing][simple][expression]") + { + SimpleExpressionTestsHelper helper; + const TokenPos pos; + helper.Tokens({ + SimpleParserValue::Integer(pos, 1337), + SimpleParserValue::Character(pos, '>'), + SimpleParserValue::Integer(pos, 420), + SimpleParserValue::EndOfFile(pos) + }); + + const auto result = helper.PerformTest(); + + REQUIRE(result); + REQUIRE(helper.m_consumed_token_count == 3); + + 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 == 1); + } + + TEST_CASE("SimpleExpressions: Can parse expression with greater equal operation", "[parsing][simple][expression]") + { + SimpleExpressionTestsHelper helper; + const TokenPos pos; + helper.Tokens({ + SimpleParserValue::Integer(pos, 1337), + SimpleParserValue::MultiCharacter(pos, SimpleExpressionMatchers::MULTI_TOKEN_OFFSET_BINARY + static_cast(SimpleBinaryOperationId::GREATER_EQUAL_THAN)), + SimpleParserValue::Integer(pos, 420), + SimpleParserValue::EndOfFile(pos) + }); + + const auto result = helper.PerformTest(); + + REQUIRE(result); + REQUIRE(helper.m_consumed_token_count == 3); + + 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 == 1); + } + + TEST_CASE("SimpleExpressions: Can parse expression with less operation", "[parsing][simple][expression]") + { + SimpleExpressionTestsHelper helper; + const TokenPos pos; + helper.Tokens({ + SimpleParserValue::Integer(pos, 420), + SimpleParserValue::Character(pos, '<'), + SimpleParserValue::Integer(pos, 421), + SimpleParserValue::EndOfFile(pos) + }); + + const auto result = helper.PerformTest(); + + REQUIRE(result); + REQUIRE(helper.m_consumed_token_count == 3); + + 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 == 1); + } + + TEST_CASE("SimpleExpressions: Can parse expression with less equal operation", "[parsing][simple][expression]") + { + SimpleExpressionTestsHelper helper; + const TokenPos pos; + helper.Tokens({ + SimpleParserValue::Integer(pos, 420), + SimpleParserValue::MultiCharacter(pos, SimpleExpressionMatchers::MULTI_TOKEN_OFFSET_BINARY + static_cast(SimpleBinaryOperationId::LESS_EQUAL_THAN)), + SimpleParserValue::Integer(pos, 421), + SimpleParserValue::EndOfFile(pos) + }); + + const auto result = helper.PerformTest(); + + REQUIRE(result); + REQUIRE(helper.m_consumed_token_count == 3); + + 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 == 1); + } + + TEST_CASE("SimpleExpressions: Can parse expression with equals operation", "[parsing][simple][expression]") + { + SimpleExpressionTestsHelper helper; + const TokenPos pos; + helper.Tokens({ + SimpleParserValue::Integer(pos, 1337), + SimpleParserValue::MultiCharacter(pos, SimpleExpressionMatchers::MULTI_TOKEN_OFFSET_BINARY + static_cast(SimpleBinaryOperationId::EQUALS)), + SimpleParserValue::Integer(pos, 1337), + SimpleParserValue::EndOfFile(pos) + }); + + const auto result = helper.PerformTest(); + + REQUIRE(result); + REQUIRE(helper.m_consumed_token_count == 3); + + 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 == 1); + } + + TEST_CASE("SimpleExpressions: Can parse expression with not equal operation", "[parsing][simple][expression]") + { + SimpleExpressionTestsHelper helper; + const TokenPos pos; + helper.Tokens({ + SimpleParserValue::Integer(pos, 1337), + SimpleParserValue::MultiCharacter(pos, SimpleExpressionMatchers::MULTI_TOKEN_OFFSET_BINARY + static_cast(SimpleBinaryOperationId::NOT_EQUAL)), + SimpleParserValue::Integer(pos, 1337), + SimpleParserValue::EndOfFile(pos) + }); + + const auto result = helper.PerformTest(); + + REQUIRE(result); + REQUIRE(helper.m_consumed_token_count == 3); + + 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 == 0); + } + + TEST_CASE("SimpleExpressions: Can parse expression with and operation", "[parsing][simple][expression]") + { + SimpleExpressionTestsHelper helper; + const TokenPos pos; + helper.Tokens({ + SimpleParserValue::Integer(pos, 1337), + SimpleParserValue::MultiCharacter(pos, SimpleExpressionMatchers::MULTI_TOKEN_OFFSET_BINARY + static_cast(SimpleBinaryOperationId::AND)), + SimpleParserValue::Integer(pos, 420), + SimpleParserValue::EndOfFile(pos) + }); + + const auto result = helper.PerformTest(); + + REQUIRE(result); + REQUIRE(helper.m_consumed_token_count == 3); + + 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: Can parse expression with or operation", "[parsing][simple][expression]") + { + SimpleExpressionTestsHelper helper; + const TokenPos pos; + helper.Tokens({ + SimpleParserValue::Integer(pos, 0), + SimpleParserValue::MultiCharacter(pos, SimpleExpressionMatchers::MULTI_TOKEN_OFFSET_BINARY + static_cast(SimpleBinaryOperationId::OR)), + SimpleParserValue::Integer(pos, 1337), + SimpleParserValue::EndOfFile(pos) + }); + + const auto result = helper.PerformTest(); + + REQUIRE(result); + REQUIRE(helper.m_consumed_token_count == 3); + + 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: Operator precedence is applied", "[parsing][simple][expression]") + { + SimpleExpressionTestsHelper helper; + const TokenPos pos; + helper.Tokens({ + SimpleParserValue::Integer(pos, 100), + SimpleParserValue::Character(pos, '+'), + SimpleParserValue::Integer(pos, 4), + SimpleParserValue::Character(pos, '*'), + SimpleParserValue::Integer(pos, 25), + SimpleParserValue::Character(pos, '+'), + SimpleParserValue::Integer(pos, 220), + SimpleParserValue::EndOfFile(pos) + }); + + const auto result = helper.PerformTest(); + + REQUIRE(result); + REQUIRE(helper.m_consumed_token_count == 7); + + 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: Parenthesis have highest precedence", "[parsing][simple][expression]") + { + SimpleExpressionTestsHelper helper; + const TokenPos pos; + helper.Tokens({ + SimpleParserValue::Character(pos, '('), + SimpleParserValue::Integer(pos, 40), + SimpleParserValue::Character(pos, '+'), + SimpleParserValue::Integer(pos, 2), + SimpleParserValue::Character(pos, ')'), + SimpleParserValue::Character(pos, '*'), + SimpleParserValue::Integer(pos, 10), + SimpleParserValue::EndOfFile(pos) + }); + + const auto result = helper.PerformTest(); + + REQUIRE(result); + REQUIRE(helper.m_consumed_token_count == 7); + + 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); + } }