private IParseTreeValue EvaluateUnaryMinus(IParseTreeValue parseTreeValue)
        {
            var opProvider = new OperatorTypesProvider(parseTreeValue.ValueType, ArithmeticOperators.ADDITIVE_INVERSE);

            if (!parseTreeValue.ParsesToConstantValue)
            {
                //Unable to resolve to a value, return an expression
                var opTypeName = opProvider.OperatorDeclaredType;
                return(_valueFactory.CreateDeclaredType($"{ArithmeticOperators.ADDITIVE_INVERSE} {parseTreeValue.Token}", opTypeName));
            }

            var effTypeName = opProvider.OperatorEffectiveType;

            if (effTypeName.Equals(Tokens.Date))
            {
                var result = DateTime.FromOADate(0 - parseTreeValue.AsDouble());
                var date   = new DateValue(result);
                return(_valueFactory.CreateDeclaredType(date.AsDate.ToString(CultureInfo.InvariantCulture), effTypeName));
            }

            var declaredTypeName = opProvider.OperatorDeclaredType;

            if (parseTreeValue.TryLetCoerce(out decimal decValue))
            {
                return(_valueFactory.CreateDeclaredType((0 - decValue).ToString(CultureInfo.InvariantCulture), declaredTypeName));
            }
            return(_valueFactory.CreateDeclaredType((0 - parseTreeValue.AsDouble()).ToString(CultureInfo.InvariantCulture), declaredTypeName));
        }
        private string Compare(IParseTreeValue LHS, IParseTreeValue RHS, Func <decimal, decimal, bool> DecimalCompare, Func <double, double, bool> DoubleCompare)
        {
            if (DecimalCompare is null && DoubleCompare is null)
            {
                throw new ArgumentNullException();
            }

            if (!(DecimalCompare is null) && LHS.TryLetCoerce(out decimal lhsValue) && RHS.TryLetCoerce(out decimal rhsValue))
            {
                return(DecimalCompare(lhsValue, rhsValue) ? Tokens.True : Tokens.False);
            }
            return(DoubleCompare(LHS.AsDouble(), RHS.AsDouble()) ? Tokens.True : Tokens.False);
        }
        private string Calculate(IParseTreeValue LHS, IParseTreeValue RHS, Func <decimal, decimal, decimal> DecimalCalc, Func <double, double, double> DoubleCalc)
        {
            if (DecimalCalc is null && DoubleCalc is null)
            {
                throw new ArgumentNullException();
            }

            if (!(DecimalCalc is null) && LHS.TryLetCoerce(out decimal lhsValue) && RHS.TryLetCoerce(out decimal rhsValue))
            {
                return(DecimalCalc(lhsValue, rhsValue).ToString(CultureInfo.InvariantCulture));
            }
            return(DoubleCalc(LHS.AsDouble(), RHS.AsDouble()).ToString(CultureInfo.InvariantCulture));
        }
        private IParseTreeValue EvaluateArithmeticOp(string opSymbol, IParseTreeValue LHS, IParseTreeValue RHS)
        {
            Debug.Assert(ArithmeticOperators.Includes(opSymbol));

            var opProvider = new OperatorTypesProvider((LHS.ValueType, RHS.ValueType), opSymbol);

            if (!(LHS.ParsesToConstantValue && RHS.ParsesToConstantValue))
            {
                //Unable to resolve to a value, return an expression
                return(_valueFactory.CreateExpression($"{LHS.Token} {opSymbol} {RHS.Token}", opProvider.OperatorDeclaredType));
            }

            if (!LHS.TryLetCoerce(opProvider.OperatorEffectiveType, out var effLHS) ||
                !RHS.TryLetCoerce(opProvider.OperatorEffectiveType, out var effRHS))
            {
                return(_valueFactory.CreateExpression($"{LHS.Token} {opSymbol} {RHS.Token}", opProvider.OperatorDeclaredType));
            }

            if (opProvider.OperatorEffectiveType.Equals(Tokens.Date))
            {
                if (!(LHS.TryLetCoerce(Tokens.Double, out effLHS) && RHS.TryLetCoerce(Tokens.Double, out effRHS)))
                {
                    return(_valueFactory.CreateExpression($"{LHS.Token} {opSymbol} {RHS.Token}", opProvider.OperatorEffectiveType));
                }
            }

            if (opSymbol.Equals(ArithmeticOperators.MULTIPLY))
            {
                return(_valueFactory.CreateValueType(Calculate(effLHS, effRHS, (decimal a, decimal b) => a * b, (double a, double b) => a * b), opProvider.OperatorDeclaredType));
            }

            if (opSymbol.Equals(ArithmeticOperators.DIVIDE))
            {
                return(_valueFactory.CreateValueType(Calculate(effLHS, effRHS, (decimal a, decimal b) => a / b, (double a, double b) => a / b), opProvider.OperatorDeclaredType));
            }

            if (opSymbol.Equals(ArithmeticOperators.INTEGER_DIVIDE))
            {
                return(_valueFactory.CreateValueType(Calculate(effLHS, effRHS, IntDivision, IntDivision), opProvider.OperatorDeclaredType));
            }

            if (opSymbol.Equals(ArithmeticOperators.PLUS))
            {
                if (opProvider.OperatorEffectiveType.Equals(Tokens.String))
                {
                    return(_valueFactory.CreateValueType(Concatenate(LHS, RHS), opProvider.OperatorDeclaredType));
                }
                if (opProvider.OperatorEffectiveType.Equals(Tokens.Date))
                {
                    var result = _valueFactory.CreateDeclaredType(Calculate(effLHS, effRHS, null, (double a, double b) => a + b), Tokens.Double);
                    return(_valueFactory.CreateDate(result.AsDouble()));
                }
                return(_valueFactory.CreateValueType(Calculate(effLHS, effRHS, (decimal a, decimal b) => a + b, (double a, double b) => a + b), opProvider.OperatorDeclaredType));
            }

            if (opSymbol.Equals(ArithmeticOperators.MINUS))
            {
                if (LHS.ValueType.Equals(Tokens.Date) && RHS.ValueType.Equals(Tokens.Date))
                {
                    return(_valueFactory.CreateDate(LHS.AsDouble() - RHS.AsDouble()));
                }
                return(_valueFactory.CreateValueType(Calculate(effLHS, effRHS, (decimal a, decimal b) => a - b, (double a, double b) => a - b), opProvider.OperatorDeclaredType));
            }

            if (opSymbol.Equals(ArithmeticOperators.EXPONENT))
            {
                //Math.Pow only takes doubles, so the decimal conversion option is null
                return(_valueFactory.CreateValueType(Calculate(effLHS, effRHS, null, Math.Pow), opProvider.OperatorDeclaredType));
            }

            if (opSymbol.Equals(ArithmeticOperators.MODULO))
            {
                return(_valueFactory.CreateValueType(Calculate(effLHS, effRHS, (decimal a, decimal b) => a % b, (double a, double b) => a % b), opProvider.OperatorDeclaredType));
            }

            //ArithmeticOperators.AMPERSAND
            return(_valueFactory.CreateValueType(Concatenate(LHS, RHS), opProvider.OperatorDeclaredType));
        }