diff --git a/ClosedXML/Excel/CalcEngine/CalcEngine.cs b/ClosedXML/Excel/CalcEngine/CalcEngine.cs index b946fa2..d5a555e 100644 --- a/ClosedXML/Excel/CalcEngine/CalcEngine.cs +++ b/ClosedXML/Excel/CalcEngine/CalcEngine.cs @@ -1,80 +1,74 @@ using System; -using System.Reflection; using System.Collections.Generic; -using System.ComponentModel; using System.Globalization; -using System.Diagnostics; -using System.Text; -using System.Text.RegularExpressions; -using ClosedXML.Excel.CalcEngine; using ClosedXML.Excel.CalcEngine.Functions; namespace ClosedXML.Excel.CalcEngine { /// - /// CalcEngine parses strings and returns Expression objects that can - /// be evaluated. - /// + /// CalcEngine parses strings and returns Expression objects that can + /// be evaluated. + /// /// - /// This class has three extensibility points: - /// Use the DataContext property to add an object's properties to the engine scope. - /// Use the RegisterFunction method to define custom functions. - /// Override the GetExternalObject method to add arbitrary variables to the engine scope. + /// This class has three extensibility points: + /// Use the DataContext property to add an object's properties to the engine scope. + /// Use the RegisterFunction method to define custom functions. + /// Override the GetExternalObject method to add arbitrary variables to the engine scope. /// - internal class CalcEngine - { - //--------------------------------------------------------------------------- - #region ** fields - - // members - string _expr; // expression being parsed - int _len; // length of the expression being parsed - int _ptr; // current pointer into expression - string _idChars; // valid characters in identifiers (besides alpha and digits) - Token _token; // current token being parsed - Dictionary _tkTbl; // table with tokens (+, -, etc) - Dictionary _fnTbl; // table with constants and functions (pi, sin, etc) - Dictionary _vars; // table with variables - object _dataContext; // object with properties - bool _optimize; // optimize expressions when parsing - ExpressionCache _cache; // cache with parsed expressions - CultureInfo _ci; // culture info used to parse numbers/dates - char _decimal, _listSep, _percent; // localized decimal separator, list separator, percent sign - - #endregion - + internal class CalcEngine + { //--------------------------------------------------------------------------- + #region ** ctor public CalcEngine() { CultureInfo = CultureInfo.InvariantCulture; _tkTbl = GetSymbolTable(); - _fnTbl = GetFunctionTable(); - _vars = new Dictionary(StringComparer.OrdinalIgnoreCase); + Functions = GetFunctionTable(); + Variables = new Dictionary(StringComparer.OrdinalIgnoreCase); _cache = new ExpressionCache(this); - _optimize = true; + OptimizeExpressions = true; #if DEBUG - //this.Test(); + //this.Test(); #endif } - + #endregion //--------------------------------------------------------------------------- - #region ** object model - /// - /// Parses a string into an . - /// - /// String to parse. - /// An object that can be evaluated. - public Expression Parse(string expression) - { - // initialize - _expr = expression; - _len = _expr.Length; - _ptr = 0; + #region ** fields + + // members + private string _expr; // expression being parsed + private int _len; // length of the expression being parsed + private int _ptr; // current pointer into expression + private Token _token; // current token being parsed + private Dictionary _tkTbl; // table with tokens (+, -, etc) + private ExpressionCache _cache; // cache with parsed expressions + private CultureInfo _ci; // culture info used to parse numbers/dates + private char _decimal; // localized decimal separator, list separator, percent sign + private char _listSep; // localized decimal separator, list separator, percent sign + private char _percent; // localized decimal separator, list separator, percent sign + + #endregion + + //--------------------------------------------------------------------------- + + #region ** object model + + /// + /// Parses a string into an . + /// + /// String to parse. + /// An object that can be evaluated. + public Expression Parse(string expression) + { + // initialize + _expr = expression; + _len = _expr.Length; + _ptr = 0; // skip leading equals sign if (_len > 0 && _expr[0] == '=') @@ -82,46 +76,48 @@ _ptr++; } - // parse the expression - var expr = ParseExpression(); + // parse the expression + var expr = ParseExpression(); - // check for errors - if (_token.ID != TKID.END) - { + // check for errors + if (_token.ID != TKID.END) + { Throw(); - } + } // optimize expression - if (_optimize) + if (OptimizeExpressions) { expr = expr.Optimize(); } // done - return expr; - } + return expr; + } + /// - /// Evaluates a string. + /// Evaluates a string. /// /// Expression to evaluate. /// The value of the expression. /// - /// If you are going to evaluate the same expression several times, - /// it is more efficient to parse it only once using the - /// method and then using the Expression.Evaluate method to evaluate - /// the parsed expression. + /// If you are going to evaluate the same expression several times, + /// it is more efficient to parse it only once using the + /// method and then using the Expression.Evaluate method to evaluate + /// the parsed expression. /// - public object Evaluate(string expression) - { + public object Evaluate(string expression) + { var x = //Parse(expression); _cache != null - ? _cache[expression] - : Parse(expression); - return x.Evaluate(); - } + ? _cache[expression] + : Parse(expression); + return x.Evaluate(); + } + /// - /// Gets or sets whether the calc engine should keep a cache with parsed - /// expressions. + /// Gets or sets whether the calc engine should keep a cache with parsed + /// expressions. /// public bool CacheExpressions { @@ -136,31 +132,26 @@ } } } + /// - /// Gets or sets whether the calc engine should optimize expressions when - /// they are parsed. + /// Gets or sets whether the calc engine should optimize expressions when + /// they are parsed. /// - public bool OptimizeExpressions - { - get { return _optimize; } - set { _optimize = value; } - } + public bool OptimizeExpressions { get; set; } + /// - /// Gets or sets a string that specifies special characters that are valid for identifiers. + /// Gets or sets a string that specifies special characters that are valid for identifiers. /// /// - /// Identifiers must start with a letter or an underscore, which may be followed by - /// additional letters, underscores, or digits. This string allows you to specify - /// additional valid characters such as ':' or '!' (used in Excel range references - /// for example). + /// Identifiers must start with a letter or an underscore, which may be followed by + /// additional letters, underscores, or digits. This string allows you to specify + /// additional valid characters such as ':' or '!' (used in Excel range references + /// for example). /// - public string IdentifierChars - { - get { return _idChars; } - set { _idChars = value; } - } + public string IdentifierChars { get; set; } + /// - /// Registers a function that can be evaluated by this . + /// Registers a function that can be evaluated by this . /// /// Function name. /// Minimum parameter count. @@ -168,10 +159,11 @@ /// Delegate that evaluates the function. public void RegisterFunction(string functionName, int parmMin, int parmMax, CalcEngineFunction fn) { - _fnTbl.Add(functionName, new FunctionDefinition(parmMin, parmMax, fn)); + Functions.Add(functionName, new FunctionDefinition(parmMin, parmMax, fn)); } + /// - /// Registers a function that can be evaluated by this . + /// Registers a function that can be evaluated by this . /// /// Function name. /// Parameter count. @@ -180,48 +172,43 @@ { RegisterFunction(functionName, parmCount, parmCount, fn); } + /// - /// Gets an external object based on an identifier. + /// Gets an external object based on an identifier. /// /// - /// This method is useful when the engine needs to create objects dynamically. - /// For example, a spreadsheet calc engine would use this method to dynamically create cell - /// range objects based on identifiers that cannot be enumerated at design time - /// (such as "AB12", "A1:AB12", etc.) + /// This method is useful when the engine needs to create objects dynamically. + /// For example, a spreadsheet calc engine would use this method to dynamically create cell + /// range objects based on identifiers that cannot be enumerated at design time + /// (such as "AB12", "A1:AB12", etc.) /// public virtual object GetExternalObject(string identifier) { return null; } + /// - /// Gets or sets the DataContext for this . + /// Gets or sets the DataContext for this . /// /// - /// Once a DataContext is set, all public properties of the object become available - /// to the CalcEngine, including sub-properties such as "Address.Street". These may - /// be used with expressions just like any other constant. + /// Once a DataContext is set, all public properties of the object become available + /// to the CalcEngine, including sub-properties such as "Address.Street". These may + /// be used with expressions just like any other constant. /// - public virtual object DataContext - { - get { return _dataContext; } - set { _dataContext = value; } - } + public virtual object DataContext { get; set; } + /// - /// Gets the dictionary that contains function definitions. + /// Gets the dictionary that contains function definitions. /// - public Dictionary Functions - { - get { return _fnTbl; } - } + public Dictionary Functions { get; private set; } + /// - /// Gets the dictionary that contains simple variables (not in the DataContext). + /// Gets the dictionary that contains simple variables (not in the DataContext). /// - public Dictionary Variables - { - get { return _vars; } - } + public Dictionary Variables { get; } + /// - /// Gets or sets the to use when parsing numbers and dates. + /// Gets or sets the to use when parsing numbers and dates. /// public CultureInfo CultureInfo { @@ -236,13 +223,14 @@ } } - #endregion + #endregion //--------------------------------------------------------------------------- + #region ** token/keyword tables // build/get static token table - Dictionary GetSymbolTable() + private Dictionary GetSymbolTable() { if (_tkTbl == null) { @@ -263,26 +251,27 @@ AddToken("<>", TKID.NE, TKTYPE.COMPARE); AddToken(">=", TKID.GE, TKTYPE.COMPARE); AddToken("<=", TKID.LE, TKTYPE.COMPARE); - + // list separator is localized, not necessarily a comma // so it can't be on the static table //AddToken(',', TKID.COMMA, TKTYPE.GROUP); } return _tkTbl; } - void AddToken(object symbol, TKID id, TKTYPE type) + + private void AddToken(object symbol, TKID id, TKTYPE type) { var token = new Token(symbol, id, type); _tkTbl.Add(symbol, token); } // build/get static keyword table - Dictionary GetFunctionTable() + private Dictionary GetFunctionTable() { - if (_fnTbl == null) + if (Functions == null) { // create table - _fnTbl = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + Functions = new Dictionary(StringComparer.InvariantCultureIgnoreCase); // register built-in functions (and constants) Is.Register(this); @@ -293,102 +282,109 @@ Statistical.Register(this); DateAndTime.Register(this); } - return _fnTbl; + return Functions; } #endregion //--------------------------------------------------------------------------- - #region ** private stuff - Expression ParseExpression() - { - GetToken(); - return ParseCompare(); - } - Expression ParseCompare() - { - var x = ParseAddSub(); - while (_token.Type == TKTYPE.COMPARE) - { - var t = _token; - GetToken(); - var exprArg = ParseAddSub(); - x = new BinaryExpression(t, x, exprArg); - } - return x; - } - Expression ParseAddSub() - { - var x = ParseMulDiv(); - while (_token.Type == TKTYPE.ADDSUB) - { - var t = _token; - GetToken(); - var exprArg = ParseMulDiv(); - x = new BinaryExpression(t, x, exprArg); - } - return x; + #region ** private stuff + + private Expression ParseExpression() + { + GetToken(); + return ParseCompare(); } - Expression ParseMulDiv() - { - var x = ParsePower(); - while (_token.Type == TKTYPE.MULDIV) - { - var t = _token; - GetToken(); - var a = ParsePower(); - x = new BinaryExpression(t, x, a); - } - return x; + + private Expression ParseCompare() + { + var x = ParseAddSub(); + while (_token.Type == TKTYPE.COMPARE) + { + var t = _token; + GetToken(); + var exprArg = ParseAddSub(); + x = new BinaryExpression(t, x, exprArg); + } + return x; } - Expression ParsePower() - { - var x = ParseUnary(); - while (_token.Type == TKTYPE.POWER) - { - var t = _token; - GetToken(); - var a = ParseUnary(); - x = new BinaryExpression(t, x, a); - } - return x; - } - Expression ParseUnary() - { - // unary plus and minus - if (_token.ID == TKID.ADD || _token.ID == TKID.SUB) - { - var t = _token; - GetToken(); + + private Expression ParseAddSub() + { + var x = ParseMulDiv(); + while (_token.Type == TKTYPE.ADDSUB) + { + var t = _token; + GetToken(); + var exprArg = ParseMulDiv(); + x = new BinaryExpression(t, x, exprArg); + } + return x; + } + + private Expression ParseMulDiv() + { + var x = ParsePower(); + while (_token.Type == TKTYPE.MULDIV) + { + var t = _token; + GetToken(); + var a = ParsePower(); + x = new BinaryExpression(t, x, a); + } + return x; + } + + private Expression ParsePower() + { + var x = ParseUnary(); + while (_token.Type == TKTYPE.POWER) + { + var t = _token; + GetToken(); + var a = ParseUnary(); + x = new BinaryExpression(t, x, a); + } + return x; + } + + private Expression ParseUnary() + { + // unary plus and minus + if (_token.ID == TKID.ADD || _token.ID == TKID.SUB) + { + var t = _token; + GetToken(); var a = ParseAtom(); return new UnaryExpression(t, a); - } + } - // not unary, return atom - return ParseAtom(); - } - Expression ParseAtom() - { + // not unary, return atom + return ParseAtom(); + } + + private Expression ParseAtom() + { string id; Expression x = null; FunctionDefinition fnDef = null; - switch (_token.Type) - { - // literals - case TKTYPE.LITERAL: - x = new Expression(_token); - break; + switch (_token.Type) + { + // literals + case TKTYPE.LITERAL: + x = new Expression(_token); + break; // identifiers case TKTYPE.IDENTIFIER: // get identifier - id = (string)_token.Value; + id = (string) _token.Value; // look for functions - if (_fnTbl.TryGetValue(id, out fnDef)) + if (Functions.TryGetValue(id, out fnDef)) { var p = GetParameters(); var pCnt = p == null ? 0 : p.Count; @@ -405,9 +401,9 @@ } // look for simple variables (much faster than binding!) - if (_vars.ContainsKey(id)) + if (Variables.ContainsKey(id)) { - x = new VariableExpression(_vars, id); + x = new VariableExpression(Variables, id); break; } @@ -425,7 +421,7 @@ var list = new List(); for (var t = _token; t != null; t = GetMember()) { - list.Add(new BindingInfo((string)t.Value, GetParameters())); + list.Add(new BindingInfo((string) t.Value, GetParameters())); } x = new BindingExpression(this, list, _ci); break; @@ -433,27 +429,27 @@ Throw("Unexpected identifier"); break; - // sub-expressions - case TKTYPE.GROUP: + // sub-expressions + case TKTYPE.GROUP: // anything other than opening parenthesis is illegal here - if (_token.ID != TKID.OPEN) - { + if (_token.ID != TKID.OPEN) + { Throw("Expression expected."); } - // get expression - GetToken(); - x = ParseCompare(); + // get expression + GetToken(); + x = ParseCompare(); - // check that the parenthesis was closed - if (_token.ID != TKID.CLOSE) - { - Throw("Unbalanced parenthesis."); - } + // check that the parenthesis was closed + if (_token.ID != TKID.CLOSE) + { + Throw("Unbalanced parenthesis."); + } - break; - } + break; + } // make sure we got something... if (x == null) @@ -461,47 +457,48 @@ Throw(); } - // done - GetToken(); - return x; - } + // done + GetToken(); + return x; + } - #endregion + #endregion - //--------------------------------------------------------------------------- - #region ** parser + //--------------------------------------------------------------------------- - void GetToken() + #region ** parser + + private void GetToken() { - // eat white space - while (_ptr < _len && _expr[_ptr] <= ' ') - { - _ptr++; - } + // eat white space + while (_ptr < _len && _expr[_ptr] <= ' ') + { + _ptr++; + } - // are we done? - if (_ptr >= _len) - { + // are we done? + if (_ptr >= _len) + { _token = new Token(null, TKID.END, TKTYPE.GROUP); - return; - } + return; + } - // prepare to parse + // prepare to parse int i; - var c = _expr[_ptr]; + var c = _expr[_ptr]; - // operators - // this gets called a lot, so it's pretty optimized. - // note that operators must start with non-letter/digit characters. + // operators + // this gets called a lot, so it's pretty optimized. + // note that operators must start with non-letter/digit characters. var isLetter = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); var isDigit = c >= '0' && c <= '9'; - if (!isLetter && !isDigit) - { - // if this is a number starting with a decimal, don't parse as operator + if (!isLetter && !isDigit) + { + // if this is a number starting with a decimal, don't parse as operator var nxt = _ptr + 1 < _len ? _expr[_ptr + 1] : 0; - bool isNumber = c == _decimal && nxt >= '0' && nxt <= '9'; - if (!isNumber) - { + var isNumber = c == _decimal && nxt >= '0' && nxt <= '9'; + if (!isNumber) + { // look up localized list separator if (c == _listSep) { @@ -509,46 +506,46 @@ _ptr++; return; } - + // look up single-char tokens on table Token tk; if (_tkTbl.TryGetValue(c, out tk)) - { - // save token we found - _token = tk; - _ptr++; + { + // save token we found + _token = tk; + _ptr++; - // look for double-char tokens (special case) - if (_ptr < _len && (c == '>' || c == '<')) - { + // look for double-char tokens (special case) + if (_ptr < _len && (c == '>' || c == '<')) + { if (_tkTbl.TryGetValue(_expr.Substring(_ptr - 1, 2), out tk)) - { - _token = tk; - _ptr++; - } - } + { + _token = tk; + _ptr++; + } + } // found token on the table - return; - } - } - } + return; + } + } + } - // parse numbers + // parse numbers if (isDigit || c == _decimal) - { - var sci = false; + { + var sci = false; var pct = false; var div = -1.0; // use double, not int (this may get really big) var val = 0.0; for (i = 0; i + _ptr < _len; i++) - { - c = _expr[_ptr + i]; + { + c = _expr[_ptr + i]; // digits always OK if (c >= '0' && c <= '9') { - val = val * 10 + (c - '0'); + val = val*10 + (c - '0'); if (div > -1) { div *= 10; @@ -556,33 +553,32 @@ continue; } - // one decimal is OK + // one decimal is OK if (c == _decimal && div < 0) - { - div = 1; - continue; - } - - // scientific notation? - if ((c == 'E' || c == 'e') && !sci) - { - sci = true; - c = _expr[_ptr + i + 1]; - if (c == '+' || c == '-') i++; - continue; - } + { + div = 1; + continue; + } + + // scientific notation? + if ((c == 'E' || c == 'e') && !sci) + { + sci = true; + c = _expr[_ptr + i + 1]; + if (c == '+' || c == '-') i++; + continue; + } // percentage? if (c == _percent) { pct = true; i++; - break; } - // end of literal - break; - } + // end of literal + break; + } // end of number, get value if (!sci) @@ -609,59 +605,59 @@ // advance pointer and return _ptr += i; return; - } + } - // parse strings - if (c == '\"') - { - // look for end quote, skip double quotes - for (i = 1; i + _ptr < _len; i++) - { - c = _expr[_ptr + i]; - if (c != '\"') continue; - char cNext = i + _ptr < _len - 1 ? _expr[_ptr + i + 1]: ' '; - if (cNext != '\"') break; - i++; - } + // parse strings + if (c == '\"') + { + // look for end quote, skip double quotes + for (i = 1; i + _ptr < _len; i++) + { + c = _expr[_ptr + i]; + if (c != '\"') continue; + var cNext = i + _ptr < _len - 1 ? _expr[_ptr + i + 1] : ' '; + if (cNext != '\"') break; + i++; + } - // check that we got the end of the string - if (c != '\"') - { - Throw("Can't find final quote."); - } + // check that we got the end of the string + if (c != '\"') + { + Throw("Can't find final quote."); + } - // end of string - var lit = _expr.Substring(_ptr + 1, i - 1); - _ptr += i + 1; + // end of string + var lit = _expr.Substring(_ptr + 1, i - 1); + _ptr += i + 1; _token = new Token(lit.Replace("\"\"", "\""), TKID.ATOM, TKTYPE.LITERAL); - return; - } + return; + } - // parse dates (review) - if (c == '#') - { - // look for end # - for (i = 1; i + _ptr < _len; i++) - { - c = _expr[_ptr + i]; - if (c == '#') break; - } + // parse dates (review) + if (c == '#') + { + // look for end # + for (i = 1; i + _ptr < _len; i++) + { + c = _expr[_ptr + i]; + if (c == '#') break; + } - // check that we got the end of the date - if (c != '#') - { - Throw("Can't find final date delimiter ('#')."); - } + // check that we got the end of the date + if (c != '#') + { + Throw("Can't find final date delimiter ('#')."); + } - // end of date - var lit = _expr.Substring(_ptr + 1, i - 1); - _ptr += i + 1; + // end of date + var lit = _expr.Substring(_ptr + 1, i - 1); + _ptr += i + 1; _token = new Token(DateTime.Parse(lit, _ci), TKID.ATOM, TKTYPE.LITERAL); - return; - } + return; + } // identifiers (functions, objects) must start with alpha or underscore - if (!isLetter && c != '_' && (_idChars == null || _idChars.IndexOf(c) < 0)) + if (!isLetter && c != '_' && (IdentifierChars == null || IdentifierChars.IndexOf(c) < 0)) { Throw("Identifier expected."); } @@ -672,7 +668,7 @@ c = _expr[_ptr + i]; isLetter = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); isDigit = c >= '0' && c <= '9'; - if (!isLetter && !isDigit && c != '_' && (_idChars == null || _idChars.IndexOf(c) < 0)) + if (!isLetter && !isDigit && c != '_' && (IdentifierChars == null || IdentifierChars.IndexOf(c) < 0)) { break; } @@ -682,59 +678,62 @@ var id = _expr.Substring(_ptr, i); _ptr += i; _token = new Token(id, TKID.ATOM, TKTYPE.IDENTIFIER); - } - static double ParseDouble(string str, CultureInfo ci) + } + + private static double ParseDouble(string str, CultureInfo ci) { if (str.Length > 0 && str[str.Length - 1] == ci.NumberFormat.PercentSymbol[0]) { str = str.Substring(0, str.Length - 1); - return double.Parse(str, NumberStyles.Any, ci) / 100.0; + return double.Parse(str, NumberStyles.Any, ci)/100.0; } return double.Parse(str, NumberStyles.Any, ci); } - List GetParameters() // e.g. myfun(a, b, c+2) - { - // check whether next token is a (, - // restore state and bail if it's not - var pos = _ptr; - var tk = _token; - GetToken(); - if (_token.ID != TKID.OPEN) - { + + private List GetParameters() // e.g. myfun(a, b, c+2) + { + // check whether next token is a (, + // restore state and bail if it's not + var pos = _ptr; + var tk = _token; + GetToken(); + if (_token.ID != TKID.OPEN) + { _ptr = pos; _token = tk; - return null; - } + return null; + } - // check for empty Parameter list - pos = _ptr; - GetToken(); + // check for empty Parameter list + pos = _ptr; + GetToken(); if (_token.ID == TKID.CLOSE) { return null; } - _ptr = pos; + _ptr = pos; - // get Parameters until we reach the end of the list + // get Parameters until we reach the end of the list var parms = new List(); - var expr = ParseExpression(); - parms.Add(expr); - while (_token.ID == TKID.COMMA) - { - expr = ParseExpression(); - parms.Add(expr); - } + var expr = ParseExpression(); + parms.Add(expr); + while (_token.ID == TKID.COMMA) + { + expr = ParseExpression(); + parms.Add(expr); + } - // make sure the list was closed correctly - if (_token.ID != TKID.CLOSE) - { + // make sure the list was closed correctly + if (_token.ID != TKID.CLOSE) + { Throw(); - } + } - // done - return parms; - } - Token GetMember() + // done + return parms; + } + + private Token GetMember() { // check whether next token is a MEMBER token ('.'), // restore state and bail if it's not @@ -757,28 +756,32 @@ return _token; } - #endregion + #endregion - //--------------------------------------------------------------------------- - #region ** static helpers + //--------------------------------------------------------------------------- - static void Throw() + #region ** static helpers + + private static void Throw() { Throw("Syntax error."); } - static void Throw(string msg) + + private static void Throw(string msg) { throw new Exception(msg); } #endregion - } + } /// - /// Delegate that represents CalcEngine functions. + /// Delegate that represents CalcEngine functions. /// - /// List of objects that represent the - /// parameters to be used in the function call. + /// + /// List of objects that represent the + /// parameters to be used in the function call. + /// /// The function result. internal delegate object CalcEngineFunction(List parms); -} +} \ No newline at end of file diff --git a/ClosedXML/Excel/CalcEngine/Functions/Logical.cs b/ClosedXML/Excel/CalcEngine/Functions/Logical.cs index 1b6695e..0e8c560 100644 --- a/ClosedXML/Excel/CalcEngine/Functions/Logical.cs +++ b/ClosedXML/Excel/CalcEngine/Functions/Logical.cs @@ -1,7 +1,5 @@ using System; -using System.Diagnostics; using System.Collections.Generic; -using System.Text; namespace ClosedXML.Excel.CalcEngine { @@ -17,46 +15,60 @@ ce.RegisterFunction("FALSE", 0, False); } - static object And(List p) + private static object And(List p) { var b = true; foreach (var v in p) { - b = b && (bool)v; + b = b && v; } return b; } - static object Or(List p) + + private static object Or(List p) { var b = false; foreach (var v in p) { - b = b || (bool)v; + b = b || v; } return b; } - static object Not(List p) + + private static object Not(List p) { - return !(bool)p[0]; + return !p[0]; } - static object If(List p) + + private static object If(List p) { - if ((bool)p[0] ) + if (p[0]) { return p[1].Evaluate(); } - else - { - return p.Count > 2 ? p[2].Evaluate() : false; - } + return p.Count > 2 ? p[2].Evaluate() : false; } - static object True(List p) + + private static object True(List p) { return true; } - static object False(List p) + + private static object False(List p) { return false; } + + private static object IfError(Expression p, object valueIfError) + { + try + { + return p.Evaluate(); + } + catch (ArgumentException) + { + return valueIfError; + } + } } -} +} \ No newline at end of file