diff --git a/src/language/ast/ASTNodeBuiltinFunctionExpressionBuilder.cpp b/src/language/ast/ASTNodeBuiltinFunctionExpressionBuilder.cpp
index 54b0970eff20190d4b57d2adfc596ba71cf174d0..cdd9c43d977c6718dc5d900dbe0b96a15de54f47 100644
--- a/src/language/ast/ASTNodeBuiltinFunctionExpressionBuilder.cpp
+++ b/src/language/ast/ASTNodeBuiltinFunctionExpressionBuilder.cpp
@@ -231,6 +231,9 @@ ASTNodeBuiltinFunctionExpressionBuilder::_getArgumentConverter(const ASTNodeData
       }
       case ASTNodeDataType::double_t: {
         return std::make_unique<FunctionTupleArgumentConverter<ParameterContentT, double>>(argument_number);
+      }
+      case ASTNodeDataType::string_t: {
+        return std::make_unique<FunctionTupleArgumentConverter<ParameterContentT, std::string>>(argument_number);
       }
         // LCOV_EXCL_START
       default: {
@@ -265,6 +268,9 @@ ASTNodeBuiltinFunctionExpressionBuilder::_getArgumentConverter(const ASTNodeData
     case ASTNodeDataType::double_t: {
       return std::make_unique<FunctionTupleArgumentConverter<ParameterContentT, double>>(argument_number);
     }
+    case ASTNodeDataType::string_t: {
+      return std::make_unique<FunctionTupleArgumentConverter<ParameterContentT, std::string>>(argument_number);
+    }
     case ASTNodeDataType::function_t: {
       const ASTNode& parent_node = argument_node_sub_data_type.m_parent_node;
       auto symbol_table          = parent_node.m_symbol_table;
@@ -457,7 +463,7 @@ ASTNodeBuiltinFunctionExpressionBuilder::_getArgumentConverter(const ASTNodeData
         }
       }
       case ASTNodeDataType::string_t: {
-        return get_function_argument_to_string_converter();
+        return get_function_argument_to_tuple_converter(std::string{});
       }
         // LCOV_EXCL_START
       default: {
diff --git a/src/language/node_processor/ASTNodeExpressionListProcessor.hpp b/src/language/node_processor/ASTNodeExpressionListProcessor.hpp
index 713eda89aec0804efb4bcd323cb9a65c10b8967d..21823176e8b7f8d9eb0c0760c52ae0fb1749f0e3 100644
--- a/src/language/node_processor/ASTNodeExpressionListProcessor.hpp
+++ b/src/language/node_processor/ASTNodeExpressionListProcessor.hpp
@@ -87,7 +87,7 @@ class ASTNodeExpressionListProcessor final : public INodeProcessor
     }
 
     Assert(list_values.size() == m_number_of_values);
-    return DataVariant{std::move(list_values)};
+    return AggregateDataVariant{std::move(list_values)};
   }
 
   ASTNodeExpressionListProcessor(ASTNode& node) : m_node{node}, m_number_of_values{this->_getNumberOfValues()} {}
diff --git a/src/language/node_processor/BinaryExpressionProcessor.hpp b/src/language/node_processor/BinaryExpressionProcessor.hpp
index 7f278d4c7d4a007ad5db4c9f5d444665ccca56d6..a2111464ce05a8148ff4886f9614952641ed65d5 100644
--- a/src/language/node_processor/BinaryExpressionProcessor.hpp
+++ b/src/language/node_processor/BinaryExpressionProcessor.hpp
@@ -314,8 +314,8 @@ struct BinaryExpressionProcessor<BinaryOpT, std::shared_ptr<ValueT>, std::shared
   PUGS_INLINE DataVariant
   _eval(const DataVariant& a, const DataVariant& b)
   {
-    if constexpr ((std::is_arithmetic_v<B_DataT>) or (is_tiny_matrix_v<B_DataT>) or (is_tiny_vector_v<B_DataT>) or
-                  (std::is_same_v<std::string, B_DataT>)) {
+    if constexpr (std::is_arithmetic_v<B_DataT> or is_tiny_matrix_v<B_DataT> or is_tiny_vector_v<B_DataT> or
+                  std::is_same_v<std::string, B_DataT> or is_std_vector_v<B_DataT>) {
       const auto& embedded_a = std::get<EmbeddedData>(a);
       const auto& b_value    = std::get<B_DataT>(b);
 
diff --git a/src/language/node_processor/FunctionArgumentConverter.hpp b/src/language/node_processor/FunctionArgumentConverter.hpp
index f747672e2c036ab59402610cb7170218f96c15e3..3e22a9ae188574db47278f69cc264d0faf0c2f2c 100644
--- a/src/language/node_processor/FunctionArgumentConverter.hpp
+++ b/src/language/node_processor/FunctionArgumentConverter.hpp
@@ -219,6 +219,48 @@ class FunctionTupleArgumentConverter final : public IFunctionArgumentConverter
           }
         },
         value);
+    } else if constexpr (std::is_same_v<std::string, ContentType>) {
+      std::visit(
+        [&](auto&& v) {
+          using ValueT = std::decay_t<decltype(v)>;
+          Assert(not std::is_same_v<ValueT, ContentType>, "should have been treated before");
+          if constexpr (is_std_vector_v<ValueT>) {
+            using ContentT = typename ValueT::value_type;
+            if constexpr (std::is_same_v<ContentT, ContentType>) {
+              TupleType list_value;
+              list_value.reserve(v.size());
+              for (size_t i = 0; i < v.size(); ++i) {
+                list_value.emplace_back(std::move(v[i]));
+              }
+              exec_policy.currentContext()[m_argument_id] = std::move(list_value);
+            } else if constexpr (not is_shared_ptr_v<ContentT>) {
+              TupleType list_value;
+              list_value.reserve(v.size());
+              for (size_t i = 0; i < v.size(); ++i) {
+                std::ostringstream os;
+                const ContentT& content = v[i];   // useless helper for clang10
+                os << content;
+                list_value.push_back(std::move(os.str()));
+              }
+              exec_policy.currentContext()[m_argument_id] = std::move(list_value);
+            } else {
+              // LCOV_EXCL_START
+              throw UnexpectedError(std::string{"cannot convert '"} + demangle<ValueT>() + "' to '" +
+                                    demangle<ContentType>() + "'");
+              // LCOV_EXCL_STOP
+            }
+          } else if constexpr (not is_shared_ptr_v<ContentType>) {
+            std::ostringstream os;
+            os << v;
+            exec_policy.currentContext()[m_argument_id] = std::move(TupleType{std::move(os.str())});
+          } else {
+            // LCOV_EXCL_START
+            throw UnexpectedError(std::string{"cannot convert '"} + demangle<ValueT>() + "' to '" +
+                                  demangle<ContentType>() + "'");
+            // LCOV_EXCL_STOP
+          }
+        },
+        value);
 
     } else {
       // LCOV_EXCL_START
diff --git a/src/language/node_processor/FunctionProcessor.hpp b/src/language/node_processor/FunctionProcessor.hpp
index c6c81d2938e9f774f0315f6a9af5c2e03c6a19e5..16e65e6e1e1ca63a7e1fa57e109856b3da25610a 100644
--- a/src/language/node_processor/FunctionProcessor.hpp
+++ b/src/language/node_processor/FunctionProcessor.hpp
@@ -124,7 +124,7 @@ class FunctionProcessor : public INodeProcessor
       for (auto& function_expression_processor : m_function_expression_processors) {
         list_values.emplace_back(function_expression_processor->execute(context_exec_policy));
       }
-      return DataVariant{std::move(list_values)};
+      return AggregateDataVariant{std::move(list_values)};
     }
   }
 
