From c9d357071e3a9be7d9e5d3bcd915673687bb23b5 Mon Sep 17 00:00:00 2001
From: Stephane Del Pino <stephane.delpino44@gmail.com>
Date: Thu, 21 May 2020 20:23:17 +0200
Subject: [PATCH] Add the possibility to passe vector of EmbeddedData to
 modules

Also one can now define tuples (which are viewed in the mathematical
sense: these are lists of elements of the same type).

As a proof of concept one can now define lists of boundary conditions
objects or pass lists to builtin functions (namely to module
functions).
---
 src/language/PEGGrammar.hpp                   | 14 +--
 src/language/ast/ASTBuilder.cpp               |  1 +
 .../ASTNodeAffectationExpressionBuilder.cpp   | 32 +++++++
 ...STNodeBuiltinFunctionExpressionBuilder.cpp | 20 ++++
 src/language/ast/ASTNodeDataType.cpp          |  9 +-
 src/language/ast/ASTNodeDataType.hpp          | 33 +++++--
 src/language/ast/ASTNodeDataTypeBuilder.cpp   | 37 ++++++++
 src/language/ast/ASTNodeExpressionBuilder.cpp |  5 +-
 .../ast/ASTNodeNaturalConversionChecker.cpp   |  9 ++
 src/language/modules/SchemeModule.cpp         | 93 +++++++------------
 .../ASTNodeExpressionListProcessor.hpp        |  8 +-
 .../node_processor/AffectationProcessor.hpp   | 46 +++++++++
 .../FunctionArgumentConverter.hpp             | 35 +++++++
 .../node_processor/OStreamProcessor.hpp       |  9 ++
 .../node_processor/TupleToVectorProcessor.hpp | 32 +++++++
 src/language/utils/ASTNodeDataTypeTraits.hpp  |  4 +
 .../utils/BuiltinFunctionEmbedder.hpp         | 15 +++
 src/language/utils/DataVariant.hpp            | 25 ++++-
 src/language/utils/SymbolTable.hpp            | 13 ++-
 src/utils/PugsTraits.hpp                      |  9 ++
 20 files changed, 366 insertions(+), 83 deletions(-)
 create mode 100644 src/language/node_processor/TupleToVectorProcessor.hpp

diff --git a/src/language/PEGGrammar.hpp b/src/language/PEGGrammar.hpp
index b68a1dd76..d2cd7c480 100644
--- a/src/language/PEGGrammar.hpp
+++ b/src/language/PEGGrammar.hpp
@@ -50,6 +50,9 @@ struct real
 struct escaped_c : one< '\'', '"', '?', '\\', 'a', 'b', 'f', 'n', 'r', 't', 'v' > {};
 struct character : if_must_else< one< '\\' >, escaped_c, ascii::any> {};
 
+struct open_parent : seq< one< '(' >, ignored > {};
+struct close_parent : seq< one< ')' >, ignored > {};
+
 struct literal : if_must< one< '"' >, until< one< '"' >, character > > {};
 
 struct import_kw :  TAO_PEGTL_KEYWORD("import") {};
@@ -72,11 +75,13 @@ struct vector_type : seq< R_set, ignored, one< '^' >, ignored, integer >{};
 struct basic_type : sor< scalar_type, string_type >{};
 
 struct type_name_id;
-struct type_specifier : sor< vector_type, basic_type, type_name_id >{};
+struct simple_type_specifier : sor< vector_type, basic_type, type_name_id >{};
+
+struct tuple_type_specifier : seq<open_parent, simple_type_specifier, ignored, close_parent>{};
 
-struct TYPE_SPECIFIER : seq< type_specifier, ignored >{};
+struct TYPE_SPECIFIER : seq< sor<simple_type_specifier, tuple_type_specifier>, ignored >{};
 
-struct type_expression : list_must< type_specifier, seq< ignored, one< '*' >, ignored > >{};
+struct type_expression : list_must< TYPE_SPECIFIER, seq< ignored, one< '*' >, ignored > >{};
 struct TYPE_EXPRESSION : seq< type_expression, ignored >{};
 
 struct and_kw : TAO_PEGTL_KEYWORD("and") {};
@@ -142,9 +147,6 @@ struct COLUMN : seq< column , ignored > {};
 struct comma : one< ',' > {};
 struct COMMA : seq< comma , ignored > {};
 
-struct open_parent : seq< one< '(' >, ignored > {};
-struct close_parent : seq< one< ')' >, ignored > {};
-
 struct expression;
 struct parented_expression : if_must< open_parent, expression, close_parent >{};
 
diff --git a/src/language/ast/ASTBuilder.cpp b/src/language/ast/ASTBuilder.cpp
index b0969abbb..cef779ffc 100644
--- a/src/language/ast/ASTBuilder.cpp
+++ b/src/language/ast/ASTBuilder.cpp
@@ -275,6 +275,7 @@ using selector = parse_tree::selector<
                                  language::unary_plus,
                                  language::unary_not,
                                  language::subscript_expression,
