diff --git a/src/language/PEGGrammar.hpp b/src/language/PEGGrammar.hpp
index 61ba912a567c4a6d868d22cc7915e7557f484518..fb3b1d74abf6ba0d33113ec95996c6a270edb77d 100644
--- a/src/language/PEGGrammar.hpp
+++ b/src/language/PEGGrammar.hpp
@@ -229,7 +229,10 @@ struct expression : logical_or {};
 
 struct tuple_expression : seq< open_parent, expression, plus< if_must< COMMA, expression > >, close_parent >{};
 
-struct expression_list : seq< open_parent, sor< tuple_expression, expression >, plus< if_must< COMMA, sor< tuple_expression, expression > > >, close_parent >{};
+struct expression_list : seq< open_parent, sor< seq< tuple_expression,
+						     star< if_must< COMMA, sor< tuple_expression, expression > > > >,
+						seq< expression,
+						     plus< if_must< COMMA, sor< tuple_expression, expression > >  > > >, close_parent >{};
 
 struct affect_op : sor< eq_op, multiplyeq_op, divideeq_op, pluseq_op, minuseq_op > {};
 
diff --git a/src/language/ast/ASTBuilder.cpp b/src/language/ast/ASTBuilder.cpp
index cef779ffce63b9c2ad808ca94b993e7eff59d4ff..35d787354c37e5b1dbc3f176548fb4725d924cbb 100644
--- a/src/language/ast/ASTBuilder.cpp
+++ b/src/language/ast/ASTBuilder.cpp
@@ -138,7 +138,7 @@ struct ASTBuilder::simplify_node_list : parse_tree::apply<ASTBuilder::simplify_n
   transform(std::unique_ptr<ASTNode>& n, States&&... st)
   {
     if (n->is_type<language::name_list>() or n->is_type<language::lvalue_list>() or
-        n->is_type<language::function_argument_list>() or n->is_type<language::expression_list>()) {
+        n->is_type<language::function_argument_list>()) {
       if (n->children.size() == 1) {
         n = std::move(n->children.back());
         transform(n, st...);
@@ -255,6 +255,7 @@ using selector = parse_tree::selector<
                                 language::fct_declaration,
                                 language::type_mapping,
                                 language::function_definition,
+                                language::expression_list,
                                 language::if_statement,
                                 language::do_while_statement,
                                 language::while_statement,
@@ -303,8 +304,7 @@ using selector = parse_tree::selector<
                                  language::post_plusplus>,
   ASTBuilder::simplify_for_statement_block::on<language::for_statement_block>,
   parse_tree::discard_empty::on<language::ignored, language::semicol, language::block>,
-  ASTBuilder::simplify_node_list::
-    on<language::name_list, language::lvalue_list, language::function_argument_list, language::expression_list>,
+  ASTBuilder::simplify_node_list::on<language::name_list, language::lvalue_list, language::function_argument_list>,
   ASTBuilder::simplify_statement_block::on<language::statement_block>,
   ASTBuilder::simplify_for_init::on<language::for_init>,
   ASTBuilder::simplify_for_test::on<language::for_test>,
diff --git a/src/language/node_processor/AffectationProcessor.hpp b/src/language/node_processor/AffectationProcessor.hpp
index e15d4aa692e9ddbc54ac086317b9ce8289c28b4c..f3f8af44f38477007a0f85b2a3ee00738a5b82c9 100644
--- a/src/language/node_processor/AffectationProcessor.hpp
+++ b/src/language/node_processor/AffectationProcessor.hpp
@@ -133,7 +133,9 @@ class AffectationExecutor final : public IAffectationExecutor
                     if constexpr (std::is_convertible_v<Vi_T, double>) {
                       m_lhs[i] = vi;
                     } else {
+                      // LCOV_EXCL_START
                       throw UnexpectedError("unexpected rhs type in affectation");
+                      // LCOV_EXCL_STOP
                     }
                   },
                   v[i]);
@@ -145,7 +147,9 @@ class AffectationExecutor final : public IAffectationExecutor
                   if constexpr (std::is_convertible_v<Vi_T, double>) {
                     m_lhs = v;
                   } else {
+                    // LCOV_EXCL_START
                     throw UnexpectedError("unexpected rhs type in affectation");
+                    // LCOV_EXCL_STOP
                   }
                 },
                 rhs);