diff --git a/src/language/utils/BinaryOperatorRegisterForB.cpp b/src/language/utils/BinaryOperatorRegisterForB.cpp
index dc228471d52330e486887e523c83dfa2d1917cef..7145ebab79c1b76a0877d92f87ca5b81cad62fcd 100644
--- a/src/language/utils/BinaryOperatorRegisterForB.cpp
+++ b/src/language/utils/BinaryOperatorRegisterForB.cpp
@@ -12,6 +12,10 @@ BinaryOperatorRegisterForB::_register_ostream()
   repository.addBinaryOperator<language::shift_left_op>(
     std::make_shared<BinaryOperatorProcessorBuilder<language::shift_left_op, std::shared_ptr<const OStream>,
                                                     std::shared_ptr<const OStream>, bool>>());
+
+  repository.addBinaryOperator<language::shift_left_op>(
+    std::make_shared<BinaryOperatorProcessorBuilder<language::shift_left_op, std::shared_ptr<const OStream>,
+                                                    std::shared_ptr<const OStream>, std::vector<bool>>>());
 }
 
 void
diff --git a/src/language/utils/BinaryOperatorRegisterForN.cpp b/src/language/utils/BinaryOperatorRegisterForN.cpp
index e32e2b53a5fb70af8b84069790dd8435a362e051..9d77d842e219e03237e4839c9fe8d3f7e027453d 100644
--- a/src/language/utils/BinaryOperatorRegisterForN.cpp
+++ b/src/language/utils/BinaryOperatorRegisterForN.cpp
@@ -12,6 +12,10 @@ BinaryOperatorRegisterForN::_register_ostream()
   repository.addBinaryOperator<language::shift_left_op>(
     std::make_shared<BinaryOperatorProcessorBuilder<language::shift_left_op, std::shared_ptr<const OStream>,
                                                     std::shared_ptr<const OStream>, uint64_t>>());
