Skip to content
Snippets Groups Projects
Select Git revision
  • 4d41f63feda460e2209008a294e6c8f7592a840c
  • 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

PugsUtils.cpp

Blame
  • 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