+                                 language::tuple_type_specifier,
                                  language::type_expression,
                                  language::unary_expression,
                                  language::name_subscript_expression>,
diff --git a/src/language/ast/ASTNodeAffectationExpressionBuilder.cpp b/src/language/ast/ASTNodeAffectationExpressionBuilder.cpp
index b0b9455db..1295383c4 100644
--- a/src/language/ast/ASTNodeAffectationExpressionBuilder.cpp
+++ b/src/language/ast/ASTNodeAffectationExpressionBuilder.cpp
@@ -5,6 +5,8 @@
 #include <language/ast/ASTNodeNaturalConversionChecker.hpp>
 #include <language/node_processor/AffectationProcessor.hpp>
 
+#include <utils/Exceptions.hpp>
+
 ASTNodeAffectationExpressionBuilder::ASTNodeAffectationExpressionBuilder(ASTNode& n)
 {
   auto set_affectation_processor = [](ASTNode& n, const auto& operator_v) {
@@ -206,6 +208,31 @@ ASTNodeAffectationExpressionBuilder::ASTNodeAffectationExpressionBuilder(ASTNode
           break;
         }
           // LCOV_EXCL_START
+        default: {
+          throw parse_error("unexpected error: undefined operand type for embedded data affectation",
+                            std::vector{n.children[1]->begin()});
+        }
+          // LCOV_EXCL_STOP
+        }
+      } else {
+        throw parse_error("invalid operator for '" + data_type.typeName() + "' affectation", std::vector{n.begin()});
+      }
+    };
+
+    auto set_affectation_processor_for_tuple_data = [&](const ASTNodeDataType& content_data_type,
+                                                        const ASTNodeDataType& data_type) {
+      if (content_data_type != ASTNodeDataType::type_id_t) {
+        throw NotImplementedError(dataTypeName(content_data_type) + " argument to tuple ");
+      }
+
+      using OperatorT = std::decay_t<decltype(operator_v)>;
+      if constexpr (std::is_same_v<OperatorT, language::eq_op>) {
+        switch (data_type) {
+        case ASTNodeDataType::list_t: {
+          n.m_node_processor = std::make_unique<AffectationFromListProcessor<OperatorT, EmbeddedData>>(n);
+          break;
+        }
+        // LCOV_EXCL_START
         default: {
           throw parse_error("unexpected error: undefined operand type for string affectation",
                             std::vector{n.children[1]->begin()});
@@ -267,6 +294,11 @@ ASTNodeAffectationExpressionBuilder::ASTNodeAffectationExpressionBuilder(ASTNode
         set_affectation_processor_for_embedded_data(data_type);
         break;
       }
+      case ASTNodeDataType::tuple_t: {
+        const ASTNodeDataType& content_type = value_type.contentType();
+        set_affectation_processor_for_tuple_data(content_type, data_type);
+        break;
+      }
       default: {
         throw parse_error("unexpected error: undefined value type for affectation",
                           std::vector{n.children[0]->begin()});
diff --git a/src/language/ast/ASTNodeBuiltinFunctionExpressionBuilder.cpp b/src/language/ast/ASTNodeBuiltinFunctionExpressionBuilder.cpp
index cda5d08cd..0ccb02573 100644
--- a/src/language/ast/ASTNodeBuiltinFunctionExpressionBuilder.cpp
+++ b/src/language/ast/ASTNodeBuiltinFunctionExpressionBuilder.cpp
@@ -76,6 +76,20 @@ ASTNodeBuiltinFunctionExpressionBuilder::_getArgumentConverter(const ASTNodeData
     }
   };
 
+  auto get_function_argument_to_tuple_converter = [&]() -> std::unique_ptr<IFunctionArgumentConverter> {
+    switch (argument_node_sub_data_type.m_data_type) {
+    case ASTNodeDataType::tuple_t: {
+      return std::make_unique<FunctionArgumentConverter<EmbeddedData, EmbeddedData>>(argument_number);
+    }
+    case ASTNodeDataType::list_t: {
+      return std::make_unique<FunctionListArgumentConverter<EmbeddedData, EmbeddedData>>(argument_number);
+    }
+    default: {
+      throw UnexpectedError(dataTypeName(argument_node_sub_data_type.m_data_type) + " argument to tuple ");
+    }
+    }
+  };
+
   auto get_function_argument_to_function_id_converter = [&]() -> std::unique_ptr<IFunctionArgumentConverter> {
     switch (argument_node_sub_data_type.m_data_type) {
     case ASTNodeDataType::function_t: {
@@ -115,6 +129,12 @@ ASTNodeBuiltinFunctionExpressionBuilder::_getArgumentConverter(const ASTNodeData
     }
     case ASTNodeDataType::function_t: {
       return get_function_argument_to_function_id_converter();
+    }
+    case ASTNodeDataType::tuple_t: {
+      if (parameter_type.contentType() != ASTNodeDataType::type_id_t) {
+        throw NotImplementedError(dataTypeName(parameter_type.contentType()) + " argument to tuple ");
+      }
+      return get_function_argument_to_tuple_converter();
     }
       // LCOV_EXCL_START
     default: {
diff --git a/src/language/ast/ASTNodeDataType.cpp b/src/language/ast/ASTNodeDataType.cpp
index 0befade16..923c751f8 100644
--- a/src/language/ast/ASTNodeDataType.cpp
+++ b/src/language/ast/ASTNodeDataType.cpp
@@ -41,6 +41,12 @@ dataTypeName(const ASTNodeDataType& data_type)
   case ASTNodeDataType::vector_t:
     name = "R^" + std::to_string(data_type.dimension());
     break;
+  case ASTNodeDataType::tuple_t:
+    name = "tuple(" + dataTypeName(data_type.contentType()) + ')';
+    break;
+  case ASTNodeDataType::list_t:
+    name = "list";
+    break;
   case ASTNodeDataType::string_t:
     name = "string";
     break;
@@ -62,9 +68,6 @@ dataTypeName(const ASTNodeDataType& data_type)
   case ASTNodeDataType::void_t:
     name = "void";
     break;
-  case ASTNodeDataType::list_t:
-    name = "list";
-    break;
   }
   return name;
 }
diff --git a/src/language/ast/ASTNodeDataType.hpp b/src/language/ast/ASTNodeDataType.hpp
index 7209d1c4f..3c6590518 100644
--- a/src/language/ast/ASTNodeDataType.hpp
+++ b/src/language/ast/ASTNodeDataType.hpp
@@ -1,7 +1,10 @@
 #ifndef AST_NODE_DATA_TYPE_HPP
 #define AST_NODE_DATA_TYPE_HPP
 
+#include <utils/PugsAssert.hpp>
+
 #include <limits>
+#include <memory>
 #include <string>
 
 class ASTNode;
@@ -17,8 +20,9 @@ class ASTNodeDataType
     unsigned_int_t     = 2,
     double_t           = 3,
     vector_t           = 4,
-    list_t             = 5,
-    string_t           = 6,
+    tuple_t            = 5,
+    list_t             = 6,
+    string_t           = 7,
     typename_t         = 10,
     type_name_id_t     = 11,
     type_id_t          = 21,
@@ -29,6 +33,7 @@ class ASTNodeDataType
 
  private:
   DataType m_data_type;
+  std::shared_ptr<ASTNodeDataType> m_content_type;
   size_t m_dimension;
   std::string m_type_name;
 
@@ -39,13 +44,20 @@ class ASTNodeDataType
     return m_dimension;
   }
 
+  const ASTNodeDataType&
+  contentType() const
+  {
+    Assert(m_content_type);
+    return *m_content_type;
+  }
+
   const std::string&
   typeName() const
   {
     return m_type_name;
   }
 
-  operator DataType() const
+  operator const DataType&() const
   {
     return m_data_type;
   }
@@ -53,14 +65,23 @@ class ASTNodeDataType
   ASTNodeDataType& operator=(const ASTNodeDataType&) = default;
   ASTNodeDataType& operator=(ASTNodeDataType&&) = default;
 
-  ASTNodeDataType(DataType data_type) : m_data_type{data_type}, m_dimension{1}, m_type_name{"unknown"} {}
+  ASTNodeDataType(DataType data_type)
+    : m_data_type{data_type}, m_content_type{nullptr}, m_dimension{1}, m_type_name{"unknown"}
+  {}
+
+  ASTNodeDataType(DataType data_type, const ASTNodeDataType& content_type)
+    : m_data_type{data_type},
+      m_content_type{std::make_shared<ASTNodeDataType>(content_type)},
+      m_dimension{1},
+      m_type_name{"unknown"}
+  {}
 
   ASTNodeDataType(DataType data_type, size_t dimension)
-    : m_data_type{data_type}, m_dimension{dimension}, m_type_name{"unknown"}
+    : m_data_type{data_type}, m_content_type{nullptr}, m_dimension{dimension}, m_type_name{"unknown"}
   {}
 
   ASTNodeDataType(DataType data_type, const std::string& type_name)
-    : m_data_type{data_type}, m_dimension{1}, m_type_name{type_name}
+    : m_data_type{data_type}, m_content_type{nullptr}, m_dimension{1}, m_type_name{type_name}
   {}
 
   ASTNodeDataType(const ASTNodeDataType&) = default;
diff --git a/src/language/ast/ASTNodeDataTypeBuilder.cpp b/src/language/ast/ASTNodeDataTypeBuilder.cpp
index 973d0fc99..ba69d9d89 100644
--- a/src/language/ast/ASTNodeDataTypeBuilder.cpp
+++ b/src/language/ast/ASTNodeDataTypeBuilder.cpp
@@ -37,6 +37,43 @@ ASTNodeDataTypeBuilder::_buildDeclarationNodeDataTypes(ASTNode& type_node, ASTNo
       data_type = ASTNodeDataType::double_t;
     } else if (type_node.is_type<language::vector_type>()) {
       data_type = getVectorDataType(type_node);
+    } else if (type_node.is_type<language::tuple_type_specifier>()) {
+      auto& content_node = type_node.children[0];
+
+      if (content_node->is_type<language::type_name_id>()) {
+        const std::string& type_name_id = content_node->string();
+
+        auto& symbol_table = *type_node.m_symbol_table;
+
+        auto [i_type_symbol, found] = symbol_table.find(type_name_id, content_node->begin());
+        if (not found) {
+          throw parse_error("undefined type identifier", std::vector{content_node->begin()});
+        } else if (i_type_symbol->attributes().dataType() != ASTNodeDataType::type_name_id_t) {
+          std::ostringstream os;
+          os << "invalid type identifier, '" << type_name_id << "' was previously defined as a '"
+             << dataTypeName(i_type_symbol->attributes().dataType()) << "'" << std::ends;
+          throw parse_error(os.str(), std::vector{content_node->begin()});
+        }
+
+        content_node->m_data_type = ASTNodeDataType{ASTNodeDataType::type_id_t, type_name_id};
+        i_type_symbol->attributes().setDataType(content_node->m_data_type);
+      } else if (content_node->is_type<language::B_set>()) {
+        data_type = ASTNodeDataType::bool_t;
+      } else if (content_node->is_type<language::Z_set>()) {
+        data_type = ASTNodeDataType::int_t;
+      } else if (content_node->is_type<language::N_set>()) {
+        data_type = ASTNodeDataType::unsigned_int_t;
+      } else if (content_node->is_type<language::R_set>()) {
+        data_type = ASTNodeDataType::double_t;
+      } else if (content_node->is_type<language::vector_type>()) {
+        data_type = getVectorDataType(type_node);
+      } else if (content_node->is_type<language::string_type>()) {
+        data_type = ASTNodeDataType::string_t;
+      } else {
+        throw UnexpectedError("unexpected content type in tuple");
+      }
+
+      data_type = ASTNodeDataType{ASTNodeDataType::tuple_t, content_node->m_data_type};
     } else if (type_node.is_type<language::string_type>()) {
       data_type = ASTNodeDataType::string_t;
     } else if (type_node.is_type<language::type_name_id>()) {
diff --git a/src/language/ast/ASTNodeExpressionBuilder.cpp b/src/language/ast/ASTNodeExpressionBuilder.cpp
index 48b807c2f..1f73444e1 100644
--- a/src/language/ast/ASTNodeExpressionBuilder.cpp
+++ b/src/language/ast/ASTNodeExpressionBuilder.cpp
@@ -19,6 +19,7 @@
 #include <language/node_processor/NameProcessor.hpp>
 #include <language/node_processor/OStreamProcessor.hpp>
 #include <language/node_processor/TupleToTinyVectorProcessor.hpp>
+#include <language/node_processor/TupleToVectorProcessor.hpp>
 #include <language/node_processor/ValueProcessor.hpp>
 #include <language/node_processor/WhileProcessor.hpp>
 
@@ -49,11 +50,9 @@ ASTNodeExpressionBuilder::_buildExpression(ASTNode& n)
       n.m_node_processor = std::make_unique<TupleToTinyVectorProcessor<ASTNodeExpressionListProcessor, 3>>(n);
       break;
     }
-      // LCOV_EXCL_START
     default: {
-      throw parse_error("unexpected error: invalid tuple size", n.begin());
+      n.m_node_processor = std::make_unique<TupleToVectorProcessor<ASTNodeExpressionListProcessor>>(n);
     }
-      // LCOV_EXCL_STOP
     }
   } else if (n.is_type<language::function_definition>()) {
     n.m_node_processor = std::make_unique<FakeProcessor>();
diff --git a/src/language/ast/ASTNodeNaturalConversionChecker.cpp b/src/language/ast/ASTNodeNaturalConversionChecker.cpp
index c32f77d30..8a6820d18 100644
--- a/src/language/ast/ASTNodeNaturalConversionChecker.cpp
+++ b/src/language/ast/ASTNodeNaturalConversionChecker.cpp
@@ -58,6 +58,15 @@ ASTNodeNaturalConversionChecker::_checkIsNaturalExpressionConversion(const ASTNo
       this->_checkIsNaturalTypeConversion(node, data_type, target_data_type);
     }
     }
+  } else if (target_data_type == ASTNodeDataType::tuple_t) {
+    const ASTNodeDataType& target_content_type = target_data_type.contentType();
+    if (node.m_data_type == ASTNodeDataType::list_t) {
+      for (const auto& child : node.children) {
+        this->_checkIsNaturalExpressionConversion(*child, child->m_data_type, target_content_type);
+      }
+    } else {
+      this->_checkIsNaturalTypeConversion(node, data_type, target_data_type);
+    }
   } else {
     this->_checkIsNaturalTypeConversion(node, data_type, target_data_type);
   }
diff --git a/src/language/modules/SchemeModule.cpp b/src/language/modules/SchemeModule.cpp
index 651516402..176489a7f 100644
--- a/src/language/modules/SchemeModule.cpp
+++ b/src/language/modules/SchemeModule.cpp
@@ -26,24 +26,10 @@ struct GlaceScheme
 
   const MeshType& m_mesh;
 
-  GlaceScheme(const IMesh& mesh,
-              std::shared_ptr<const IBoundaryConditionDescriptor> boundary_0,
-              std::shared_ptr<const IBoundaryConditionDescriptor> boundary_1,
-              std::shared_ptr<const IBoundaryConditionDescriptor> boundary_2,
-              std::shared_ptr<const IBoundaryConditionDescriptor> boundary_3,
-              std::shared_ptr<const IBoundaryConditionDescriptor> boundary_4,
-              std::shared_ptr<const IBoundaryConditionDescriptor> boundary_5)
+  GlaceScheme(const IMesh& mesh, std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>> bc_descriptor_list)
     : m_mesh{dynamic_cast<const MeshType&>(mesh)}
   {
     MeshDataType mesh_data(m_mesh);
-    std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>> bc_descriptor_list;
-    if constexpr (Dimension == 1) {
-      bc_descriptor_list = {boundary_0, boundary_1};
-    } else if constexpr (Dimension == 2) {
-      bc_descriptor_list = {boundary_0, boundary_1, boundary_2, boundary_3};
-    } else if constexpr (Dimension == 3) {
-      bc_descriptor_list = {boundary_0, boundary_1, boundary_2, boundary_3, boundary_4, boundary_5};
-    }
 
     std::cout << "number of bc descr = " << bc_descriptor_list.size() << '\n';
 
@@ -177,50 +163,35 @@ SchemeModule::SchemeModule()
 
                             ));
 
-  this->_addBuiltinFunction("glace",
-                            std::make_shared<BuiltinFunctionEmbedder<
-                              void, std::shared_ptr<const IMesh>, std::shared_ptr<const IBoundaryConditionDescriptor>,
-                              std::shared_ptr<const IBoundaryConditionDescriptor>,
-                              std::shared_ptr<const IBoundaryConditionDescriptor>,
-                              std::shared_ptr<const IBoundaryConditionDescriptor>,
-                              std::shared_ptr<const IBoundaryConditionDescriptor>,
-                              std::shared_ptr<const IBoundaryConditionDescriptor>>>(
-                              std::function<void(std::shared_ptr<const IMesh>,
-                                                 std::shared_ptr<const IBoundaryConditionDescriptor>,
-                                                 std::shared_ptr<const IBoundaryConditionDescriptor>,
-                                                 std::shared_ptr<const IBoundaryConditionDescriptor>,
-                                                 std::shared_ptr<const IBoundaryConditionDescriptor>,
-                                                 std::shared_ptr<const IBoundaryConditionDescriptor>,
-                                                 std::shared_ptr<const IBoundaryConditionDescriptor>)>{
-
-                                [](std::shared_ptr<const IMesh> p_mesh,
-                                   std::shared_ptr<const IBoundaryConditionDescriptor> boundary_0,
-                                   std::shared_ptr<const IBoundaryConditionDescriptor> boundary_1,
-                                   std::shared_ptr<const IBoundaryConditionDescriptor> boundary_2,
-                                   std::shared_ptr<const IBoundaryConditionDescriptor> boundary_3,
-                                   std::shared_ptr<const IBoundaryConditionDescriptor> boundary_4,
-                                   std::shared_ptr<const IBoundaryConditionDescriptor> boundary_5) -> void {
-                                  switch (p_mesh->dimension()) {
-                                  case 1: {
-                                    GlaceScheme<1>{*p_mesh,    boundary_0, boundary_1, boundary_2,
-                                                   boundary_3, boundary_4, boundary_5};
-                                    break;
-                                  }
-                                  case 2: {
-                                    GlaceScheme<2>{*p_mesh,    boundary_0, boundary_1, boundary_2,
-                                                   boundary_3, boundary_4, boundary_5};
-                                    break;
-                                  }
-                                  case 3: {
-                                    GlaceScheme<3>{*p_mesh,    boundary_0, boundary_1, boundary_2,
-                                                   boundary_3, boundary_4, boundary_5};
-                                    break;
-                                  }
-                                  default: {
-                                    throw UnexpectedError("invalid mesh dimension");
-                                  }
-                                  }
-                                }}
-
-                              ));
+  this
+    ->_addBuiltinFunction("glace",
+                          std::make_shared<
+                            BuiltinFunctionEmbedder<void, std::shared_ptr<const IMesh>,
+                                                    std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>>>(
+                            std::function<void(std::shared_ptr<const IMesh>,
+                                               std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>)>{
+
+                              [](std::shared_ptr<const IMesh> p_mesh,
+                                 const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&
+                                   bc_descriptor_list) -> void {
+                                switch (p_mesh->dimension()) {
+                                case 1: {
+                                  GlaceScheme<1>{*p_mesh, bc_descriptor_list};
+                                  break;
+                                }
+                                case 2: {
+                                  GlaceScheme<2>{*p_mesh, bc_descriptor_list};
+                                  break;
+                                }
+                                case 3: {
+                                  GlaceScheme<3>{*p_mesh, bc_descriptor_list};
+                                  break;
+                                }
+                                default: {
+                                  throw UnexpectedError("invalid mesh dimension");
+                                }
+                                }
+                              }
+
+                            }));
 }