+
+  repository.addBinaryOperator<language::shift_left_op>(
+    std::make_shared<BinaryOperatorProcessorBuilder<language::shift_left_op, std::shared_ptr<const OStream>,
+                                                    std::shared_ptr<const OStream>, std::vector<uint64_t>>>());
 }
 
 void
diff --git a/src/language/utils/BinaryOperatorRegisterForR.cpp b/src/language/utils/BinaryOperatorRegisterForR.cpp
index cc8ba8a79dc59c447317200a308103b8d7dfc1d8..8f5d512752b2a9f7013085c3d10a5f5ec7e4aec1 100644
--- a/src/language/utils/BinaryOperatorRegisterForR.cpp
+++ b/src/language/utils/BinaryOperatorRegisterForR.cpp
@@ -12,6 +12,10 @@ BinaryOperatorRegisterForR::_register_ostream()
   repository.addBinaryOperator<language::shift_left_op>(
     std::make_shared<BinaryOperatorProcessorBuilder<language::shift_left_op, std::shared_ptr<const OStream>,
                                                     std::shared_ptr<const OStream>, double>>());
+
+  repository.addBinaryOperator<language::shift_left_op>(
+    std::make_shared<BinaryOperatorProcessorBuilder<language::shift_left_op, std::shared_ptr<const OStream>,
+                                                    std::shared_ptr<const OStream>, std::vector<double>>>());
 }
 
 void
diff --git a/src/language/utils/BinaryOperatorRegisterForRn.cpp b/src/language/utils/BinaryOperatorRegisterForRn.cpp
index 6e900d036871ed79eb13dfa737445becd50a1a9a..81e1d539d048a32815ec7e6749370370fc6b6f8e 100644
--- a/src/language/utils/BinaryOperatorRegisterForRn.cpp
+++ b/src/language/utils/BinaryOperatorRegisterForRn.cpp
@@ -16,6 +16,10 @@ BinaryOperatorRegisterForRn<Dimension>::_register_ostream()
   repository.addBinaryOperator<language::shift_left_op>(
     std::make_shared<BinaryOperatorProcessorBuilder<language::shift_left_op, std::shared_ptr<const OStream>,
                                                     std::shared_ptr<const OStream>, Rn>>());
