#include <catch2/catch_test_macros.hpp> #include <catch2/matchers/catch_matchers_all.hpp> #include <language/ast/ASTBuilder.hpp> #include <language/ast/ASTModulesImporter.hpp> #include <language/ast/ASTNodeDataTypeBuilder.hpp> #include <language/ast/ASTNodeTypeCleaner.hpp> #include <language/ast/ASTSymbolTableBuilder.hpp> #include <language/utils/ASTNodeDataTypeTraits.hpp> #include <language/utils/ASTPrinter.hpp> #include <language/utils/ParseError.hpp> #include <language/utils/TypeDescriptor.hpp> #include <utils/Exceptions.hpp> #include <pegtl/string_input.hpp> #define CHECK_AST(data, expected_output) \ { \ 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_output)>, std::string_view>); \ \ TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; \ auto ast = ASTBuilder::build(input); \ \ ASTModulesImporter{*ast}; \ ASTNodeTypeCleaner<language::import_instruction>{*ast}; \ \ ASTSymbolTableBuilder{*ast}; \ ASTNodeDataTypeBuilder{*ast}; \ \ std::stringstream ast_output; \ ast_output << '\n' << ASTPrinter{*ast, ASTPrinter::Format::raw, {ASTPrinter::Info::data_type}}; \ \ REQUIRE(ast_output.str() == expected_output); \ } template <> inline ASTNodeDataType ast_node_data_type_from<std::shared_ptr<const double>> = ASTNodeDataType::build<ASTNodeDataType::type_id_t>("builtin_t"); const auto builtin_data_type = ast_node_data_type_from<std::shared_ptr<const double>>; #define CHECK_AST_WITH_BUILTIN(data, expected_output) \ { \ 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_output)>, std::string_view>); \ \ TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; \ auto ast = ASTBuilder::build(input); \ \ SymbolTable& symbol_table = *ast->m_symbol_table; \ auto [i_symbol, success] = symbol_table.add(builtin_data_type.nameOfTypeId(), ast->begin()); \ if (not success) { \ throw UnexpectedError("cannot add '" + builtin_data_type.nameOfTypeId() + "' type for testing"); \ } \ \ i_symbol->attributes().setDataType(ASTNodeDataType::build<ASTNodeDataType::type_name_id_t>()); \ i_symbol->attributes().setIsInitialized(); \ i_symbol->attributes().value() = symbol_table.typeEmbedderTable().size(); \ symbol_table.typeEmbedderTable().add(std::make_shared<TypeDescriptor>(builtin_data_type.nameOfTypeId())); \ \ ASTSymbolTableBuilder{*ast}; \ ASTNodeDataTypeBuilder{*ast}; \ \ std::stringstream ast_output; \ ast_output << '\n' << ASTPrinter{*ast, ASTPrinter::Format::raw, {ASTPrinter::Info::data_type}}; \ \ REQUIRE(ast_output.str() == expected_output); \ } // clazy:excludeall=non-pod-global-static TEST_CASE("ASTNodeDataTypeBuilder", "[language]") { SECTION("module") { std::string_view data = R"( import a_module_name; )"; std::string_view result = R"( (root:void) `-(language::import_instruction:void) `-(language::module_name:string) )"; TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; auto ast = ASTBuilder::build(input); ASTSymbolTableBuilder{*ast}; ASTNodeDataTypeBuilder{*ast}; std::stringstream ast_output; ast_output << '\n' << ASTPrinter{*ast, ASTPrinter::Format::raw, {ASTPrinter::Info::data_type}}; REQUIRE(ast_output.str() == result); } SECTION("integer") { std::string_view data = R"( 1; )"; std::string_view result = R"( (root:void) `-(language::integer:1:Z) )"; CHECK_AST(data, result); } SECTION("real 1") { std::string_view data = R"( 1.3; )"; std::string_view result = R"( (root:void) `-(language::real:1.3:R) )"; CHECK_AST(data, result); } SECTION("real 2") { std::string_view data = R"( .5; )"; std::string_view result = R"( (root:void) `-(language::real:.5:R) )"; CHECK_AST(data, result); } SECTION("real 3") { std::string_view data = R"( 5e-1; )"; std::string_view result = R"( (root:void) `-(language::real:5e-1:R) )"; CHECK_AST(data, result); } SECTION("real 4") { std::string_view data = R"( 2e+1; )"; std::string_view result = R"( (root:void) `-(language::real:2e+1:R) )"; CHECK_AST(data, result); } SECTION("real 5") { std::string_view data = R"( 2e1; )"; std::string_view result = R"( (root:void) `-(language::real:2e1:R) )"; CHECK_AST(data, result); } SECTION("real 6") { std::string_view data = R"( 5.e-1; )"; std::string_view result = R"( (root:void) `-(language::real:5.e-1:R) )"; CHECK_AST(data, result); } SECTION("real 7") { std::string_view data = R"( 5.e+1; )"; std::string_view result = R"( (root:void) `-(language::real:5.e+1:R) )"; CHECK_AST(data, result); } SECTION("real 8") { std::string_view data = R"( 3.4e+1; )"; std::string_view result = R"( (root:void) `-(language::real:3.4e+1:R) )"; CHECK_AST(data, result); } SECTION("real 9") { std::string_view data = R"( .231e1; )"; std::string_view result = R"( (root:void) `-(language::real:.231e1:R) )"; CHECK_AST(data, result); } SECTION("true") { std::string_view data = R"( true; )"; std::string_view result = R"( (root:void) `-(language::true_kw:B) )"; CHECK_AST(data, result); } SECTION("false") { std::string_view data = R"( false; )"; std::string_view result = R"( (root:void) `-(language::false_kw:B) )"; CHECK_AST(data, result); } SECTION("block") { std::string_view data = R"( { 1; 2.3; } )"; std::string_view result = R"( (root:void) `-(language::block:void) +-(language::integer:1:Z) `-(language::real:2.3:R) )"; CHECK_AST(data, result); } SECTION("compound") { SECTION("declaration") { std::string_view data = R"( let (x,b,n,s) : R*B*N*string; )"; std::string_view result = R"( (root:void) `-(language::var_declaration:void) +-(language::name_list:R*B*N*string) | +-(language::name:x:R) | +-(language::name:b:B) | +-(language::name:n:N) | `-(language::name:s:string) `-(language::type_expression:R*B*N*string) +-(language::R_set:R) +-(language::B_set:B) +-(language::N_set:N) `-(language::string_type:string) )"; CHECK_AST(data, result); } SECTION("errors") { SECTION("invalid array subscript") { std::string_view data = R"( let x : R; x[2]; )"; TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; auto ast = ASTBuilder::build(input); ASTSymbolTableBuilder{*ast}; REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, "invalid subscript expression: R cannot be indexed"); } SECTION("invalid R^d subscript index list") { std::string_view data = R"( let x : R^2; x[2,2]; )"; TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; auto ast = ASTBuilder::build(input); ASTSymbolTableBuilder{*ast}; REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, "invalid index type: R^2 requires a single integer"); } SECTION("invalid R^dxd subscript index list 1") { std::string_view data = R"( let x : R^2x2; x[2]; )"; TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; auto ast = ASTBuilder::build(input); ASTSymbolTableBuilder{*ast}; REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, "invalid index type: R^2x2 requires two integers"); } SECTION("invalid R^dxd subscript index list 2") { std::string_view data = R"( let x : R^2x2; x[2,3,1]; )"; TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; auto ast = ASTBuilder::build(input); ASTSymbolTableBuilder{*ast}; REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, "invalid index type: R^2x2 requires two integers"); } SECTION("too many variables") { std::string_view data = R"( let (x,b,n,s,t) : R*B*N*string; )"; TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; auto ast = ASTBuilder::build(input); ASTSymbolTableBuilder{*ast}; REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, "number of product spaces (4) R*B*N*string differs from " "number of variables (5) (x,b,n,s,t) "); } SECTION("too few variables") { std::string_view data = R"( let (x,b,n) : R*B*N*string; )"; TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; auto ast = ASTBuilder::build(input); ASTSymbolTableBuilder{*ast}; REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, "number of product spaces (4) R*B*N*string differs from " "number of variables (3) (x,b,n) "); } SECTION("unexpected variable list") { std::string_view data = R"( let (x,y) : R; )"; TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; auto ast = ASTBuilder::build(input); ASTSymbolTableBuilder{*ast}; REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, "unexpected variable list for single space"); } SECTION("invalid R-list -> R^d") { std::string_view data = R"( let square : R -> R^3, x -> (x, 2); )"; TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; auto ast = ASTBuilder::build(input); ASTSymbolTableBuilder{*ast}; REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, "expecting 3 scalar expressions or an R^3, found 2 scalar expressions"); } } } SECTION("let declaration") { SECTION("tuples") { SECTION("B tuples") { std::string_view data = R"( let t : (B), t = (true, false); )"; std::string_view result = R"( (root:void) `-(language::var_declaration:void) +-(language::name:t:(B...)) +-(language::tuple_type_specifier:(B...)) | `-(language::B_set:B) +-(language::name:t:(B...)) `-(language::expression_list:B*B) +-(language::true_kw:B) `-(language::false_kw:B) )"; CHECK_AST(data, result); } SECTION("N tuples") { std::string_view data = R"( let t : (N), t = (1, 2, 3, 5); )"; std::string_view result = R"( (root:void) `-(language::var_declaration:void) +-(language::name:t:(N...)) +-(language::tuple_type_specifier:(N...)) | `-(language::N_set:N) +-(language::name:t:(N...)) `-(language::expression_list:Z*Z*Z*Z) +-(language::integer:1:Z) +-(language::integer:2:Z) +-(language::integer:3:Z) `-(language::integer:5:Z) )"; CHECK_AST(data, result); } SECTION("Z tuples") { std::string_view data = R"( let n : N, n = 3; let t : (Z), t = (2, n, true); )"; std::string_view result = R"( (root:void) +-(language::var_declaration:void) | +-(language::name:n:N) | +-(language::N_set:N) | +-(language::name:n:N) | `-(language::integer:3:Z) `-(language::var_declaration:void) +-(language::name:t:(Z...)) +-(language::tuple_type_specifier:(Z...)) | `-(language::Z_set:Z) +-(language::name:t:(Z...)) `-(language::expression_list:Z*N*B) +-(language::integer:2:Z) +-(language::name:n:N) `-(language::true_kw:B) )"; CHECK_AST(data, result); } SECTION("R tuples") { std::string_view data = R"( let t : (R), t = (2, 3.1, 5); )"; std::string_view result = R"( (root:void) `-(language::var_declaration:void) +-(language::name:t:(R...)) +-(language::tuple_type_specifier:(R...)) | `-(language::R_set:R) +-(language::name:t:(R...)) `-(language::expression_list:Z*R*Z) +-(language::integer:2:Z) +-(language::real:3.1:R) `-(language::integer:5:Z) )"; CHECK_AST(data, result); } SECTION("R^d tuples") { std::string_view data = R"( let a : R^2, a = (2,3.1); let t1 : (R^2), t1 = (a, (1,2), 0); let t2 : (R^3), t2 = (0, 0); )"; std::string_view result = R"( (root:void) +-(language::var_declaration:void) | +-(language::name:a:R^2) | +-(language::vector_type:R^2) | | +-(language::R_set:R) | | `-(language::integer:2:Z) | +-(language::name:a:R^2) | `-(language::expression_list:Z*R) | +-(language::integer:2:Z) | `-(language::real:3.1:R) +-(language::var_declaration:void) | +-(language::name:t1:(R^2...)) | +-(language::tuple_type_specifier:(R^2...)) | | `-(language::vector_type:R^2) | | +-(language::R_set:R) | | `-(language::integer:2:Z) | +-(language::name:t1:(R^2...)) | `-(language::expression_list:R^2*(Z*Z)*Z) | +-(language::name:a:R^2) | +-(language::tuple_expression:Z*Z) | | +-(language::integer:1:Z) | | `-(language::integer:2:Z) | `-(language::integer:0:Z) `-(language::var_declaration:void) +-(language::name:t2:(R^3...)) +-(language::tuple_type_specifier:(R^3...)) | `-(language::vector_type:R^3) | +-(language::R_set:R) | `-(language::integer:3:Z) +-(language::name:t2:(R^3...)) `-(language::expression_list:Z*Z) +-(language::integer:0:Z) `-(language::integer:0:Z) )"; CHECK_AST(data, result); } SECTION("R^dxd tuples") { std::string_view data = R"( let a : R^2x2, a = (2, 3.1, -1.2, 4); let t1 : (R^2x2), t1 = (a, (1,2,1,3), 0); let t2 : (R^3x3), t2 = (0, 0); )"; std::string_view result = R"( (root:void) +-(language::var_declaration:void) | +-(language::name:a:R^2x2) | +-(language::matrix_type:R^2x2) | | +-(language::R_set:R) | | +-(language::integer:2:Z) | | `-(language::integer:2:Z) | +-(language::name:a:R^2x2) | `-(language::expression_list:Z*R*R*Z) | +-(language::integer:2:Z) | +-(language::real:3.1:R) | +-(language::unary_minus:R) | | `-(language::real:1.2:R) | `-(language::integer:4:Z) +-(language::var_declaration:void) | +-(language::name:t1:(R^2x2...)) | +-(language::tuple_type_specifier:(R^2x2...)) | | `-(language::matrix_type:R^2x2) | | +-(language::R_set:R) | | +-(language::integer:2:Z) | | `-(language::integer:2:Z) | +-(language::name:t1:(R^2x2...)) | `-(language::expression_list:R^2x2*(Z*Z*Z*Z)*Z) | +-(language::name:a:R^2x2) | +-(language::tuple_expression:Z*Z*Z*Z) | | +-(language::integer:1:Z) | | +-(language::integer:2:Z) | | +-(language::integer:1:Z) | | `-(language::integer:3:Z) | `-(language::integer:0:Z) `-(language::var_declaration:void) +-(language::name:t2:(R^3x3...)) +-(language::tuple_type_specifier:(R^3x3...)) | `-(language::matrix_type:R^3x3) | +-(language::R_set:R) | +-(language::integer:3:Z) | `-(language::integer:3:Z) +-(language::name:t2:(R^3x3...)) `-(language::expression_list:Z*Z) +-(language::integer:0:Z) `-(language::integer:0:Z) )"; CHECK_AST(data, result); } SECTION("string tuples") { std::string_view data = R"( let t : (string), t = ("foo", "bar"); )"; std::string_view result = R"( (root:void) `-(language::var_declaration:void) +-(language::name:t:(string...)) +-(language::tuple_type_specifier:(string...)) | `-(language::string_type:string) +-(language::name:t:(string...)) `-(language::expression_list:string*string) +-(language::literal:"foo":string) `-(language::literal:"bar":string) )"; CHECK_AST(data, result); } SECTION("type_id tuples") { std::string_view data = R"( // invalid conversion just checking grammar let t : (builtin_t), t= (1,2,3); )"; std::string_view result = R"( (root:void) `-(language::var_declaration:void) +-(language::name:t:(builtin_t...)) +-(language::tuple_type_specifier:(builtin_t...)) | `-(language::type_name_id:builtin_t) +-(language::name:t:(builtin_t...)) `-(language::expression_list:Z*Z*Z) +-(language::integer:1:Z) +-(language::integer:2:Z) `-(language::integer:3:Z) )"; CHECK_AST_WITH_BUILTIN(data, result); } SECTION("errors") { SECTION("type_id") { std::string_view data = R"( let t : builtin_t; )"; TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; auto ast = ASTBuilder::build(input); ASTSymbolTableBuilder{*ast}; REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, "undefined type identifier"); } SECTION("type_id 2") { std::string_view data = R"( let a: R, a = 3; let t : a; )"; TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; auto ast = ASTBuilder::build(input); ASTSymbolTableBuilder{*ast}; REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, "invalid type identifier, 'a' was previously defined as a 'R'"); } SECTION("type_id tuples") { std::string_view data = R"( let t : (builtin_t); )"; TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; auto ast = ASTBuilder::build(input); ASTSymbolTableBuilder{*ast}; REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, "undefined type identifier"); } SECTION("type_id tuples 2") { std::string_view data = R"( let a: R, a = 3; let t : (a); )"; TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; auto ast = ASTBuilder::build(input); ASTSymbolTableBuilder{*ast}; REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, "invalid type identifier, 'a' was previously defined as a 'R'"); } } } SECTION("R^d-functions") { SECTION("vector function") { std::string_view data = R"( let double : R^2 -> R^2, x -> 2*x; )"; std::string_view result = R"( (root:void) `-(language::fct_declaration:void) `-(language::name:double:function) )"; CHECK_AST(data, result); } SECTION("R-list -> R^d") { std::string_view data = R"( let square : R -> R^2, x -> (x, x*x); )"; std::string_view result = R"( (root:void) `-(language::fct_declaration:void) `-(language::name:square:function) )"; CHECK_AST(data, result); } } SECTION("R^dxd-functions") { SECTION("matrix function") { std::string_view data = R"( let double : R^2x2 -> R^2x2, x -> 2*x; )"; std::string_view result = R"( (root:void) `-(language::fct_declaration:void) `-(language::name:double:function) )"; CHECK_AST(data, result); } SECTION("matrix vector product") { std::string_view data = R"( let prod : R^2x2*R^2 -> R^2, (A,x) -> A*x; )"; std::string_view result = R"( (root:void) `-(language::fct_declaration:void) `-(language::name:prod:function) )"; CHECK_AST(data, result); } SECTION("matrix function") { std::string_view data = R"( let det : R^2x2 -> R, x -> x[0,0]*x[1,1]-x[1,0]*x[0,1]; )"; std::string_view result = R"( (root:void) `-(language::fct_declaration:void) `-(language::name:det:function) )"; CHECK_AST(data, result); } SECTION("R-list -> R^dxd") { std::string_view data = R"( let f : R -> R^2x2, x -> (x, x*x, 2-x, 0); )"; std::string_view result = R"( (root:void) `-(language::fct_declaration:void) `-(language::name:f:function) )"; CHECK_AST(data, result); } SECTION("R^d*R^d -> R^dxd") { std::string_view data = R"( let f : R^2*R^2 -> R^2x2, (x,y) -> (x[0], y[0], x[1], y[1]); )"; std::string_view result = R"( (root:void) `-(language::fct_declaration:void) `-(language::name:f:function) )"; CHECK_AST(data, result); } } SECTION("R-functions") { SECTION("multiple variable") { std::string_view data = R"( let weird : R^2*R -> R, (x,y) -> x[0]-y*x[1]; )"; std::string_view result = R"( (root:void) `-(language::fct_declaration:void) `-(language::name:weird:function) )"; CHECK_AST(data, result); } SECTION("multiple variable") { std::string_view data = R"( let substract : R*R -> R, (x,y) -> x-y; )"; std::string_view result = R"( (root:void) `-(language::fct_declaration:void) `-(language::name:substract:function) )"; CHECK_AST(data, result); } SECTION("multiple values") { std::string_view data = R"( let square : R -> R*R, x -> (x,x*x); )"; std::string_view result = R"( (root:void) `-(language::fct_declaration:void) `-(language::name:square:function) )"; CHECK_AST(data, result); } SECTION("name -> expression") { std::string_view data = R"( let f : R -> R, x -> x; )"; std::string_view result = R"( (root:void) `-(language::fct_declaration:void) `-(language::name:f:function) )"; CHECK_AST(data, result); } SECTION("name list -> expression") { std::string_view data = R"( let f : R -> R, (x) -> (x+2)/2; )"; std::string_view result = R"( (root:void) `-(language::fct_declaration:void) `-(language::name:f:function) )"; CHECK_AST(data, result); } SECTION("name -> expression list") { std::string_view data = R"( let f : R -> R, x -> (x*x); )"; std::string_view result = R"( (root:void) `-(language::fct_declaration:void) `-(language::name:f:function) )"; CHECK_AST(data, result); } SECTION("name list -> expression list") { std::string_view data = R"( let f : R -> R, (x) -> (x+1); )"; std::string_view result = R"( (root:void) `-(language::fct_declaration:void) `-(language::name:f:function) )"; CHECK_AST(data, result); } } SECTION("Z-functions") { std::string_view data = R"( let f : Z -> Z, z -> z-1; )"; std::string_view result = R"( (root:void) `-(language::fct_declaration:void) `-(language::name:f:function) )"; CHECK_AST(data, result); } SECTION("N-functions") { std::string_view data = R"( let f : N -> N, x -> (x+1)/2; )"; std::string_view result = R"( (root:void) `-(language::fct_declaration:void) `-(language::name:f:function) )"; CHECK_AST(data, result); } SECTION("B-functions") { std::string_view data = R"( let f : N*B -> B, (n,b) -> (n>3) and b; )"; std::string_view result = R"( (root:void) `-(language::fct_declaration:void) `-(language::name:f:function) )"; CHECK_AST(data, result); } SECTION("string-functions") { std::string_view data = R"( let cat : string*N -> string, (s,n) -> s+n; )"; std::string_view result = R"( (root:void) `-(language::fct_declaration:void) `-(language::name:cat:function) )"; CHECK_AST(data, result); } SECTION("builtin-functions") { std::string_view data = R"( let foo : builtin_t*N -> builtin_t, (b,n) -> b; )"; std::string_view result = R"( (root:void) `-(language::fct_declaration:void) `-(language::name:foo:function) )"; CHECK_AST_WITH_BUILTIN(data, result); } SECTION("errors") { SECTION("wrong parameter number") { std::string_view data = R"( let f : R*Z -> B, x -> 3; )"; TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; auto ast = ASTBuilder::build(input); ASTSymbolTableBuilder{*ast}; REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, "number of product spaces (2) R*Z differs from number of variables (1) x"); } SECTION("wrong parameter number 2") { std::string_view data = R"( let f : R -> B, (x,y) -> 3; )"; TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; auto ast = ASTBuilder::build(input); ASTSymbolTableBuilder{*ast}; REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, "number of product spaces (1) R differs from number of variables (2) (x,y) "); } SECTION("wrong image size") { std::string_view data = R"( let f : R*Z -> B, (x,z) -> (3, x); )"; TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; auto ast = ASTBuilder::build(input); ASTSymbolTableBuilder{*ast}; REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, "number of image spaces (1) B differs from number of expressions (2) (3, x)"); } SECTION("wrong image size 2") { std::string_view data = R"( let f : R -> R*R, x -> x*x*x; )"; TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; auto ast = ASTBuilder::build(input); ASTModulesImporter{*ast}; ASTNodeTypeCleaner<language::import_instruction>{*ast}; ASTSymbolTableBuilder{*ast}; REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, "number of image spaces (2) R*R differs from number of expressions (1) x*x*x"); } SECTION("wrong image size 3") { std::string_view data = R"( let f : R -> R^2x2, x -> (x, 2*x, 2); )"; TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; auto ast = ASTBuilder::build(input); ASTModulesImporter{*ast}; ASTNodeTypeCleaner<language::import_instruction>{*ast}; ASTSymbolTableBuilder{*ast}; REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, "expecting 4 scalar expressions or an R^2x2, found 3 scalar expressions"); } SECTION("undefined type identifier") { std::string_view data = R"( let x:X; )"; TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; auto ast = ASTBuilder::build(input); ASTSymbolTableBuilder{*ast}; REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, "undefined type identifier"); } SECTION("undefined type identifier") { std::string_view data = R"( let X:R, X = 3; let x:X; )"; TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; auto ast = ASTBuilder::build(input); ASTSymbolTableBuilder{*ast}; REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, "invalid type identifier, 'X' was previously defined as a 'R'"); } SECTION("undefined image type identifier") { std::string_view data = R"( let f: R -> X, x -> x; )"; TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; auto ast = ASTBuilder::build(input); ASTSymbolTableBuilder{*ast}; REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, "undefined type identifier"); } SECTION("invalid image type identifier") { std::string_view data = R"( let X: R, X = 3; let f: R -> X, x -> x; )"; TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; auto ast = ASTBuilder::build(input); ASTSymbolTableBuilder{*ast}; REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, "invalid type identifier, 'X' was previously defined as a 'R'"); } } } SECTION("function evaluation") { SECTION("R^d-functions") { SECTION("single argument") { std::string_view data = R"( let f : R^2 -> R^2, x -> (x[0]+1, x[1]-2); let x : R^2, x = (1,2); x = f(x); )"; std::string_view result = R"( (root:void) +-(language::fct_declaration:void) | `-(language::name:f:function) +-(language::var_declaration:void) | +-(language::name:x:R^2) | +-(language::vector_type:R^2) | | +-(language::R_set:R) | | `-(language::integer:2:Z) | +-(language::name:x:R^2) | `-(language::expression_list:Z*Z) | +-(language::integer:1:Z) | `-(language::integer:2:Z) `-(language::eq_op:void) +-(language::name:x:R^2) `-(language::function_evaluation:R^2) +-(language::name:f:function) `-(language::name:x:R^2) )"; CHECK_AST(data, result); } } SECTION("R-functions") { SECTION("single argument") { std::string_view data = R"( let incr : R -> R, x -> x+1; let x : R, x = incr(3); )"; std::string_view result = R"( (root:void) +-(language::fct_declaration:void) | `-(language::name:incr:function) `-(language::var_declaration:void) +-(language::name:x:R) +-(language::R_set:R) +-(language::name:x:R) `-(language::function_evaluation:R) +-(language::name:incr:function) `-(language::integer:3:Z) )"; CHECK_AST(data, result); } SECTION("multiple variable") { std::string_view data = R"( let substract : R*R -> R, (x,y) -> x-y; let diff : R, diff = substract(3,2); )"; std::string_view result = R"( (root:void) +-(language::fct_declaration:void) | `-(language::name:substract:function) `-(language::var_declaration:void) +-(language::name:diff:R) +-(language::R_set:R) +-(language::name:diff:R) `-(language::function_evaluation:R) +-(language::name:substract:function) `-(language::function_argument_list:Z*Z) +-(language::integer:3:Z) `-(language::integer:2:Z) )"; CHECK_AST(data, result); } } SECTION("Z-functions") { std::string_view data = R"( let incr : Z -> Z, z -> z+1; let z : Z, z = incr(3); )"; std::string_view result = R"( (root:void) +-(language::fct_declaration:void) | `-(language::name:incr:function) `-(language::var_declaration:void) +-(language::name:z:Z) +-(language::Z_set:Z) +-(language::name:z:Z) `-(language::function_evaluation:Z) +-(language::name:incr:function) `-(language::integer:3:Z) )"; CHECK_AST(data, result); } SECTION("N-function") { std::string_view data = R"( let double : N -> N, n -> 2*n; let n : N, n = double(3); )"; std::string_view result = R"( (root:void) +-(language::fct_declaration:void) | `-(language::name:double:function) `-(language::var_declaration:void) +-(language::name:n:N) +-(language::N_set:N) +-(language::name:n:N) `-(language::function_evaluation:N) +-(language::name:double:function) `-(language::integer:3:Z) )"; CHECK_AST(data, result); } SECTION("B-function") { std::string_view data = R"( let greater_than_2 : R -> B, x -> x>2; let b : B, b = greater_than_2(3); )"; std::string_view result = R"( (root:void) +-(language::fct_declaration:void) | `-(language::name:greater_than_2:function) `-(language::var_declaration:void) +-(language::name:b:B) +-(language::B_set:B) +-(language::name:b:B) `-(language::function_evaluation:B) +-(language::name:greater_than_2:function) `-(language::integer:3:Z) )"; CHECK_AST(data, result); } SECTION("string-function") { std::string_view data = R"( let cat : string*string -> string, (s,t) -> s+t; let s : string, s = cat("foo", "bar"); )"; std::string_view result = R"( (root:void) +-(language::fct_declaration:void) | `-(language::name:cat:function) `-(language::var_declaration:void) +-(language::name:s:string) +-(language::string_type:string) +-(language::name:s:string) `-(language::function_evaluation:string) +-(language::name:cat:function) `-(language::function_argument_list:string*string) +-(language::literal:"foo":string) `-(language::literal:"bar":string) )"; CHECK_AST(data, result); } SECTION("bultin_t-function") { std::string_view data = R"( let foo : builtin_t*N -> builtin_t, (b,n) -> b; let b0: builtin_t; let b : builtin_t, b = foo(b0, 1); )"; std::string_view result = R"( (root:void) +-(language::fct_declaration:void) | `-(language::name:foo:function) +-(language::var_declaration:void) | +-(language::name:b0:builtin_t) | `-(language::type_name_id:builtin_t) `-(language::var_declaration:void) +-(language::name:b:builtin_t) +-(language::type_name_id:builtin_t) +-(language::name:b:builtin_t) `-(language::function_evaluation:builtin_t) +-(language::name:foo:function) `-(language::function_argument_list:builtin_t*Z) +-(language::name:b0:builtin_t) `-(language::integer:1:Z) )"; CHECK_AST_WITH_BUILTIN(data, result); } SECTION("compound return function") { std::string_view data = R"( let x_x2 : R -> R*R, x -> (x,x*x); let (x,x2) : R*R, (x,x2) = x_x2(3); )"; std::string_view result = R"( (root:void) +-(language::fct_declaration:void) | `-(language::name:x_x2:function) `-(language::var_declaration:void) +-(language::name_list:R*R) | +-(language::name:x:R) | `-(language::name:x2:R) +-(language::type_expression:R*R) | +-(language::R_set:R) | `-(language::R_set:R) +-(language::name_list:R*R) | +-(language::name:x:R) | `-(language::name:x2:R) `-(language::function_evaluation:R*R) +-(language::name:x_x2:function) `-(language::integer:3:Z) )"; CHECK_AST(data, result); } SECTION("errors") { SECTION("not a function") { std::string_view data = R"( let not_a_function : R, not_a_function = 3; not_a_function(2,3); )"; TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; auto ast = ASTBuilder::build(input); ASTSymbolTableBuilder{*ast}; REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, "invalid function call\n" "note: 'not_a_function' (type: R) is not a function!"); } } } SECTION("ostream") { std::string_view data = R"( cout << "cout\n"; cerr << "cerr\n"; clog << "clog\n"; )"; std::string_view result = R"( (root:void) +-(language::cout_kw:void) | `-(language::literal:"cout\n":string) +-(language::cerr_kw:void) | `-(language::literal:"cerr\n":string) `-(language::clog_kw:void) `-(language::literal:"clog\n":string) )"; CHECK_AST(data, result); } SECTION("for-statement") { std::string_view data = R"( for (let i : N, i=0; i<3; ++i){ cout << i << "\n"; } )"; std::string_view result = R"( (root:void) `-(language::for_statement:void) +-(language::var_declaration:void) | +-(language::name:i:N) | +-(language::N_set:N) | +-(language::name:i:N) | `-(language::integer:0:Z) +-(language::lesser_op:B) | +-(language::name:i:N) | `-(language::integer:3:Z) +-(language::unary_plusplus:N) | `-(language::name:i:N) `-(language::cout_kw:void) +-(language::name:i:N) `-(language::literal:"\n":string) )"; CHECK_AST(data, result); } SECTION("B set") { std::string_view data = R"( let b:B; )"; std::string_view result = R"( (root:void) `-(language::var_declaration:void) +-(language::name:b:B) `-(language::B_set:B) )"; CHECK_AST(data, result); } SECTION("N set") { std::string_view data = R"( let n :N; )"; std::string_view result = R"( (root:void) `-(language::var_declaration:void) +-(language::name:n:N) `-(language::N_set:N) )"; CHECK_AST(data, result); } SECTION("Z set") { std::string_view data = R"( let z:Z; )"; std::string_view result = R"( (root:void) `-(language::var_declaration:void) +-(language::name:z:Z) `-(language::Z_set:Z) )"; CHECK_AST(data, result); } SECTION("R set") { std::string_view data = R"( let r:R; )"; std::string_view result = R"( (root:void) `-(language::var_declaration:void) +-(language::name:r:R) `-(language::R_set:R) )"; CHECK_AST(data, result); } SECTION("string") { std::string_view data = R"( let s: string; )"; std::string_view result = R"( (root:void) `-(language::var_declaration:void) +-(language::name:s:string) `-(language::string_type:string) )"; CHECK_AST(data, result); } SECTION("type_id") { std::string_view data = R"( // invalid conversion just checking grammar let t : builtin_t, t= 1; )"; std::string_view result = R"( (root:void) `-(language::var_declaration:void) +-(language::name:t:builtin_t) +-(language::type_name_id:builtin_t) +-(language::name:t:builtin_t) `-(language::integer:1:Z) )"; CHECK_AST_WITH_BUILTIN(data, result); } SECTION("continue") { std::string_view data = R"( continue; )"; std::string_view result = R"( (root:void) `-(language::continue_kw:void) )"; CHECK_AST(data, result); } SECTION("break") { std::string_view data = R"( break; )"; std::string_view result = R"( (root:void) `-(language::break_kw:void) )"; CHECK_AST(data, result); } SECTION("eq_op") { std::string_view data = R"( let a:N; a = 1; )"; std::string_view result = R"( (root:void) +-(language::var_declaration:void) | +-(language::name:a:N) | `-(language::N_set:N) `-(language::eq_op:void) +-(language::name:a:N) `-(language::integer:1:Z) )"; CHECK_AST(data, result); } SECTION("multiplyeq_op") { std::string_view data = R"( let a:N, a = 1; a *= 1.2; )"; std::string_view result = R"( (root:void) +-(language::var_declaration:void) | +-(language::name:a:N) | +-(language::N_set:N) | +-(language::name:a:N) | `-(language::integer:1:Z) `-(language::multiplyeq_op:void) +-(language::name:a:N) `-(language::real:1.2:R) )"; CHECK_AST(data, result); } SECTION("divideeq_op") { std::string_view data = R"( let a:R, a = 3; a /= 2; )"; std::string_view result = R"( (root:void) +-(language::var_declaration:void) | +-(language::name:a:R) | +-(language::R_set:R) | +-(language::name:a:R) | `-(language::integer:3:Z) `-(language::divideeq_op:void) +-(language::name:a:R) `-(language::integer:2:Z) )"; CHECK_AST(data, result); } SECTION("pluseq_op") { std::string_view data = R"( let a :Z, a = 3; a += 2; )"; std::string_view result = R"( (root:void) +-(language::var_declaration:void) | +-(language::name:a:Z) | +-(language::Z_set:Z) | +-(language::name:a:Z) | `-(language::integer:3:Z) `-(language::pluseq_op:void) +-(language::name:a:Z) `-(language::integer:2:Z) )"; CHECK_AST(data, result); } SECTION("minuseq_op") { std::string_view data = R"( let a:Z, a = 1; a -= 2; )"; std::string_view result = R"( (root:void) +-(language::var_declaration:void) | +-(language::name:a:Z) | +-(language::Z_set:Z) | +-(language::name:a:Z) | `-(language::integer:1:Z) `-(language::minuseq_op:void) +-(language::name:a:Z) `-(language::integer:2:Z) )"; CHECK_AST(data, result); } SECTION("for simple") { std::string_view data = R"( for (;;); )"; std::string_view result = R"( (root:void) `-(language::for_statement:void) +-(language::for_init:void) +-(language::for_test:B) +-(language::for_post:void) `-(language::for_statement_block:void) )"; CHECK_AST(data, result); } SECTION("for std") { std::string_view data = R"( for (let i:Z, i=0; i<3; i += 1) { i += 2; } )"; std::string_view result = R"( (root:void) `-(language::for_statement:void) +-(language::var_declaration:void) | +-(language::name:i:Z) | +-(language::Z_set:Z) | +-(language::name:i:Z) | `-(language::integer:0:Z) +-(language::lesser_op:B) | +-(language::name:i:Z) | `-(language::integer:3:Z) +-(language::pluseq_op:void) | +-(language::name:i:Z) | `-(language::integer:1:Z) `-(language::pluseq_op:void) +-(language::name:i:Z) `-(language::integer:2:Z) )"; CHECK_AST(data, result); } SECTION("empty block") { std::string_view data = R"( {} )"; std::string_view result = R"( (root:void) )"; CHECK_AST(data, result); } SECTION("block") { std::string_view data = R"( { 3; } )"; std::string_view result = R"( (root:void) `-(language::block:void) `-(language::integer:3:Z) )"; CHECK_AST(data, result); } SECTION("if statements") { SECTION("empty if") { std::string_view data = R"( if (true); )"; std::string_view result = R"( (root:void) `-(language::if_statement:void) +-(language::true_kw:B) `-(language::statement_block:void) )"; CHECK_AST(data, result); } SECTION("if else") { std::string_view data = R"( if (true) 1; else 2; )"; std::string_view result = R"( (root:void) `-(language::if_statement:void) +-(language::true_kw:B) +-(language::integer:1:Z) `-(language::integer:2:Z) )"; CHECK_AST(data, result); } SECTION("if else simplify block") { std::string_view data = R"( if (true) { 1; } else { 2; } )"; std::string_view result = R"( (root:void) `-(language::if_statement:void) +-(language::true_kw:B) +-(language::integer:1:Z) `-(language::integer:2:Z) )"; CHECK_AST(data, result); } SECTION("if block") { std::string_view data = R"( if (true) { 1; 2; } )"; std::string_view result = R"( (root:void) `-(language::if_statement:void) +-(language::true_kw:B) `-(language::block:void) +-(language::integer:1:Z) `-(language::integer:2:Z) )"; CHECK_AST(data, result); } SECTION("if invalid condition") { std::string_view data = R"( if ("string"); )"; TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; auto ast = ASTBuilder::build(input); ASTSymbolTableBuilder{*ast}; REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, "invalid implicit conversion: string -> B"); } } SECTION("while statements") { SECTION("empty while") { std::string_view data = R"( while (true); )"; std::string_view result = R"( (root:void) `-(language::while_statement:void) +-(language::true_kw:B) `-(language::statement_block:void) )"; CHECK_AST(data, result); } SECTION("simple while") { std::string_view data = R"( while (true) 1; )"; std::string_view result = R"( (root:void) `-(language::while_statement:void) +-(language::true_kw:B) `-(language::integer:1:Z) )"; CHECK_AST(data, result); } SECTION("while simplified block") { std::string_view data = R"( while (true) { 1; } )"; std::string_view result = R"( (root:void) `-(language::while_statement:void) +-(language::true_kw:B) `-(language::integer:1:Z) )"; CHECK_AST(data, result); } SECTION("while block_statement") { std::string_view data = R"( while (true) { 1; 2; } )"; std::string_view result = R"( (root:void) `-(language::while_statement:void) +-(language::true_kw:B) `-(language::block:void) +-(language::integer:1:Z) `-(language::integer:2:Z) )"; CHECK_AST(data, result); } SECTION("while invalid condition") { std::string_view data = R"( while ("string"); )"; TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; auto ast = ASTBuilder::build(input); ASTSymbolTableBuilder{*ast}; REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, "invalid implicit conversion: string -> B"); } } SECTION("do-while statements") { SECTION("empty do-while") { std::string_view data = R"( do ; while (true); )"; std::string_view result = R"( (root:void) `-(language::do_while_statement:void) +-(language::statement_block:void) `-(language::true_kw:B) )"; CHECK_AST(data, result); } SECTION("simple do-while") { std::string_view data = R"( do 1; while (true); )"; std::string_view result = R"( (root:void) `-(language::do_while_statement:void) +-(language::integer:1:Z) `-(language::true_kw:B) )"; CHECK_AST(data, result); } SECTION("do-while simplified block") { std::string_view data = R"( do { 1; } while (true); )"; std::string_view result = R"( (root:void) `-(language::do_while_statement:void) +-(language::integer:1:Z) `-(language::true_kw:B) )"; CHECK_AST(data, result); } SECTION("do-while block") { std::string_view data = R"( do { 1; 2; } while (true); )"; std::string_view result = R"( (root:void) `-(language::do_while_statement:void) +-(language::block:void) | +-(language::integer:1:Z) | `-(language::integer:2:Z) `-(language::true_kw:B) )"; CHECK_AST(data, result); } SECTION("do-while invalid condition") { std::string_view data = R"( do 1; while ("string"); )"; TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; auto ast = ASTBuilder::build(input); ASTSymbolTableBuilder{*ast}; REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, "invalid implicit conversion: string -> B"); } } SECTION("boolean statements") { SECTION("unary not") { std::string_view data = R"( not false; )"; std::string_view result = R"( (root:void) `-(language::unary_not:B) `-(language::false_kw:B) )"; CHECK_AST(data, result); } SECTION("lesser op") { std::string_view data = R"( 1<2; )"; std::string_view result = R"( (root:void) `-(language::lesser_op:B) +-(language::integer:1:Z) `-(language::integer:2:Z) )"; CHECK_AST(data, result); } SECTION("lesser_or_eq op") { std::string_view data = R"( 1<=2; )"; std::string_view result = R"( (root:void) `-(language::lesser_or_eq_op:B) +-(language::integer:1:Z) `-(language::integer:2:Z) )"; CHECK_AST(data, result); } SECTION("greater op") { std::string_view data = R"( 1>2; )"; std::string_view result = R"( (root:void) `-(language::greater_op:B) +-(language::integer:1:Z) `-(language::integer:2:Z) )"; CHECK_AST(data, result); } SECTION("greater_or_eq op") { std::string_view data = R"( 1>=2; )"; std::string_view result = R"( (root:void) `-(language::greater_or_eq_op:B) +-(language::integer:1:Z) `-(language::integer:2:Z) )"; CHECK_AST(data, result); } SECTION("eqeq op") { std::string_view data = R"( 1==2; )"; std::string_view result = R"( (root:void) `-(language::eqeq_op:B) +-(language::integer:1:Z) `-(language::integer:2:Z) )"; CHECK_AST(data, result); } SECTION("not_eq op") { std::string_view data = R"( 1!=2; )"; std::string_view result = R"( (root:void) `-(language::not_eq_op:B) +-(language::integer:1:Z) `-(language::integer:2:Z) )"; CHECK_AST(data, result); } SECTION("and op") { std::string_view data = R"( false and true; )"; std::string_view result = R"( (root:void) `-(language::and_op:B) +-(language::false_kw:B) `-(language::true_kw:B) )"; CHECK_AST(data, result); } SECTION("or op") { std::string_view data = R"( false or true; )"; std::string_view result = R"( (root:void) `-(language::or_op:B) +-(language::false_kw:B) `-(language::true_kw:B) )"; CHECK_AST(data, result); } SECTION("xor op") { std::string_view data = R"( true xor false; )"; std::string_view result = R"( (root:void) `-(language::xor_op:B) +-(language::true_kw:B) `-(language::false_kw:B) )"; CHECK_AST(data, result); } } SECTION("unary operators") { SECTION("unary minus") { std::string_view data = R"( - 1; )"; std::string_view result = R"( (root:void) `-(language::unary_minus:Z) `-(language::integer:1:Z) )"; CHECK_AST(data, result); } SECTION("unary plusplus") { std::string_view data = R"( ++1; )"; std::string_view result = R"( (root:void) `-(language::unary_plusplus:Z) `-(language::integer:1:Z) )"; CHECK_AST(data, result); } SECTION("unary minusminus") { std::string_view data = R"( --1; )"; std::string_view result = R"( (root:void) `-(language::unary_minusminus:Z) `-(language::integer:1:Z) )"; CHECK_AST(data, result); } SECTION("post plusplus") { std::string_view data = R"( 1++; )"; std::string_view result = R"( (root:void) `-(language::post_plusplus:Z) `-(language::integer:1:Z) )"; CHECK_AST(data, result); } SECTION("post minusminus") { std::string_view data = R"( 1--; )"; std::string_view result = R"( (root:void) `-(language::post_minusminus:Z) `-(language::integer:1:Z) )"; CHECK_AST(data, result); } } SECTION("binary operators") { SECTION("plus") { std::string_view data = R"( 1+2; )"; std::string_view result = R"( (root:void) `-(language::plus_op:Z) +-(language::integer:1:Z) `-(language::integer:2:Z) )"; CHECK_AST(data, result); } SECTION("minus") { std::string_view data = R"( 1-2; )"; std::string_view result = R"( (root:void) `-(language::minus_op:Z) +-(language::integer:1:Z) `-(language::integer:2:Z) )"; CHECK_AST(data, result); } SECTION("multiply") { std::string_view data = R"( 1*2; )"; std::string_view result = R"( (root:void) `-(language::multiply_op:Z) +-(language::integer:1:Z) `-(language::integer:2:Z) )"; CHECK_AST(data, result); } SECTION("divide") { std::string_view data = R"( 1/2; )"; std::string_view result = R"( (root:void) `-(language::divide_op:Z) +-(language::integer:1:Z) `-(language::integer:2:Z) )"; CHECK_AST(data, result); } SECTION("invalid operands") { std::string_view data = R"( 1+"string"; )"; TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; auto ast = ASTBuilder::build(input); ASTSymbolTableBuilder{*ast}; REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, "undefined binary operator\n" "note: incompatible operand types Z and string"); } } }