|
|
|
/*
|
|
|
|
* IdealIRC Script Engine - Scripting tailored for IRC clients.
|
|
|
|
* Copyright (C) 2021 Tom-Andre Barstad.
|
|
|
|
* This software is licensed under the Software Attribution License.
|
|
|
|
* See LICENSE for more information.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "ValueHolder.h"
|
|
|
|
#include "ScriptException.h"
|
|
|
|
#include "ParserOperator.h"
|
|
|
|
#include <sstream>
|
|
|
|
#include <functional>
|
|
|
|
#include <locale>
|
|
|
|
#include <iostream>
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
ValueHolder VoidItem;
|
|
|
|
|
|
|
|
inline char decimalSeparator()
|
|
|
|
{
|
|
|
|
return std::use_facet<std::numpunct<char>>(std::cout.getloc()).decimal_point();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ValueHolder::ValueHolder(const ValueHolder& other)
|
|
|
|
{
|
|
|
|
if (other.isReference())
|
|
|
|
m_val = std::get<ValueRef>(*other.m_val);
|
|
|
|
else
|
|
|
|
*this = other;
|
|
|
|
}
|
|
|
|
|
|
|
|
ValueHolder::ValueHolder(ValueHolder&& other)
|
|
|
|
: m_val(std::move(other.m_val))
|
|
|
|
, m_const(other.m_const)
|
|
|
|
{}
|
|
|
|
|
|
|
|
ValueHolder::~ValueHolder() = default;
|
|
|
|
|
|
|
|
ValueHolder::ValueHolder(int val)
|
|
|
|
: m_val(val)
|
|
|
|
{}
|
|
|
|
|
|
|
|
ValueHolder::ValueHolder(double val)
|
|
|
|
: m_val(val)
|
|
|
|
{}
|
|
|
|
|
|
|
|
ValueHolder::ValueHolder(bool val)
|
|
|
|
: m_val(val)
|
|
|
|
{}
|
|
|
|
|
|
|
|
ValueHolder::ValueHolder(const std::string& val)
|
|
|
|
: m_val(val)
|
|
|
|
{}
|
|
|
|
|
|
|
|
ValueHolder::ValueHolder(const ValueArray& array)
|
|
|
|
: m_val(ValueArray())
|
|
|
|
{
|
|
|
|
auto& valref = std::get<ValueArray>(*m_val);
|
|
|
|
for (const auto& [key,val] : array)
|
|
|
|
valref.insert_or_assign(key, std::make_unique<ValueHolder>(*val));
|
|
|
|
*this = array;
|
|
|
|
}
|
|
|
|
|
|
|
|
ValueHolder::ValueHolder(ValueArray&& array)
|
|
|
|
: m_val(std::move(array))
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
ValueHolder::ValueHolder(const ValueRef& ref)
|
|
|
|
: m_val(ref)
|
|
|
|
{}
|
|
|
|
|
|
|
|
ValueHolder& ValueHolder::operator=(int val)
|
|
|
|
{
|
|
|
|
mval() = val;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
ValueHolder& ValueHolder::operator=(double val)
|
|
|
|
{
|
|
|
|
mval() = val;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
ValueHolder& ValueHolder::operator=(bool val)
|
|
|
|
{
|
|
|
|
mval() = val;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
ValueHolder& ValueHolder::operator=(const std::string& val)
|
|
|
|
{
|
|
|
|
mval() = val;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
ValueHolder& ValueHolder::operator=(const ValueHolder& other)
|
|
|
|
{
|
|
|
|
switch (other.getType()) {
|
|
|
|
case Type::Integer:
|
|
|
|
mval() = std::get<int>(*other.mval());
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Type::Real:
|
|
|
|
mval() = std::get<double>(*other.mval());
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Type::Bool:
|
|
|
|
mval() = std::get<bool>(*other.mval());
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Type::String:
|
|
|
|
mval() = std::get<std::string>(*other.mval());
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Type::Map:
|
|
|
|
{
|
|
|
|
mval() = ValueArray();
|
|
|
|
auto& array = std::get<ValueArray>(*mval());
|
|
|
|
|
|
|
|
auto& otherArray = std::get<ValueArray>(*other.mval());
|
|
|
|
for (const auto& [key,val] : otherArray)
|
|
|
|
array.emplace(key, std::make_unique<ValueHolder>(*val));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case Type::Void:
|
|
|
|
mval() = {};
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_const = other.m_const;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
ValueHolder& ValueHolder::operator=(const ValueArray& val)
|
|
|
|
{
|
|
|
|
if (getType() != Type::Map)
|
|
|
|
mval() = ValueArray();
|
|
|
|
|
|
|
|
auto& valref = std::get<ValueArray>(*mval());
|
|
|
|
for (auto& [k,v] : val)
|
|
|
|
valref.emplace(k, new ValueHolder(*v));
|
|
|
|
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
ValueHolder& ValueHolder::operator[](const std::string& subscript)
|
|
|
|
{
|
|
|
|
if (getType() != Type::Map) {
|
|
|
|
VoidItem.clear();
|
|
|
|
return VoidItem;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto& valref = std::get<ValueArray>(*mval());
|
|
|
|
auto it = valref.find(subscript);
|
|
|
|
if (it == valref.end()) {
|
|
|
|
VoidItem.clear();
|
|
|
|
return VoidItem;
|
|
|
|
}
|
|
|
|
|
|
|
|
return *it->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
const ValueHolder& ValueHolder::operator[](const std::string& subscript) const
|
|
|
|
{
|
|
|
|
if (getType() != Type::Map) {
|
|
|
|
VoidItem.clear();
|
|
|
|
return VoidItem;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto& valref = std::get<ValueArray>(*mval());
|
|
|
|
auto it = valref.find(subscript);
|
|
|
|
if (it == valref.end()) {
|
|
|
|
VoidItem.clear();
|
|
|
|
return VoidItem;
|
|
|
|
}
|
|
|
|
|
|
|
|
return *it->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ValueHolder::rangedOperator(ParserOperator op, const ValueHolder& other) const
|
|
|
|
{
|
|
|
|
std::function<bool(int,int)> intOp = [](bool,bool){ return false; };
|
|
|
|
std::function<bool(double,double)> realOp = [](double,double){ return false; };
|
|
|
|
std::function<bool(std::string,std::string)> stringOp = [](std::string,std::string){ return false; };
|
|
|
|
|
|
|
|
using Op = ParserOperator;
|
|
|
|
switch (op) {
|
|
|
|
case Op::Lt:
|
|
|
|
intOp = std::less<int>();
|
|
|
|
realOp = std::less<double>();
|
|
|
|
stringOp = std::less<std::string>();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Op::Gt:
|
|
|
|
intOp = std::greater<int>();
|
|
|
|
realOp = std::greater<double>();
|
|
|
|
stringOp = std::greater<std::string>();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Op::Lte:
|
|
|
|
intOp = std::less_equal<int>();
|
|
|
|
realOp = std::less_equal<double>();
|
|
|
|
stringOp = std::less_equal<std::string>();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Op::Gte:
|
|
|
|
intOp = std::greater_equal<int>();
|
|
|
|
realOp = std::greater_equal<double>();
|
|
|
|
stringOp = std::greater_equal<std::string>();
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const Type mtype = getType();
|
|
|
|
switch (mtype) {
|
|
|
|
case Type::Integer:
|
|
|
|
{
|
|
|
|
const int mv_i = std::get<int>(*mval());
|
|
|
|
switch (other.getType()) {
|
|
|
|
case Type::Integer:
|
|
|
|
return intOp(mv_i, std::get<int>(*other.mval()));
|
|
|
|
case Type::Real:
|
|
|
|
return realOp(double(mv_i), std::get<double>(*other.mval()));
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
case Type::Real:
|
|
|
|
{
|
|
|
|
const double mv_d = std::get<double>(*mval());
|
|
|
|
switch (other.getType()) {
|
|
|
|
case Type::Integer:
|
|
|
|
return realOp(mv_d, double(std::get<int>(*other.mval())));
|
|
|
|
case Type::Real:
|
|
|
|
return realOp(mv_d, std::get<double>(*other.mval()));
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
case Type::String:
|
|
|
|
{
|
|
|
|
const auto& mv_s = std::get<std::string>(*mval());
|
|
|
|
switch (other.getType()) {
|
|
|
|
case Type::String:
|
|
|
|
return stringOp(mv_s, std::get<std::string>(*other.mval()));
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ValueHolder::equalityOperator(ParserOperator op, const ValueHolder& other) const
|
|
|
|
{
|
|
|
|
std::function<bool(int,int)> intOp;
|
|
|
|
std::function<bool(double,double)> realOp;
|
|
|
|
std::function<bool(bool,bool)> boolOp;
|
|
|
|
std::function<bool(std::string,std::string)> stringOp;
|
|
|
|
|
|
|
|
using Op = ParserOperator;
|
|
|
|
switch (op) {
|
|
|
|
case Op::Eq:
|
|
|
|
intOp = std::equal_to<int>();
|
|
|
|
realOp = std::equal_to<double>();
|
|
|
|
boolOp = std::equal_to<bool>();
|
|
|
|
stringOp = std::equal_to<std::string>();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Op::Ne:
|
|
|
|
intOp = std::not_equal_to<int>();
|
|
|
|
realOp = std::not_equal_to<double>();
|
|
|
|
boolOp = std::not_equal_to<bool>();
|
|
|
|
stringOp = std::not_equal_to<std::string>();
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const Type mtype = getType();
|
|
|
|
switch (mtype) {
|
|
|
|
case Type::Integer:
|
|
|
|
{
|
|
|
|
const int mv_i = std::get<int>(*mval());
|
|
|
|
switch (other.getType()) {
|
|
|
|
case Type::Integer:
|
|
|
|
return intOp(mv_i, std::get<int>(*other.mval()));
|
|
|
|
case Type::Real:
|
|
|
|
return realOp(double(mv_i), std::get<double>(*other.mval())); // TODO use some comparison with an epsilon value
|
|
|
|
case Type::Bool:
|
|
|
|
return boolOp(mv_i != 0, std::get<bool>(*other.mval()));
|
|
|
|
case Type::Void:
|
|
|
|
return op == Op::Ne;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
case Type::Real:
|
|
|
|
{
|
|
|
|
const double mv_d = std::get<double>(*mval());
|
|
|
|
switch (other.getType()) {
|
|
|
|
case Type::Integer:
|
|
|
|
return realOp(mv_d, double(std::get<int>(*other.mval()))); // TODO use some comparison with an epsilon value
|
|
|
|
case Type::Real:
|
|
|
|
return realOp(mv_d, std::get<double>(*other.mval())); // TODO use some comparison with an epsilon value
|
|
|
|
case Type::Void:
|
|
|
|
return op == Op::Ne;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
case Type::Bool:
|
|
|
|
{
|
|
|
|
const int mv_b = std::get<bool>(*mval());
|
|
|
|
switch (other.getType()) {
|
|
|
|
case Type::Integer:
|
|
|
|
return boolOp(mv_b, std::get<int>(*other.mval()) != 0);
|
|
|
|
case Type::Bool:
|
|
|
|
return boolOp(mv_b, std::get<bool>(*other.mval()));
|
|
|
|
case Type::Void:
|
|
|
|
return op == Op::Ne;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
case Type::String:
|
|
|
|
{
|
|
|
|
const std::string& mv_s = std::get<std::string>(*mval());
|
|
|
|
switch (other.getType()) {
|
|
|
|
case Type::String:
|
|
|
|
return stringOp(mv_s, std::get<std::string>(*other.mval()));
|
|
|
|
case Type::Void:
|
|
|
|
return op == Op::Ne;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
case Type::Map:
|
|
|
|
{
|
|
|
|
auto otherType = other.getType();
|
|
|
|
if (otherType == Type::Void)
|
|
|
|
return op == Op::Ne;
|
|
|
|
if (otherType != Type::Map)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
const ValueArray& my_a = std::get<ValueArray>(*mval());
|
|
|
|
const ValueArray& oth_a = std::get<ValueArray>(*other.mval());
|
|
|
|
// Compare 'this' values
|
|
|
|
for (const auto& [key,pval] : my_a) {
|
|
|
|
auto itOth = oth_a.find(key);
|
|
|
|
if (itOth == oth_a.end() || *pval != *itOth->second)
|
|
|
|
return op == ParserOperator::Ne;
|
|
|
|
}
|
|
|
|
// Compare 'other' values
|
|
|
|
for (const auto& [key,pval] : oth_a) {
|
|
|
|
auto itThs = my_a.find(key);
|
|
|
|
if (itThs == my_a.end() || *pval != *itThs->second)
|
|
|
|
return op == ParserOperator::Ne;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO for now this is basically comparing types, ie, if two values is void */
|
|
|
|
case Type::Void:
|
|
|
|
if (op == Op::Eq)
|
|
|
|
return mtype == other.getType();
|
|
|
|
else if (op == Op::Ne)
|
|
|
|
return mtype != other.getType();
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
ValueHolder ValueHolder::arithmeticOperator(ParserOperator op, const ValueHolder& other) const
|
|
|
|
{
|
|
|
|
// modulo (%) has special handling.
|
|
|
|
|
|
|
|
std::function<int(int,int)> intOp = [](bool,bool){ return false; };
|
|
|
|
std::function<double(double,double)> realOp = [](double,double){ return false; };
|
|
|
|
|
|
|
|
std::function<std::string(const std::string&, const std::string&)> stringOp
|
|
|
|
= [](const std::string&, const std::string&) -> std::string { throw ScriptException("Unsuitable operator for string operation"); };
|
|
|
|
|
|
|
|
using Op = ParserOperator;
|
|
|
|
switch (op) {
|
|
|
|
case Op::Plus:
|
|
|
|
intOp = std::plus<int>();
|
|
|
|
realOp = std::plus<double>();
|
|
|
|
stringOp = std::plus<std::string>();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Op::Minus:
|
|
|
|
intOp = std::minus<int>();
|
|
|
|
realOp = std::minus<double>();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Op::Multi:
|
|
|
|
intOp = std::multiplies<int>();
|
|
|
|
realOp = std::multiplies<double>();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Op::Div:
|
|
|
|
intOp = std::divides<int>();
|
|
|
|
realOp = std::divides<double>();
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
const Type mtype = getType();
|
|
|
|
const Type otherType = other.getType();
|
|
|
|
if (otherType == Type::Void)
|
|
|
|
throw ScriptException("Right hand side is void");
|
|
|
|
|
|
|
|
ValueHolder ret;
|
|
|
|
switch (mtype) {
|
|
|
|
case Type::Void:
|
|
|
|
throw ScriptException("Left hand side is void");
|
|
|
|
|
|
|
|
case Type::Integer:
|
|
|
|
{
|
|
|
|
const int mv_i = std::get<int>(*mval());
|
|
|
|
if (otherType == Type::Integer)
|
|
|
|
ret = intOp(mv_i, std::get<int>(*other.mval()));
|
|
|
|
else if (otherType == Type::Real)
|
|
|
|
ret = realOp(double(mv_i), std::get<double>(*other.mval()));
|
|
|
|
else if (otherType == Type::String)
|
|
|
|
ret = stringOp(std::to_string(mv_i), std::get<std::string>(*other.mval()));
|
|
|
|
else
|
|
|
|
throw ScriptException("Unsuitable operator for right hand operand");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Type::Real:
|
|
|
|
{
|
|
|
|
const double mv_d = std::get<double>(*mval());
|
|
|
|
if (otherType == Type::Integer)
|
|
|
|
ret = realOp(mv_d, double(std::get<int>(*other.mval())));
|
|
|
|
else if (otherType == Type::Real)
|
|
|
|
ret = realOp(mv_d, std::get<double>(*other.mval()));
|
|
|
|
else if (otherType == Type::String)
|
|
|
|
ret = stringOp(std::to_string(mv_d), std::get<std::string>(*other.mval()));
|
|
|
|
else
|
|
|
|
throw ScriptException("Unsuitable operator for right hand operand");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Type::String:
|
|
|
|
if (op == Op::Plus) {
|
|
|
|
const std::string& mv_s = std::get<std::string>(*mval());
|
|
|
|
const Type otherType = other.getType();
|
|
|
|
if (otherType == Type::Integer)
|
|
|
|
ret = mv_s + std::to_string(std::get<int>(*other.mval()));
|
|
|
|
else if (otherType == Type::Real)
|
|
|
|
ret = mv_s + std::to_string(std::get<double>(*other.mval()));
|
|
|
|
else if (otherType == Type::String)
|
|
|
|
ret = mv_s + std::get<std::string>(*other.mval());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
throw ScriptException("Unsuitable operator for string operation");
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
throw ScriptException("Unsuitable operator for left hand operand");
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ValueHolder::logicOperator(ParserOperator op, const ValueHolder& other) const
|
|
|
|
{
|
|
|
|
std::function<bool(int,int)> intOp = [](bool,bool){ return false; };
|
|
|
|
std::function<bool(double,double)> realOp = [](double,double){ return false; };
|
|
|
|
std::function<bool(bool,bool)> boolOp = [](bool,bool){ return false; };
|
|
|
|
|
|
|
|
std::function<bool(std::string,bool)> stringOpL = [](std::string,bool){ return false; };
|
|
|
|
std::function<bool(bool,std::string)> stringOpR = [](bool,std::string){ return false; };
|
|
|
|
std::function<bool(std::string,std::string)> stringOpB = [](std::string,std::string){ return false; };
|
|
|
|
|
|
|
|
using Op = ParserOperator;
|
|
|
|
switch (op) {
|
|
|
|
case Op::LAnd:
|
|
|
|
intOp = std::logical_and<int>();
|
|
|
|
realOp = std::logical_and<double>();
|
|
|
|
boolOp = std::logical_and<bool>();
|
|
|
|
stringOpL = [](std::string l, bool r){ return !l.empty() && r; };
|
|
|
|
stringOpR = [](bool l, std::string r){ return l && !r.empty(); };
|
|
|
|
stringOpB = [](std::string l, std::string r){ return !l.empty() && r.empty(); };
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Op::LOr:
|
|
|
|
intOp = std::logical_or<int>();
|
|
|
|
realOp = std::logical_or<double>();
|
|
|
|
boolOp = std::logical_or<bool>();
|
|
|
|
stringOpL = [](std::string l, bool r){ return !l.empty() || r; };
|
|
|
|
stringOpR = [](bool l, std::string r){ return l || !r.empty(); };
|
|
|
|
stringOpB = [](std::string l, std::string r){ return !l.empty() || r.empty(); };
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
const Type mtype = getType();
|
|
|
|
bool ret;
|
|
|
|
switch (mtype) {
|
|
|
|
case Type::Integer:
|
|
|
|
{
|
|
|
|
const auto mv_i = std::get<int>(*mval());
|
|
|
|
const Type otherType = other.getType();
|
|
|
|
if (otherType == Type::Integer)
|
|
|
|
ret = intOp(mv_i, std::get<int>(*other.mval()));
|
|
|
|
else if (otherType == Type::Real)
|
|
|
|
ret = realOp(static_cast<double>(mv_i), std::get<double>(*other.mval())); // TODO use some comparison with an epsilon value
|
|
|
|
if (otherType == Type::Bool)
|
|
|
|
ret = boolOp(mv_i, std::get<bool>(*other.mval()));
|
|
|
|
else if (otherType == Type::Void)
|
|
|
|
ret = 0;
|
|
|
|
else if (otherType == Type::String)
|
|
|
|
ret = stringOpR(mv_i, other.toStdString());
|
|
|
|
else
|
|
|
|
throw ScriptException("Unsuitable operator for right hand operand");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Type::Real:
|
|
|
|
{
|
|
|
|
const auto mv_d = std::get<double>(*mval());
|
|
|
|
const Type otherType = other.getType();
|
|
|
|
if (otherType == Type::Integer)
|
|
|
|
ret = realOp(mv_d, std::get<int>(*other.mval())); // TODO use some comparison with an epsilon value
|
|
|
|
else if (otherType == Type::Real)
|
|
|
|
ret = realOp(mv_d, std::get<double>(*other.mval())); // TODO use some comparison with an epsilon value
|
|
|
|
else if (otherType == Type::Bool)
|
|
|
|
ret = realOp(mv_d, std::get<bool>(*other.mval())); // TODO use some comparison with an epsilon value
|
|
|
|
else if (otherType == Type::Void)
|
|
|
|
ret = 0;
|
|
|
|
else if (otherType == Type::String)
|
|
|
|
ret = stringOpR(mv_d != 0, other.toStdString()); // TODO use some comparison with an epsilon value
|
|
|
|
else
|
|
|
|
throw ScriptException("Unsuitable operator for right hand operand");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Type::Bool:
|
|
|
|
{
|
|
|
|
const auto mv_b = std::get<bool>(*mval());
|
|
|
|
const Type otherType = other.getType();
|
|
|
|
if (otherType == Type::Integer)
|
|
|
|
ret = boolOp(mv_b, std::get<int>(*other.mval()));
|
|
|
|
else if (otherType == Type::Real)
|
|
|
|
ret = boolOp(mv_b, std::get<double>(*other.mval()) != 0); // TODO use some comparison with an epsilon value
|
|
|
|
else if (otherType == Type::Bool)
|
|
|
|
ret = boolOp(mv_b, std::get<bool>(*other.mval()));
|
|
|
|
else if (otherType == Type::Void)
|
|
|
|
ret = 0;
|
|
|
|
else if (otherType == Type::String)
|
|
|
|
ret = boolOp(mv_b, !other.toStdString().empty());
|
|
|
|
else
|
|
|
|
throw ScriptException("Unsuitable operator for right hand operand");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Type::Void:
|
|
|
|
{
|
|
|
|
const Type otherType = other.getType();
|
|
|
|
if (otherType == Type::Void)
|
|
|
|
ret = 1;
|
|
|
|
else
|
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Type::String:
|
|
|
|
{
|
|
|
|
const auto mv_s = std::get<std::string>(*mval());
|
|
|
|
const Type otherType = other.getType();
|
|
|
|
if (otherType == Type::Integer)
|
|
|
|
ret = stringOpL(mv_s, std::get<int>(*other.mval()));
|
|
|
|
else if (otherType == Type::Real)
|
|
|
|
ret = stringOpL(mv_s, std::get<double>(*other.mval()) != 0); // TODO use some comparison with an epsilon value
|
|
|
|
if (otherType == Type::Bool)
|
|
|
|
ret = boolOp(!mv_s.empty(), std::get<bool>(*other.mval()));
|
|
|
|
else if (otherType == Type::Void)
|
|
|
|
ret = 0;
|
|
|
|
else if (otherType == Type::String)
|
|
|
|
ret = stringOpB(mv_s, other.toStdString());
|
|
|
|
else
|
|
|
|
throw ScriptException("Unsuitable operator for right hand operand");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Type::Map:
|
|
|
|
throw ScriptException("Unsuitable operator for left hand operand");
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ValueHolder::operator<(const ValueHolder& other) const
|
|
|
|
{
|
|
|
|
return rangedOperator(ParserOperator::Lt, other);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ValueHolder::operator>(const ValueHolder& other) const
|
|
|
|
{
|
|
|
|
return rangedOperator(ParserOperator::Gt, other);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ValueHolder::operator<=(const ValueHolder& other) const
|
|
|
|
{
|
|
|
|
return rangedOperator(ParserOperator::Lte, other);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ValueHolder::operator>=(const ValueHolder& other) const
|
|
|
|
{
|
|
|
|
return rangedOperator(ParserOperator::Gte, other);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ValueHolder::operator==(const ValueHolder& other) const
|
|
|
|
{
|
|
|
|
return equalityOperator(ParserOperator::Eq, other);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ValueHolder::operator!=(const ValueHolder& other) const
|
|
|
|
{
|
|
|
|
return equalityOperator(ParserOperator::Ne, other);
|
|
|
|
}
|
|
|
|
|
|
|
|
ValueHolder ValueHolder::operator+(const ValueHolder& other) const
|
|
|
|
{
|
|
|
|
return arithmeticOperator(ParserOperator::Plus, other);
|
|
|
|
}
|
|
|
|
|
|
|
|
ValueHolder ValueHolder::operator-(const ValueHolder& other) const
|
|
|
|
{
|
|
|
|
return arithmeticOperator(ParserOperator::Minus, other);
|
|
|
|
}
|
|
|
|
|
|
|
|
ValueHolder ValueHolder::operator*(const ValueHolder& other) const
|
|
|
|
{
|
|
|
|
return arithmeticOperator(ParserOperator::Multi, other);
|
|
|
|
}
|
|
|
|
|
|
|
|
ValueHolder ValueHolder::operator/(const ValueHolder& other) const
|
|
|
|
{
|
|
|
|
return arithmeticOperator(ParserOperator::Div, other);
|
|
|
|
}
|
|
|
|
|
|
|
|
ValueHolder ValueHolder::operator%(const ValueHolder& other) const
|
|
|
|
{
|
|
|
|
const Type mtype = getType();
|
|
|
|
ValueHolder ret;
|
|
|
|
switch (mtype) {
|
|
|
|
case Type::Integer:
|
|
|
|
{
|
|
|
|
const int mv_i = std::get<int>(*mval());
|
|
|
|
const Type otherType = other.getType();
|
|
|
|
if (otherType == Type::Integer)
|
|
|
|
ret = mv_i % std::get<int>(*other.mval());
|
|
|
|
else if (otherType == Type::Real)
|
|
|
|
ret = mv_i % static_cast<int>(std::get<double>(*other.mval()));
|
|
|
|
else if (otherType == Type::Void)
|
|
|
|
throw ScriptException("Right hand side is void");
|
|
|
|
else
|
|
|
|
throw ScriptException("Unsuitable operator for right hand operand");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Type::Real:
|
|
|
|
{
|
|
|
|
const int mv_d_i_trunc = static_cast<int>(std::get<double>(*mval()));
|
|
|
|
const Type otherType = other.getType();
|
|
|
|
if (otherType == Type::Integer)
|
|
|
|
ret = mv_d_i_trunc % std::get<int>(*other.mval());
|
|
|
|
else if (otherType == Type::Real)
|
|
|
|
ret = mv_d_i_trunc % static_cast<int>(std::get<double>(*other.mval()));
|
|
|
|
else if (otherType == Type::Void)
|
|
|
|
throw ScriptException("Right hand side is void");
|
|
|
|
else
|
|
|
|
throw ScriptException("Unsuitable operator for right hand operand");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Type::Void:
|
|
|
|
throw ScriptException("Left hand side is void");
|
|
|
|
|
|
|
|
default:
|
|
|
|
throw ScriptException("Unsuitable operator for left hand operand");
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ValueHolder::operator&&(const ValueHolder& other) const
|
|
|
|
{
|
|
|
|
return logicOperator(ParserOperator::LAnd, other);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ValueHolder::operator||(const ValueHolder& other) const
|
|
|
|
{
|
|
|
|
return logicOperator(ParserOperator::LOr, other);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ValueHolder::clear()
|
|
|
|
{
|
|
|
|
mval().reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ValueHolder::reset()
|
|
|
|
{
|
|
|
|
m_val.reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ValueHolder::hasValue() const
|
|
|
|
{
|
|
|
|
return mval().has_value();
|
|
|
|
}
|
|
|
|
|
|
|
|
ValueHolder::Type ValueHolder::getType() const
|
|
|
|
{
|
|
|
|
if (!m_val.has_value())
|
|
|
|
return Type::Void;
|
|
|
|
|
|
|
|
std::size_t valIdx = mval()->index();
|
|
|
|
|
|
|
|
if (valIdx < 1 || valIdx > static_cast<std::size_t>(Type::Map))
|
|
|
|
return Type::Void;
|
|
|
|
else
|
|
|
|
return static_cast<Type>(valIdx);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string ValueHolder::toStdString() const
|
|
|
|
{
|
|
|
|
const Type mtype = getType();
|
|
|
|
switch (mtype) {
|
|
|
|
case Type::Integer:
|
|
|
|
return std::to_string(std::get<int>(*mval()));
|
|
|
|
|
|
|
|
case Type::Real:
|
|
|
|
{
|
|
|
|
std::string val = std::to_string(std::get<double>(*mval()));
|
|
|
|
char ds = decimalSeparator();
|
|
|
|
if (ds != '.') {
|
|
|
|
auto dspos = val.find_first_of(ds);
|
|
|
|
if (dspos == val.npos)
|
|
|
|
return val;
|
|
|
|
else
|
|
|
|
return val.replace(dspos, 1, ".");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
case Type::Bool:
|
|
|
|
return std::get<bool>(*mval()) ? "true" : "false";
|
|
|
|
|
|
|
|
case Type::String:
|
|
|
|
return std::get<std::string>(*mval());
|
|
|
|
|
|
|
|
case Type::Map:
|
|
|
|
{
|
|
|
|
auto& array = std::get<ValueArray>(*mval());
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << "[Array of " << array.size() << "]";
|
|
|
|
return ss.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
case Type::Void:
|
|
|
|
return "[void]";
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compiler complains even if all cases are covered... So this return is highly unlikely to happen... unless a new type is introduced.
|
|
|
|
return "[???]";
|
|
|
|
}
|
|
|
|
|
|
|
|
void ValueHolder::setArray(const std::string& subscript, const ValueHolder& val)
|
|
|
|
{
|
|
|
|
if (getType() != Type::Map)
|
|
|
|
m_val = ValueArray();
|
|
|
|
|
|
|
|
auto& array = std::get<ValueArray>(*mval());
|
|
|
|
try {
|
|
|
|
auto& valptr = array.at(subscript);
|
|
|
|
*valptr = val;
|
|
|
|
} catch (std::out_of_range&) {
|
|
|
|
array.emplace(subscript, new ValueHolder(val));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ValueHolder::isReference() const
|
|
|
|
{
|
|
|
|
if (!m_val)
|
|
|
|
return false;
|
|
|
|
return m_val->index() == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ValueHolder::markAsConst()
|
|
|
|
{
|
|
|
|
m_const = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ValueHolder::isConst() const
|
|
|
|
{
|
|
|
|
return m_const;
|
|
|
|
}
|
|
|
|
|
|
|
|
ValueHolder::ValueTypes& ValueHolder::mval()
|
|
|
|
{
|
|
|
|
if (m_val.has_value() && m_val->index() == 0) // ValueRef
|
|
|
|
return std::get<ValueRef>(*m_val).get().m_val;
|
|
|
|
else
|
|
|
|
return m_val;
|
|
|
|
}
|
|
|
|
|
|
|
|
const ValueHolder::ValueTypes& ValueHolder::mval() const
|
|
|
|
{
|
|
|
|
if (m_val.has_value() && m_val->index() == 0) // ValueRef
|
|
|
|
return std::get<ValueRef>(*m_val).get().m_val;
|
|
|
|
else
|
|
|
|
return m_val;
|
|
|
|
}
|