+
+  repository.addBinaryOperator<language::shift_left_op>(
+    std::make_shared<BinaryOperatorProcessorBuilder<language::shift_left_op, std::shared_ptr<const OStream>,
+                                                    std::shared_ptr<const OStream>, std::vector<Rn>>>());
 }
 
 template <size_t Dimension>
diff --git a/src/language/utils/BinaryOperatorRegisterForRnxn.cpp b/src/language/utils/BinaryOperatorRegisterForRnxn.cpp
index f164203ddaca12e83f5ae5770dcb4848d366d3a4..4970fe434e318bdbf4ac037bd4f7fc6cedc9a006 100644
--- a/src/language/utils/BinaryOperatorRegisterForRnxn.cpp
+++ b/src/language/utils/BinaryOperatorRegisterForRnxn.cpp
@@ -16,6 +16,10 @@ BinaryOperatorRegisterForRnxn<Dimension>::_register_ostream()
   repository.addBinaryOperator<language::shift_left_op>(
     std::make_shared<BinaryOperatorProcessorBuilder<language::shift_left_op, std::shared_ptr<const OStream>,
                                                     std::shared_ptr<const OStream>, Rnxn>>());
+
+  repository.addBinaryOperator<language::shift_left_op>(
+    std::make_shared<BinaryOperatorProcessorBuilder<language::shift_left_op, std::shared_ptr<const OStream>,
+                                                    std::shared_ptr<const OStream>, std::vector<Rnxn>>>());
 }
 
 template <size_t Dimension>
diff --git a/src/language/utils/BinaryOperatorRegisterForString.cpp b/src/language/utils/BinaryOperatorRegisterForString.cpp
index 999950cf1b1310bbe52614cc6823f37be53f82a4..cae341bf9ad565a5fc87801526a3024dd2f945ae 100644
--- a/src/language/utils/BinaryOperatorRegisterForString.cpp
+++ b/src/language/utils/BinaryOperatorRegisterForString.cpp
@@ -14,6 +14,10 @@ BinaryOperatorRegisterForString::_register_ostream()
   repository.addBinaryOperator<language::shift_left_op>(
     std::make_shared<BinaryOperatorProcessorBuilder<language::shift_left_op, std::shared_ptr<const OStream>,
                                                     std::shared_ptr<const OStream>, std::string>>());
+
+  repository.addBinaryOperator<language::shift_left_op>(
+    std::make_shared<BinaryOperatorProcessorBuilder<language::shift_left_op, std::shared_ptr<const OStream>,
+                                                    std::shared_ptr<const OStream>, std::vector<std::string>>>());
 }
 
 void
diff --git a/src/language/utils/BinaryOperatorRegisterForZ.cpp b/src/language/utils/BinaryOperatorRegisterForZ.cpp
index 1d318850226beac44d27fe5f4fdba6c8e36a4e61..8174f3f493b417bf39e41cc4e3344b98abf071e4 100644
--- a/src/language/utils/BinaryOperatorRegisterForZ.cpp
+++ b/src/language/utils/BinaryOperatorRegisterForZ.cpp
@@ -12,6 +12,10 @@ BinaryOperatorRegisterForZ::_register_ostream()
   repository.addBinaryOperator<language::shift_left_op>(
     std::make_shared<BinaryOperatorProcessorBuilder<language::shift_left_op, std::shared_ptr<const OStream>,
                                                     std::shared_ptr<const OStream>, int64_t>>());
+
+  repository.addBinaryOperator<language::shift_left_op>(
+    std::make_shared<BinaryOperatorProcessorBuilder<language::shift_left_op, std::shared_ptr<const OStream>,
+                                                    std::shared_ptr<const OStream>, std::vector<int64_t>>>());
 }
 
 void
