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