diff --git a/src/language/ASTNodeAffectationExpressionBuilder.cpp b/src/language/ASTNodeAffectationExpressionBuilder.cpp index 18e1d70beb695b6ad0660ed696f5ac284e79220e..592c9295c2ed93c98272e9706616c6abfe9f4e4b 100644 --- a/src/language/ASTNodeAffectationExpressionBuilder.cpp +++ b/src/language/ASTNodeAffectationExpressionBuilder.cpp @@ -76,7 +76,9 @@ ASTNodeAffectationExpressionBuilder::ASTNodeAffectationExpressionBuilder(ASTNode n.m_node_processor = std::make_unique<AffectationProcessor<OperatorT, ValueT, int64_t>>(n); break; } - throw parse_error("invalid operand value", std::vector{n.children[1]->begin()}); + // LCOV_EXCL_START + throw parse_error("unexpected error: invalid integral value", std::vector{n.children[1]->begin()}); + // LCOV_EXCL_STOP } case ASTNodeDataType::double_t: { if constexpr (std::is_same_v<ValueT, TinyVector<1>>) { @@ -86,7 +88,7 @@ ASTNodeAffectationExpressionBuilder::ASTNodeAffectationExpressionBuilder(ASTNode } // LCOV_EXCL_START default: { - throw parse_error("invalid operand type", std::vector{n.children[1]->begin()}); + throw parse_error("unexpected error: invalid operand type", std::vector{n.children[1]->begin()}); } // LCOV_EXCL_STOP } @@ -99,7 +101,7 @@ ASTNodeAffectationExpressionBuilder::ASTNodeAffectationExpressionBuilder(ASTNode } // LCOV_EXCL_START default: { - throw parse_error("invalid operand type", std::vector{n.children[1]->begin()}); + throw parse_error("unexpected error: invalid operand type", std::vector{n.children[1]->begin()}); } // LCOV_EXCL_STOP } @@ -122,11 +124,11 @@ ASTNodeAffectationExpressionBuilder::ASTNodeAffectationExpressionBuilder(ASTNode break; } default: { - throw parse_error("invalid operand", std::vector{n.children[1]->begin()}); + throw parse_error("expecting scalar operand type", std::vector{n.children[1]->begin()}); } } } else { - throw parse_error("invalid operator type", std::vector{n.begin()}); + throw parse_error("invalid affectation operator for " + dataTypeName(n.m_data_type), std::vector{n.begin()}); } }; @@ -163,7 +165,7 @@ ASTNodeAffectationExpressionBuilder::ASTNodeAffectationExpressionBuilder(ASTNode // LCOV_EXCL_STOP } } else { - throw parse_error("unexpected error: undefined operator type", std::vector{n.children[0]->begin()}); + throw parse_error("invalid operator for string affectation", std::vector{n.begin()}); } }; @@ -201,9 +203,11 @@ ASTNodeAffectationExpressionBuilder::ASTNodeAffectationExpressionBuilder(ASTNode set_affectation_processor_for_vector_data(TinyVector<3>{}, data_type); break; } + // LCOV_EXCL_START default: { throw parse_error("unexpected error: unexpected vector dimension", std::vector{n.begin()}); } + // LCOV_EXCL_STOP } break; } @@ -217,9 +221,13 @@ ASTNodeAffectationExpressionBuilder::ASTNodeAffectationExpressionBuilder(ASTNode } }; + using OperatorT = std::decay_t<decltype(operator_v)>; // Special treatment dedicated to R^1 to be able to initialize them - if ((n.m_data_type != n.children[1]->m_data_type) and (n.m_data_type == ASTNodeDataType::vector_t) and - (n.m_data_type.dimension() == 1)) { + if (((n.m_data_type != n.children[1]->m_data_type) and (n.m_data_type == ASTNodeDataType::vector_t) and + (n.m_data_type.dimension() == 1)) or + // Special treatment for R^d vectors and operator *= + ((n.m_data_type == ASTNodeDataType::vector_t) and (n.children[1]->m_data_type != ASTNodeDataType::vector_t) and + std::is_same_v<OperatorT, language::multiplyeq_op>)) { ASTNodeNaturalConversionChecker{*n.children[1], ASTNodeDataType::double_t}; } else { ASTNodeNaturalConversionChecker{*n.children[1], n.m_data_type}; diff --git a/tests/test_ASTNodeAffectationExpressionBuilder.cpp b/tests/test_ASTNodeAffectationExpressionBuilder.cpp index fbe7418e6e3198400a33a54906a24457d86edfbe..15c9af9d1804b435b33dd2544d1eb80ae090b929 100644 --- a/tests/test_ASTNodeAffectationExpressionBuilder.cpp +++ b/tests/test_ASTNodeAffectationExpressionBuilder.cpp @@ -41,6 +41,24 @@ REQUIRE(ast_output.str() == expected_output); \ } +#define CHECK_AST_THROWS_WITH(data, expected_error) \ + { \ + static_assert(std::is_same_v<std::decay_t<decltype(data)>, std::string_view>); \ + static_assert(std::is_same_v<std::decay_t<decltype(expected_error)>, std::string_view> or \ + std::is_same_v<std::decay_t<decltype(expected_error)>, std::string>); \ + \ + string_input input{data, "test.pgs"}; \ + auto ast = ASTBuilder::build(input); \ + \ + ASTSymbolTableBuilder{*ast}; \ + ASTNodeDataTypeBuilder{*ast}; \ + \ + ASTNodeDeclarationToAffectationConverter{*ast}; \ + ASTNodeTypeCleaner<language::declaration>{*ast}; \ + \ + REQUIRE_THROWS_WITH(ASTNodeExpressionBuilder{*ast}, expected_error); \ + } + TEST_CASE("ASTNodeAffectationExpressionBuilder", "[language]") { const std::string demangled_stdstring = demangle(typeid(std::string{}).name()); @@ -354,6 +372,24 @@ R^2 y = x; CHECK_AST(data, result); } + SECTION("R^2 <- (.,.)") + { + std::string_view data = R"( +R^2 y = (0,1); +)"; + + std::string_view result = R"( +(root:ASTNodeListProcessor) + `-(language::eq_op:AffectationFromListProcessor<language::eq_op, TinyVector<2ul, double> >) + +-(language::name:y:NameProcessor) + `-(language::expression_list:ASTNodeExpressionListProcessor) + +-(language::integer:0:ValueProcessor) + `-(language::integer:1:ValueProcessor) +)"; + + CHECK_AST(data, result); + } + SECTION("R^2 <- 0") { std::string_view data = R"( @@ -402,6 +438,25 @@ R^3 x = 0; CHECK_AST(data, result); } + + SECTION("R^3 <- (.,.)") + { + std::string_view data = R"( +R^3 y = (1,2,3); +)"; + + std::string_view result = R"( +(root:ASTNodeListProcessor) + `-(language::eq_op:AffectationFromListProcessor<language::eq_op, TinyVector<3ul, double> >) + +-(language::name:y:NameProcessor) + `-(language::expression_list:ASTNodeExpressionListProcessor) + +-(language::integer:1:ValueProcessor) + +-(language::integer:2:ValueProcessor) + `-(language::integer:3:ValueProcessor) +)"; + + CHECK_AST(data, result); + } } SECTION("string affectation") @@ -556,6 +611,60 @@ string s="foo"; s+=2; CHECK_AST(data, result); } + + SECTION("R^1 += R^1") + { + std::string_view data = R"( +R^1 x; +R^1 y; +x += y; +)"; + + std::string result = R"( +(root:ASTNodeListProcessor) + `-(language::pluseq_op:AffectationProcessor<language::pluseq_op, TinyVector<1ul, double>, TinyVector<1ul, double> >) + +-(language::name:x:NameProcessor) + `-(language::name:y:NameProcessor) +)"; + + CHECK_AST(data, result); + } + + SECTION("R^2 += R^2") + { + std::string_view data = R"( +R^2 x; +R^2 y; +x += y; +)"; + + std::string result = R"( +(root:ASTNodeListProcessor) + `-(language::pluseq_op:AffectationProcessor<language::pluseq_op, TinyVector<2ul, double>, TinyVector<2ul, double> >) + +-(language::name:x:NameProcessor) + `-(language::name:y:NameProcessor) +)"; + + CHECK_AST(data, result); + } + + SECTION("R^3 += R^3") + { + std::string_view data = R"( +R^3 x; +R^3 y; +x += y; +)"; + + std::string result = R"( +(root:ASTNodeListProcessor) + `-(language::pluseq_op:AffectationProcessor<language::pluseq_op, TinyVector<3ul, double>, TinyVector<3ul, double> >) + +-(language::name:x:NameProcessor) + `-(language::name:y:NameProcessor) +)"; + + CHECK_AST(data, result); + } } SECTION("-=") @@ -598,22 +707,58 @@ R x=1; x-=2.3; CHECK_AST(data, result); } - SECTION("string -= string") + SECTION("R^1 -= R^1") { std::string_view data = R"( -string s="foo"; s-="bar"; +R^1 x; +R^1 y; +x -= y; +)"; + + std::string result = R"( +(root:ASTNodeListProcessor) + `-(language::minuseq_op:AffectationProcessor<language::minuseq_op, TinyVector<1ul, double>, TinyVector<1ul, double> >) + +-(language::name:x:NameProcessor) + `-(language::name:y:NameProcessor) )"; - string_input input{data, "test.pgs"}; - auto ast = ASTBuilder::build(input); + CHECK_AST(data, result); + } - ASTSymbolTableBuilder{*ast}; - ASTNodeDataTypeBuilder{*ast}; + SECTION("R^2 -= R^2") + { + std::string_view data = R"( +R^2 x; +R^2 y; +x -= y; +)"; - ASTNodeDeclarationToAffectationConverter{*ast}; - ASTNodeTypeCleaner<language::declaration>{*ast}; + std::string result = R"( +(root:ASTNodeListProcessor) + `-(language::minuseq_op:AffectationProcessor<language::minuseq_op, TinyVector<2ul, double>, TinyVector<2ul, double> >) + +-(language::name:x:NameProcessor) + `-(language::name:y:NameProcessor) +)"; - REQUIRE_THROWS_AS(ASTNodeExpressionBuilder{*ast}, parse_error); + CHECK_AST(data, result); + } + + SECTION("R^3 -= R^3") + { + std::string_view data = R"( +R^3 x; +R^3 y; +x -= y; +)"; + + std::string result = R"( +(root:ASTNodeListProcessor) + `-(language::minuseq_op:AffectationProcessor<language::minuseq_op, TinyVector<3ul, double>, TinyVector<3ul, double> >) + +-(language::name:x:NameProcessor) + `-(language::name:y:NameProcessor) +)"; + + CHECK_AST(data, result); } } @@ -657,22 +802,259 @@ R x=1; x*=2.3; CHECK_AST(data, result); } - SECTION("string *= Z") + SECTION("R^1 *= R") { std::string_view data = R"( -string s="foo"; s*=2; +R^1 x; x*=2.3; +)"; + + std::string_view result = R"( +(root:ASTNodeListProcessor) + `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, TinyVector<1ul, double>, double>) + +-(language::name:x:NameProcessor) + `-(language::real:2.3:ValueProcessor) +)"; + + CHECK_AST(data, result); + } + + SECTION("R^2 *= R") + { + std::string_view data = R"( +R^2 x; x*= 6.2; +)"; + + std::string_view result = R"( +(root:ASTNodeListProcessor) + `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, TinyVector<2ul, double>, double>) + +-(language::name:x:NameProcessor) + `-(language::real:6.2:ValueProcessor) +)"; + + CHECK_AST(data, result); + } + + SECTION("R^3 *= R") + { + std::string_view data = R"( +R^3 x; x*= 3.1; +)"; + + std::string_view result = R"( +(root:ASTNodeListProcessor) + `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, TinyVector<3ul, double>, double>) + +-(language::name:x:NameProcessor) + `-(language::real:3.1:ValueProcessor) +)"; + + CHECK_AST(data, result); + } + + SECTION("R *= Z") + { + std::string_view data = R"( +R x=1; x*=2; +)"; + + std::string_view result = R"( +(root:ASTNodeListProcessor) + +-(language::eq_op:AffectationProcessor<language::eq_op, double, long>) + | +-(language::name:x:NameProcessor) + | `-(language::integer:1:ValueProcessor) + `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, double, long>) + +-(language::name:x:NameProcessor) + `-(language::integer:2:ValueProcessor) +)"; + + CHECK_AST(data, result); + } + + SECTION("R^1 *= Z") + { + std::string_view data = R"( +R^1 x; x *= 3; +)"; + + std::string_view result = R"( +(root:ASTNodeListProcessor) + `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, TinyVector<1ul, double>, long>) + +-(language::name:x:NameProcessor) + `-(language::integer:3:ValueProcessor) +)"; + + CHECK_AST(data, result); + } + + SECTION("R^2 *= Z") + { + std::string_view data = R"( +R^2 x; x *= 6; +)"; + + std::string_view result = R"( +(root:ASTNodeListProcessor) + `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, TinyVector<2ul, double>, long>) + +-(language::name:x:NameProcessor) + `-(language::integer:6:ValueProcessor) +)"; + + CHECK_AST(data, result); + } + + SECTION("R^3 *= Z") + { + std::string_view data = R"( +R^3 x; x *= 4; +)"; + + std::string_view result = R"( +(root:ASTNodeListProcessor) + `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, TinyVector<3ul, double>, long>) + +-(language::name:x:NameProcessor) + `-(language::integer:4:ValueProcessor) +)"; + + CHECK_AST(data, result); + } + + SECTION("R *= N") + { + std::string_view data = R"( +N n=2; R x=1; x *= n; +)"; + + std::string_view result = R"( +(root:ASTNodeListProcessor) + +-(language::eq_op:AffectationProcessor<language::eq_op, unsigned long, long>) + | +-(language::name:n:NameProcessor) + | `-(language::integer:2:ValueProcessor) + +-(language::eq_op:AffectationProcessor<language::eq_op, double, long>) + | +-(language::name:x:NameProcessor) + | `-(language::integer:1:ValueProcessor) + `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, double, unsigned long>) + +-(language::name:x:NameProcessor) + `-(language::name:n:NameProcessor) +)"; + + CHECK_AST(data, result); + } + + SECTION("R^1 *= N") + { + std::string_view data = R"( +N n; +R^1 x; x *= n; +)"; + + std::string_view result = R"( +(root:ASTNodeListProcessor) + `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, TinyVector<1ul, double>, unsigned long>) + +-(language::name:x:NameProcessor) + `-(language::name:n:NameProcessor) +)"; + + CHECK_AST(data, result); + } + + SECTION("R^2 *= N") + { + std::string_view data = R"( +N n; +R^2 x; x *= n; )"; - string_input input{data, "test.pgs"}; - auto ast = ASTBuilder::build(input); + std::string_view result = R"( +(root:ASTNodeListProcessor) + `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, TinyVector<2ul, double>, unsigned long>) + +-(language::name:x:NameProcessor) + `-(language::name:n:NameProcessor) +)"; + + CHECK_AST(data, result); + } + + SECTION("R^3 *= N") + { + std::string_view data = R"( +N n; +R^3 x; x *= n; +)"; + + std::string_view result = R"( +(root:ASTNodeListProcessor) + `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, TinyVector<3ul, double>, unsigned long>) + +-(language::name:x:NameProcessor) + `-(language::name:n:NameProcessor) +)"; + + CHECK_AST(data, result); + } + + SECTION("R *= B") + { + std::string_view data = R"( +R x=1; x *= true; +)"; + + std::string_view result = R"( +(root:ASTNodeListProcessor) + +-(language::eq_op:AffectationProcessor<language::eq_op, double, long>) + | +-(language::name:x:NameProcessor) + | `-(language::integer:1:ValueProcessor) + `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, double, bool>) + +-(language::name:x:NameProcessor) + `-(language::true_kw:ValueProcessor) +)"; + + CHECK_AST(data, result); + } + + SECTION("R^1 *= B") + { + std::string_view data = R"( +R^1 x; x *= true; +)"; + + std::string_view result = R"( +(root:ASTNodeListProcessor) + `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, TinyVector<1ul, double>, bool>) + +-(language::name:x:NameProcessor) + `-(language::true_kw:ValueProcessor) +)"; + + CHECK_AST(data, result); + } + + SECTION("R^2 *= B") + { + std::string_view data = R"( +R^2 x; x *= false; +)"; - ASTSymbolTableBuilder{*ast}; - ASTNodeDataTypeBuilder{*ast}; + std::string_view result = R"( +(root:ASTNodeListProcessor) + `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, TinyVector<2ul, double>, bool>) + +-(language::name:x:NameProcessor) + `-(language::false_kw:ValueProcessor) +)"; + + CHECK_AST(data, result); + } - ASTNodeDeclarationToAffectationConverter{*ast}; - ASTNodeTypeCleaner<language::declaration>{*ast}; + SECTION("R^3 *= B") + { + std::string_view data = R"( +B b; R^3 x; x *= b; +)"; + + std::string_view result = R"( +(root:ASTNodeListProcessor) + `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, TinyVector<3ul, double>, bool>) + +-(language::name:x:NameProcessor) + `-(language::name:b:NameProcessor) +)"; - REQUIRE_THROWS_AS(ASTNodeExpressionBuilder{*ast}, parse_error); + CHECK_AST(data, result); } } @@ -715,24 +1097,6 @@ R x=1; x/=2.3; CHECK_AST(data, result); } - - SECTION("string /= string") - { - std::string_view data = R"( -string s="foo"; s/="bar"; -)"; - - string_input input{data, "test.pgs"}; - auto ast = ASTBuilder::build(input); - - ASTSymbolTableBuilder{*ast}; - ASTNodeDataTypeBuilder{*ast}; - - ASTNodeDeclarationToAffectationConverter{*ast}; - ASTNodeTypeCleaner<language::declaration>{*ast}; - - REQUIRE_THROWS_AS(ASTNodeExpressionBuilder{*ast}, parse_error); - } } SECTION("Errors") @@ -776,5 +1140,207 @@ string s="foo"; s/="bar"; REQUIRE_THROWS_WITH(ASTNodeAffectationExpressionBuilder{*ast}, "invalid implicit conversion: undefined -> string"); } + + SECTION("Invalid string affectation operator") + { + SECTION("string -= string") + { + std::string_view data = R"( +string s="foo"; s-="bar"; +)"; + + std::string error_message = "invalid operator for string affectation"; + + CHECK_AST_THROWS_WITH(data, error_message); + } + + SECTION("string *= Z") + { + std::string_view data = R"( +string s="foo"; s*=2; +)"; + + std::string error_message = "invalid operator for string affectation"; + + CHECK_AST_THROWS_WITH(data, error_message); + } + + SECTION("string /= string") + { + std::string_view data = R"( +string s="foo"; s/="bar"; +)"; + + std::string error_message = "invalid operator for string affectation"; + + CHECK_AST_THROWS_WITH(data, error_message); + } + } + + SECTION("Invalid R^n -> R^m affectation") + { + SECTION("R^3 <- R^1") + { + std::string_view data = R"( +R^3 x; R^1 y; x = y; +)"; + + std::string error_message = "incompatible dimensions in affectation"; + + CHECK_AST_THROWS_WITH(data, error_message); + } + + SECTION("R^3 <- R^2") + { + std::string_view data = R"( +R^3 x; R^2 y; x = y; +)"; + + std::string error_message = "incompatible dimensions in affectation"; + + CHECK_AST_THROWS_WITH(data, error_message); + } + + SECTION("R^2 <- R^1") + { + std::string_view data = R"( +R^2 x; R^1 y; x = y; +)"; + + std::string error_message = "incompatible dimensions in affectation"; + + CHECK_AST_THROWS_WITH(data, error_message); + } + + SECTION("R^2 <- R^3") + { + std::string_view data = R"( +R^2 x; R^3 y; x = y; +)"; + + std::string error_message = "incompatible dimensions in affectation"; + + CHECK_AST_THROWS_WITH(data, error_message); + } + + SECTION("R^1 <- R^2") + { + std::string_view data = R"( +R^1 x; R^2 y; x = y; +)"; + + std::string error_message = "incompatible dimensions in affectation"; + + CHECK_AST_THROWS_WITH(data, error_message); + } + + SECTION("R^1 <- R^3") + { + std::string_view data = R"( +R^1 x; R^2 y; x = y; +)"; + + std::string error_message = "incompatible dimensions in affectation"; + + CHECK_AST_THROWS_WITH(data, error_message); + } + } + + SECTION("Invalid Z -> R^m affectation [non-zero]") + { + SECTION("R^3 <- Z") + { + std::string_view data = R"( +R^3 x = 3; +)"; + + std::string error_message = "invalid implicit conversion: Z -> R^3"; + + CHECK_AST_THROWS_WITH(data, error_message); + } + + SECTION("R^2 <- Z") + { + std::string_view data = R"( +R^2 x = 2; +)"; + + std::string error_message = "invalid implicit conversion: Z -> R^2"; + + CHECK_AST_THROWS_WITH(data, error_message); + } + } + + SECTION("Invalid R^d -> R^d affectation operator") + { + SECTION("R^3 <- R^3") + { + std::string_view data = R"( +R^3 x; R^3 y; x /= y; +)"; + + std::string error_message = "invalid affectation operator for R^3"; + + CHECK_AST_THROWS_WITH(data, error_message); + } + + SECTION("R^2 <- R^2") + { + std::string_view data = R"( +R^2 x; R^2 y; x /= y; +)"; + + std::string error_message = "invalid affectation operator for R^2"; + + CHECK_AST_THROWS_WITH(data, error_message); + } + + SECTION("R^1 <- R^1") + { + std::string_view data = R"( +R^1 x; R^1 y; x /= y; +)"; + + std::string error_message = "invalid affectation operator for R^1"; + + CHECK_AST_THROWS_WITH(data, error_message); + } + } + + SECTION("Invalid R^d -> R^d *= operand") + { + SECTION("R^3 <- R^3") + { + std::string_view data = R"( +R^3 x; R^3 y; x *= y; +)"; + + std::string error_message = "expecting scalar operand type"; + + CHECK_AST_THROWS_WITH(data, error_message); + } + + SECTION("R^2 <- R^2") + { + std::string_view data = R"( +R^2 x; R^2 y; x *= y; +)"; + + std::string error_message = "expecting scalar operand type"; + + CHECK_AST_THROWS_WITH(data, error_message); + } + + SECTION("R^1 <- R^1") + { + std::string_view data = R"( +R^1 x; R^1 y; x *= y; +)"; + + std::string error_message = "expecting scalar operand type"; + + CHECK_AST_THROWS_WITH(data, error_message); + } + } } }