diff --git a/src/language/utils/BuiltinFunctionEmbedder.hpp b/src/language/utils/BuiltinFunctionEmbedder.hpp
index 265379a368ef3b7f847cd8de2b763a70b525f8a4..ac3ba21fe241f8ea9416192fc0ce97f865bb0883 100644
--- a/src/language/utils/BuiltinFunctionEmbedder.hpp
+++ b/src/language/utils/BuiltinFunctionEmbedder.hpp
@@ -204,7 +204,7 @@ class BuiltinFunctionEmbedder<FX(Args...)> : public IBuiltinFunctionEmbedder
     std::apply([&](auto&&... result) { ((vector_result.emplace_back(_resultToDataVariant(result))), ...); },
                tuple_result);
 
-    return vector_result;
+    return AggregateDataVariant{std::move(vector_result)};
   }
 
  public:
@@ -337,7 +337,7 @@ class BuiltinFunctionEmbedder<FX(void)> : public IBuiltinFunctionEmbedder
     std::apply([&](auto&&... result) { ((vector_result.emplace_back(_resultToDataVariant(result))), ...); },
                tuple_result);
 
-    return vector_result;
+    return AggregateDataVariant{std::move(vector_result)};
   }
 
  public:
diff --git a/src/language/utils/DataVariant.cpp b/src/language/utils/DataVariant.cpp
index fc82eec7b805cd49daa2a097490b36fa33e0c561..005dda6f17f9d38cb2c9a7a63322244657f89e50 100644
--- a/src/language/utils/DataVariant.cpp
+++ b/src/language/utils/DataVariant.cpp
@@ -13,6 +13,9 @@ operator<<(std::ostream& os, const DataVariant& v)
         os << "--";
       } else if constexpr (is_std_vector_v<ValueT>) {
         os << '(';
+        if constexpr (std::is_same_v<bool, typename ValueT::value_type>) {
+          os << std::boolalpha;
+        }
         if (v.size() > 0) {
           os << v[0];
         }
diff --git a/src/language/utils/DataVariant.hpp b/src/language/utils/DataVariant.hpp
index 964044c6121478b4247c089a45eaf3ebe025195e..9a09cad6fc531b1cf8a5c3de8b09bfad5487ccf0 100644
--- a/src/language/utils/DataVariant.hpp
+++ b/src/language/utils/DataVariant.hpp
@@ -97,7 +97,7 @@ class AggregateDataVariant   // LCOV_EXCL_LINE
   AggregateDataVariant& operator=(const AggregateDataVariant&) = default;
   AggregateDataVariant& operator=(AggregateDataVariant&&) = default;
 
-  AggregateDataVariant(std::vector<DataVariant>&& data_vector) : m_data_vector{data_vector} {}
+  explicit AggregateDataVariant(std::vector<DataVariant>&& data_vector) : m_data_vector{data_vector} {}
 
   AggregateDataVariant(const AggregateDataVariant&) = default;
   AggregateDataVariant(AggregateDataVariant&&)      = default;
diff --git a/tests/test_BinaryExpressionProcessor_shift.cpp b/tests/test_BinaryExpressionProcessor_shift.cpp
index c1892a2ff3a793a1cdc9a48e6f1a454b090fe77d..127e90161416444f652c16206ddbd8c4f97d5718 100644
--- a/tests/test_BinaryExpressionProcessor_shift.cpp
+++ b/tests/test_BinaryExpressionProcessor_shift.cpp
@@ -6,6 +6,7 @@
 
 #include <fstream>
 #include <netdb.h>
+#include <regex>
 #include <unistd.h>
 
 // clazy:excludeall=non-pod-global-static
@@ -24,8 +25,29 @@ TEST_CASE("BinaryExpressionProcessor shift", "[language]")
       data << R"(import socket;)";
       data << "let fout:ostream, fout = ofstream(\"" << path.string() << "\");\n";
       data << R"(fout << 2 << " " << true << " " << 2 + 3 << "\n";
-fout << createSocketServer(0);)";
-
+fout << createSocketServer(0) << "\n";)";
+      data << "let b:(B), b = (true,false);\n";
+      data << "let n:(N), n = (1,3);\n";
+      data << "let z:(Z), z = (-1,3);\n";
+      data << "let r:(R), r = (2.3,4);\n";
+      data << "let u1:(R^1), u1 = (2.3,4);\n";
+      data << "let u2:(R^2), u2 = ((2.3,4), (3,2));\n";
+      data << "let u3:(R^3), u3 = ((2.3,4,-1), (3,2,1));\n";
+      data << "let A1:(R^1x1), A1 = (2.3, 4);\n";
+      data << "let A2:(R^2x2), A2 = ((2.3,4,2,0.3), (3,2.3,1,-4));\n";
+      data << "let A3:(R^3x3), A3 = ((2.3,4,-1,2,7,2.3,6,2,8), (3,2,1,1,2,5,2.1,3,-2.6));\n";
+      data << R"(let s:(string), s = ("foo", "bar");)";
+      data << R"(fout << b  << "\n";)";
+      data << R"(fout << n  << "\n";)";
+      data << R"(fout << z  << "\n";)";
+      data << R"(fout << r  << "\n";)";
+      data << R"(fout << u1 << "\n";)";
+      data << R"(fout << u2 << "\n";)";
+      data << R"(fout << u3 << "\n";)";
+      data << R"(fout << A1 << "\n";)";
+      data << R"(fout << A2 << "\n";)";
+      data << R"(fout << A3 << "\n";)";
+      data << R"(fout << s  << "\n";)";
       TAO_PEGTL_NAMESPACE::string_input input{data.str(), "test.pgs"};
       auto ast = ASTBuilder::build(input);
 
