#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