Skip to content
Snippets Groups Projects
Select Git revision
  • e786a7b321bcaab6d5dfb5a6d391123b31437771
  • develop default protected
  • feature/gmsh-reader
  • origin/stage/bouguettaia
  • feature/kinetic-schemes
  • feature/reconstruction
  • feature/local-dt-fsi
  • feature/composite-scheme-sources
  • feature/composite-scheme-other-fluxes
  • feature/serraille
  • feature/variational-hydro
  • feature/composite-scheme
  • hyperplastic
  • feature/polynomials
  • feature/gks
  • feature/implicit-solver-o2
  • feature/coupling_module
  • feature/implicit-solver
  • feature/merge-local-dt-fsi
  • master protected
  • feature/escobar-smoother
  • v0.5.0 protected
  • v0.4.1 protected
  • v0.4.0 protected
  • v0.3.0 protected
  • v0.2.0 protected
  • v0.1.0 protected
  • Kidder
  • v0.0.4 protected
  • v0.0.3 protected
  • v0.0.2 protected
  • v0 protected
  • v0.0.1 protected
33 results

SchemeModule.hpp

Blame
    • Stéphane Del Pino's avatar
      e786a7b3
      Change ASTNodeDataType construction and handling of type descriptors · e786a7b3
      Stéphane Del Pino authored
      From the ast point of view this gives cleaner data type description.
      Nodes used to describe type names are now explicitly described as
      type names. Previously,
      
      - the data type could be marked as the described type. Now it is
      referred as the `typename` of the type. For instance the `R^2`
      type descriptor node was given `R^2` as a data type. Now it is given
      the `typename(R^2)`: it is a typename which refers to `R^2`.
      - In some other cases, it could have been just `typename` which forced
      to re-parse entries (which is never good).
      
      Also `affectations` types is fixed: it is now given the `void` type
      instead of the type of the affected data.
      
      Functions input and output spaces types should now be correctly
      defined: some more tests are however required.
      
      The case of compound types is however not that clear since the data
      type of the type `R*R*R^2` is just defined as `typename(list)`. This
      may be improved (in order to help checking of function definition/use.
      
      An initialization issue related to tuples of R^1 was fixed on the way
      and some error messages improved.
      
      This is related to issue #21
      e786a7b3
      History
      Change ASTNodeDataType construction and handling of type descriptors
      Stéphane Del Pino authored
      From the ast point of view this gives cleaner data type description.
      Nodes used to describe type names are now explicitly described as
      type names. Previously,
      
      - the data type could be marked as the described type. Now it is
      referred as the `typename` of the type. For instance the `R^2`
      type descriptor node was given `R^2` as a data type. Now it is given
      the `typename(R^2)`: it is a typename which refers to `R^2`.
      - In some other cases, it could have been just `typename` which forced
      to re-parse entries (which is never good).
      
      Also `affectations` types is fixed: it is now given the `void` type
      instead of the type of the affected data.
      
      Functions input and output spaces types should now be correctly
      defined: some more tests are however required.
      
      The case of compound types is however not that clear since the data
      type of the type `R*R*R^2` is just defined as `typename(list)`. This
      may be improved (in order to help checking of function definition/use.
      
      An initialization issue related to tuples of R^1 was fixed on the way
      and some error messages improved.
      
      This is related to issue #21
    AffectationProcessor.hpp 25.08 KiB
    #ifndef AFFECTATION_PROCESSOR_HPP
    #define AFFECTATION_PROCESSOR_HPP
    
    #include <language/PEGGrammar.hpp>
    #include <language/node_processor/INodeProcessor.hpp>
    #include <language/utils/ParseError.hpp>
    #include <language/utils/SymbolTable.hpp>
    #include <utils/Exceptions.hpp>
    #include <utils/PugsTraits.hpp>
    
    template <typename Op>
    struct AffOp;
    
    template <>
    struct AffOp<language::multiplyeq_op>
    {
      template <typename A, typename B>
      PUGS_INLINE void
      eval(A& a, const B& b)
      {
        a *= b;
      }
    };
    
    template <>
    struct AffOp<language::divideeq_op>
    {
      template <typename A, typename B>
      PUGS_INLINE void
      eval(A& a, const B& b)
      {
        a /= b;
      }
    };
    
    template <>
    struct AffOp<language::pluseq_op>
    {
      template <typename A, typename B>
      PUGS_INLINE void
      eval(A& a, const B& b)
      {
        a += b;
      }
    };
    
    template <>
    struct AffOp<language::minuseq_op>
    {
      template <typename A, typename B>
      PUGS_INLINE void
      eval(A& a, const B& b)
      {
        a -= b;
      }
    };
    
    struct IAffectationExecutor
    {
      virtual void affect(ExecutionPolicy& exec_policy, DataVariant&& rhs) = 0;
    
      IAffectationExecutor(const IAffectationExecutor&) = delete;
      IAffectationExecutor(IAffectationExecutor&&)      = delete;
    
      IAffectationExecutor() = default;
    
      virtual ~IAffectationExecutor() = default;
    };
    
    template <typename OperatorT, typename ValueT, typename DataT>
    class AffectationExecutor final : public IAffectationExecutor
    {
     private:
      ValueT& m_lhs;
    
      static inline const bool m_is_defined{[] {
        if constexpr (std::is_same_v<std::decay_t<ValueT>, bool>) {
          if constexpr (not std::is_same_v<OperatorT, language::eq_op>) {
            return false;
          }
        }
        return true;
      }()};
    
      template <typename T>
      std::string
      _stringify(const T& value)
      {
        std::ostringstream os;
        os << std::boolalpha << value;
        return os.str();
      }
    
     public:
      AffectationExecutor(ASTNode& node, ValueT& lhs) : m_lhs(lhs)
      {
        // LCOV_EXCL_START
        if constexpr (not m_is_defined) {
          throw ParseError("unexpected error: invalid operands to affectation expression", std::vector{node.begin()});
        }
        // LCOV_EXCL_STOP
      }
    
      PUGS_INLINE void
      affect(ExecutionPolicy&, DataVariant&& rhs)
      {
        if constexpr (m_is_defined) {
          if constexpr (not std::is_same_v<DataT, ZeroType>) {
            if constexpr (std::is_same_v<ValueT, std::string>) {
              if constexpr (std::is_same_v<OperatorT, language::eq_op>) {
                if constexpr (std::is_same_v<std::string, DataT>) {
                  m_lhs = std::get<DataT>(rhs);
                } else {
                  m_lhs = std::move(_stringify(std::get<DataT>(rhs)));
                }
              } else {
                if constexpr (std::is_same_v<std::string, DataT>) {
                  m_lhs += std::get<std::string>(rhs);
                } else {
                  m_lhs += std::move(_stringify(std::get<DataT>(rhs)));
                }
              }
            } else {
              if constexpr (std::is_same_v<OperatorT, language::eq_op>) {
                if constexpr (std::is_convertible_v<DataT, ValueT>) {
                  m_lhs = std::get<DataT>(rhs);
                } else if constexpr (std::is_same_v<TinyVector<1>, ValueT>) {
                  std::visit(
                    [&](auto&& v) {
                      using Vi_T = std::decay_t<decltype(v)>;
                      if constexpr (std::is_convertible_v<Vi_T, double>) {
                        m_lhs = TinyVector<1>(v);
                      } else {
                        // LCOV_EXCL_START
                        throw UnexpectedError("unexpected rhs type in affectation");
                        // LCOV_EXCL_STOP
                      }
                    },
                    rhs);
                } else if constexpr (std::is_same_v<TinyMatrix<1>, ValueT>) {
                  std::visit(
                    [&](auto&& v) {
                      using Vi_T = std::decay_t<decltype(v)>;
                      if constexpr (std::is_convertible_v<Vi_T, double>) {
                        m_lhs = TinyMatrix<1>(v);
                      } else {
                        // LCOV_EXCL_START
                        throw UnexpectedError("unexpected rhs type in affectation");
                        // LCOV_EXCL_STOP
                      }
                    },
                    rhs);
                } else if constexpr (is_std_vector_v<ValueT> and is_std_vector_v<DataT>) {
                  using ValueContentT = typename ValueT::value_type;
                  using DataContentT  = typename DataT::value_type;
    
                  static_assert(not std::is_same_v<ValueContentT, DataContentT>,
                                "the case should have been treated previously");
                  if constexpr (std::is_convertible_v<DataContentT, ValueContentT>) {
                    m_lhs.resize(std::get<DataT>(rhs).size());
                    std::visit(
                      [&](auto&& v) {
                        using Vi_T = std::decay_t<decltype(v)>;
                        if constexpr (is_std_vector_v<Vi_T>) {
                          if constexpr (std::is_arithmetic_v<typename Vi_T::value_type>) {
                            for (size_t i = 0; i < v.size(); ++i) {
                              m_lhs[i] = v[i];
                            }
                          } else {
                            // LCOV_EXCL_START
                            throw UnexpectedError("unexpected rhs type in affectation");
                            // LCOV_EXCL_STOP
                          }
                        } else {
                          // LCOV_EXCL_START
                          throw UnexpectedError("unexpected rhs type in affectation");
                          // LCOV_EXCL_STOP
                        }
                      },
                      rhs);
                  } else if constexpr (std::is_same_v<ValueContentT, std::string>) {
                    m_lhs.resize(std::get<DataT>(rhs).size());
    
                    std::visit(
                      [&](auto&& v) {
                        using V_T = std::decay_t<decltype(v)>;
                        if constexpr (is_std_vector_v<V_T>) {
                          for (size_t i = 0; i < v.size(); ++i) {
                            if constexpr (std::is_same_v<typename V_T::value_type, bool>) {
                              // Ugly workaround to allow compilation with libstdc++-9
                              bool v_i = v[i];
                              m_lhs[i] = std::move(_stringify(v_i));
                            } else {
                              m_lhs[i] = std::move(_stringify(v[i]));
                            }
                          }
                        } else {
                          // LCOV_EXCL_START
                          throw UnexpectedError("unexpected rhs type in affectation");
                          // LCOV_EXCL_STOP
                        }
                      },
                      rhs);
                  } else if constexpr (is_tiny_vector_v<ValueContentT>) {
                    static_assert(not std::is_same_v<DataContentT, ValueContentT>, "should have been treated before");
                    static_assert(ValueContentT::Dimension == 1,
                                  "conversions are only allowed for dimension 1 TinyVector's");
    
                    m_lhs.resize(std::get<DataT>(rhs).size());
    
                    std::visit(
                      [&](auto&& v) {
                        if constexpr (is_std_vector_v<std::decay_t<decltype(v)>>) {
                          using Vi_T = typename std::decay_t<decltype(v)>::value_type;
                          for (size_t i = 0; i < v.size(); ++i) {
                            if constexpr (std::is_arithmetic_v<Vi_T>) {
                              m_lhs[i][0] = v[i];
                            } else {
                              // LCOV_EXCL_START
                              throw UnexpectedError("unexpected rhs type in affectation");
                              // LCOV_EXCL_STOP
                            }
                          }
                        } else {
                          // LCOV_EXCL_START
                          throw UnexpectedError("unexpected rhs type in affectation");
                          // LCOV_EXCL_STOP
                        }
                      },
                      rhs);
    
                  } else if constexpr (is_tiny_matrix_v<ValueContentT>) {
                    static_assert(not std::is_same_v<DataContentT, ValueContentT>, "should have been treated before");
                    static_assert(ValueContentT::Dimension == 1, "conversions are only allowed for 1x1 TinyMatrix's");
    
                    m_lhs.resize(std::get<DataT>(rhs).size());
    
                    std::visit(
                      [&](auto&& v) {
                        if constexpr (is_std_vector_v<std::decay_t<decltype(v)>>) {
                          using Vi_T = typename std::decay_t<decltype(v)>::value_type;
                          for (size_t i = 0; i < v.size(); ++i) {
                            if constexpr (std::is_arithmetic_v<Vi_T>) {
                              m_lhs[i](0, 0) = v[i];
                            } else {
                              // LCOV_EXCL_START
                              throw UnexpectedError("unexpected rhs type in affectation");
                              // LCOV_EXCL_STOP
                            }
                          }
                        } else {
                          // LCOV_EXCL_START
                          throw UnexpectedError("unexpected rhs type in affectation");
                          // LCOV_EXCL_STOP
                        }
                      },
                      rhs);
    
                  } else {
                    // LCOV_EXCL_START
                    throw UnexpectedError("invalid value type");
                    // LCOV_EXCL_STOP
                  }
                } else if constexpr (is_std_vector_v<ValueT> and std::is_same_v<DataT, AggregateDataVariant>) {
                  using ValueContentT                         = typename ValueT::value_type;
                  const AggregateDataVariant& children_values = std::get<AggregateDataVariant>(rhs);
                  m_lhs.resize(children_values.size());
                  auto& tuple_value = m_lhs;
                  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, ValueContentT>) {
                          tuple_value[i] = child_value;
                        } else if constexpr (std::is_arithmetic_v<ValueContentT> and
                                             std::is_convertible_v<T, ValueContentT>) {
                          tuple_value[i] = static_cast<ValueContentT>(child_value);
                        } else if constexpr (std::is_same_v<std::string, ValueContentT>) {
                          tuple_value[i] = std::move(_stringify(child_value));
                        } else if constexpr (is_tiny_vector_v<ValueContentT>) {
                          if constexpr (std::is_arithmetic_v<T>) {
                            if constexpr (std::is_same_v<ValueContentT, TinyVector<1>>) {
                              tuple_value[i][0] = child_value;
                            } else {
                              // in this case a 0 is given
                              Assert(child_value == 0);
                              tuple_value[i] = ZeroType{};
                            }
                          } else {
                            // LCOV_EXCL_START
                            throw UnexpectedError("unexpected error: unexpected right hand side type in affectation");
                            // LCOV_EXCL_STOP
                          }
                        } else if constexpr (is_tiny_matrix_v<ValueContentT>) {
                          if constexpr (std::is_arithmetic_v<T>) {
                            if constexpr (std::is_same_v<ValueContentT, TinyMatrix<1>>) {
                              tuple_value[i](0, 0) = child_value;
                            } else {
                              // in this case a 0 is given
                              Assert(child_value == 0);
                              tuple_value[i] = ZeroType{};
                            }
                          } else {
                            // LCOV_EXCL_START
                            throw UnexpectedError("unexpected error: unexpected right hand side type in affectation");
                            // LCOV_EXCL_STOP
                          }
                        } else {
                          // LCOV_EXCL_START
                          throw UnexpectedError("unexpected error: unexpected right hand side type in affectation");
                          // LCOV_EXCL_STOP
                        }
                      },
                      children_values[i]);
                  }
                } else if constexpr (is_std_vector_v<ValueT>) {
                  using ValueContentT = typename ValueT::value_type;
                  m_lhs.resize(1);
                  auto& tuple_value = m_lhs;
                  std::visit(
                    [&](auto&& child_value) {
                      using T = std::decay_t<decltype(child_value)>;
                      if constexpr (std::is_same_v<T, ValueContentT>) {
                        tuple_value[0] = child_value;
                      } else if constexpr (std::is_arithmetic_v<ValueContentT> and
                                           std::is_convertible_v<T, ValueContentT>) {
                        tuple_value[0] = static_cast<ValueContentT>(child_value);
                      } else if constexpr (std::is_same_v<std::string, ValueContentT>) {
                        tuple_value[0] = std::move(_stringify(child_value));
                      } else if constexpr (is_tiny_vector_v<ValueContentT>) {
                        if constexpr (std::is_arithmetic_v<T>) {
                          if constexpr (std::is_same_v<ValueContentT, TinyVector<1>>) {
                            tuple_value[0][0] = child_value;
                          } else {
                            // in this case a 0 is given
                            Assert(child_value == 0);
                            tuple_value[0] = ZeroType{};
                          }
                        } else {
                          // LCOV_EXCL_START
                          throw UnexpectedError("unexpected error: unexpected right hand side type in affectation");
                          // LCOV_EXCL_STOP
                        }
                      } else if constexpr (is_tiny_matrix_v<ValueContentT>) {
                        if constexpr (std::is_arithmetic_v<T>) {
                          if constexpr (std::is_same_v<ValueContentT, TinyMatrix<1>>) {
                            tuple_value[0](0, 0) = child_value;
                          } else {
                            // in this case a 0 is given
                            Assert(child_value == 0);
                            tuple_value[0] = ZeroType{};
                          }
                        } else {
                          // LCOV_EXCL_START
                          throw UnexpectedError("unexpected error: unexpected right hand side type in affectation");
                          // LCOV_EXCL_STOP
                        }
                      } else {
                        // LCOV_EXCL_START
                        throw UnexpectedError("unexpected error: unexpected right hand side type in affectation");
                        // LCOV_EXCL_STOP
                      }
                    },
                    rhs);
    
                } else {
                  // LCOV_EXCL_START
                  throw UnexpectedError("invalid value type");
                  // LCOV_EXCL_STOP
                }
              } else {
                AffOp<OperatorT>().eval(m_lhs, std::get<DataT>(rhs));
              }
            }
          } else if (std::is_same_v<OperatorT, language::eq_op>) {
            m_lhs = ValueT{zero};
          } else {
            static_assert(std::is_same_v<OperatorT, language::eq_op>, "unexpected operator type");
          }
        }
      }
    };
    
    template <typename OperatorT, typename ValueT, typename DataT>
    class AffectationProcessor final : public INodeProcessor
    {
     private:
      ASTNode& m_rhs_node;
    
      std::unique_ptr<IAffectationExecutor> m_affectation_executor;
    
      std::unique_ptr<IAffectationExecutor>
      _buildAffectationExecutor(ASTNode& lhs_node)
      {
        if (lhs_node.is_type<language::name>()) {
          const std::string& symbol = lhs_node.string();
          auto [i_symbol, found]    = lhs_node.m_symbol_table->find(symbol, lhs_node.begin());
          Assert(found);
          DataVariant& value = i_symbol->attributes().value();
    
          if (not std::holds_alternative<ValueT>(value)) {
            value = ValueT{};
          }
    
          using AffectationExecutorT = AffectationExecutor<OperatorT, ValueT, DataT>;
          return std::make_unique<AffectationExecutorT>(lhs_node, std::get<ValueT>(value));
        } else {
          // LCOV_EXCL_START
          throw ParseError("unexpected error: invalid lhs", std::vector{lhs_node.begin()});
          // LCOV_EXCL_STOP
        }
      }
    
     public:
      DataVariant
      execute(ExecutionPolicy& exec_policy)
      {
        m_affectation_executor->affect(exec_policy, m_rhs_node.execute(exec_policy));
    
        return {};
      }
    
      AffectationProcessor(ASTNode& lhs_node, ASTNode& rhs_node)
        : m_rhs_node{rhs_node}, m_affectation_executor{this->_buildAffectationExecutor(lhs_node)}
      {}
    };
    
    class AffectationToDataVariantProcessorBase : public INodeProcessor
    {
     protected:
      DataVariant* m_lhs;
    
     public:
      AffectationToDataVariantProcessorBase(ASTNode& lhs_node)
      {
        const std::string& symbol = lhs_node.string();
        auto [i_symbol, found]    = lhs_node.m_symbol_table->find(symbol, lhs_node.begin());
        Assert(found);
    
        m_lhs = &i_symbol->attributes().value();
      }
    
      virtual ~AffectationToDataVariantProcessorBase() = default;
    };
    
    template <typename ValueT>
    class AffectationToTupleProcessor final : public AffectationToDataVariantProcessorBase
    {
     private:
      ASTNode& m_rhs_node;
    
      template <typename T>
      std::string
      _stringify(const T& value)
      {
        std::ostringstream os;
        os << std::boolalpha << value;
        return os.str();
      }
    
     public:
      DataVariant
      execute(ExecutionPolicy& exec_policy)
      {
        DataVariant value = m_rhs_node.execute(exec_policy);
    
        std::visit(
          [&](auto&& v) {
            using T = std::decay_t<decltype(v)>;
            if constexpr (std::is_same_v<T, ValueT>) {
              *m_lhs = std::vector{std::move(v)};
            } else if constexpr (std::is_arithmetic_v<ValueT> and std::is_convertible_v<T, ValueT>) {
              *m_lhs = std::vector{std::move(static_cast<ValueT>(v))};
            } else if constexpr (std::is_same_v<std::string, ValueT>) {
              *m_lhs = std::vector{std::move(_stringify(v))};
            } else if constexpr (is_tiny_vector_v<ValueT> or is_tiny_matrix_v<ValueT>) {
              if constexpr (std::is_same_v<ValueT, TinyVector<1>> and std::is_arithmetic_v<T>) {
                *m_lhs = std::vector<TinyVector<1>>{TinyVector<1>{static_cast<double>(v)}};
              } else if constexpr (std::is_same_v<ValueT, TinyMatrix<1>> and std::is_arithmetic_v<T>) {
                *m_lhs = std::vector<TinyMatrix<1>>{TinyMatrix<1>{static_cast<double>(v)}};
              } else if constexpr (std::is_same_v<T, int64_t>) {
                Assert(v == 0);
                *m_lhs = std::vector<ValueT>{ValueT{zero}};
              } else {
                // LCOV_EXCL_START
                throw ParseError("unexpected error: unexpected right hand side type in affectation", m_rhs_node.begin());
                // LCOV_EXCL_STOP
              }
            } else {
              // LCOV_EXCL_START
              throw ParseError("unexpected error: unexpected right hand side type in affectation", m_rhs_node.begin());
              // LCOV_EXCL_STOP
            }
          },
          value);
    
        return {};
      }
    
      AffectationToTupleProcessor(ASTNode& lhs_node, ASTNode& rhs_node)
        : AffectationToDataVariantProcessorBase(lhs_node), m_rhs_node{rhs_node}
      {}
    };
    
    template <typename ValueT>
    class AffectationToTupleFromListProcessor final : public AffectationToDataVariantProcessorBase
    {
     private:
      ASTNode& m_rhs_node;
    
      template <typename T>
      std::string
      _stringify(const T& value)
      {
        std::ostringstream os;
        os << std::boolalpha << value;
        return os.str();
      }
    
      void
      _copyAggregateDataVariant(const AggregateDataVariant& children_values)
      {
        std::vector<ValueT> 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, ValueT>) {
                tuple_value[i] = child_value;
              } else if constexpr (std::is_arithmetic_v<ValueT> and std::is_convertible_v<T, ValueT>) {
                tuple_value[i] = static_cast<ValueT>(child_value);
              } else if constexpr (std::is_same_v<std::string, ValueT>) {
                tuple_value[i] = std::move(_stringify(child_value));
              } else if constexpr (is_tiny_vector_v<ValueT>) {
                if constexpr (std::is_arithmetic_v<T>) {
                  if constexpr (std::is_same_v<ValueT, TinyVector<1>>) {
                    tuple_value[i][0] = child_value;
                  } else {
                    // in this case a 0 is given
                    Assert(child_value == 0);
                    tuple_value[i] = ZeroType{};
                  }
                } else {
                  // LCOV_EXCL_START
                  throw ParseError("unexpected error: unexpected right hand side type in affectation",
                                   m_rhs_node.children[i]->begin());
                  // LCOV_EXCL_STOP
                }
              } else if constexpr (is_tiny_matrix_v<ValueT>) {
                if constexpr (std::is_arithmetic_v<T>) {
                  if constexpr (std::is_same_v<ValueT, TinyMatrix<1>>) {
                    tuple_value[i](0, 0) = child_value;
                  } else {
                    // in this case a 0 is given
                    Assert(child_value == 0);
                    tuple_value[i] = ZeroType{};
                  }
                } else {
                  // LCOV_EXCL_START
                  throw ParseError("unexpected error: unexpected right hand side type in affectation",
                                   m_rhs_node.children[i]->begin());
                  // LCOV_EXCL_STOP
                }
              } else {
                // LCOV_EXCL_START
                throw ParseError("unexpected error: unexpected right hand side type in affectation",
                                 m_rhs_node.children[i]->begin());
                // LCOV_EXCL_STOP
              }
            },
            children_values[i]);
        }
        *m_lhs = std::move(tuple_value);
      }
    
      template <typename DataType>
      void
      _copyVector(const std::vector<DataType>& values)
      {
        std::vector<ValueT> v(values.size());
        if constexpr (std::is_same_v<ValueT, DataType>) {
          for (size_t i = 0; i < values.size(); ++i) {
            v[i] = values[i];
          }
        } else if constexpr (std::is_arithmetic_v<ValueT> and std::is_convertible_v<DataType, ValueT>) {
          for (size_t i = 0; i < values.size(); ++i) {
            v[i] = static_cast<DataType>(values[i]);
          }
        } else if constexpr (std::is_same_v<ValueT, std::string>) {
          for (size_t i = 0; i < values.size(); ++i) {
            v[i] = std::move(_stringify(values[i]));
          }
        } else {
          // LCOV_EXCL_START
          throw ParseError("unexpected error: unexpected right hand side type in tuple affectation", m_rhs_node.begin());
          // LCOV_EXCL_STOP
        }
    
        *m_lhs = std::move(v);
      }
    
     public:
      DataVariant
      execute(ExecutionPolicy& exec_policy)
      {
        std::visit(
          [&](auto&& value_list) {
            using ValueListT = std::decay_t<decltype(value_list)>;
            if constexpr (std::is_same_v<AggregateDataVariant, ValueListT>) {
              this->_copyAggregateDataVariant(value_list);
            } else if constexpr (is_std_vector_v<ValueListT>) {
              this->_copyVector(value_list);
            } else {
              // LCOV_EXCL_START
              throw ParseError("unexpected error: invalid lhs (expecting list or tuple)", std::vector{m_rhs_node.begin()});
              // LCOV_EXCL_STOP
            }
          },
          m_rhs_node.execute(exec_policy));
    
        return {};
      }
    
      AffectationToTupleFromListProcessor(ASTNode& lhs_node, ASTNode& rhs_node)
        : AffectationToDataVariantProcessorBase(lhs_node), m_rhs_node{rhs_node}
      {}
    };
    
    template <typename ValueT>
    class AffectationFromZeroProcessor final : public AffectationToDataVariantProcessorBase
    {
     public:
      DataVariant
      execute(ExecutionPolicy&)
      {
        *m_lhs = ValueT{zero};
        return {};
      }
    
      AffectationFromZeroProcessor(ASTNode& lhs_node) : AffectationToDataVariantProcessorBase(lhs_node) {}
    };
    
    template <typename OperatorT>
    class ListAffectationProcessor final : public INodeProcessor
    {
     private:
      ASTNode& m_node;
    
      std::vector<std::unique_ptr<IAffectationExecutor>> m_affectation_executor_list;
    
     public:
      template <typename ValueT, typename DataT>
      void
      add(ASTNode& lhs_node)
      {
        using AffectationExecutorT = AffectationExecutor<OperatorT, ValueT, DataT>;
    
        if (lhs_node.is_type<language::name>()) {
          const std::string& symbol = lhs_node.string();
          auto [i_symbol, found]    = m_node.m_symbol_table->find(symbol, m_node.children[0]->end());
          Assert(found);
          DataVariant& value = i_symbol->attributes().value();
    
          if (not std::holds_alternative<ValueT>(value)) {
            value = ValueT{};
          }
    
          m_affectation_executor_list.emplace_back(std::make_unique<AffectationExecutorT>(m_node, std::get<ValueT>(value)));
        } else {
          // LCOV_EXCL_START
          throw ParseError("unexpected error: invalid left hand side", std::vector{lhs_node.begin()});
          // LCOV_EXCL_STOP
        }
      }
    
      DataVariant
      execute(ExecutionPolicy& exec_policy)
      {
        AggregateDataVariant children_values = std::get<AggregateDataVariant>(m_node.children[1]->execute(exec_policy));
        Assert(m_affectation_executor_list.size() == children_values.size());
    
        for (size_t i = 0; i < m_affectation_executor_list.size(); ++i) {
          m_affectation_executor_list[i]->affect(exec_policy, std::move(children_values[i]));
        }
    
        return {};
      }
    
      ListAffectationProcessor(ASTNode& node) : m_node{node} {}
    };
    
    #endif   // AFFECTATION_PROCESSOR_HPP