@@ -415,10 +419,16 @@ class AffectationToTupleProcessor final : public INodeProcessor
           } else {
             std::ostringstream os;
             os << v << std::ends;
-            *m_lhs = std::vector{os.str()};
+            *m_lhs = std::vector<std::string>{os.str()};
           }
         } else if constexpr (std::is_same_v<ValueT, TinyVector<1>> and std::is_arithmetic_v<T>) {
-          *m_lhs = std::vector{TinyVector<1>{static_cast<double>(v)}};
+          *m_lhs = std::vector<TinyVector<1>>{TinyVector<1>{static_cast<double>(v)}};
+        } else if constexpr (std::is_same_v<ValueT, TinyVector<2>> and std::is_same_v<T, int64_t>) {
+          Assert(v == 0);
+          *m_lhs = std::vector<TinyVector<2>>{TinyVector<2>{zero}};
+        } else if constexpr (std::is_same_v<ValueT, TinyVector<3>> and std::is_same_v<T, int64_t>) {
+          Assert(v == 0);
+          *m_lhs = std::vector<TinyVector<3>>{TinyVector<3>{zero}};
         } else {
           // LCOV_EXCL_START
           throw ParseError("unexpected error: unexpected right hand side type in affectation", m_node.begin());
diff --git a/src/language/node_processor/BuiltinFunctionProcessor.hpp b/src/language/node_processor/BuiltinFunctionProcessor.hpp
index 302cbaf2d2ce3570678fb62a57d4e463e47a2432..4e5583adb2d269e7552ff3cccf7dfcbe811b0791 100644
--- a/src/language/node_processor/BuiltinFunctionProcessor.hpp
+++ b/src/language/node_processor/BuiltinFunctionProcessor.hpp
@@ -64,7 +64,9 @@ class BuiltinFunctionProcessor : public INodeProcessor
     }
 
     if (SignalManager::pauseOnError()) {
+      // LCOV_EXCL_START
       return m_function_expression_processor->execute(context_exec_policy);
+      // LCOV_EXCL_STOP
     } else {
       try {
         return m_function_expression_processor->execute(context_exec_policy);
diff --git a/src/language/node_processor/DoWhileProcessor.hpp b/src/language/node_processor/DoWhileProcessor.hpp
index 5b03b769ffe3e376c5e8bddafa91db5d01e2f59f..6f3c14b7f8706c6d5480f792e449c1f110b74498 100644
--- a/src/language/node_processor/DoWhileProcessor.hpp
+++ b/src/language/node_processor/DoWhileProcessor.hpp
@@ -3,6 +3,7 @@
 
 #include <language/ast/ASTNode.hpp>
 #include <language/node_processor/INodeProcessor.hpp>
+#include <language/utils/SymbolTable.hpp>
 
 class DoWhileProcessor final : public INodeProcessor
 {
diff --git a/src/language/node_processor/ForProcessor.hpp b/src/language/node_processor/ForProcessor.hpp
index 8680aaf7ded0f7fabbc345682a9a32c772a44f08..4199e0c42867398167c2f1fa5927d87e7f4fddc6 100644
--- a/src/language/node_processor/ForProcessor.hpp
+++ b/src/language/node_processor/ForProcessor.hpp
@@ -3,6 +3,7 @@
 
 #include <language/ast/ASTNode.hpp>
 #include <language/node_processor/INodeProcessor.hpp>
+#include <language/utils/SymbolTable.hpp>
 
 class ForProcessor final : public INodeProcessor
 {
diff --git a/src/language/node_processor/FunctionArgumentConverter.hpp b/src/language/node_processor/FunctionArgumentConverter.hpp
index 4bf7e56eebfd93b244d13a774e2e69aac2834794..9e7eec757fb2c396125d6d299f5a7529ca420009 100644
--- a/src/language/node_processor/FunctionArgumentConverter.hpp
+++ b/src/language/node_processor/FunctionArgumentConverter.hpp
@@ -31,9 +31,20 @@ class FunctionArgumentToStringConverter final : public IFunctionArgumentConverte
   DataVariant
   convert(ExecutionPolicy& exec_policy, DataVariant&& value)
   {
-    std::ostringstream sout;
-    sout << value;
-    exec_policy.currentContext()[m_argument_id] = sout.str();
+    std::visit(
+      [&](auto&& v) {
+        using T = std::decay_t<decltype(v)>;
+        if constexpr (std::is_arithmetic_v<T>) {
+          exec_policy.currentContext()[m_argument_id] = std::to_string(v);
+        } else if constexpr (std::is_same_v<T, std::string>) {
+          exec_policy.currentContext()[m_argument_id] = v;
+        } else {
+          std::ostringstream sout;
+          sout << value << std::ends;
+          exec_policy.currentContext()[m_argument_id] = sout.str();
+        }
+      },
+      value);
     return {};
   }
 
@@ -100,6 +111,7 @@ class FunctionTinyVectorArgumentConverter final : public IFunctionArgumentConver
     } else if constexpr (std::is_same_v<ProvidedValueType, ZeroType>) {
       exec_policy.currentContext()[m_argument_id] = ExpectedValueType{ZeroType::zero};
     } else {
+      static_assert(std::is_same_v<ExpectedValueType, TinyVector<1>>);
       exec_policy.currentContext()[m_argument_id] =
         std::move(static_cast<ExpectedValueType>(std::get<ProvidedValueType>(value)));
     }
@@ -132,19 +144,36 @@ class FunctionTupleArgumentConverter final : public IFunctionArgumentConverter
               TupleType list_value;
               list_value.reserve(v.size());
               for (size_t i = 0; i < v.size(); ++i) {
-                list_value.emplace_back(v[i]);
+                list_value.emplace_back(std::move(v[i]));
               }
               exec_policy.currentContext()[m_argument_id] = std::move(list_value);
+            } else if constexpr ((std::is_convertible_v<ContentT, ContentType>)and not is_tiny_vector_v<ContentType>) {
+              TupleType list_value;
+              list_value.reserve(v.size());
+              for (size_t i = 0; i < v.size(); ++i) {
+                list_value.push_back(static_cast<ContentType>(v[i]));
+              }
+              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 (std::is_convertible_v<ValueT, ContentType> and not is_tiny_vector_v<ContentType>) {
             exec_policy.currentContext()[m_argument_id] = std::move(TupleType{static_cast<ContentType>(v)});
           } else {
-            throw UnexpectedError(demangle<ValueT>() + " unexpected value type");
+            throw UnexpectedError(std::string{"cannot convert '"} + demangle<ValueT>() + "' to '" +
+                                  demangle<ContentType>() + "'");
           }
         },
         value);
+
     } else {
-      throw UnexpectedError(demangle<std::decay_t<decltype(*this)>>() + ": did nothing!");
+      // LCOV_EXCL_START
+      throw UnexpectedError(std::string{"cannot convert '"} + demangle<ProvidedValueType>() + "' to '" +
+                            demangle<ContentType>() + "'");
+      // LCOV_EXCL_STOP
     }
     return {};
   }