diff --git a/src/language/node_processor/ASTNodeExpressionListProcessor.hpp b/src/language/node_processor/ASTNodeExpressionListProcessor.hpp
index e75992ee7..ec37d74ee 100644
--- a/src/language/node_processor/ASTNodeExpressionListProcessor.hpp
+++ b/src/language/node_processor/ASTNodeExpressionListProcessor.hpp
@@ -20,8 +20,12 @@ class ASTNodeExpressionListProcessor final : public INodeProcessor
       [&](auto&& v) {
         using ValueT = std::decay_t<decltype(v)>;
         if constexpr (std::is_same_v<ValueT, AggregateDataVariant>) {
-          for (size_t i = 0; i < v.size(); ++i) {
-            _flattenResults(std::move(v[i]), list_values);
+          if (v.isFlattenable()) {
+            for (size_t i = 0; i < v.size(); ++i) {
+              _flattenResults(std::move(v[i]), list_values);
+            }
+          } else {
+            list_values.emplace_back(v);
           }
         } else {
           list_values.emplace_back(v);
diff --git a/src/language/node_processor/AffectationProcessor.hpp b/src/language/node_processor/AffectationProcessor.hpp
index bd0431e5d..93d1396af 100644
--- a/src/language/node_processor/AffectationProcessor.hpp
+++ b/src/language/node_processor/AffectationProcessor.hpp
@@ -361,6 +361,52 @@ class AffectationFromListProcessor final : public INodeProcessor
   }
 };
 
+template <typename OperatorT>
+class AffectationFromListProcessor<OperatorT, EmbeddedData> final : public INodeProcessor
+{
+ private:
+  ASTNode& m_node;
+
+  DataVariant* m_lhs;
+
+ public:
+  DataVariant
+  execute(ExecutionPolicy& exec_policy)
+  {
+    AggregateDataVariant children_values = std::get<AggregateDataVariant>(m_node.children[1]->execute(exec_policy));
+
+    static_assert(std::is_same_v<OperatorT, language::eq_op>, "forbidden affection operator for list to vectors");
+
+    std::vector<EmbeddedData> tuple_value(children_values.size());
+    for (size_t i = 0; i < children_values.size(); ++i) {
+      std::visit(
+        [&](auto&& child_value) {
+          using T = std::decay_t<decltype(child_value)>;
+          if constexpr (std::is_same_v<T, EmbeddedData>) {
+            tuple_value[i] = child_value;
+          } else {
+            // LCOV_EXCL_START
+            throw parse_error("unexpected error: unexpected right hand side type in affectation", m_node.begin());
+            // LCOV_EXCL_STOP
+          }
+        },
+        children_values[i]);
+    }
+
+    *m_lhs = std::move(tuple_value);
+    return {};
+  }
+
+  AffectationFromListProcessor(ASTNode& node) : m_node{node}
+  {
+    const std::string& symbol = m_node.children[0]->string();
+    auto [i_symbol, found]    = m_node.m_symbol_table->find(symbol, m_node.children[0]->begin());
+    Assert(found);
+
+    m_lhs = &i_symbol->attributes().value();
+  }
+};
+
 template <typename ValueT>
 class AffectationFromZeroProcessor final : public INodeProcessor
 {
diff --git a/src/language/node_processor/FunctionArgumentConverter.hpp b/src/language/node_processor/FunctionArgumentConverter.hpp
index 8733211dc..445bf7ea9 100644
--- a/src/language/node_processor/FunctionArgumentConverter.hpp
+++ b/src/language/node_processor/FunctionArgumentConverter.hpp
@@ -3,6 +3,7 @@
 
 #include <language/node_processor/ExecutionPolicy.hpp>
 #include <language/utils/DataVariant.hpp>
+#include <utils/Exceptions.hpp>
 
 class IFunctionArgumentConverter
 {
@@ -43,6 +44,40 @@ class FunctionArgumentConverter final : public IFunctionArgumentConverter
   FunctionArgumentConverter(size_t argument_id) : m_argument_id{argument_id} {}
 };
 
+template <typename ExpectedValueType, typename ProvidedValueType>
+class FunctionListArgumentConverter final : public IFunctionArgumentConverter
+{
+ private:
+  size_t m_argument_id;
+
+ public:
+  DataVariant
+  convert(ExecutionPolicy& exec_policy, DataVariant&& value)
+  {
+    if constexpr (std::is_same_v<ExpectedValueType, ProvidedValueType>) {
+      std::visit(
+        [&](auto&& v) {
+          using Value_T = std::decay_t<decltype(v)>;
+          if constexpr (std::is_same_v<Value_T, AggregateDataVariant>) {
+            std::vector<ExpectedValueType> list_value;
+            for (size_t i = 0; i < v.size(); ++i) {
+              list_value.emplace_back(std::get<ProvidedValueType>(v[i]));
+            }
+            exec_policy.currentContext()[m_argument_id] = std::move(list_value);
+          } else {
+            throw UnexpectedError("unexpected value type");
+          }
+        },
+        value);
+    }
+    static_assert(std::is_same_v<ExpectedValueType, ProvidedValueType>, "conversion is not implemented");
+
+    return {};
+  }
+
+  FunctionListArgumentConverter(size_t argument_id) : m_argument_id{argument_id} {}
+};
+
 class FunctionArgumentToFunctionSymbolIdConverter final : public IFunctionArgumentConverter
 {
  private:
diff --git a/src/language/node_processor/OStreamProcessor.hpp b/src/language/node_processor/OStreamProcessor.hpp
index bc5778bc0..43d07364c 100644
--- a/src/language/node_processor/OStreamProcessor.hpp
+++ b/src/language/node_processor/OStreamProcessor.hpp
@@ -21,6 +21,15 @@ class OStreamProcessor final : public INodeProcessor
           if constexpr (not std::is_same_v<std::monostate, ValueT>) {
             if constexpr (std::is_same_v<bool, ValueT>) {
               m_os << std::boolalpha << value;
+            } else if constexpr (std::is_same_v<std::vector<EmbeddedData>, ValueT>) {
+              m_os << '(';
+              if (value.size() > 0) {
+                m_os << value[0];
+              }
+              for (size_t i = 1; i < value.size(); ++i) {
+                m_os << ", " << value[i];
+              }
+              m_os << ')';
             } else {
               m_os << value;
             }
diff --git a/src/language/node_processor/TupleToVectorProcessor.hpp b/src/language/node_processor/TupleToVectorProcessor.hpp
new file mode 100644
index 000000000..4908bfaa5
--- /dev/null
+++ b/src/language/node_processor/TupleToVectorProcessor.hpp
@@ -0,0 +1,32 @@
+#ifndef TUPLE_TO_VECTOR_PROCESSOR_HPP
+#define TUPLE_TO_VECTOR_PROCESSOR_HPP
+
+#include <language/ast/ASTNode.hpp>
+#include <language/node_processor/INodeProcessor.hpp>
+
+template <typename TupleProcessorT>
+class TupleToVectorProcessor final : public INodeProcessor
+{
+ private:
+  ASTNode& m_node;
+
+  std::unique_ptr<TupleProcessorT> m_tuple_processor;
+
+ public:
+  DataVariant
+  execute(ExecutionPolicy& exec_policy)
+  {
+    AggregateDataVariant v = std::get<AggregateDataVariant>(m_tuple_processor->execute(exec_policy));
+
+    v.setIsFlattenable(false);
+    return DataVariant{std::move(v)};
+  }
+
+  TupleToVectorProcessor(ASTNode& node) : m_node{node}, m_tuple_processor{std::make_unique<TupleProcessorT>(node)} {}
+
+  TupleToVectorProcessor(ASTNode& node, std::unique_ptr<TupleProcessorT>&& tuple_processor)
+    : m_node{node}, m_tuple_processor{std::move(tuple_processor)}
+  {}
+};
+
+#endif   // TUPLE_TO_TINY_VECTOR_PROCESSOR_HPP
diff --git a/src/language/utils/ASTNodeDataTypeTraits.hpp b/src/language/utils/ASTNodeDataTypeTraits.hpp
index 44d40a771..1956f2bc2 100644
--- a/src/language/utils/ASTNodeDataTypeTraits.hpp
+++ b/src/language/utils/ASTNodeDataTypeTraits.hpp
@@ -25,4 +25,8 @@ inline ASTNodeDataType ast_node_data_type_from<FunctionSymbolId> = ASTNodeDataTy
 template <size_t N>
 inline ASTNodeDataType ast_node_data_type_from<TinyVector<N>> = {ASTNodeDataType::vector_t, N};
 
+template <typename T>
+inline ASTNodeDataType ast_node_data_type_from<std::vector<T>> =
+  ASTNodeDataType{ASTNodeDataType::tuple_t, ast_node_data_type_from<T>};
+
 #endif   // AST_NODE_DATA_TYPE_TRAITS_H
diff --git a/src/language/utils/BuiltinFunctionEmbedder.hpp b/src/language/utils/BuiltinFunctionEmbedder.hpp
index 78efd7d30..4df112ac2 100644
--- a/src/language/utils/BuiltinFunctionEmbedder.hpp
+++ b/src/language/utils/BuiltinFunctionEmbedder.hpp
@@ -61,6 +61,21 @@ class BuiltinFunctionEmbedder : public IBuiltinFunctionEmbedder
           } else {
             throw UnexpectedError("unexpected argument types while casting: expecting EmbeddedData");
           }
+        } else if constexpr (std::is_same_v<Vi_Type, std::vector<EmbeddedData>>) {
+          if constexpr (is_vector_v<Ti_Type>) {
+            using Ti_value_type = typename Ti_Type::value_type;
+            static_assert(is_shared_ptr_v<Ti_value_type>, "expecting shared_ptr");
+
+            using Ti_handeled_type = typename Ti_value_type::element_type;
+            std::get<I>(t).resize(v_i.size());
+            for (size_t j = 0; j < v_i.size(); ++j) {
+              auto& data_handler = dynamic_cast<const DataHandler<Ti_handeled_type>&>(v_i[j].get());
+              std::get<I>(t)[j]  = data_handler.data_ptr();
+            }
+          } else {
+            throw UnexpectedError("Unexpected argument types while casting " + demangle<Vi_Type>() + " -> " +
+                                  demangle<Ti_Type>());
+          }
         } else {
           throw UnexpectedError("Unexpected argument types while casting " + demangle<Vi_Type>() + " -> " +
                                 demangle<Ti_Type>());
diff --git a/src/language/utils/DataVariant.hpp b/src/language/utils/DataVariant.hpp
index 1ca8a869d..0bb9a656c 100644
--- a/src/language/utils/DataVariant.hpp
+++ b/src/language/utils/DataVariant.hpp
@@ -19,6 +19,7 @@ using DataVariant = std::variant<std::monostate,
                                  double,
                                  std::string,
                                  EmbeddedData,
+                                 std::vector<EmbeddedData>,
                                  AggregateDataVariant,
                                  FunctionSymbolId,
                                  TinyVector<1>,
@@ -29,6 +30,7 @@ class AggregateDataVariant   // LCOV_EXCL_LINE
 {
  private:
   std::vector<DataVariant> m_data_vector;
+  bool m_is_flattenable = true;
 
   std::ostream&
   _printComponent(std::ostream& os, const DataVariant& value) const
@@ -37,6 +39,15 @@ class AggregateDataVariant   // LCOV_EXCL_LINE
       [&](auto&& v) {
         if constexpr (std::is_same_v<std::decay_t<decltype(v)>, std::monostate>) {
           os << " -- ";
+        } else if constexpr (std::is_same_v<std::decay_t<decltype(v)>, std::vector<EmbeddedData>>) {
+          os << '(';
+          if (v.size() > 0) {
+            os << v[0];
+          }
+          for (size_t i = 1; i < v.size(); ++i) {
+            os << ", " << v[i];
+          }
+          os << ')';
         } else {
           os << v;
         }
@@ -66,6 +77,18 @@ class AggregateDataVariant   // LCOV_EXCL_LINE
     return compound._print(os);
   }
 
+  void
+  setIsFlattenable(bool is_flattenable)
+  {
+    m_is_flattenable = is_flattenable;
+  }
+
+  bool
+  isFlattenable() const
+  {
+    return m_is_flattenable;
+  }
+
   PUGS_INLINE
   size_t
   size() const
@@ -90,7 +113,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) {}
+  AggregateDataVariant(std::vector<DataVariant>&& data_vector) : m_data_vector{data_vector} {}
 
   AggregateDataVariant(const AggregateDataVariant&) = default;
   AggregateDataVariant(AggregateDataVariant&&)      = default;
diff --git a/src/language/utils/SymbolTable.hpp b/src/language/utils/SymbolTable.hpp
index 957251371..75baa7e73 100644
--- a/src/language/utils/SymbolTable.hpp
+++ b/src/language/utils/SymbolTable.hpp
@@ -92,12 +92,23 @@ class SymbolTable
         os << "type_name_id:";
       } else if (attributes.m_data_type == ASTNodeDataType::type_id_t) {
         os << attributes.m_data_type.typeName() << ':';
+      } else if (attributes.m_data_type == ASTNodeDataType::tuple_t) {
+        os << attributes.m_data_type.typeName() << ':';
       }
       std::visit(
-        [&](const auto& value) {
+        [&](auto&& value) {
           using T = std::decay_t<decltype(value)>;
           if constexpr (std::is_same_v<T, std::monostate>) {
             os << "--";
+          } else if constexpr (std::is_same_v<T, std::vector<EmbeddedData>>) {
+            os << '(';
+            if (value.size() > 0) {
+              os << value[0];
+            }
+            for (size_t i = 1; i < value.size(); ++i) {
+              os << ", " << value[i];
+            }
+            os << ')';
           } else {
             os << value;
           }
diff --git a/src/utils/PugsTraits.hpp b/src/utils/PugsTraits.hpp
index 60316bfe4..b6e627200 100644
--- a/src/utils/PugsTraits.hpp
+++ b/src/utils/PugsTraits.hpp
@@ -4,6 +4,7 @@
 #include <cstddef>
 #include <memory>
 #include <type_traits>
+#include <vector>
 
 template <size_t N, typename T>
 class TinyVector;
@@ -38,4 +39,12 @@ inline constexpr bool is_shared_ptr_v = false;
 template <typename T>
 inline constexpr bool is_shared_ptr_v<std::shared_ptr<T>> = true;
 
+// Traits is_vector
+
+template <typename T>
+inline constexpr bool is_vector_v = false;
+
+template <typename T>
+inline constexpr bool is_vector_v<std::vector<T>> = true;
+
 #endif   // PUGS_TRAITS_HPP
-- 
GitLab