@@ -69,7 +91,9 @@ fout << createSocketServer(0);)";
 
       auto is_int = [](const std::string& s) {
         for (const char& c : s) {
-          if (not std::isdigit(c)) {
+          if (c == '\n') {
+            break;
+          } else if (not std::isdigit(c)) {
             return false;
           }
         }
@@ -79,6 +103,54 @@ fout << createSocketServer(0);)";
       REQUIRE(is_int(suffix));
     }
 
+    {
+      std::ifstream fin(filename.c_str());
+      char line[1024];
+
+      fin.getline(line, 1023);
+      REQUIRE(std::string_view(line) == "2 true 5");
+
+      char hbuf[NI_MAXHOST];
+      ::gethostname(hbuf, NI_MAXHOST);
+      std::ostringstream os;
+      os << '^' << hbuf << ":\\d+$";
+      fin.getline(line, 1023);
+      REQUIRE(std::regex_match(line, std::regex{os.str()}));
+
+      fin.getline(line, 1023);
+      REQUIRE(std::string_view(line) == "(true, false)");
+
+      fin.getline(line, 1023);
+      REQUIRE(std::string_view(line) == "(1, 3)");
+
+      fin.getline(line, 1023);
+      REQUIRE(std::string_view(line) == "(-1, 3)");
+
+      fin.getline(line, 1023);
+      REQUIRE(std::string_view(line) == "(2.3, 4)");
+
+      fin.getline(line, 1023);
+      REQUIRE(std::string_view(line) == "((2.3), (4))");
+
+      fin.getline(line, 1023);
+      REQUIRE(std::string_view(line) == "((2.3,4), (3,2))");
+
+      fin.getline(line, 1023);
+      REQUIRE(std::string_view(line) == "((2.3,4,-1), (3,2,1))");
+
+      fin.getline(line, 1023);
+      REQUIRE(std::string_view(line) == "([(2.3)], [(4)])");
+
+      fin.getline(line, 1023);
+      REQUIRE(std::string_view(line) == "([(2.3,4)(2,0.3)], [(3,2.3)(1,-4)])");
+
+      fin.getline(line, 1023);
+      REQUIRE(std::string_view(line) == "([(2.3,4,-1)(2,7,2.3)(6,2,8)], [(3,2,1)(1,2,5)(2.1,3,-2.6)])");
+
+      fin.getline(line, 1023);
+      REQUIRE(std::string_view(line) == "(foo, bar)");
+    }
+
     std::filesystem::remove(filename);
     REQUIRE(not std::filesystem::exists(filename));
   }