@@ -174,31 +203,43 @@ class FunctionListArgumentConverter final : public IFunctionArgumentConverter
               std::visit(
                 [&](auto&& vi) {
                   using Vi_T = std::decay_t<decltype(vi)>;
-                  if constexpr (is_tiny_vector_v<ContentType>) {
-                    throw NotImplementedError("TinyVector case");
+                  if constexpr (std::is_same_v<Vi_T, ContentType>) {
+                    list_value.emplace_back(vi);
+                  } else if constexpr (is_tiny_vector_v<ContentType>) {
+                    // LCOV_EXCL_START
+                    throw UnexpectedError(std::string{"invalid conversion of '"} + demangle<Vi_T>() + "' to '" +
+                                          demangle<ContentType>() + "'");
+                    // LCOV_EXCL_STOP
                   } else if constexpr (std::is_convertible_v<Vi_T, ContentType>) {
                     list_value.emplace_back(vi);
                   } else {
+                    // LCOV_EXCL_START
                     throw UnexpectedError("unexpected types");
+                    // LCOV_EXCL_STOP
                   }
                 },
                 (v[i]));
             }
             exec_policy.currentContext()[m_argument_id] = std::move(list_value);
-          } else if constexpr (std::is_same_v<ValueT, ContentType>) {
-            exec_policy.currentContext()[m_argument_id] = std::move(v);
           } else 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(v[i]);
-              }
-              exec_policy.currentContext()[m_argument_id] = std::move(list_value);
+              exec_policy.currentContext()[m_argument_id] = v;
+            } else {
+              // LCOV_EXCL_START
+              throw UnexpectedError(std::string{"invalid conversion of '"} + demangle<ContentT>() + "' to '" +
+                                    demangle<ContentType>() + "'");
+              // LCOV_EXCL_STOP
             }
+          } else if constexpr (std::is_same_v<ValueT, ContentType>) {
+            exec_policy.currentContext()[m_argument_id] = std::move(TupleType{v});
+          } else if constexpr (std::is_convertible_v<ValueT, ContentType> and not is_tiny_vector_v<ValueT> and
+                               not is_tiny_vector_v<ContentType>) {
+            exec_policy.currentContext()[m_argument_id] = std::move(TupleType{static_cast<ContentType>(v)});
           } else {
+            // LCOV_EXCL_START
             throw UnexpectedError(demangle<ValueT>() + " unexpected value type");
+            // LCOV_EXCL_STOP
           }
         },
         value);
diff --git a/src/language/node_processor/IfProcessor.hpp b/src/language/node_processor/IfProcessor.hpp
index 1a76e3068dcb6bd29f4e4e250dc3d6cc1f9f6d41..18d87bb89f11ee0f335fe0637f4569dc5b3cab15 100644
--- a/src/language/node_processor/IfProcessor.hpp
+++ b/src/language/node_processor/IfProcessor.hpp
@@ -3,6 +3,7 @@
 
 #include <language/ast/ASTNode.hpp>
 #include <language/node_processor/INodeProcessor.hpp>
+#include <language/utils/SymbolTable.hpp>
 
 class IfProcessor final : public INodeProcessor
 {
@@ -39,8 +40,7 @@ class IfProcessor final : public INodeProcessor
       }
     }
 
-    if (m_node.children[0]->m_symbol_table != m_node.m_symbol_table)
-      m_node.children[0]->m_symbol_table->clearValues();
+    Assert(m_node.children[0]->m_symbol_table == m_node.m_symbol_table);
     return {};
   }
 
diff --git a/src/language/node_processor/WhileProcessor.hpp b/src/language/node_processor/WhileProcessor.hpp
index 20b0ac985ae43bfaf3f9b165f9a346aa82a9f3cb..af7cc604bb6c2d5f74cb894a1f05ba20acfb35f6 100644
--- a/src/language/node_processor/WhileProcessor.hpp
+++ b/src/language/node_processor/WhileProcessor.hpp
@@ -3,6 +3,7 @@
 
 #include <language/ast/ASTNode.hpp>
 #include <language/node_processor/INodeProcessor.hpp>
+#include <language/utils/SymbolTable.hpp>
 
 class WhileProcessor final : public INodeProcessor
 {
diff --git a/src/language/utils/DataVariant.hpp b/src/language/utils/DataVariant.hpp
index 13744172a73c2e3c54d97412cd654ae7211ad42c..55e537f0922a53020797b360d4c235f358bcbc8c 100644
--- a/src/language/utils/DataVariant.hpp
+++ b/src/language/utils/DataVariant.hpp
@@ -71,14 +71,16 @@ class AggregateDataVariant   // LCOV_EXCL_LINE
   }
 
   PUGS_INLINE
-  DataVariant& operator[](size_t i)
+  DataVariant&
+  operator[](size_t i)
   {
     Assert(i < m_data_vector.size());
     return m_data_vector[i];
   }
 
   PUGS_INLINE
