diff --git a/ClosedXML/Excel/CalcEngine/Expression.cs b/ClosedXML/Excel/CalcEngine/Expression.cs
index 831db51..1b97924 100644
--- a/ClosedXML/Excel/CalcEngine/Expression.cs
+++ b/ClosedXML/Excel/CalcEngine/Expression.cs
@@ -10,503 +10,510 @@
namespace ClosedXML.Excel.CalcEngine
{
- ///
- /// Base class that represents parsed expressions.
- ///
- ///
- /// For example:
- ///
- /// Expression expr = scriptEngine.Parse(strExpression);
- /// object val = expr.Evaluate();
- ///
- ///
- internal class Expression : IComparable
- {
- //---------------------------------------------------------------------------
- #region ** fields
+ ///
+ /// Base class that represents parsed expressions.
+ ///
+ ///
+ /// For example:
+ ///
+ /// Expression expr = scriptEngine.Parse(strExpression);
+ /// object val = expr.Evaluate();
+ ///
+ ///
+ internal class Expression : IComparable
+ {
+ //---------------------------------------------------------------------------
+ #region ** fields
- internal Token _token;
+ internal Token _token;
- #endregion
+ #endregion
- //---------------------------------------------------------------------------
- #region ** ctors
+ //---------------------------------------------------------------------------
+ #region ** ctors
- internal Expression()
- {
- _token = new Token(null, TKID.ATOM, TKTYPE.IDENTIFIER);
- }
- internal Expression(object value)
- {
- _token = new Token(value, TKID.ATOM, TKTYPE.LITERAL);
- }
- internal Expression(Token tk)
- {
- _token = tk;
- }
+ internal Expression()
+ {
+ _token = new Token(null, TKID.ATOM, TKTYPE.IDENTIFIER);
+ }
+ internal Expression(object value)
+ {
+ _token = new Token(value, TKID.ATOM, TKTYPE.LITERAL);
+ }
+ internal Expression(Token tk)
+ {
+ _token = tk;
+ }
- #endregion
+ #endregion
- //---------------------------------------------------------------------------
- #region ** object model
+ //---------------------------------------------------------------------------
+ #region ** object model
- public virtual object Evaluate()
- {
- if (_token.Type != TKTYPE.LITERAL)
- {
- throw new ArgumentException("Bad expression.");
- }
- return _token.Value;
- }
- public virtual Expression Optimize()
- {
- return this;
- }
+ public virtual object Evaluate()
+ {
+ if (_token.Type != TKTYPE.LITERAL)
+ {
+ throw new ArgumentException("Bad expression.");
+ }
+ return _token.Value;
+ }
+ public virtual Expression Optimize()
+ {
+ return this;
+ }
- #endregion
+ #endregion
- //---------------------------------------------------------------------------
- #region ** implicit converters
+ //---------------------------------------------------------------------------
+ #region ** implicit converters
- public static implicit operator string(Expression x)
- {
- var v = x.Evaluate();
- return v == null ? string.Empty : v.ToString();
- }
- public static implicit operator double(Expression x)
- {
- // evaluate
- var v = x.Evaluate();
+ public static implicit operator string(Expression x)
+ {
+ var v = x.Evaluate();
+ return v == null ? string.Empty : v.ToString();
+ }
+ public static implicit operator double(Expression x)
+ {
+ // evaluate
+ var v = x.Evaluate();
- // handle doubles
- if (v is double)
- {
- return (double)v;
- }
+ // handle doubles
+ if (v is double)
+ {
+ return (double)v;
+ }
- // handle booleans
- if (v is bool)
- {
- return (bool)v ? 1 : 0;
- }
+ // handle booleans
+ if (v is bool)
+ {
+ return (bool)v ? 1 : 0;
+ }
- // handle dates
- if (v is DateTime)
- {
- return ((DateTime)v).ToOADate();
- }
+ // handle dates
+ if (v is DateTime)
+ {
+ return ((DateTime)v).ToOADate();
+ }
- // handle nulls
- if (v == null || v is string)
- {
- return 0;
- }
-
- // handle everything else
- CultureInfo _ci = Thread.CurrentThread.CurrentCulture;
- return (double)Convert.ChangeType(v, typeof(double), _ci);
- }
- public static implicit operator bool(Expression x)
- {
- // evaluate
- var v = x.Evaluate();
-
- // handle booleans
- if (v is bool)
- {
- return (bool)v;
- }
-
- // handle nulls
- if (v == null)
- {
- return false;
- }
-
- // handle doubles
- if (v is double)
- {
- return (double)v == 0 ? false : true;
- }
-
- // handle everything else
- return (double)x == 0 ? false : true;
- }
- public static implicit operator DateTime(Expression x)
- {
- // evaluate
- var v = x.Evaluate();
-
- // handle dates
- if (v is DateTime)
- {
- return (DateTime)v;
- }
-
- // handle doubles
- if (v is double || v is int)
- {
- return DateTime.FromOADate((double)x);
- }
+ // handle nulls
+ if (v == null || v is string)
+ {
+ return 0;
+ }
// handle everything else
CultureInfo _ci = Thread.CurrentThread.CurrentCulture;
- return (DateTime)Convert.ChangeType(v, typeof(DateTime), _ci);
- }
+ return (double)Convert.ChangeType(v, typeof(double), _ci);
+ }
+ public static implicit operator bool(Expression x)
+ {
+ // evaluate
+ var v = x.Evaluate();
- #endregion
+ // handle booleans
+ if (v is bool)
+ {
+ return (bool)v;
+ }
- //---------------------------------------------------------------------------
- #region ** IComparable
+ // handle nulls
+ if (v == null)
+ {
+ return false;
+ }
- public int CompareTo(Expression other)
- {
- // get both values
- var c1 = this.Evaluate() as IComparable;
- var c2 = other.Evaluate() as IComparable;
+ // handle doubles
+ if (v is double)
+ {
+ return (double)v == 0 ? false : true;
+ }
- // handle nulls
- if (c1 == null && c2 == null)
- {
- return 0;
- }
- if (c2 == null)
- {
- return -1;
- }
- if (c1 == null)
- {
- return +1;
- }
+ // handle everything else
+ return (double)x == 0 ? false : true;
+ }
+ public static implicit operator DateTime(Expression x)
+ {
+ // evaluate
+ var v = x.Evaluate();
- // make sure types are the same
- if (c1.GetType() != c2.GetType())
- {
- if (c1 is DateTime)
- c2 = ((DateTime)other);
- else if (c2 is DateTime)
- c1 = ((DateTime)this);
- else
- c2 = Convert.ChangeType(c2, c1.GetType()) as IComparable;
- }
+ // handle dates
+ if (v is DateTime)
+ {
+ return (DateTime)v;
+ }
- // compare
- return c1.CompareTo(c2);
- }
+ // handle doubles
+ if (v is double || v is int)
+ {
+ return DateTime.FromOADate((double)x);
+ }
- #endregion
- }
- ///
- /// Unary expression, e.g. +123
- ///
- class UnaryExpression : Expression
- {
- // ** fields
- Expression _expr;
+ // handle everything else
+ CultureInfo _ci = Thread.CurrentThread.CurrentCulture;
+ return (DateTime)Convert.ChangeType(v, typeof(DateTime), _ci);
+ }
- // ** ctor
- public UnaryExpression(Token tk, Expression expr) : base(tk)
- {
- _expr = expr;
- }
+ #endregion
- // ** object model
- override public object Evaluate()
- {
- switch (_token.ID)
- {
- case TKID.ADD:
- return +(double)_expr;
- case TKID.SUB:
- return -(double)_expr;
- }
- throw new ArgumentException("Bad expression.");
- }
- public override Expression Optimize()
- {
- _expr = _expr.Optimize();
- return _expr._token.Type == TKTYPE.LITERAL
- ? new Expression(this.Evaluate())
- : this;
- }
- }
- ///
- /// Binary expression, e.g. 1+2
- ///
- class BinaryExpression : Expression
- {
- // ** fields
- Expression _lft;
- Expression _rgt;
+ //---------------------------------------------------------------------------
+ #region ** IComparable
- // ** ctor
- public BinaryExpression(Token tk, Expression exprLeft, Expression exprRight) : base(tk)
- {
- _lft = exprLeft;
- _rgt = exprRight;
- }
+ public int CompareTo(Expression other)
+ {
+ // get both values
+ var c1 = this.Evaluate() as IComparable;
+ var c2 = other.Evaluate() as IComparable;
- // ** object model
- override public object Evaluate()
- {
- // handle comparisons
- if (_token.Type == TKTYPE.COMPARE)
- {
- var cmp = _lft.CompareTo(_rgt);
- switch (_token.ID)
- {
- case TKID.GT: return cmp > 0;
- case TKID.LT: return cmp < 0;
- case TKID.GE: return cmp >= 0;
- case TKID.LE: return cmp <= 0;
- case TKID.EQ: return cmp == 0;
- case TKID.NE: return cmp != 0;
- }
- }
+ // handle nulls
+ if (c1 == null && c2 == null)
+ {
+ return 0;
+ }
+ if (c2 == null)
+ {
+ return -1;
+ }
+ if (c1 == null)
+ {
+ return +1;
+ }
- // handle everything else
- switch (_token.ID)
- {
- case TKID.CONCAT:
- return (string)_lft + (string)_rgt;
- case TKID.ADD:
- return (double)_lft + (double)_rgt;
- case TKID.SUB:
- return (double)_lft - (double)_rgt;
- case TKID.MUL:
- return (double)_lft * (double)_rgt;
- case TKID.DIV:
- return (double)_lft / (double)_rgt;
- case TKID.DIVINT:
- return (double)(int)((double)_lft / (double)_rgt);
- case TKID.MOD:
- return (double)(int)((double)_lft % (double)_rgt);
- case TKID.POWER:
- var a = (double)_lft;
- var b = (double)_rgt;
- if (b == 0.0) return 1.0;
- if (b == 0.5) return Math.Sqrt(a);
- if (b == 1.0) return a;
- if (b == 2.0) return a * a;
- if (b == 3.0) return a * a * a;
- if (b == 4.0) return a * a * a * a;
- return Math.Pow((double)_lft, (double)_rgt);
- }
- throw new ArgumentException("Bad expression.");
- }
- public override Expression Optimize()
- {
- _lft = _lft.Optimize();
- _rgt = _rgt.Optimize();
- return _lft._token.Type == TKTYPE.LITERAL && _rgt._token.Type == TKTYPE.LITERAL
- ? new Expression(this.Evaluate())
- : this;
- }
- }
- ///
- /// Function call expression, e.g. sin(0.5)
- ///
- class FunctionExpression : Expression
- {
- // ** fields
- FunctionDefinition _fn;
- List _parms;
+ // make sure types are the same
+ if (c1.GetType() != c2.GetType())
+ {
+ try
+ {
+ if (c1 is DateTime)
+ c2 = ((DateTime)other);
+ else if (c2 is DateTime)
+ c1 = ((DateTime)this);
+ else
+ c2 = Convert.ChangeType(c2, c1.GetType()) as IComparable;
+ }
+ catch (InvalidCastException) { return -1; }
+ catch (FormatException) { return -1; }
+ catch (OverflowException) { return -1; }
+ catch (ArgumentNullException) { return -1; }
+ }
- // ** ctor
- internal FunctionExpression()
- {
- }
- public FunctionExpression(FunctionDefinition function, List parms)
- {
- _fn = function;
- _parms = parms;
- }
+ // compare
+ return c1.CompareTo(c2);
+ }
- // ** object model
- override public object Evaluate()
- {
- return _fn.Function(_parms);
- }
- public override Expression Optimize()
- {
- bool allLits = true;
- if (_parms != null)
- {
- for (int i = 0; i < _parms.Count; i++)
- {
- var p = _parms[i].Optimize();
- _parms[i] = p;
- if (p._token.Type != TKTYPE.LITERAL)
- {
- allLits = false;
- }
- }
- }
- return allLits
- ? new Expression(this.Evaluate())
- : this;
- }
- }
- ///
- /// Simple variable reference.
- ///
- class VariableExpression : Expression
- {
- Dictionary _dct;
- string _name;
+ #endregion
+ }
+ ///
+ /// Unary expression, e.g. +123
+ ///
+ class UnaryExpression : Expression
+ {
+ // ** fields
+ Expression _expr;
- public VariableExpression(Dictionary dct, string name)
- {
- _dct = dct;
- _name = name;
- }
- public override object Evaluate()
- {
- return _dct[_name];
- }
- }
- ///
- /// Expression based on an object's properties.
- ///
- class BindingExpression : Expression
- {
- CalcEngine _ce;
- CultureInfo _ci;
- List _bindingPath;
+ // ** ctor
+ public UnaryExpression(Token tk, Expression expr) : base(tk)
+ {
+ _expr = expr;
+ }
- // ** ctor
- internal BindingExpression(CalcEngine engine, List bindingPath, CultureInfo ci)
- {
- _ce = engine;
- _bindingPath = bindingPath;
- _ci = ci;
- }
+ // ** object model
+ override public object Evaluate()
+ {
+ switch (_token.ID)
+ {
+ case TKID.ADD:
+ return +(double)_expr;
+ case TKID.SUB:
+ return -(double)_expr;
+ }
+ throw new ArgumentException("Bad expression.");
+ }
+ public override Expression Optimize()
+ {
+ _expr = _expr.Optimize();
+ return _expr._token.Type == TKTYPE.LITERAL
+ ? new Expression(this.Evaluate())
+ : this;
+ }
+ }
+ ///
+ /// Binary expression, e.g. 1+2
+ ///
+ class BinaryExpression : Expression
+ {
+ // ** fields
+ Expression _lft;
+ Expression _rgt;
- // ** object model
- override public object Evaluate()
- {
- return GetValue(_ce.DataContext);
- }
+ // ** ctor
+ public BinaryExpression(Token tk, Expression exprLeft, Expression exprRight) : base(tk)
+ {
+ _lft = exprLeft;
+ _rgt = exprRight;
+ }
- // ** implementation
- object GetValue(object obj)
- {
- const BindingFlags bf =
- BindingFlags.IgnoreCase |
- BindingFlags.Instance |
- BindingFlags.Public |
- BindingFlags.Static;
+ // ** object model
+ override public object Evaluate()
+ {
+ // handle comparisons
+ if (_token.Type == TKTYPE.COMPARE)
+ {
+ var cmp = _lft.CompareTo(_rgt);
+ switch (_token.ID)
+ {
+ case TKID.GT: return cmp > 0;
+ case TKID.LT: return cmp < 0;
+ case TKID.GE: return cmp >= 0;
+ case TKID.LE: return cmp <= 0;
+ case TKID.EQ: return cmp == 0;
+ case TKID.NE: return cmp != 0;
+ }
+ }
- if (obj != null)
- {
- foreach (var bi in _bindingPath)
- {
- // get property
- if (bi.PropertyInfo == null)
- {
- bi.PropertyInfo = obj.GetType().GetProperty(bi.Name, bf);
- }
+ // handle everything else
+ switch (_token.ID)
+ {
+ case TKID.CONCAT:
+ return (string)_lft + (string)_rgt;
+ case TKID.ADD:
+ return (double)_lft + (double)_rgt;
+ case TKID.SUB:
+ return (double)_lft - (double)_rgt;
+ case TKID.MUL:
+ return (double)_lft * (double)_rgt;
+ case TKID.DIV:
+ return (double)_lft / (double)_rgt;
+ case TKID.DIVINT:
+ return (double)(int)((double)_lft / (double)_rgt);
+ case TKID.MOD:
+ return (double)(int)((double)_lft % (double)_rgt);
+ case TKID.POWER:
+ var a = (double)_lft;
+ var b = (double)_rgt;
+ if (b == 0.0) return 1.0;
+ if (b == 0.5) return Math.Sqrt(a);
+ if (b == 1.0) return a;
+ if (b == 2.0) return a * a;
+ if (b == 3.0) return a * a * a;
+ if (b == 4.0) return a * a * a * a;
+ return Math.Pow((double)_lft, (double)_rgt);
+ }
+ throw new ArgumentException("Bad expression.");
+ }
+ public override Expression Optimize()
+ {
+ _lft = _lft.Optimize();
+ _rgt = _rgt.Optimize();
+ return _lft._token.Type == TKTYPE.LITERAL && _rgt._token.Type == TKTYPE.LITERAL
+ ? new Expression(this.Evaluate())
+ : this;
+ }
+ }
+ ///
+ /// Function call expression, e.g. sin(0.5)
+ ///
+ class FunctionExpression : Expression
+ {
+ // ** fields
+ FunctionDefinition _fn;
+ List _parms;
- // get object
- try
- {
- obj = bi.PropertyInfo.GetValue(obj, null);
- }
- catch
- {
- // REVIEW: is this needed?
- System.Diagnostics.Debug.Assert(false, "shouldn't happen!");
- bi.PropertyInfo = obj.GetType().GetProperty(bi.Name, bf);
- bi.PropertyInfoItem = null;
- obj = bi.PropertyInfo.GetValue(obj, null);
- }
+ // ** ctor
+ internal FunctionExpression()
+ {
+ }
+ public FunctionExpression(FunctionDefinition function, List parms)
+ {
+ _fn = function;
+ _parms = parms;
+ }
- // handle indexers (lists and dictionaries)
- if (bi.Parms != null && bi.Parms.Count > 0)
- {
- // get indexer property (always called "Item")
- if (bi.PropertyInfoItem == null)
- {
- bi.PropertyInfoItem = obj.GetType().GetProperty("Item", bf);
- }
+ // ** object model
+ override public object Evaluate()
+ {
+ return _fn.Function(_parms);
+ }
+ public override Expression Optimize()
+ {
+ bool allLits = true;
+ if (_parms != null)
+ {
+ for (int i = 0; i < _parms.Count; i++)
+ {
+ var p = _parms[i].Optimize();
+ _parms[i] = p;
+ if (p._token.Type != TKTYPE.LITERAL)
+ {
+ allLits = false;
+ }
+ }
+ }
+ return allLits
+ ? new Expression(this.Evaluate())
+ : this;
+ }
+ }
+ ///
+ /// Simple variable reference.
+ ///
+ class VariableExpression : Expression
+ {
+ Dictionary _dct;
+ string _name;
- // get indexer parameters
- var pip = bi.PropertyInfoItem.GetIndexParameters();
- var list = new List