Select Git revision
PugsUtils.cpp
-
Stéphane Del Pino authored
Actually, the "naive" (non reproducible mode) could not be used. The code was always computing reproducible sums, which is the default.
Stéphane Del Pino authoredActually, the "naive" (non reproducible mode) could not be used. The code was always computing reproducible sums, which is the default.
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