Skip to content
Snippets Groups Projects
Select Git revision
  • d93dd7ad2e36f7c59ecfb314f0e7a534340e952c
  • develop default protected
  • 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
  • feature/hypoelasticity-clean
  • 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

FunctionProcessor.hpp

Blame
  • AffectationProcessor.hpp 35.74 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;
      }()};
    
     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 if constexpr (std::is_arithmetic_v<DataT>) {
                  m_lhs = std::to_string(std::get<DataT>(rhs));
                } else {
                  std::ostringstream os;
                  os << std::get<DataT>(rhs);
                  m_lhs = os.str();
                }
              } else {
                if constexpr (std::is_same_v<std::string, DataT>) {
                  m_lhs += std::get<std::string>(rhs);
                } else if constexpr (std::is_arithmetic_v<DataT>) {
                  m_lhs += std::to_string(std::get<DataT>(rhs));
                } else {
                  std::ostringstream os;
                  os << std::get<DataT>(rhs);
                  m_lhs += os.str();
                }
              }
            } else {
              if constexpr (std::is_same_v<OperatorT, language::eq_op>) {
                if constexpr (std::is_convertible_v<ValueT, DataT>) {
                  m_lhs = std::get<DataT>(rhs);
                } else if constexpr (std::is_same_v<DataT, AggregateDataVariant>) {
                  const AggregateDataVariant& v = std::get<AggregateDataVariant>(rhs);
                  static_assert(is_tiny_vector_v<ValueT>, "expecting lhs TinyVector");
                  for (size_t i = 0; i < m_lhs.dimension(); ++i) {
                    std::visit(
                      [&](auto&& vi) {
                        using Vi_T = std::decay_t<decltype(vi)>;
                        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]);
                  }
                } 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 = 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 = v;
                      } else {
                        // LCOV_EXCL_START
                        throw UnexpectedError("unexpected rhs type in affectation");
                        // LCOV_EXCL_STOP
                      }
                    },
                    rhs);
                } else {
                  throw UnexpectedError("invalid value type");
                }
              } 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 ArrayT, typename ValueT, typename DataT>
    class MatrixComponentAffectationExecutor final : public IAffectationExecutor
    {
     private:
      ArrayT& m_lhs_array;
      ASTNode& m_index0_expression;
      ASTNode& m_index1_expression;
    
      static inline const bool m_is_defined{[] {
        if constexpr (not std::is_same_v<typename ArrayT::data_type, ValueT>) {
          return false;
        } else 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;
      }()};
    
     public:
      MatrixComponentAffectationExecutor(ASTNode& node,
                                         ArrayT& lhs_array,
                                         ASTNode& index0_expression,
                                         ASTNode& index1_expression)
        : m_lhs_array{lhs_array}, m_index0_expression{index0_expression}, m_index1_expression{index1_expression}
      {
        // 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& exec_policy, DataVariant&& rhs)
      {
        if constexpr (m_is_defined) {
          auto get_index_value = [&](DataVariant&& value_variant) -> int64_t {
            int64_t index_value = 0;
            std::visit(
              [&](auto&& value) {
                using IndexValueT = std::decay_t<decltype(value)>;
                if constexpr (std::is_integral_v<IndexValueT>) {
                  index_value = value;
                } else {
                  // LCOV_EXCL_START
                  throw UnexpectedError("invalid index type");
                  // LCOV_EXCL_STOP
                }
              },
              value_variant);
            return index_value;
          };
    
          const int64_t index0_value = get_index_value(m_index0_expression.execute(exec_policy));
          const int64_t index1_value = get_index_value(m_index1_expression.execute(exec_policy));
    
          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_array(index0_value, index1_value) = std::get<DataT>(rhs);
              } else {
                m_lhs_array(index0_value, index1_value) = std::to_string(std::get<DataT>(rhs));
              }
            } else {
              if constexpr (std::is_same_v<std::string, DataT>) {
                m_lhs_array(index0_value, index1_value) += std::get<std::string>(rhs);
              } else {
                m_lhs_array(index0_value, index1_value) += std::to_string(std::get<DataT>(rhs));
              }
            }
          } else {
            if constexpr (std::is_same_v<OperatorT, language::eq_op>) {
              if constexpr (std::is_same_v<ValueT, DataT>) {
                m_lhs_array(index0_value, index1_value) = std::get<DataT>(rhs);
              } else {
                m_lhs_array(index0_value, index1_value) = static_cast<ValueT>(std::get<DataT>(rhs));
              }
            } else {
              AffOp<OperatorT>().eval(m_lhs_array(index0_value, index1_value), std::get<DataT>(rhs));
            }
          }
        }
      }
    };
    
    template <typename OperatorT, typename ArrayT, typename ValueT, typename DataT>
    class VectorComponentAffectationExecutor final : public IAffectationExecutor
    {
     private:
      ArrayT& m_lhs_array;
      ASTNode& m_index_expression;
    
      static inline const bool m_is_defined{[] {
        if constexpr (not std::is_same_v<typename ArrayT::data_type, ValueT>) {
          return false;
        } else 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;
      }()};
    
     public:
      VectorComponentAffectationExecutor(ASTNode& node, ArrayT& lhs_array, ASTNode& index_expression)
        : m_lhs_array{lhs_array}, m_index_expression{index_expression}
      {
        // 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& exec_policy, DataVariant&& rhs)
      {
        if constexpr (m_is_defined) {
          const int64_t index_value = [&](DataVariant&& value_variant) -> int64_t {
            int64_t index_value = 0;
            std::visit(
              [&](auto&& value) {
                using IndexValueT = std::decay_t<decltype(value)>;
                if constexpr (std::is_integral_v<IndexValueT>) {
                  index_value = value;
                } else {
                  // LCOV_EXCL_START
                  throw ParseError("unexpected error: invalid index type", std::vector{m_index_expression.begin()});
                  // LCOV_EXCL_STOP
                }
              },
              value_variant);
            return index_value;
          }(m_index_expression.execute(exec_policy));
    
          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_array[index_value] = std::get<DataT>(rhs);
              } else {
                m_lhs_array[index_value] = std::to_string(std::get<DataT>(rhs));
              }
            } else {
              if constexpr (std::is_same_v<std::string, DataT>) {
                m_lhs_array[index_value] += std::get<std::string>(rhs);
              } else {
                m_lhs_array[index_value] += std::to_string(std::get<DataT>(rhs));
              }
            }
          } else {
            if constexpr (std::is_same_v<OperatorT, language::eq_op>) {
              if constexpr (std::is_same_v<ValueT, DataT>) {
                m_lhs_array[index_value] = std::get<DataT>(rhs);
              } else {
                m_lhs_array[index_value] = static_cast<ValueT>(std::get<DataT>(rhs));
              }
            } else {
              AffOp<OperatorT>().eval(m_lhs_array[index_value], std::get<DataT>(rhs));
            }
          }
        }
      }
    };
    
    template <typename OperatorT, typename ValueT, typename DataT>
    class AffectationProcessor final : public INodeProcessor
    {
     private:
      ASTNode& m_node;
    
      std::unique_ptr<IAffectationExecutor> m_affectation_executor;
    
     public:
      DataVariant
      execute(ExecutionPolicy& exec_policy)
      {
        m_affectation_executor->affect(exec_policy, m_node.children[1]->execute(exec_policy));
    
        return {};
      }
    
      AffectationProcessor(ASTNode& node) : m_node{node}
      {
        if (node.children[0]->is_type<language::name>()) {
          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);
          DataVariant& value = i_symbol->attributes().value();
    
          if (not std::holds_alternative<ValueT>(value)) {
            value = ValueT{};
          }
    
          using AffectationExecutorT = AffectationExecutor<OperatorT, ValueT, DataT>;
          m_affectation_executor     = std::make_unique<AffectationExecutorT>(m_node, std::get<ValueT>(value));
        } else if (node.children[0]->is_type<language::subscript_expression>()) {
          auto& array_subscript_expression = *node.children[0];
    
          auto& array_expression = *array_subscript_expression.children[0];
          Assert(array_expression.is_type<language::name>());
    
          const std::string& symbol = array_expression.string();
    
          auto [i_symbol, found] = m_node.m_symbol_table->find(symbol, array_subscript_expression.begin());
          Assert(found);
          DataVariant& value = i_symbol->attributes().value();
    
          if (array_expression.m_data_type == ASTNodeDataType::vector_t) {
            Assert(array_subscript_expression.children.size() == 2);
    
            auto& index_expression = *array_subscript_expression.children[1];
    
            switch (array_expression.m_data_type.dimension()) {
            case 1: {
              using ArrayTypeT = TinyVector<1>;
              if (not std::holds_alternative<ArrayTypeT>(value)) {
                value = ArrayTypeT{};
              }
              using AffectationExecutorT = VectorComponentAffectationExecutor<OperatorT, ArrayTypeT, ValueT, DataT>;
              m_affectation_executor =
                std::make_unique<AffectationExecutorT>(node, std::get<ArrayTypeT>(value), index_expression);
              break;
            }
            case 2: {
              using ArrayTypeT = TinyVector<2>;
              if (not std::holds_alternative<ArrayTypeT>(value)) {
                value = ArrayTypeT{};
              }
              using AffectationExecutorT = VectorComponentAffectationExecutor<OperatorT, ArrayTypeT, ValueT, DataT>;
              m_affectation_executor =
                std::make_unique<AffectationExecutorT>(node, std::get<ArrayTypeT>(value), index_expression);
              break;
            }
            case 3: {
              using ArrayTypeT = TinyVector<3>;
              if (not std::holds_alternative<ArrayTypeT>(value)) {
                value = ArrayTypeT{};
              }
              using AffectationExecutorT = VectorComponentAffectationExecutor<OperatorT, ArrayTypeT, ValueT, DataT>;
              m_affectation_executor =
                std::make_unique<AffectationExecutorT>(node, std::get<ArrayTypeT>(value), index_expression);
              break;
            }
              // LCOV_EXCL_START
            default: {
              throw ParseError("unexpected error: invalid vector dimension",
                               std::vector{array_subscript_expression.begin()});
            }
              // LCOV_EXCL_STOP
            }
          } else if (array_expression.m_data_type == ASTNodeDataType::matrix_t) {
            Assert(array_subscript_expression.children.size() == 3);
            Assert(array_expression.m_data_type.nbRows() == array_expression.m_data_type.nbColumns());
    
            auto& index0_expression = *array_subscript_expression.children[1];
            auto& index1_expression = *array_subscript_expression.children[2];
    
            switch (array_expression.m_data_type.nbRows()) {
            case 1: {
              using ArrayTypeT = TinyMatrix<1>;
              if (not std::holds_alternative<ArrayTypeT>(value)) {
                value = ArrayTypeT{};
              }
              using AffectationExecutorT = MatrixComponentAffectationExecutor<OperatorT, ArrayTypeT, ValueT, DataT>;
              m_affectation_executor     = std::make_unique<AffectationExecutorT>(node, std::get<ArrayTypeT>(value),
                                                                              index0_expression, index1_expression);
              break;
            }
            case 2: {
              using ArrayTypeT = TinyMatrix<2>;
              if (not std::holds_alternative<ArrayTypeT>(value)) {
                value = ArrayTypeT{};
              }
              using AffectationExecutorT = MatrixComponentAffectationExecutor<OperatorT, ArrayTypeT, ValueT, DataT>;
              m_affectation_executor     = std::make_unique<AffectationExecutorT>(node, std::get<ArrayTypeT>(value),
                                                                              index0_expression, index1_expression);
              break;
            }
            case 3: {
              using ArrayTypeT = TinyMatrix<3>;
              if (not std::holds_alternative<ArrayTypeT>(value)) {
                value = ArrayTypeT{};
              }
              using AffectationExecutorT = MatrixComponentAffectationExecutor<OperatorT, ArrayTypeT, ValueT, DataT>;
              m_affectation_executor     = std::make_unique<AffectationExecutorT>(node, std::get<ArrayTypeT>(value),
                                                                              index0_expression, index1_expression);
              break;
            }
              // LCOV_EXCL_START
            default: {
              throw ParseError("unexpected error: invalid vector dimension",
                               std::vector{array_subscript_expression.begin()});
            }
              // LCOV_EXCL_STOP
            }
          } else {
            // LCOV_EXCL_START
            throw UnexpectedError("invalid subscript expression");
            // LCOV_EXCL_STOP
          }
        } else {
          // LCOV_EXCL_START
          throw ParseError("unexpected error: invalid lhs", std::vector{node.children[0]->begin()});
          // LCOV_EXCL_STOP
        }
      }
    };
    
    template <typename OperatorT, typename ValueT>
    class AffectationToTinyVectorFromListProcessor 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");
    
        ValueT v;
        for (size_t i = 0; i < v.dimension(); ++i) {
          std::visit(
            [&](auto&& child_value) {
              using T = std::decay_t<decltype(child_value)>;
              if constexpr (std::is_same_v<T, bool> or std::is_same_v<T, uint64_t> or std::is_same_v<T, int64_t> or
                            std::is_same_v<T, double>) {
                v[i] = child_value;
              } else {
                // LCOV_EXCL_START
                throw ParseError("unexpected error: unexpected right hand side type in affectation", m_node.begin());
                // LCOV_EXCL_STOP
              }
            },
            children_values[i]);
        }
    
        *m_lhs = v;
        return {};
      }
    
      AffectationToTinyVectorFromListProcessor(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 OperatorT, typename ValueT>
    class AffectationToTinyMatrixFromListProcessor 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");
    
        ValueT v;
        for (size_t i = 0, l = 0; i < v.nbRows(); ++i) {
          for (size_t j = 0; j < v.nbColumns(); ++j, ++l) {
            std::visit(
              [&](auto&& child_value) {
                using T = std::decay_t<decltype(child_value)>;
                if constexpr (std::is_same_v<T, bool> or std::is_same_v<T, uint64_t> or std::is_same_v<T, int64_t> or
                              std::is_same_v<T, double>) {
                  v(i, j) = child_value;
                } else {
                  // LCOV_EXCL_START
                  throw ParseError("unexpected error: unexpected right hand side type in affectation", m_node.begin());
                  // LCOV_EXCL_STOP
                }
              },
              children_values[l]);
          }
        }
    
        *m_lhs = v;
        return {};
      }
    
      AffectationToTinyMatrixFromListProcessor(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 AffectationToTupleProcessor final : public INodeProcessor
    {
     private:
      ASTNode& m_node;
    
      DataVariant* m_lhs;
    
     public:
      DataVariant
      execute(ExecutionPolicy& exec_policy)
      {
        DataVariant value = m_node.children[1]->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>) {
              if constexpr (std::is_arithmetic_v<T>) {
                *m_lhs = std::vector{std::move(std::to_string(v))};
              } else {
                std::ostringstream os;
                os << v;
                *m_lhs = std::vector<std::string>{os.str()};
              }
            } 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_node.begin());
                // LCOV_EXCL_STOP
              }
            } else {
              // LCOV_EXCL_START
              throw ParseError("unexpected error: unexpected right hand side type in affectation", m_node.begin());
              // LCOV_EXCL_STOP
            }
          },
          value);
    
        return {};
      }
    
      AffectationToTupleProcessor(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 AffectationToTupleFromListProcessor final : public INodeProcessor
    {
     private:
      ASTNode& m_node;
    
      DataVariant* m_lhs;
    
      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>) {
                if constexpr (std::is_arithmetic_v<T>) {
                  tuple_value[i] = std::to_string(child_value);
                } else {
                  std::ostringstream os;
                  os << child_value;
                  tuple_value[i] = os.str();
                }
              } else if constexpr (is_tiny_vector_v<ValueT>) {
                if constexpr (std::is_same_v<T, AggregateDataVariant>) {
                  ValueT& v = tuple_value[i];
                  Assert(ValueT::Dimension == child_value.size());
                  for (size_t j = 0; j < ValueT::Dimension; ++j) {
                    std::visit(
                      [&](auto&& vj) {
                        using Ti = std::decay_t<decltype(vj)>;
                        if constexpr (std::is_convertible_v<Ti, typename ValueT::data_type>) {
                          v[j] = vj;
                        } else {
                          // LCOV_EXCL_START
                          throw ParseError("unexpected error: unexpected right hand side type in affectation",
                                           m_node.children[1]->children[i]->begin());
                          // LCOV_EXCL_STOP
                        }
                      },
                      child_value[j]);
                  }
                } else 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_node.children[1]->children[i]->begin());
                  // LCOV_EXCL_STOP
                }
              } else if constexpr (is_tiny_matrix_v<ValueT>) {
                if constexpr (std::is_same_v<T, AggregateDataVariant>) {
                  ValueT& A = tuple_value[i];
                  Assert(A.nbRows() * A.nbColumns() == child_value.size());
                  for (size_t j = 0, l = 0; j < A.nbRows(); ++j) {
                    for (size_t k = 0; k < A.nbColumns(); ++k, ++l) {
                      std::visit(
                        [&](auto&& Ajk) {
                          using Ti = std::decay_t<decltype(Ajk)>;
                          if constexpr (std::is_convertible_v<Ti, typename ValueT::data_type>) {
                            A(j, k) = Ajk;
                          } else {
                            // LCOV_EXCL_START
                            throw ParseError("unexpected error: unexpected right hand side type in affectation",
                                             m_node.children[1]->children[i]->begin());
                            // LCOV_EXCL_STOP
                          }
                        },
                        child_value[l]);
                    }
                  }
                } else 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_node.children[1]->children[i]->begin());
                  // LCOV_EXCL_STOP
                }
              } else {
                // LCOV_EXCL_START
                throw ParseError("unexpected error: unexpected right hand side type in affectation",
                                 m_node.children[1]->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>) {
          if constexpr (std::is_arithmetic_v<DataType>) {
            for (size_t i = 0; i < values.size(); ++i) {
              v[i] = std::to_string(values[i]);
            }
          } else {
            for (size_t i = 0; i < values.size(); ++i) {
              std::ostringstream sout;
              sout << values[i];
              v[i] = sout.str();
            }
          }
        } else {
          // LCOV_EXCL_START
          throw ParseError("unexpected error: unexpected right hand side type in tuple affectation",
                           m_node.children[1]->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_node.children[1]->begin()});
              // LCOV_EXCL_STOP
            }
          },
          m_node.children[1]->execute(exec_policy));
    
        return {};
      }
    
      AffectationToTupleFromListProcessor(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
    {
     private:
      ASTNode& m_node;
    
      DataVariant* m_lhs;
    
     public:
      DataVariant
      execute(ExecutionPolicy&)
      {
        *m_lhs = ValueT{zero};
        return {};
      }
    
      AffectationFromZeroProcessor(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 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 if (lhs_node.is_type<language::subscript_expression>()) {
          auto& array_subscript_expression = lhs_node;
    
          auto& array_expression = *array_subscript_expression.children[0];
          Assert(array_expression.is_type<language::name>());
    
          const std::string& symbol = array_expression.string();
    
          auto [i_symbol, found] = m_node.m_symbol_table->find(symbol, array_subscript_expression.begin());
          Assert(found);
          DataVariant& value = i_symbol->attributes().value();
    
          if (array_expression.m_data_type == ASTNodeDataType::vector_t) {
            Assert(array_subscript_expression.children.size() == 2);
    
            auto& index_expression = *array_subscript_expression.children[1];
    
            switch (array_expression.m_data_type.dimension()) {
            case 1: {
              using ArrayTypeT = TinyVector<1>;
              if (not std::holds_alternative<ArrayTypeT>(value)) {
                value = ArrayTypeT{};
              }
              using AffectationExecutorT = VectorComponentAffectationExecutor<OperatorT, ArrayTypeT, ValueT, DataT>;
              m_affectation_executor_list.emplace_back(
                std::make_unique<AffectationExecutorT>(lhs_node, std::get<ArrayTypeT>(value), index_expression));
              break;
            }
            case 2: {
              using ArrayTypeT = TinyVector<2>;
              if (not std::holds_alternative<ArrayTypeT>(value)) {
                value = ArrayTypeT{};
              }
              using AffectationExecutorT = VectorComponentAffectationExecutor<OperatorT, ArrayTypeT, ValueT, DataT>;
              m_affectation_executor_list.emplace_back(
                std::make_unique<AffectationExecutorT>(lhs_node, std::get<ArrayTypeT>(value), index_expression));
              break;
            }
            case 3: {
              using ArrayTypeT = TinyVector<3>;
              if (not std::holds_alternative<ArrayTypeT>(value)) {
                value = ArrayTypeT{};
              }
              using AffectationExecutorT = VectorComponentAffectationExecutor<OperatorT, ArrayTypeT, ValueT, DataT>;
              m_affectation_executor_list.emplace_back(
                std::make_unique<AffectationExecutorT>(lhs_node, std::get<ArrayTypeT>(value), index_expression));
              break;
            }
              // LCOV_EXCL_START
            default: {
              throw ParseError("unexpected error: invalid vector dimension",
                               std::vector{array_subscript_expression.begin()});
            }
              // LCOV_EXCL_STOP
            }
          } else if (array_expression.m_data_type == ASTNodeDataType::matrix_t) {
            Assert(array_subscript_expression.children.size() == 3);
    
            auto& index0_expression = *array_subscript_expression.children[1];
            auto& index1_expression = *array_subscript_expression.children[2];
    
            Assert(array_expression.m_data_type.nbRows() == array_expression.m_data_type.nbColumns());
    
            switch (array_expression.m_data_type.nbRows()) {
            case 1: {
              using ArrayTypeT = TinyMatrix<1>;
              if (not std::holds_alternative<ArrayTypeT>(value)) {
                value = ArrayTypeT{};
              }
              using AffectationExecutorT = MatrixComponentAffectationExecutor<OperatorT, ArrayTypeT, ValueT, DataT>;
              m_affectation_executor_list.emplace_back(
                std::make_unique<AffectationExecutorT>(lhs_node, std::get<ArrayTypeT>(value), index0_expression,
                                                       index1_expression));
              break;
            }
            case 2: {
              using ArrayTypeT = TinyMatrix<2>;
              if (not std::holds_alternative<ArrayTypeT>(value)) {
                value = ArrayTypeT{};
              }
              using AffectationExecutorT = MatrixComponentAffectationExecutor<OperatorT, ArrayTypeT, ValueT, DataT>;
              m_affectation_executor_list.emplace_back(
                std::make_unique<AffectationExecutorT>(lhs_node, std::get<ArrayTypeT>(value), index0_expression,
                                                       index1_expression));
              break;
            }
            case 3: {
              using ArrayTypeT = TinyMatrix<3>;
              if (not std::holds_alternative<ArrayTypeT>(value)) {
                value = ArrayTypeT{};
              }
              using AffectationExecutorT = MatrixComponentAffectationExecutor<OperatorT, ArrayTypeT, ValueT, DataT>;
              m_affectation_executor_list.emplace_back(
                std::make_unique<AffectationExecutorT>(lhs_node, std::get<ArrayTypeT>(value), index0_expression,
                                                       index1_expression));
              break;
            }
              // LCOV_EXCL_START
            default: {
              throw ParseError("unexpected error: invalid vector dimension",
                               std::vector{array_subscript_expression.begin()});
            }
              // LCOV_EXCL_STOP
            }
          }
        } 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