#ifndef JINJA2CPP_SRC_VALUE_VISITORS_H
#define JINJA2CPP_SRC_VALUE_VISITORS_H
#include "expression_evaluator.h"
#include "helpers.h"
#include "jinja2cpp/value.h"
#include <boost/algorithm/string/predicate.hpp>
#include <boost/optional.hpp>
#include <fmt/format.h>
#include <fmt/xchar.h>
#include <iostream>
#include <cmath>
#include <limits>
#include <utility>
#include <typeinfo>
namespace jinja2
{
namespace detail
{
template<typename V>
struct RecursiveUnwrapper
{
V* m_visitor{};
RecursiveUnwrapper(V* v)
: m_visitor(v)
{}
template<typename T>
static const auto& UnwrapRecursive(const T& arg)
{
return arg; // std::forward<T>(arg);
}
template<typename T>
static auto& UnwrapRecursive(const RecursiveWrapper<T>& arg)
{
return arg.GetValue();
}
// template<typename T>
// static auto& UnwrapRecursive(RecursiveWrapper<T>& arg)
// {
// return arg.GetValue();
// }
template<typename ... Args>
auto operator()(const Args& ... args) const
{
assert(m_visitor != nullptr);
return (*m_visitor)(UnwrapRecursive(args)...);
}
};
template<typename Fn>
auto ApplyUnwrapped(const InternalValueData& val, Fn&& fn)
{
auto valueRef = GetIf<ValueRef>(&val);
auto targetString = GetIf<TargetString>(&val);
auto targetSV = GetIf<TargetStringView>(&val);
// auto internalValueRef = GetIf<InternalValueRef>(&val);
if (valueRef != nullptr)
return fn(valueRef->get().data());
else if (targetString != nullptr)
return fn(*targetString);
else if (targetSV != nullptr)
return fn(*targetSV);
// else if (internalValueRef != nullptr)
// return fn(internalValueRef->get());
return fn(val);
}
} // namespace detail
template<typename V, typename ... Args>
auto Apply(const InternalValue& val, Args&& ... args)
{
return detail::ApplyUnwrapped(val.GetData(), [&args...](auto& val) {
auto v = V(args...);
return std::visit(detail::RecursiveUnwrapper<V>(&v), val);
});
}
template<typename V, typename ... Args>
auto Apply2(const InternalValue& val1, const InternalValue& val2, Args&& ... args)
{
return detail::ApplyUnwrapped(val1.GetData(), [&val2, &args...](auto& uwVal1) {
return detail::ApplyUnwrapped(val2.GetData(), [&uwVal1, &args...](auto& uwVal2) {
auto v = V(args...);
return std::visit(detail::RecursiveUnwrapper<V>(&v), uwVal1, uwVal2);
});
});
}
bool ConvertToBool(const InternalValue& val);
namespace visitors
{
template<typename R = InternalValue>
struct BaseVisitor
{
R operator() (const GenericMap&) const
{
assert(false);
return R();
}
R operator() (const GenericList&) const
{
assert(false);
return R();
}
R operator() (const ValueRef&) const
{
assert(false);
return R();
}
R operator() (const TargetString&) const
{
assert(false);
return R();
}
template<typename T>
R operator() (T&&) const
{
return R();
}
template<typename T, typename U>
R operator() (T&&, U&&) const
{
return R();
}
};
template<typename CharT>
struct ValueRendererBase
{
ValueRendererBase(std::basic_string<CharT>& os)
: m_os(&os)
{
}
template<typename T>
void operator()(const T& val) const;
void operator()(double val) const;
void operator()(const std::basic_string_view<CharT>& val) const
{
m_os->append(val.begin(), val.end());
}
void operator()(const std::basic_string<CharT>& val) const
{
m_os->append(val.begin(), val.end());
}
void operator()(const EmptyValue&) const {}
void operator()(const ValuesList&) const {}
void operator()(const ValuesMap&) const {}
void operator()(const GenericMap&) const {}
void operator()(const GenericList&) const {}
void operator()(const MapAdapter&) const {}
void operator()(const ListAdapter&) const {}
void operator()(const ValueRef&) const {}
void operator()(const TargetString&) const {}
void operator()(const TargetStringView&) const {}
void operator()(const KeyValuePair&) const {}
void operator()(const Callable&) const {}
void operator()(const UserCallable&) const {}
void operator()(const std::shared_ptr<IRendererBase>) const {}
template<typename T>
void operator()(const boost::recursive_wrapper<T>&) const {}
template<typename T>
void operator()(const RecWrapper<T>&) const {}
auto GetOs() const { return std::back_inserter(*m_os); }
std::basic_string<CharT>* m_os;
};
template<>
template<typename T>
void ValueRendererBase<char>::operator()(const T& val) const
{
fmt::format_to(GetOs(), "{}", val);
}
template<>
template<typename T>
void ValueRendererBase<wchar_t>::operator()(const T& val) const
{
fmt::format_to(GetOs(), L"{}", val);
}
template<>
inline void ValueRendererBase<char>::operator()(double val) const
{
fmt::format_to(GetOs(), "{:.8g}", val);
}
template<>
inline void ValueRendererBase<wchar_t>::operator()(double val) const
{
fmt::format_to(GetOs(), L"{:.8g}", val);
}
struct InputValueConvertor
{
using result_t = boost::optional<InternalValue>;
InputValueConvertor(bool byValue, bool allowStringRef)
: m_byValue(byValue)
, m_allowStringRef(allowStringRef)
{
}
template<typename ChT>
result_t operator()(const std::basic_string<ChT>& val) const
{
if (m_allowStringRef)
return result_t(TargetStringView(std::basic_string_view<ChT>(val)));
return result_t(TargetString(val));
}
template<typename ChT>
result_t operator()(std::basic_string<ChT>& val) const
{
return result_t(TargetString(std::move(val)));
}
result_t operator()(const ValuesList& vals) const
{
if (m_byValue)
{
ValuesList newVals(vals);
return result_t(InternalValue(ListAdapter::CreateAdapter(std::move(newVals))));
}
return result_t(InternalValue(ListAdapter::CreateAdapter(vals)));
}
result_t operator() (ValuesList& vals) const
{
return result_t(InternalValue(ListAdapter::CreateAdapter(std::move(vals))));
}
result_t operator() (const GenericList& vals) const
{
if (m_byValue)
{
GenericList newVals(vals);
return result_t(InternalValue(ListAdapter::CreateAdapter(std::move(newVals))));
}
return result_t(InternalValue(ListAdapter::CreateAdapter(vals)));
}
result_t operator() (GenericList& vals) const
{
return result_t(InternalValue(ListAdapter::CreateAdapter(std::move(vals))));
}
result_t operator()(const ValuesMap& vals) const
{
if (m_byValue)
{
ValuesMap newVals(vals);
return result_t(CreateMapAdapter(std::move(newVals)));
}
return result_t(CreateMapAdapter(vals));
}
result_t operator()(ValuesMap& vals) const { return result_t(CreateMapAdapter(std::move(vals))); }
result_t operator()(const GenericMap& vals) const
{
if (m_byValue)
{
GenericMap newVals(vals);
return result_t(CreateMapAdapter(std::move(newVals)));
}
return result_t(CreateMapAdapter(vals));
}
result_t operator()(GenericMap& vals) const { return result_t(CreateMapAdapter(std::move(vals))); }
result_t operator()(const UserCallable& val) const { return ConvertUserCallable(val); }
result_t operator()(UserCallable& val) const { return ConvertUserCallable(std::move(val)); }
template<typename T>
result_t operator()(const RecWrapper<T>& val) const
{
return this->operator()(const_cast<const T&>(*val));
}
template<typename T>
result_t operator()(RecWrapper<T>& val) const
{
return this->operator()(*val);
}
template<typename T>
result_t operator()(const T& val) const
{
return result_t(InternalValue(val));
}
static result_t ConvertUserCallable(const UserCallable& val);
bool m_byValue{};
bool m_allowStringRef{};
};
template<typename CharT>
struct ValueRenderer;
template<>
struct ValueRenderer<char> : ValueRendererBase<char>
{
ValueRenderer(std::string& os)
: ValueRendererBase<char>::ValueRendererBase<char>(os)
{
}
using ValueRendererBase<char>::operator ();
void operator()(const std::wstring& str) const
{
(*m_os) += ConvertString<std::string>(str);
}
void operator()(const std::wstring_view& str) const
{
(*m_os) += ConvertString<std::string>(str);
}
void operator() (bool val) const
{
m_os->append(val ? "true" : "false");
}
};
template<>
struct ValueRenderer<wchar_t> : ValueRendererBase<wchar_t>
{
ValueRenderer(std::wstring& os)
: ValueRendererBase<wchar_t>::ValueRendererBase<wchar_t>(os)
{
}
using ValueRendererBase<wchar_t>::operator ();
void operator()(const std::string& str) const
{
(*m_os) += ConvertString<std::wstring>(str);
}
void operator()(const std::string_view& str) const
{
(*m_os) += ConvertString<std::wstring>(str);
}
void operator() (bool val) const
{
// fmt::format_to(GetOs(), L"{}", (const wchar_t*)(val ? "true" : "false"));
m_os->append(val ? L"true" : L"false");
}
};
struct UnaryOperation : BaseVisitor<InternalValue>
{
using BaseVisitor::operator ();
UnaryOperation(UnaryExpression::Operation oper)
: m_oper(oper)
{
}
InternalValue operator() (int64_t val) const
{
InternalValue result;
switch (m_oper)
{
case jinja2::UnaryExpression::LogicalNot:
result = val ? false : true;
break;
case jinja2::UnaryExpression::UnaryPlus:
result = +val;
break;
case jinja2::UnaryExpression::UnaryMinus:
result = -val;
break;
}
return result;
}
InternalValue operator() (double val) const
{
InternalValue result;
switch (m_oper)
{
case jinja2::UnaryExpression::LogicalNot:
result = fabs(val) > std::numeric_limits<double>::epsilon() ? false : true;
break;
case jinja2::UnaryExpression::UnaryPlus:
result = +val;
break;
case jinja2::UnaryExpression::UnaryMinus:
result = -val;
break;
}
return result;
}
InternalValue operator() (bool val) const
{
InternalValue result;
switch (m_oper)
{
case jinja2::UnaryExpression::LogicalNot:
result = !val;
break;
default:
break;
}
return result;
}
InternalValue operator() (const MapAdapter&) const
{
InternalValue result;
switch (m_oper)
{
case jinja2::UnaryExpression::LogicalNot:
result = true;
break;
default:
break;
}
return result;
}
InternalValue operator() (const ListAdapter&) const
{
InternalValue result;
switch (m_oper)
{
case jinja2::UnaryExpression::LogicalNot:
result = true;
break;
default:
break;
}
return result;
}
template<typename CharT>
InternalValue operator() (const std::basic_string<CharT>& val) const
{
InternalValue result;
switch (m_oper)
{
case jinja2::UnaryExpression::LogicalNot:
result = val.empty();
break;
default:
break;
}
return result;
}
template<typename CharT>
InternalValue operator() (const std::basic_string_view<CharT>& val) const
{
InternalValue result;
switch (m_oper)
{
case jinja2::UnaryExpression::LogicalNot:
result = val.empty();
break;
default:
break;
}
return result;
}
InternalValue operator() (const EmptyValue&) const
{
InternalValue result;
switch (m_oper)
{
case jinja2::UnaryExpression::LogicalNot:
result = true;
break;
default:
break;
}
return result;
}
UnaryExpression::Operation m_oper;
};
struct BinaryMathOperation : BaseVisitor<>
{
using BaseVisitor::operator ();
using ResultType = InternalValue;
// InternalValue operator() (int, int) const {return InternalValue();}
bool AlmostEqual(double x, double y) const
{
return std::abs(x - y) <= std::numeric_limits<double>::epsilon() * std::abs(x + y) * 6
|| std::abs(x - y) < std::numeric_limits<double>::min();
}
BinaryMathOperation(BinaryExpression::Operation oper, BinaryExpression::CompareType compType = BinaryExpression::CaseSensitive)
: m_oper(oper)
, m_compType(compType)
{
}
ResultType operator() (double left, double right) const
{
ResultType result = 0.0;
switch (m_oper)
{
case jinja2::BinaryExpression::Plus:
result = left + right;
break;
case jinja2::BinaryExpression::Minus:
result = left - right;
break;
case jinja2::BinaryExpression::Mul:
result = left * right;
break;
case jinja2::BinaryExpression::Div:
result = left / right;
break;
case jinja2::BinaryExpression::DivReminder:
result = std::remainder(left, right);
break;
case jinja2::BinaryExpression::DivInteger:
{
double val = left / right;
result = val < 0 ? ceil(val) : floor(val);
break;
}
case jinja2::BinaryExpression::Pow:
result = pow(left, right);
break;
case jinja2::BinaryExpression::LogicalEq:
result = AlmostEqual(left, right);
break;
case jinja2::BinaryExpression::LogicalNe:
result = !AlmostEqual(left, right);
break;
case jinja2::BinaryExpression::LogicalGt:
result = left > right;
break;
case jinja2::BinaryExpression::LogicalLt:
result = left < right;
break;
case jinja2::BinaryExpression::LogicalGe:
result = left > right || AlmostEqual(left, right);
break;
case jinja2::BinaryExpression::LogicalLe:
result = left < right || AlmostEqual(left, right);
break;
default:
break;
}
return result;
}
ResultType operator() (int64_t left, int64_t right) const
{
ResultType result;
switch (m_oper)
{
case jinja2::BinaryExpression::Plus:
result = left + right;
break;
case jinja2::BinaryExpression::Minus:
result = left - right;
break;
case jinja2::BinaryExpression::Mul:
result = left * right;
break;
case jinja2::BinaryExpression::DivInteger:
result = left / right;
break;
case jinja2::BinaryExpression::Div:
case jinja2::BinaryExpression::DivReminder:
case jinja2::BinaryExpression::Pow:
result = this->operator ()(static_cast<double>(left), static_cast<double>(right));
break;
case jinja2::BinaryExpression::LogicalEq:
result = left == right;
break;
case jinja2::BinaryExpression::LogicalNe:
result = left != right;
break;
case jinja2::BinaryExpression::LogicalGt:
result = left > right;
break;
case jinja2::BinaryExpression::LogicalLt:
result = left < right;
break;
case jinja2::BinaryExpression::LogicalGe:
result = left >= right;
break;
case jinja2::BinaryExpression::LogicalLe:
result = left <= right;
break;
default:
break;
}
return result;
}
ResultType operator() (int64_t left, double right) const
{
return this->operator ()(static_cast<double>(left), static_cast<double>(right));
}
ResultType operator() (double left, int64_t right) const
{
return this->operator ()(static_cast<double>(left), static_cast<double>(right));
}
template<typename CharT>
ResultType operator() (const std::basic_string<CharT> &left, const std::basic_string<CharT> &right) const
{
return ProcessStrings(std::basic_string_view<CharT>(left), std::basic_string_view<CharT>(right));
}
template<typename CharT1, typename CharT2>
std::enable_if_t<!std::is_same<CharT1, CharT2>::value, ResultType> operator() (const std::basic_string<CharT1>& left, const std::basic_string<CharT2>& right) const
{
auto rightStr = ConvertString<std::basic_string<CharT1>>(right);
return ProcessStrings(std::basic_string_view<CharT1>(left), std::basic_string_view<CharT1>(rightStr));
}
template<typename CharT>
ResultType operator() (const std::basic_string_view<CharT> &left, const std::basic_string<CharT> &right) const
{
return ProcessStrings(left, std::basic_string_view<CharT>(right));
}
template<typename CharT1, typename CharT2>
std::enable_if_t<!std::is_same<CharT1, CharT2>::value, ResultType> operator() (const std::basic_string_view<CharT1>& left, const std::basic_string<CharT2>& right) const
{
auto rightStr = ConvertString<std::basic_string<CharT1>>(right);
return ProcessStrings(left, std::basic_string_view<CharT1>(rightStr));
}
template<typename CharT>
ResultType operator() (const std::basic_string<CharT> &left, const std::basic_string_view<CharT> &right) const
{
return ProcessStrings(std::basic_string_view<CharT>(left), right);
}
template<typename CharT1, typename CharT2>
std::enable_if_t<!std::is_same<CharT1, CharT2>::value, ResultType> operator() (const std::basic_string<CharT1>& left, const std::basic_string_view<CharT2>& right) const
{
auto rightStr = ConvertString<std::basic_string<CharT1>>(right);
return ProcessStrings(std::basic_string_view<CharT1>(left), std::basic_string_view<CharT1>(rightStr));
}
template<typename CharT>
ResultType operator() (const std::basic_string_view<CharT> &left, const std::basic_string_view<CharT> &right) const
{
return ProcessStrings(left, right);
}
template<typename CharT1, typename CharT2>
std::enable_if_t<!std::is_same<CharT1, CharT2>::value, ResultType> operator() (const std::basic_string_view<CharT1>& left, const std::basic_string_view<CharT2>& right) const
{
auto rightStr = ConvertString<std::basic_string<CharT1>>(right);
return ProcessStrings(left, std::basic_string_view<CharT1>(rightStr));
}
template<typename CharT>
ResultType operator() (const std::basic_string<CharT> &left, int64_t right) const
{
return RepeatString(std::basic_string_view<CharT>(left), right);
}
template<typename CharT>
ResultType operator() (const std::basic_string_view<CharT> &left, int64_t right) const
{
return RepeatString(left, right);
}
template<typename CharT>
ResultType RepeatString(const std::basic_string_view<CharT>& left, const int64_t right) const
{
using string = std::basic_string<CharT>;
ResultType result;
if(m_oper == jinja2::BinaryExpression::Mul)
{
string str;
for (int i = 0; i < right; ++i)
str.append(left.begin(), left.end());
result = TargetString(std::move(str));
}
return result;
}
template<typename CharT>
ResultType ProcessStrings(const std::basic_string_view<CharT>& left, const std::basic_string_view<CharT>& right) const
{
using string = std::basic_string<CharT>;
ResultType result;
switch (m_oper)
{
case jinja2::BinaryExpression::Plus:
{
auto str = string(left.begin(), left.end());
str.append(right.begin(), right.end());
result = TargetString(std::move(str));
break;
}
case jinja2::BinaryExpression::LogicalEq:
result = m_compType == BinaryExpression::CaseSensitive ? left == right : boost::iequals(left, right);
break;
case jinja2::BinaryExpression::LogicalNe:
result = m_compType == BinaryExpression::CaseSensitive ? left != right : !boost::iequals(left, right);
break;
case jinja2::BinaryExpression::LogicalGt:
result = m_compType == BinaryExpression::CaseSensitive ? left > right : boost::lexicographical_compare(right, left, boost::algorithm::is_iless());
break;
case jinja2::BinaryExpression::LogicalLt:
result = m_compType == BinaryExpression::CaseSensitive ? left < right : boost::lexicographical_compare(left, right, boost::algorithm::is_iless());
break;
case jinja2::BinaryExpression::LogicalGe:
if (m_compType == BinaryExpression::CaseSensitive)
{
result = left >= right;
}
else
{
result = boost::iequals(left, right) ? true : boost::lexicographical_compare(right, left, boost::algorithm::is_iless());
}
break;
case jinja2::BinaryExpression::LogicalLe:
if (m_compType == BinaryExpression::CaseSensitive)
{
result = left <= right;
}
else
{
result = boost::iequals(left, right) ? true : boost::lexicographical_compare(left, right, boost::algorithm::is_iless());
}
break;
default:
break;
}
return result;
}
ResultType operator() (const KeyValuePair& left, const KeyValuePair& right) const
{
ResultType result;
switch (m_oper)
{
case jinja2::BinaryExpression::LogicalEq:
result = ConvertToBool(this->operator ()(left.key, right.key)) && ConvertToBool(Apply2<BinaryMathOperation>(left.value, right.value, BinaryExpression::LogicalEq, m_compType));
break;
case jinja2::BinaryExpression::LogicalNe:
result = ConvertToBool(this->operator ()(left.key, right.key)) || ConvertToBool(Apply2<BinaryMathOperation>(left.value, right.value, BinaryExpression::LogicalNe, m_compType));
break;
default:
break;
}
return result;
}
ResultType operator() (const ListAdapter& left, const ListAdapter& right) const
{
ResultType result;
if (m_oper == jinja2::BinaryExpression::Plus)
{
InternalValueList values;
values.reserve(left.GetSize().value_or(0) + right.GetSize().value_or(0));
for (auto& v : left)
values.push_back(v);
for (auto& v : right)
values.push_back(v);
result = ListAdapter::CreateAdapter(std::move(values));
}
return result;
}
ResultType operator() (const ListAdapter& left, int64_t right) const
{
ResultType result;
if (right >= 0 && m_oper == jinja2::BinaryExpression::Mul)
{
InternalValueList values;
values.reserve(left.GetSize().value_or(0));
for (auto& v : left)
values.push_back(v);
auto listSize = values.size() * right;
result = ListAdapter::CreateAdapter(static_cast<size_t>(listSize),
[size = values.size(), values = std::move(values)](size_t idx) { return values[idx % size]; });
}
return result;
}
ResultType operator() (bool left, bool right) const
{
ResultType result;
switch (m_oper)
{
case jinja2::BinaryExpression::LogicalEq:
result = left == right;
break;
case jinja2::BinaryExpression::LogicalNe:
result = left != right;
break;
case jinja2::BinaryExpression::LogicalLt:
result = (left ? 1 : 0) < (right ? 1 : 0);
break;
default:
break;
}
return result;
}
ResultType operator() (EmptyValue, EmptyValue) const
{
ResultType result;
switch (m_oper)
{
case jinja2::BinaryExpression::LogicalEq:
result = true;
break;
case jinja2::BinaryExpression::LogicalNe:
result = false;
break;
default:
break;
}
return result;
}
template<typename T>
ResultType operator() (EmptyValue, T&&) const
{
ResultType result;
switch (m_oper)
{
case jinja2::BinaryExpression::LogicalEq:
result = false;
break;
case jinja2::BinaryExpression::LogicalNe:
result = true;
break;
default:
break;
}
return result;
}
template<typename T>
ResultType operator() (T&&, EmptyValue) const
{
ResultType result;
switch (m_oper)
{
case jinja2::BinaryExpression::LogicalEq:
result = false;
break;
case jinja2::BinaryExpression::LogicalNe:
result = true;
break;
default:
break;
}
return result;
}
BinaryExpression::Operation m_oper;
BinaryExpression::CompareType m_compType;
};
struct BooleanEvaluator : BaseVisitor<bool>
{
using BaseVisitor::operator ();
bool operator() (int64_t val) const
{
return val != 0;
}
bool operator() (double val) const
{
return fabs(val) < std::numeric_limits<double>::epsilon();
}
bool operator() (bool val) const
{
return val;
}
template<typename CharT>
bool operator()(const std::basic_string<CharT>& str) const
{
return !str.empty();
}
template<typename CharT>
bool operator()(const std::basic_string_view<CharT>& str) const
{
return !str.empty();
}
bool operator() (const MapAdapter& val) const
{
return val.GetSize() != 0ULL;
}
bool operator() (const ListAdapter& val) const
{
return val.GetSize() != 0ULL;
}
bool operator() (const EmptyValue&) const
{
return false;
}
};
template<typename TargetType>
struct NumberEvaluator
{
NumberEvaluator(TargetType def = 0) : m_def(def)
{}
TargetType operator ()(int64_t val) const
{
return static_cast<TargetType>(val);
}
TargetType operator ()(double val) const
{
return static_cast<TargetType>(val);
}
TargetType operator ()(bool val) const
{
return static_cast<TargetType>(val);
}
template<typename U>
TargetType operator()(U&&) const
{
return m_def;
}
TargetType m_def;
};
using IntegerEvaluator = NumberEvaluator<int64_t>;
using DoubleEvaluator = NumberEvaluator<double>;
struct StringJoiner : BaseVisitor<TargetString>
{
using BaseVisitor::operator ();
template<typename CharT>
TargetString operator() (EmptyValue, const std::basic_string<CharT>& str) const
{
return str;
}
template<typename CharT>
TargetString operator() (EmptyValue, const std::basic_string_view<CharT>& str) const
{
return std::basic_string<CharT>(str.begin(), str.end());
}
template<typename CharT>
TargetString operator() (const std::basic_string<CharT>& left, const std::basic_string<CharT>& right) const
{
return left + right;
}
template<typename CharT1, typename CharT2>
std::enable_if_t<!std::is_same<CharT1, CharT2>::value, TargetString> operator() (const std::basic_string<CharT1>& left, const std::basic_string<CharT2>& right) const
{
return left + ConvertString<std::basic_string<CharT1>>(right);
}
template<typename CharT>
TargetString operator() (std::basic_string<CharT> left, const std::basic_string_view<CharT>& right) const
{
left.append(right.begin(), right.end());
return std::move(left);
}
template<typename CharT1, typename CharT2>
std::enable_if_t<!std::is_same<CharT1, CharT2>::value, TargetString> operator() (std::basic_string<CharT1> left, const std::basic_string_view<CharT2>& right) const
{
auto r = ConvertString<std::basic_string<CharT1>>(right);
left.append(r.begin(), r.end());
return std::move(left);
}
};
template<typename Fn>
struct StringConverterImpl : public BaseVisitor<decltype(std::declval<Fn>()(std::declval<std::string_view>()))>
{
using R = decltype(std::declval<Fn>()(std::string_view()));
using BaseVisitor<R>::operator ();
StringConverterImpl(const Fn& fn) : m_fn(fn) {}
template<typename CharT>
R operator()(const std::basic_string<CharT>& str) const
{
return m_fn(std::basic_string_view<CharT>(str));
}
template<typename CharT>
R operator()(const std::basic_string_view<CharT>& str) const
{
return m_fn(str);
}
const Fn& m_fn;
};
template<typename CharT>
struct SameStringGetter : public visitors::BaseVisitor<nonstd::expected<void, std::basic_string<CharT>>>
{
using ResultString = std::basic_string<CharT>;
using OtherString = std::conditional_t<std::is_same<CharT, char>::value, std::wstring, std::string>;
using ResultStringView = std::basic_string_view<CharT>;
using OtherStringView = std::conditional_t<std::is_same<CharT, char>::value, std::wstring_view, std::string_view>;
using Result = nonstd::expected<void, ResultString>;
using BaseVisitor<Result>::operator ();
Result operator()(const ResultString& str) const
{
return nonstd::make_unexpected(str);
}
Result operator()(const ResultStringView& str) const
{
return nonstd::make_unexpected(ResultString(str.begin(), str.end()));
}
Result operator()(const OtherString& str) const
{
return nonstd::make_unexpected(ConvertString<ResultString>(str));
}
Result operator()(const OtherStringView& str) const
{
return nonstd::make_unexpected(ConvertString<ResultString>(str));
}
};
} // namespace visitors
inline bool ConvertToBool(const InternalValue& val)
{
return Apply<visitors::BooleanEvaluator>(val);
}
inline int64_t ConvertToInt(const InternalValue& val, int64_t def = 0)
{
return Apply<visitors::IntegerEvaluator>(val, def);
}
inline double ConvertToDouble(const InternalValue& val, double def = 0)
{
return Apply<visitors::DoubleEvaluator>(val, def);
}
template<template<typename> class Cvt = visitors::StringConverterImpl, typename Fn>
auto ApplyStringConverter(const InternalValue& str, Fn&& fn)
{
return Apply<Cvt<Fn>>(str, std::forward<Fn>(fn));
}
template<typename CharT>
auto GetAsSameString(const std::basic_string<CharT>&, const InternalValue& val)
{
using Result = std::optional<std::basic_string<CharT>>;
auto result = Apply<visitors::SameStringGetter<CharT>>(val);
if (!result)
return Result(result.error());
return Result();
}
template<typename CharT>
auto GetAsSameString(const std::basic_string_view<CharT>&, const InternalValue& val)
{
using Result = std::optional<std::basic_string<CharT>>;
auto result = Apply<visitors::SameStringGetter<CharT>>(val);
if (!result)
return Result(result.error());
return Result();
}
inline bool operator==(const InternalValueData& lhs, const InternalValueData& rhs)
{
InternalValue cmpRes;
cmpRes = Apply2<visitors::BinaryMathOperation>(lhs, rhs, BinaryExpression::LogicalEq);
return ConvertToBool(cmpRes);
}
inline bool operator!=(const InternalValueData& lhs, const InternalValueData& rhs)
{
return !(lhs == rhs);
}
} // namespace jinja2
#endif // JINJA2CPP_SRC_VALUE_VISITORS_H