diff --git a/src/language/ASTNodeDataTypeBuilder.cpp b/src/language/ASTNodeDataTypeBuilder.cpp
index 1fe2d74b067718ad1425309f838fed1a9dfc9464..68bb5a9203f71eb27d7e64a0eb17d0440e7edf9a 100644
--- a/src/language/ASTNodeDataTypeBuilder.cpp
+++ b/src/language/ASTNodeDataTypeBuilder.cpp
@@ -142,9 +142,11 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const
             data_type = ASTNodeDataType::string_t;
           }
 
+          // LCOV_EXCL_START
           if (data_type == ASTNodeDataType::undefined_t) {
             throw parse_error("invalid parameter type", type_node.begin());
           }
+          // LCOV_EXCL_STOP
 
           symbol_node.m_data_type   = data_type;
           const std::string& symbol = symbol_node.string();
@@ -207,9 +209,11 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const
             value_type = ASTNodeDataType::string_t;
           }
 
+          // LCOV_EXCL_START
           if (value_type == ASTNodeDataType::undefined_t) {
             throw parse_error("invalid value type", image_node.begin());
           }
+          // LCOV_EXCL_STOP
         };
 
         if (image_domain_node.children.size() == 0) {
diff --git a/tests/test_ASTNodeDataTypeBuilder.cpp b/tests/test_ASTNodeDataTypeBuilder.cpp
index 3fecc130b0daf20e177e3c9fe61aecb947ebb280..8e297e78b60c433a3f1748a32338be859c548f27 100644
--- a/tests/test_ASTNodeDataTypeBuilder.cpp
+++ b/tests/test_ASTNodeDataTypeBuilder.cpp
@@ -115,6 +115,77 @@ false;
     CHECK_AST(data, result);
   }
 
+  SECTION("compound")
+  {
+    SECTION("declaration")
+    {
+      std::string_view data = R"(
+R*B*N*string (x,b,n,s);
+)";
+
+      std::string_view result = R"(
+(root:void)
+ `-(language::declaration:typename)
+     +-(language::type_expression:typename)
+     |   +-(language::R_set:typename)
+     |   +-(language::B_set:typename)
+     |   +-(language::N_set:typename)
+     |   `-(language::string_type:typename)
+     `-(language::name_list:void)
+         +-(language::name:x:R)
+         +-(language::name:b:B)
+         +-(language::name:n:N)
+         `-(language::name:s:string)
+)";
+
+      CHECK_AST(data, result);
+    }
+
+    SECTION("errors")
+    {
+      SECTION("too many variables")
+      {
+        std::string_view data = R"(
+R*B*N*string (x,b,n,s,t);
+)";
+
+        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"(
+R*B*N*string (x,b,n);
+)";
+
+        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"(
+R (x,y);
+)";
+
+        string_input input{data, "test.pgs"};
+        auto ast = ASTBuilder::build(input);
+        ASTSymbolTableBuilder{*ast};
+
+        REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, "unexpected variable list for scalar space");
+      }
+    }
+  }
+
   SECTION("let declaration")
   {
     SECTION("R-functions")
@@ -255,6 +326,21 @@ let f : N*B -> B, (n,b) -> (n>3) and b;
       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::let_declaration:void)
+     `-(language::name:cat:function)
+)";
+
+      CHECK_AST(data, result);
+    }
+
     SECTION("errors")
     {
       SECTION("wrong parameter number")
@@ -280,6 +366,19 @@ let f : R -> B, (x,y) -> 3;
 
         REQUIRE_THROWS_AS(ASTNodeDataTypeBuilder{*ast}, parse_error);
       }
+
+      SECTION("wrong image size")
+      {
+        std::string_view data = R"(
+let f : R*Z -> B, (x,z) -> (3, x);
+)";
+        string_input input{data, "test.pgs"};
+        auto ast = ASTBuilder::build(input);
+        ASTSymbolTableBuilder{*ast};
+
+        REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast},
+                            "note: number of image spaces (1) B differs from number of expressions (2) (3, x)");
+      }
     }
   }
 
@@ -356,7 +455,7 @@ Z z = incr(3);
       CHECK_AST(data, result);
     }
 
-    SECTION("N-functions")
+    SECTION("N-function")
     {
       std::string_view data = R"(
 let double : N -> N, n -> 2*n;
@@ -378,7 +477,7 @@ N n = double(3);
       CHECK_AST(data, result);
     }
 
-    SECTION("B-functions")
+    SECTION("B-function")
     {
       std::string_view data = R"(
 let greater_than_2 : R -> B, x -> x>2;
@@ -400,6 +499,56 @@ B b = greater_than_2(3);
       CHECK_AST(data, result);
     }
 
+    SECTION("string-function")
+    {
+      std::string_view data = R"(
+let cat : string*string -> string, (s,t) -> s+t;
+string s = cat("foo", "bar");
+)";
+
+      std::string_view result = R"(
+(root:void)
+ +-(language::let_declaration:void)
+ |   `-(language::name:cat:function)
+ `-(language::declaration:string)
+     +-(language::string_type:typename)
+     +-(language::name:s:string)
+     `-(language::function_evaluation:string)
+         +-(language::name:cat:function)
+         `-(language::function_argument_list:void)
+             +-(language::literal:"foo":string)
+             `-(language::literal:"bar":string)
+)";
+
+      CHECK_AST(data, result);
+    }
+
+    SECTION("compound return function")
+    {
+      std::string_view data = R"(
+let x_x2 : R -> R*R, x -> (x,x*x);
+R*R (x,x2) = x_x2(3);
+)";
+
+      std::string_view result = R"(
+(root:void)
+ +-(language::let_declaration:void)
+ |   `-(language::name:x_x2:function)
+ `-(language::declaration:typename)
+     +-(language::type_expression:typename)
+     |   +-(language::R_set:typename)
+     |   `-(language::R_set:typename)
+     +-(language::name_list:void)
+     |   +-(language::name:x:R)
+     |   `-(language::name:x2:R)
+     `-(language::function_evaluation:typename)
+         +-(language::name:x_x2:function)
+         `-(language::integer:3:Z)
+)";
+
+      CHECK_AST(data, result);
+    }
+
     SECTION("errors")
     {
       SECTION("not a function")