-  const DataVariant& operator[](size_t i) const
+  const DataVariant&
+  operator[](size_t i) const
   {
     Assert(i < m_data_vector.size());
     return m_data_vector[i];
diff --git a/src/language/utils/SymbolTable.hpp b/src/language/utils/SymbolTable.hpp
index 6bcf267780884d1d8a1492b664ff492026ddd869..39d414b1e120255e9d51c17266512458fcc5f1b8 100644
--- a/src/language/utils/SymbolTable.hpp
+++ b/src/language/utils/SymbolTable.hpp
@@ -267,7 +267,12 @@ class SymbolTable
   clearValues()
   {
     for (auto& symbol : m_symbol_list) {
-      symbol.attributes().value() = DataVariant{};
+      std::visit(
+        [](auto&& value) {
+          using T = std::decay_t<decltype(value)>;
+          value   = T{};
+        },
+        symbol.attributes().value());
     }
   }
 
diff --git a/src/utils/Types.hpp b/src/utils/Types.hpp
index 482190cb6f4c34c614a1237c4c025bbb37812e20..2d53c59f17d00fbd44d92ebc5db06e529a5d47db 100644
--- a/src/utils/Types.hpp
+++ b/src/utils/Types.hpp
@@ -5,12 +5,12 @@ enum class ZeroType
 {
   zero
 };
-constexpr ZeroType zero = ZeroType::zero;
+constexpr inline ZeroType zero = ZeroType::zero;
 
 enum class IdentityType
 {
   identity
 };
-constexpr IdentityType identity = IdentityType::identity;
+constexpr inline IdentityType identity = IdentityType::identity;
 
 #endif   // TYPES_HPP
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 8d0344a03785fc30e30a20b71b498d7cd2b28d4a..dbaf666c108fe6d96830c53bdb7c87aa50a912c2 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -8,6 +8,7 @@ add_executable (unit_tests
   test_main.cpp
   test_AffectationProcessor.cpp
   test_AffectationToStringProcessor.cpp
+  test_AffectationToTupleProcessor.cpp
   test_Array.cpp
   test_ArraySubscriptProcessor.cpp
   test_ArrayUtils.cpp
@@ -57,6 +58,7 @@ add_executable (unit_tests
   test_ExecutionPolicy.cpp
   test_FakeProcessor.cpp
   test_ForProcessor.cpp
+  test_FunctionArgumentConverter.cpp
   test_FunctionProcessor.cpp
   test_FunctionSymbolId.cpp
   test_FunctionTable.cpp
@@ -68,6 +70,7 @@ add_executable (unit_tests
   test_MathModule.cpp
   test_NameProcessor.cpp
   test_OStreamProcessor.cpp
+  test_ParseError.cpp
   test_PugsFunctionAdapter.cpp
   test_PugsAssert.cpp
   test_RevisionInfo.cpp
diff --git a/tests/test_ASTBuilder.cpp b/tests/test_ASTBuilder.cpp
index 2399a78617d6a6f30774fbc70be735d5850967d3..7353fa9a46d6d08542c62d4bee89299c930fc5a1 100644
--- a/tests/test_ASTBuilder.cpp
+++ b/tests/test_ASTBuilder.cpp
@@ -620,6 +620,41 @@ clog << "log " << l << "\n";
      +-(language::literal:"log ")
      +-(language::name:l)
      `-(language::literal:"\n")
+)";
+      CHECK_AST(data, result);
+    }
+
+    SECTION("tuple list simplification")
+    {
+      std::string_view data = R"(
+let x:(R^2), x=((0,0),(2,3));
+let y:(R^2), y=((0));
+)";
+
+      std::string_view result = R"(
+(root)
+ +-(language::var_declaration)
+ |   +-(language::name:x)
+ |   +-(language::tuple_type_specifier)
+ |   |   `-(language::vector_type)
+ |   |       +-(language::R_set)
+ |   |       `-(language::integer:2)
+ |   +-(language::name:x)
+ |   `-(language::expression_list)
+ |       +-(language::tuple_expression)
+ |       |   +-(language::integer:0)
+ |       |   `-(language::integer:0)
+ |       `-(language::tuple_expression)
+ |           +-(language::integer:2)
+ |           `-(language::integer:3)
+ `-(language::var_declaration)
+     +-(language::name:y)
+     +-(language::tuple_type_specifier)
+     |   `-(language::vector_type)
+     |       +-(language::R_set)
+     |       `-(language::integer:2)
+     +-(language::name:y)
+     `-(language::integer:0)
 )";
       CHECK_AST(data, result);
     }
diff --git a/tests/test_ASTSymbolInitializationChecker.cpp b/tests/test_ASTSymbolInitializationChecker.cpp
index fe59815acf5c65c642a81155fa30abf422a5b471..fb0c8598beb79861f6cbbb66ebe37461c66b61e5 100644
--- a/tests/test_ASTSymbolInitializationChecker.cpp
+++ b/tests/test_ASTSymbolInitializationChecker.cpp
@@ -301,5 +301,18 @@ let f : R->R, x->x+y;
       ASTSymbolTableBuilder{*ast};
       REQUIRE_THROWS_WITH(ASTSymbolInitializationChecker{*ast}, std::string{"uninitialized symbol 'y'"});
     }
+
+    SECTION("expecting a list of identifiers")
+    {
+      std::string_view data = R"(
+let (x,y,z):R*R*R, x = 3;
+)";
+
+      string_input input{data, "test.pgs"};
+      auto ast = ASTBuilder::build(input);
+
+      ASTSymbolTableBuilder{*ast};
+      REQUIRE_THROWS_WITH(ASTSymbolInitializationChecker{*ast}, std::string{"expecting a list of identifiers"});
+    }
   }
 }
diff --git a/tests/test_AffectationToTupleProcessor.cpp b/tests/test_AffectationToTupleProcessor.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3720bc856b30fa39468e90a7a56cc0ce2d01058d
--- /dev/null
+++ b/tests/test_AffectationToTupleProcessor.cpp
@@ -0,0 +1,175 @@
+#include <catch2/catch.hpp>
+
+#include <language/ast/ASTBuilder.hpp>
+#include <language/ast/ASTNodeAffectationExpressionBuilder.hpp>
+#include <language/ast/ASTNodeDataTypeBuilder.hpp>
+#include <language/ast/ASTNodeDeclarationToAffectationConverter.hpp>
+#include <language/ast/ASTNodeExpressionBuilder.hpp>
+#include <language/ast/ASTNodeTypeCleaner.hpp>
+#include <language/ast/ASTSymbolTableBuilder.hpp>
+#include <language/utils/ASTPrinter.hpp>
+#include <utils/Demangle.hpp>
+
+#include <pegtl/string_input.hpp>
+
+#include <sstream>
+
+#define CHECK_AFFECTATION_RESULT(data, variable_name, expected_value)         \
+  {                                                                           \
+    string_input input{data, "test.pgs"};                                     \
+    auto ast = ASTBuilder::build(input);                                      \
+                                                                              \
+    ASTSymbolTableBuilder{*ast};                                              \
+    ASTNodeDataTypeBuilder{*ast};                                             \
+                                                                              \
+    ASTNodeDeclarationToAffectationConverter{*ast};                           \
+    ASTNodeTypeCleaner<language::var_declaration>{*ast};                      \
+                                                                              \
+    ASTNodeExpressionBuilder{*ast};                                           \
+    ExecutionPolicy exec_policy;                                              \
+    ast->execute(exec_policy);                                                \
+                                                                              \
+    auto symbol_table = ast->m_symbol_table;                                  \
+                                                                              \
+    using namespace TAO_PEGTL_NAMESPACE;                                      \
+    position use_position{internal::iterator{"fixture"}, "fixture"};          \
+    use_position.byte    = 10000;                                             \
+    auto [symbol, found] = symbol_table->find(variable_name, use_position);   \
+                                                                              \
+    auto attributes = symbol->attributes();                                   \
+    auto value      = std::get<decltype(expected_value)>(attributes.value()); \
+                                                                              \
+    REQUIRE(value == expected_value);                                         \
+  }
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("ASTAffectationToTupleProcessor", "[language]")
+{
+  SECTION("Affectations from value")
+  {
+    CHECK_AFFECTATION_RESULT(R"(
+let s :(R); s = 2.;
+)",
+                             "s", (std::vector<double>{2.}));
+
+    CHECK_AFFECTATION_RESULT(R"(
+let s :(R); s = 2;
+)",
+                             "s", (std::vector<double>{2}));
+
+    CHECK_AFFECTATION_RESULT(R"(
+let s :(string); s = 2.;
+)",
+                             "s", (std::vector<std::string>{std::to_string(2.)}));
+
+    const std::string x_string = []() -> std::string {
+      std::ostringstream os;
+      os << TinyVector<3, double>{1, 2, 3} << std::ends;
+      return os.str();
+    }();
+
+    CHECK_AFFECTATION_RESULT(R"(
+let x :R^3, x = (1,2,3);
+let s :(string); s = x;
+)",
+                             "s", (std::vector<std::string>{x_string}));
+
+    CHECK_AFFECTATION_RESULT(R"(
+let s :(R^1); s = 1.3;
+)",
+                             "s", (std::vector<TinyVector<1>>{TinyVector<1>{1.3}}));
+  }
+
+  SECTION("Affectations from list")
+  {
+    CHECK_AFFECTATION_RESULT(R"(
+let t :(R); t = (2.,3);
+)",
+                             "t", (std::vector<double>{2., 3}));
+
+    CHECK_AFFECTATION_RESULT(R"(
+let s :(string); s = (2.,3);
+)",
+                             "s", (std::vector<std::string>{std::to_string(2.), std::to_string(3)}));
+
+    CHECK_AFFECTATION_RESULT(R"(
+let s :(string); s = (2.,3,"foo");
+)",
+                             "s",
+                             (std::vector<std::string>{std::to_string(2.), std::to_string(3), std::string{"foo"}}));
+
+    const std::string x_string = []() -> std::string {
+      std::ostringstream os;
+      os << TinyVector<2, double>{1, 2} << std::ends;
+      return os.str();
+    }();
+
+    CHECK_AFFECTATION_RESULT(R"(
+let x : R^2, x = (1,2);
+let s : (string); s = (2.,3, x);
+)",
+                             "s", (std::vector<std::string>{std::to_string(2.), std::to_string(3), x_string}));
+
+    CHECK_AFFECTATION_RESULT(R"(
+let x : R^2, x = (1,2);
+let t :(R^2); t = (x,0);
+)",
+                             "t", (std::vector<TinyVector<2>>{TinyVector<2>{1, 2}, TinyVector<2>{0, 0}}));
+
+    CHECK_AFFECTATION_RESULT(R"(
+let t :(R^2); t = ((1,2),0);
+)",
+                             "t", (std::vector<TinyVector<2>>{TinyVector<2>{1, 2}, TinyVector<2>{0, 0}}));
+
+    CHECK_AFFECTATION_RESULT(R"(
+let t :(R^2); t = (0);
+)",
+                             "t", (std::vector<TinyVector<2>>{TinyVector<2>{0, 0}}));
+
+    CHECK_AFFECTATION_RESULT(R"(
+let t :(R^3); t = (0);
+)",
+                             "t", (std::vector<TinyVector<3>>{TinyVector<3>{0, 0, 0}}));
+
+    CHECK_AFFECTATION_RESULT(R"(
+let x : R^1, x = 1;
+let t :(R^1); t = (x,2);
+)",
+                             "t", (std::vector<TinyVector<1>>{TinyVector<1>{1}, TinyVector<1>{2}}));
+  }
+
+  SECTION("Affectations from tuple")
+  {
+    const std::string x_string = []() -> std::string {
+      std::ostringstream os;
+      os << TinyVector<3, double>{1, 2, 3} << std::ends;
+      return os.str();
+    }();
+
+    CHECK_AFFECTATION_RESULT(R"(
+let x :(R^3), x = ((1,2,3));
+let s :(string); s = x;
+)",
+                             "s", (std::vector<std::string>{x_string}));
+
+    CHECK_AFFECTATION_RESULT(R"(
+let x :(R), x = (1,2,3);
+let s :(string); s = x;
+)",
+                             "s",
+                             (std::vector<std::string>{std::to_string(1.), std::to_string(2.), std::to_string(3.)}));
+
+    CHECK_AFFECTATION_RESULT(R"(
+let n :(N), n = (1,2,3);
+let t :(R); t = n;
+)",
+                             "t", (std::vector<double>{1, 2, 3}));
+
+    CHECK_AFFECTATION_RESULT(R"(
+let s :(N), s = (1,2,3);
+let t :(N); t = s;
+)",
+                             "t", (std::vector<uint64_t>{1, 2, 3}));
+  }
+}
diff --git a/tests/test_DataVariant.cpp b/tests/test_DataVariant.cpp
index 1fa8cf72be599782c25fd88f22613e465df818e8..92ca82b9aeb7ee68c9698f973ce48d457fd27696 100644
--- a/tests/test_DataVariant.cpp
+++ b/tests/test_DataVariant.cpp
@@ -34,7 +34,7 @@ TEST_CASE("DataVariant", "[language]")
       REQUIRE(std::get<std::vector<double>>(aggregate[2]) == std::vector<double>{1, 2.7});
     }
 
-    SECTION("Copy")
+    SECTION("copy")
     {
       AggregateDataVariant aggregate_copy{aggregate};
 
@@ -48,5 +48,40 @@ TEST_CASE("DataVariant", "[language]")
       REQUIRE(std::get<int64_t>(aggregate[1]) == std::get<int64_t>(aggregate_copy[1]));
       REQUIRE(std::get<std::vector<double>>(aggregate[2]) == std::get<std::vector<double>>(aggregate_copy[2]));
     }
+
+    SECTION("affectation")
+    {
+      AggregateDataVariant aggregate_copy;
+      aggregate_copy = aggregate;
+
+      REQUIRE(aggregate.size() == aggregate_copy.size());
+
+      for (size_t i = 0; i < aggregate.size(); ++i) {
+        REQUIRE(aggregate[i].index() == aggregate_copy[i].index());
+      }
+
+      REQUIRE(std::get<double>(aggregate[0]) == std::get<double>(aggregate_copy[0]));
+      REQUIRE(std::get<int64_t>(aggregate[1]) == std::get<int64_t>(aggregate_copy[1]));
+      REQUIRE(std::get<std::vector<double>>(aggregate[2]) == std::get<std::vector<double>>(aggregate_copy[2]));
+    }
+
+    SECTION("move affectation")
+    {
+      AggregateDataVariant aggregate_move_copy;
+      {
+        AggregateDataVariant aggregate_copy{aggregate};
+        aggregate_move_copy = std::move(aggregate_copy);
+      }
+
+      REQUIRE(aggregate.size() == aggregate_move_copy.size());
+
+      for (size_t i = 0; i < aggregate.size(); ++i) {
+        REQUIRE(aggregate[i].index() == aggregate_move_copy[i].index());
+      }
+
+      REQUIRE(std::get<double>(aggregate[0]) == std::get<double>(aggregate_move_copy[0]));
+      REQUIRE(std::get<int64_t>(aggregate[1]) == std::get<int64_t>(aggregate_move_copy[1]));
+      REQUIRE(std::get<std::vector<double>>(aggregate[2]) == std::get<std::vector<double>>(aggregate_move_copy[2]));
+    }
   }
 }
diff --git a/tests/test_FunctionArgumentConverter.cpp b/tests/test_FunctionArgumentConverter.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e3d2ee0939c306fb971274c782268b161125bee6
--- /dev/null
+++ b/tests/test_FunctionArgumentConverter.cpp
@@ -0,0 +1,160 @@
+#include <catch2/catch.hpp>
+
+#include <language/node_processor/FunctionArgumentConverter.hpp>
+#include <language/utils/SymbolTable.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("FunctionArgumentConverter", "[language]")
+{
+  ExecutionPolicy::Context context(0, std::make_shared<ExecutionPolicy::Context::Values>(3));
+  ExecutionPolicy execution_policy(ExecutionPolicy{}, context);
+
+  SECTION("FunctionArgumentToStringConverter")
+  {
+    const std::string s{"foo"};
+    FunctionArgumentToStringConverter converter0{0};
+    converter0.convert(execution_policy, s);
+
+    const TinyVector<3> X{1, 3.2, 4};
+    FunctionArgumentToStringConverter converter1{1};
+    converter1.convert(execution_policy, X);
+    std::ostringstream os_X;
+    os_X << X << std::ends;
+
+    const double x = 3.2;
+    FunctionArgumentToStringConverter converter2{2};
+    converter2.convert(execution_policy, x);
+
+    REQUIRE(std::get<std::string>(execution_policy.currentContext()[0]) == s);
+    REQUIRE(std::get<std::string>(execution_policy.currentContext()[1]) == os_X.str());
+    REQUIRE(std::get<std::string>(execution_policy.currentContext()[2]) == std::to_string(x));
+  }
+
+  SECTION("FunctionArgumentConverter")
+  {
+    const double double_value = 1.7;
+    FunctionArgumentConverter<double, double> converter0{0};
+    converter0.convert(execution_policy, double{double_value});
+
+    const uint64_t uint64_value = 3;
+    FunctionArgumentConverter<double, uint64_t> converter1{1};
+    converter1.convert(execution_policy, uint64_value);
+
+    const bool bool_value = false;
+    FunctionArgumentConverter<uint64_t, bool> converter2{2};
+    converter2.convert(execution_policy, bool_value);
+
+    REQUIRE(std::get<double>(execution_policy.currentContext()[0]) == double_value);
+    REQUIRE(std::get<double>(execution_policy.currentContext()[1]) == static_cast<double>(uint64_value));
+    REQUIRE(std::get<uint64_t>(execution_policy.currentContext()[2]) == static_cast<uint64_t>(bool_value));
+  }
+
+  SECTION("FunctionTinyVectorArgumentConverter")
+  {
+    const TinyVector<3> x3{1.7, 2.9, -3};
+    FunctionTinyVectorArgumentConverter<TinyVector<3>, TinyVector<3>> converter0{0};
+    converter0.convert(execution_policy, TinyVector{x3});
+
+    const double x1 = 6.3;
+    FunctionTinyVectorArgumentConverter<TinyVector<1>, double> converter1{1};
+    converter1.convert(execution_policy, double{x1});
+
+    AggregateDataVariant values{std::vector<DataVariant>{6.3, 3.2, 4ul}};
+    FunctionTinyVectorArgumentConverter<TinyVector<3>, TinyVector<3>> converter2{2};
+    converter2.convert(execution_policy, values);
+
+    REQUIRE(std::get<TinyVector<3>>(execution_policy.currentContext()[0]) == x3);
+    REQUIRE(std::get<TinyVector<1>>(execution_policy.currentContext()[1]) == TinyVector<1>{x1});
+    REQUIRE(std::get<TinyVector<3>>(execution_policy.currentContext()[2]) == TinyVector<3>{6.3, 3.2, 4ul});
+
+    AggregateDataVariant bad_values{std::vector<DataVariant>{6.3, 3.2, std::string{"bar"}}};
+
+    REQUIRE_THROWS_WITH(converter2.convert(execution_policy, bad_values), std::string{"unexpected error: "} +
+                                                                            demangle<std::string>() +
+                                                                            " unexpected aggregate value type");
+  }
+
+  SECTION("FunctionTupleArgumentConverter")
+  {
+    const TinyVector<3> x3{1.7, 2.9, -3};
+    FunctionTupleArgumentConverter<TinyVector<3>, TinyVector<3>> converter0{0};
+    converter0.convert(execution_policy, TinyVector{x3});
+
+    const double a = 1.2;
+    const double b = -3.5;
+    const double c = 2.6;
+    FunctionTupleArgumentConverter<double, double> converter1{1};
+    converter1.convert(execution_policy, std::vector{a, b, c});
+
+    const uint64_t i = 1;
+    const uint64_t j = 3;
+    const uint64_t k = 6;
+    FunctionTupleArgumentConverter<double, uint64_t> converter2{2};
+    converter2.convert(execution_policy, std::vector<uint64_t>{i, j, k});
+
+    REQUIRE(std::get<std::vector<TinyVector<3>>>(execution_policy.currentContext()[0]) ==
+            std::vector<TinyVector<3>>{x3});
+    REQUIRE(std::get<std::vector<double>>(execution_policy.currentContext()[1]) == std::vector<double>{a, b, c});
+    REQUIRE(std::get<std::vector<double>>(execution_policy.currentContext()[2]) == std::vector<double>{i, j, k});
+
+    converter1.convert(execution_policy, a);
+    REQUIRE(std::get<std::vector<double>>(execution_policy.currentContext()[1]) == std::vector<double>{a});
+
+    converter1.convert(execution_policy, j);
+    REQUIRE(std::get<std::vector<double>>(execution_policy.currentContext()[1]) == std::vector<double>{j});
+
+    // Errors
+    REQUIRE_THROWS_WITH(converter0.convert(execution_policy, j),
+                        "unexpected error: cannot convert 'unsigned long' to 'TinyVector<3ul, double>'");
+  }
+
+  SECTION("FunctionListArgumentConverter")
+  {
+    const uint64_t i = 3;
+    FunctionListArgumentConverter<double, double> converter0{0};
+    converter0.convert(execution_policy, i);
+
+    const double a = 6.3;
+    const double b = -1.3;
+    const double c = 3.6;
+    FunctionListArgumentConverter<double, double> converter1{1};
+    converter1.convert(execution_policy, std::vector<double>{a, b, c});
+
+    AggregateDataVariant v{std::vector<DataVariant>{1ul, 2.3, -3l}};
+    FunctionListArgumentConverter<double, double> converter2{2};
+    converter2.convert(execution_policy, v);
+
+    REQUIRE(std::get<std::vector<double>>(execution_policy.currentContext()[0]) == std::vector<double>{i});
+    REQUIRE(std::get<std::vector<double>>(execution_policy.currentContext()[1]) == std::vector<double>{a, b, c});
+    REQUIRE(std::get<std::vector<double>>(execution_policy.currentContext()[2]) == std::vector<double>{1ul, 2.3, -3l});
+
+    FunctionListArgumentConverter<TinyVector<2>, TinyVector<2>> converterR2_0{0};
+    converterR2_0.convert(execution_policy, TinyVector<2>{1, 3.2});
+
+    FunctionListArgumentConverter<TinyVector<2>, TinyVector<2>> converterR2_1{1};
+    converterR2_1.convert(execution_policy, std::vector{TinyVector<2>{1, 3.2}, TinyVector<2>{-1, 0.2}});
+
+    AggregateDataVariant v_R2{std::vector<DataVariant>{TinyVector<2>{-3, 12.2}, TinyVector<2>{2, 1.2}}};
+    FunctionListArgumentConverter<TinyVector<2>, TinyVector<2>> converterR2_2{2};
+    converterR2_2.convert(execution_policy, v_R2);
+
+    REQUIRE(std::get<std::vector<TinyVector<2>>>(execution_policy.currentContext()[0]) ==
+            std::vector<TinyVector<2>>{TinyVector<2>{1, 3.2}});
+    REQUIRE(std::get<std::vector<TinyVector<2>>>(execution_policy.currentContext()[1]) ==
+            std::vector<TinyVector<2>>{TinyVector<2>{1, 3.2}, TinyVector<2>{-1, 0.2}});
+    REQUIRE(std::get<std::vector<TinyVector<2>>>(execution_policy.currentContext()[2]) ==
+            std::vector<TinyVector<2>>{TinyVector<2>{-3, 12.2}, TinyVector<2>{2, 1.2}});
+  }
+
+  SECTION("FunctionArgumentToFunctionSymbolIdConverter")
+  {
+    std::shared_ptr symbol_table = std::make_shared<SymbolTable>();
+
+    const uint64_t f_id = 3;
+    FunctionArgumentToFunctionSymbolIdConverter converter0{0, symbol_table};
+    converter0.convert(execution_policy, f_id);
+
+    REQUIRE(std::get<FunctionSymbolId>(execution_policy.currentContext()[0]).id() == f_id);
+  }
+}
diff --git a/tests/test_IfProcessor.cpp b/tests/test_IfProcessor.cpp
index ccade16253ae5b1024a97cc5ba038ec222bf5156..71fc766845d22090e319ea02f071e8707d72eed5 100644
--- a/tests/test_IfProcessor.cpp
+++ b/tests/test_IfProcessor.cpp
@@ -102,6 +102,30 @@ if(false) {
     CHECK_IF_PROCESSOR_RESULT(data, "i", 2ul);
   }
 
+  SECTION("simple if(true) with local variable")
+  {
+    std::string_view data = R"(
+let i:N, i = 0;
+if(true) {
+  let j:N, j = 1;
+  i = j;
+}
+)";
+    CHECK_IF_PROCESSOR_RESULT(data, "i", 1ul);
+  }
+
+  SECTION("simple if(false) with else local variable")
+  {
+    std::string_view data = R"(
+let i:N, i = 0;
+if(false) {} else {
+  let j:N, j = 1;
+  i = j;
+}
+)";
+    CHECK_IF_PROCESSOR_RESULT(data, "i", 1ul);
+  }
+
   SECTION("errors")
   {
     SECTION("bad test type")
diff --git a/tests/test_ParseError.cpp b/tests/test_ParseError.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0fb8eb5dca61f1098592eafd1d84d51f2e953bc0
--- /dev/null
+++ b/tests/test_ParseError.cpp
@@ -0,0 +1,39 @@
+#include <catch2/catch.hpp>
+
+#include <language/utils/ParseError.hpp>
+
+#include <string>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("ParseError", "[language]")
+{
+  SECTION("single position")
+  {
+    const std::string source = R"(
+a first line
+a second line
+)";
+    TAO_PEGTL_NAMESPACE::internal::iterator i(&source[0], 3, 1, 2);
+    TAO_PEGTL_NAMESPACE::position p{i, source};
+    ParseError parse_error("error message", p);
+    REQUIRE(parse_error.positions() == std::vector{p});
+    REQUIRE(parse_error.what() == std::string{"error message"});
+  }
+
+  SECTION("position list")
+  {
+    const std::string source = R"(
+a first line
+a second line
+)";
+    TAO_PEGTL_NAMESPACE::internal::iterator i0(&source[0], 3, 1, 2);
+    TAO_PEGTL_NAMESPACE::position p0{i0, source};
+    TAO_PEGTL_NAMESPACE::internal::iterator i1(&source[0], 4, 1, 3);
+    TAO_PEGTL_NAMESPACE::position p1{i1, source};
+
+    ParseError parse_error("error message", std::vector{p0, p1});
+    REQUIRE(parse_error.positions() == std::vector{p0, p1});
+    REQUIRE(parse_error.what() == std::string{"error message"});
+  }
+}
diff --git a/tests/test_SparseMatrixDescriptor.cpp b/tests/test_SparseMatrixDescriptor.cpp
index f90cc83b3778e39a3319824f53a34e2041dadcf7..8616a41da0ff8a60857ef314c74e04becf6c651f 100644
--- a/tests/test_SparseMatrixDescriptor.cpp
+++ b/tests/test_SparseMatrixDescriptor.cpp
@@ -132,7 +132,8 @@ TEST_CASE("SparseMatrixDescriptor", "[algebra]")
     REQUIRE(graph[3].size() == 2);
     REQUIRE(graph[4].size() == 2);
 
-    REQUIRE(graph[0][0] == 2);
+    REQUIRE(graph[0][0] == 0);
+    REQUIRE(graph[0][1] == 2);
     REQUIRE(graph[1][0] == 1);
     REQUIRE(graph[1][1] == 2);
     REQUIRE(graph[2][0] == 2);
@@ -159,14 +160,15 @@ TEST_CASE("SparseMatrixDescriptor", "[algebra]")
 
     REQUIRE(value_array.size() == 9);
 
-    REQUIRE(value_array[0] == 5);
-    REQUIRE(value_array[1] == 1);
-    REQUIRE(value_array[2] == 11);
-    REQUIRE(value_array[3] == 4);
-    REQUIRE(value_array[4] == -3);
-    REQUIRE(value_array[5] == 5);
-    REQUIRE(value_array[6] == 1);
-    REQUIRE(value_array[7] == -2);
+    REQUIRE(value_array[0] == 0);
+    REQUIRE(value_array[1] == 5);
+    REQUIRE(value_array[2] == 1);
+    REQUIRE(value_array[3] == 11);
+    REQUIRE(value_array[4] == 4);
+    REQUIRE(value_array[5] == -3);
+    REQUIRE(value_array[6] == 5);
+    REQUIRE(value_array[7] == 1);
+    REQUIRE(value_array[8] == -2);
   }
 
   SECTION("output")