blob: e6d25e2fb86107d31ee2e6a95d67fd65021043b3 [file] [log] [blame]
/*******************************************************************************
* You may amend and distribute as you like, but don't remove this header!
*
* EPPlus provides server-side generation of Excel 2007/2010 spreadsheets.
* See http://www.codeplex.com/EPPlus for details.
*
* Copyright (C) 2011 Jan Källman
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* The GNU Lesser General Public License can be viewed at http://www.opensource.org/licenses/lgpl-license.php
* If you unfamiliar with this license or have questions about it, here is an http://www.gnu.org/licenses/gpl-faq.html
*
* All code and executables are provided "as is" with no warranty either express or implied.
* The author accepts no liability for any damage or loss of business that this product may cause.
*
* Code change notes:
*
* Author Change Date
* ******************************************************************************
* Mats Alm Added 2013-03-01 (Prior file history on https://github.com/swmal/ExcelFormulaParser)
*******************************************************************************/
using System;
using OfficeOpenXml.FormulaParsing.ExpressionGraph;
using OfficeOpenXml.Utils;
namespace OfficeOpenXml.FormulaParsing.Excel.Operators;
public class Operator : IOperator {
private const int _precedencePercent = 2;
private const int _precedenceExp = 4;
private const int _precedenceMultiplyDevide = 6;
private const int _precedenceAddSubtract = 12;
private const int _precedenceConcat = 15;
private const int _precedenceComparison = 25;
private Operator(
Operators @operator,
int precedence,
Func<CompileResult, CompileResult, CompileResult> implementation) {
_implementation = implementation;
_precedence = precedence;
_operator = @operator;
}
private readonly Func<CompileResult, CompileResult, CompileResult> _implementation;
private readonly int _precedence;
private readonly Operators _operator;
int IOperator.Precedence => _precedence;
Operators IOperator.Operator => _operator;
public CompileResult Apply(CompileResult left, CompileResult right) {
if (left.Result is ExcelErrorValue) {
return new(left.Result, DataType.ExcelError);
//throw(new ExcelErrorValueException((ExcelErrorValue)left.Result));
}
if (right.Result is ExcelErrorValue) {
return new(right.Result, DataType.ExcelError);
//throw(new ExcelErrorValueException((ExcelErrorValue)right.Result));
}
return _implementation(left, right);
}
public override string ToString() {
return "Operator: " + _operator;
}
private static IOperator _plus;
public static IOperator Plus {
get {
return _plus
?? (_plus = new Operator(
Operators.Plus,
_precedenceAddSubtract,
(l, r) => {
l = l == null || l.Result == null ? new(0, DataType.Integer) : l;
r = r == null || r.Result == null ? new(0, DataType.Integer) : r;
if (EitherIsError(l, r, out var errorVal)) {
return new(errorVal);
}
if (l.DataType == DataType.Integer && r.DataType == DataType.Integer) {
return new(l.ResultNumeric + r.ResultNumeric, DataType.Integer);
}
if ((l.IsNumeric
|| l.IsNumericString
|| l.Result is ExcelDataProvider.IRangeInfo)
&& (r.IsNumeric
|| r.IsNumericString
|| r.Result is ExcelDataProvider.IRangeInfo)) {
return new(l.ResultNumeric + r.ResultNumeric, DataType.Decimal);
}
return new(eErrorType.Value);
}));
}
}
private static IOperator _minus;
public static IOperator Minus {
get {
return _minus
?? (_minus = new Operator(
Operators.Minus,
_precedenceAddSubtract,
(l, r) => {
l = l == null || l.Result == null ? new(0, DataType.Integer) : l;
r = r == null || r.Result == null ? new(0, DataType.Integer) : r;
if (l.DataType == DataType.Integer && r.DataType == DataType.Integer) {
return new(l.ResultNumeric - r.ResultNumeric, DataType.Integer);
}
if ((l.IsNumeric
|| l.IsNumericString
|| l.Result is ExcelDataProvider.IRangeInfo)
&& (r.IsNumeric
|| r.IsNumericString
|| r.Result is ExcelDataProvider.IRangeInfo)) {
return new(l.ResultNumeric - r.ResultNumeric, DataType.Decimal);
}
return new(eErrorType.Value);
}));
}
}
private static IOperator _multiply;
public static IOperator Multiply {
get {
return _multiply
?? (_multiply = new Operator(
Operators.Multiply,
_precedenceMultiplyDevide,
(l, r) => {
l = l ?? new CompileResult(0, DataType.Integer);
r = r ?? new CompileResult(0, DataType.Integer);
if (l.DataType == DataType.Integer && r.DataType == DataType.Integer) {
return new(l.ResultNumeric * r.ResultNumeric, DataType.Integer);
}
if ((l.IsNumeric
|| l.IsNumericString
|| l.Result is ExcelDataProvider.IRangeInfo)
&& (r.IsNumeric
|| r.IsNumericString
|| r.Result is ExcelDataProvider.IRangeInfo)) {
return new(l.ResultNumeric * r.ResultNumeric, DataType.Decimal);
}
return new(eErrorType.Value);
}));
}
}
private static IOperator _divide;
public static IOperator Divide {
get {
return _divide
?? (_divide = new Operator(
Operators.Divide,
_precedenceMultiplyDevide,
(l, r) => {
if (!(l.IsNumeric
|| l.IsNumericString
|| l.Result is ExcelDataProvider.IRangeInfo)
|| !(r.IsNumeric
|| r.IsNumericString
|| r.Result is ExcelDataProvider.IRangeInfo)) {
return new(eErrorType.Value);
}
var left = l.ResultNumeric;
var right = r.ResultNumeric;
if (Math.Abs(right - 0d) < double.Epsilon) {
return new(eErrorType.Div0);
}
if ((l.IsNumeric
|| l.IsNumericString
|| l.Result is ExcelDataProvider.IRangeInfo)
&& (r.IsNumeric
|| r.IsNumericString
|| r.Result is ExcelDataProvider.IRangeInfo)) {
return new(left / right, DataType.Decimal);
}
return new(eErrorType.Value);
}));
}
}
public static IOperator Exp {
get {
return new Operator(
Operators.Exponentiation,
_precedenceExp,
(l, r) => {
if (l == null && r == null) {
return new(eErrorType.Value);
}
l = l ?? new CompileResult(0, DataType.Integer);
r = r ?? new CompileResult(0, DataType.Integer);
if ((l.IsNumeric || l.Result is ExcelDataProvider.IRangeInfo)
&& (r.IsNumeric || r.Result is ExcelDataProvider.IRangeInfo)) {
return new(Math.Pow(l.ResultNumeric, r.ResultNumeric), DataType.Decimal);
}
return new(0d, DataType.Decimal);
});
}
}
public static IOperator Concat {
get {
return new Operator(
Operators.Concat,
_precedenceConcat,
(l, r) => {
l = l ?? new CompileResult(string.Empty, DataType.String);
r = r ?? new CompileResult(string.Empty, DataType.String);
var lStr = l.Result != null ? l.ResultValue.ToString() : string.Empty;
var rStr = r.Result != null ? r.ResultValue.ToString() : string.Empty;
return new(string.Concat(lStr, rStr), DataType.String);
});
}
}
private static IOperator _greaterThan;
public static IOperator GreaterThan {
get {
//return new Operator(Operators.GreaterThan, PrecedenceComparison, (l, r) => new CompileResult(Compare(l, r) > 0, DataType.Boolean));
return _greaterThan
?? (_greaterThan = new Operator(
Operators.LessThanOrEqual,
_precedenceComparison,
(l, r) => Compare(l, r, compRes => compRes > 0)));
}
}
private static IOperator _eq;
public static IOperator Eq {
get {
//return new Operator(Operators.Equals, PrecedenceComparison, (l, r) => new CompileResult(Compare(l, r) == 0, DataType.Boolean));
return _eq
?? (_eq = new Operator(
Operators.LessThanOrEqual,
_precedenceComparison,
(l, r) => Compare(l, r, compRes => compRes == 0)));
}
}
private static IOperator _notEqualsTo;
public static IOperator NotEqualsTo {
get {
//return new Operator(Operators.NotEqualTo, PrecedenceComparison, (l, r) => new CompileResult(Compare(l, r) != 0, DataType.Boolean));
return _notEqualsTo
?? (_notEqualsTo = new Operator(
Operators.LessThanOrEqual,
_precedenceComparison,
(l, r) => Compare(l, r, compRes => compRes != 0)));
}
}
private static IOperator _greaterThanOrEqual;
public static IOperator GreaterThanOrEqual {
get {
//return new Operator(Operators.GreaterThanOrEqual, PrecedenceComparison, (l, r) => new CompileResult(Compare(l, r) >= 0, DataType.Boolean));
return _greaterThanOrEqual
?? (_greaterThanOrEqual = new Operator(
Operators.LessThanOrEqual,
_precedenceComparison,
(l, r) => Compare(l, r, compRes => compRes >= 0)));
}
}
private static IOperator _lessThan;
public static IOperator LessThan {
get {
//return new Operator(Operators.LessThan, PrecedenceComparison, (l, r) => new CompileResult(Compare(l, r) < 0, DataType.Boolean));
return _lessThan
?? (_lessThan = new Operator(
Operators.LessThanOrEqual,
_precedenceComparison,
(l, r) => Compare(l, r, compRes => compRes < 0)));
}
}
public static IOperator LessThanOrEqual {
get {
//return new Operator(Operators.LessThanOrEqual, PrecedenceComparison, (l, r) => new CompileResult(Compare(l, r) <= 0, DataType.Boolean));
return new Operator(
Operators.LessThanOrEqual,
_precedenceComparison,
(l, r) => Compare(l, r, compRes => compRes <= 0));
}
}
private static IOperator _percent;
public static IOperator Percent {
get {
if (_percent == null) {
_percent = new Operator(
Operators.Percent,
_precedencePercent,
(l, r) => {
l = l ?? new CompileResult(0, DataType.Integer);
r = r ?? new CompileResult(0, DataType.Integer);
if (l.DataType == DataType.Integer && r.DataType == DataType.Integer) {
return new(l.ResultNumeric * r.ResultNumeric, DataType.Integer);
}
if ((l.IsNumeric || l.Result is ExcelDataProvider.IRangeInfo)
&& (r.IsNumeric || r.Result is ExcelDataProvider.IRangeInfo)) {
return new(l.ResultNumeric * r.ResultNumeric, DataType.Decimal);
}
return new(eErrorType.Value);
});
}
return _percent;
}
}
private static object GetObjFromOther(CompileResult obj, CompileResult other) {
if (obj.Result == null) {
if (other.DataType == DataType.String) {
return string.Empty;
}
return 0d;
}
return obj.ResultValue;
}
private static CompileResult Compare(
CompileResult l,
CompileResult r,
Func<int, bool> comparison) {
if (EitherIsError(l, r, out var errorVal)) {
return new(errorVal);
}
object left,
right;
left = GetObjFromOther(l, r);
right = GetObjFromOther(r, l);
if (ConvertUtil.IsNumeric(left) && ConvertUtil.IsNumeric(right)) {
var lnum = ConvertUtil.GetValueDouble(left);
var rnum = ConvertUtil.GetValueDouble(right);
if (Math.Abs(lnum - rnum) < double.Epsilon) {
return new(comparison(0), DataType.Boolean);
}
var comparisonResult = lnum.CompareTo(rnum);
return new(comparison(comparisonResult), DataType.Boolean);
} else {
var comparisonResult = CompareString(left, right);
return new(comparison(comparisonResult), DataType.Boolean);
}
}
private static int CompareString(object l, object r) {
var sl = (l ?? "").ToString();
var sr = (r ?? "").ToString();
return String.Compare(sl, sr, StringComparison.Ordinal);
}
private static bool EitherIsError(
CompileResult l,
CompileResult r,
out ExcelErrorValue errorVal) {
if (l.DataType == DataType.ExcelError) {
errorVal = (ExcelErrorValue)l.Result;
return true;
}
if (r.DataType == DataType.ExcelError) {
errorVal = (ExcelErrorValue)r.Result;
return true;
}
errorVal = null;
return false;
}
}