diff --git a/ClosedXML/ClosedXML.csproj b/ClosedXML/ClosedXML.csproj
index c7da769..d4073e5 100644
--- a/ClosedXML/ClosedXML.csproj
+++ b/ClosedXML/ClosedXML.csproj
@@ -64,7 +64,7 @@
-
+
diff --git a/ClosedXML/Excel/CalcEngine/CalcEngine.cs b/ClosedXML/Excel/CalcEngine/CalcEngine.cs
index b946fa2..9ba60b2 100644
--- a/ClosedXML/Excel/CalcEngine/CalcEngine.cs
+++ b/ClosedXML/Excel/CalcEngine/CalcEngine.cs
@@ -9,8 +9,7 @@
using ClosedXML.Excel.CalcEngine;
using ClosedXML.Excel.CalcEngine.Functions;
-namespace ClosedXML.Excel.CalcEngine
-{
+namespace ClosedXML.Excel.CalcEngine {
///
/// CalcEngine parses strings and returns Expression objects that can
/// be evaluated.
@@ -21,19 +20,18 @@
/// 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
+ 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
+ // 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 _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
@@ -46,8 +44,7 @@
//---------------------------------------------------------------------------
#region ** ctor
- public CalcEngine()
- {
+ public CalcEngine() {
CultureInfo = CultureInfo.InvariantCulture;
_tkTbl = GetSymbolTable();
_fnTbl = GetFunctionTable();
@@ -58,48 +55,44 @@
//this.Test();
#endif
}
-
+
#endregion
//---------------------------------------------------------------------------
- #region ** object model
+ #region ** object model
- ///
- /// Parses a string into an .
- ///
- /// String to parse.
+ ///
+ /// 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;
+ public Expression Parse(string expression) {
+ // initialize
+ _expr = expression;
+ _len = _expr.Length;
+ _ptr = 0;
// skip leading equals sign
- if (_len > 0 && _expr[0] == '=')
- {
+ if (_len > 0 && _expr[0] == '=') {
_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 (_optimize) {
expr = expr.Optimize();
}
// done
- return expr;
- }
+ return expr;
+ }
///
/// Evaluates a string.
///
@@ -111,25 +104,21 @@
/// 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();
- }
+ return x.Evaluate();
+ }
///
/// Gets or sets whether the calc engine should keep a cache with parsed
/// expressions.
///
- public bool CacheExpressions
- {
+ public bool CacheExpressions {
get { return _cache != null; }
- set
- {
- if (value != CacheExpressions)
- {
+ set {
+ if (value != CacheExpressions) {
_cache = value
? new ExpressionCache(this)
: null;
@@ -140,8 +129,7 @@
/// Gets or sets whether the calc engine should optimize expressions when
/// they are parsed.
///
- public bool OptimizeExpressions
- {
+ public bool OptimizeExpressions {
get { return _optimize; }
set { _optimize = value; }
}
@@ -154,8 +142,7 @@
/// additional valid characters such as ':' or '!' (used in Excel range references
/// for example).
///
- public string IdentifierChars
- {
+ public string IdentifierChars {
get { return _idChars; }
set { _idChars = value; }
}
@@ -166,8 +153,7 @@
/// Minimum parameter count.
/// Maximum parameter count.
/// Delegate that evaluates the function.
- public void RegisterFunction(string functionName, int parmMin, int parmMax, CalcEngineFunction fn)
- {
+ public void RegisterFunction(string functionName, int parmMin, int parmMax, CalcEngineFunction fn) {
_fnTbl.Add(functionName, new FunctionDefinition(parmMin, parmMax, fn));
}
///
@@ -176,8 +162,7 @@
/// Function name.
/// Parameter count.
/// Delegate that evaluates the function.
- public void RegisterFunction(string functionName, int parmCount, CalcEngineFunction fn)
- {
+ public void RegisterFunction(string functionName, int parmCount, CalcEngineFunction fn) {
RegisterFunction(functionName, parmCount, parmCount, fn);
}
///
@@ -189,8 +174,7 @@
/// range objects based on identifiers that cannot be enumerated at design time
/// (such as "AB12", "A1:AB12", etc.)
///
- public virtual object GetExternalObject(string identifier)
- {
+ public virtual object GetExternalObject(string identifier) {
return null;
}
///
@@ -201,33 +185,28 @@
/// 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
- {
+ public virtual object DataContext {
get { return _dataContext; }
set { _dataContext = value; }
}
///
/// Gets the dictionary that contains function definitions.
///
- public Dictionary Functions
- {
+ public Dictionary Functions {
get { return _fnTbl; }
}
///
/// Gets the dictionary that contains simple variables (not in the DataContext).
///
- public Dictionary Variables
- {
+ public Dictionary Variables {
get { return _vars; }
}
///
/// Gets or sets the to use when parsing numbers and dates.
///
- public CultureInfo CultureInfo
- {
+ public CultureInfo CultureInfo {
get { return _ci; }
- set
- {
+ set {
_ci = value;
var nf = _ci.NumberFormat;
_decimal = nf.NumberDecimalSeparator[0];
@@ -236,16 +215,14 @@
}
}
- #endregion
+ #endregion
//---------------------------------------------------------------------------
#region ** token/keyword tables
// build/get static token table
- Dictionary GetSymbolTable()
- {
- if (_tkTbl == null)
- {
+ Dictionary GetSymbolTable() {
+ if (_tkTbl == null) {
_tkTbl = new Dictionary();
AddToken('&', TKID.CONCAT, TKTYPE.ADDSUB);
AddToken('+', TKID.ADD, TKTYPE.ADDSUB);
@@ -263,29 +240,26 @@
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)
- {
+ 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()
- {
- if (_fnTbl == null)
- {
+ Dictionary GetFunctionTable() {
+ if (_fnTbl == null) {
// create table
_fnTbl = new Dictionary(StringComparer.InvariantCultureIgnoreCase);
// register built-in functions (and constants)
- Is.Register(this);
+ Information.Register(this);
Logical.Register(this);
Lookup.Register(this);
MathTrig.Register(this);
@@ -299,87 +273,74 @@
#endregion
//---------------------------------------------------------------------------
- #region ** private stuff
+ #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;
+ 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;
+ 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();
+ 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;
+ }
+ 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;
+ }
+ 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();
var a = ParseAtom();
return new UnaryExpression(t, a);
- }
+ }
- // not unary, return atom
- return ParseAtom();
- }
- Expression ParseAtom()
- {
+ // not unary, return atom
+ return ParseAtom();
+ }
+ 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:
@@ -388,16 +349,13 @@
id = (string)_token.Value;
// look for functions
- if (_fnTbl.TryGetValue(id, out fnDef))
- {
+ if (_fnTbl.TryGetValue(id, out fnDef)) {
var p = GetParameters();
var pCnt = p == null ? 0 : p.Count;
- if (fnDef.ParmMin != -1 && pCnt < fnDef.ParmMin)
- {
+ if (fnDef.ParmMin != -1 && pCnt < fnDef.ParmMin) {
Throw("Too few parameters.");
}
- if (fnDef.ParmMax != -1 && pCnt > fnDef.ParmMax)
- {
+ if (fnDef.ParmMax != -1 && pCnt > fnDef.ParmMax) {
Throw("Too many parameters.");
}
x = new FunctionExpression(fnDef, p);
@@ -405,26 +363,22 @@
}
// look for simple variables (much faster than binding!)
- if (_vars.ContainsKey(id))
- {
+ if (_vars.ContainsKey(id)) {
x = new VariableExpression(_vars, id);
break;
}
// look for external objects
var xObj = GetExternalObject(id);
- if (xObj != null)
- {
+ if (xObj != null) {
x = new XObjectExpression(xObj);
break;
}
// look for bindings
- if (DataContext != null)
- {
+ if (DataContext != null) {
var list = new List();
- for (var t = _token; t != null; t = GetMember())
- {
+ for (var t = _token; t != null; t = GetMember()) {
list.Add(new BindingInfo((string)t.Value, GetParameters()));
}
x = new BindingExpression(this, list, _ci);
@@ -433,172 +387,148 @@
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)
- {
+ if (x == null) {
Throw();
}
- // done
- GetToken();
- return x;
- }
+ // done
+ GetToken();
+ return x;
+ }
- #endregion
+ #endregion
- //---------------------------------------------------------------------------
- #region ** parser
+ //---------------------------------------------------------------------------
+ #region ** parser
- void GetToken()
- {
- // eat white space
- while (_ptr < _len && _expr[_ptr] <= ' ')
- {
- _ptr++;
- }
+ void GetToken() {
+ // 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)
- {
+ if (!isNumber) {
// look up localized list separator
- if (c == _listSep)
- {
+ if (c == _listSep) {
_token = new Token(c, TKID.COMMA, TKTYPE.GROUP);
_ptr++;
return;
}
-
+
// look up single-char tokens on table
Token tk;
- if (_tkTbl.TryGetValue(c, out tk))
- {
- // save token we found
- _token = tk;
- _ptr++;
+ if (_tkTbl.TryGetValue(c, out tk)) {
+ // save token we found
+ _token = tk;
+ _ptr++;
- // 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++;
- }
- }
+ // 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++;
+ }
+ }
// found token on the table
- return;
- }
- }
- }
+ return;
+ }
+ }
+ }
- // parse numbers
- if (isDigit || c == _decimal)
- {
- var sci = false;
+ // parse numbers
+ if (isDigit || c == _decimal) {
+ 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];
+ for (i = 0; i + _ptr < _len; i++) {
+ c = _expr[_ptr + i];
// digits always OK
- if (c >= '0' && c <= '9')
- {
+ if (c >= '0' && c <= '9') {
val = val * 10 + (c - '0');
- if (div > -1)
- {
+ if (div > -1) {
div *= 10;
}
continue;
}
- // 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;
- }
+ // 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;
+ }
// percentage?
- if (c == _percent)
- {
+ if (c == _percent) {
pct = true;
i++;
break;
}
- // end of literal
- break;
- }
+ // end of literal
+ break;
+ }
// end of number, get value
- if (!sci)
- {
+ if (!sci) {
// much faster than ParseDouble
- if (div > 1)
- {
+ if (div > 1) {
val /= div;
}
- if (pct)
- {
+ if (pct) {
val /= 100.0;
}
- }
- else
- {
+ } else {
var lit = _expr.Substring(_ptr, i);
val = ParseDouble(lit, _ci);
}
@@ -609,71 +539,62 @@
// 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;
+ char 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 != '_' && (_idChars == null || _idChars.IndexOf(c) < 0)) {
Throw("Identifier expected.");
}
// and must contain only letters/digits/_idChars
- for (i = 1; i + _ptr < _len; i++)
- {
+ for (i = 1; i + _ptr < _len; i++) {
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 != '_' && (_idChars == null || _idChars.IndexOf(c) < 0)) {
break;
}
}
@@ -682,67 +603,59 @@
var id = _expr.Substring(_ptr, i);
_ptr += i;
_token = new Token(id, TKID.ATOM, TKTYPE.IDENTIFIER);
- }
- static double ParseDouble(string str, CultureInfo ci)
- {
- if (str.Length > 0 && str[str.Length - 1] == ci.NumberFormat.PercentSymbol[0])
- {
+ }
+ 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);
}
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)
- {
+ {
+ // 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;
- }
-
- // check for empty Parameter list
- pos = _ptr;
- GetToken();
- if (_token.ID == TKID.CLOSE)
- {
return null;
}
- _ptr = pos;
- // get Parameters until we reach the end of the list
+ // check for empty Parameter list
+ pos = _ptr;
+ GetToken();
+ if (_token.ID == TKID.CLOSE) {
+ return null;
+ }
+ _ptr = pos;
+
+ // 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;
+ }
+ Token GetMember() {
// check whether next token is a MEMBER token ('.'),
// restore state and bail if it's not
var pos = _ptr;
var tk = _token;
GetToken();
- if (_token.ID != TKID.PERIOD)
- {
+ if (_token.ID != TKID.PERIOD) {
_ptr = pos;
_token = tk;
return null;
@@ -750,29 +663,26 @@
// skip member token
GetToken();
- if (_token.Type != TKTYPE.IDENTIFIER)
- {
+ if (_token.Type != TKTYPE.IDENTIFIER) {
Throw("Identifier expected");
}
return _token;
}
- #endregion
+ #endregion
- //---------------------------------------------------------------------------
- #region ** static helpers
+ //---------------------------------------------------------------------------
+ #region ** static helpers
- static void Throw()
- {
+ static void Throw() {
Throw("Syntax error.");
}
- static void Throw(string msg)
- {
+ static void Throw(string msg) {
throw new Exception(msg);
}
#endregion
- }
+ }
///
/// Delegate that represents CalcEngine functions.
diff --git a/ClosedXML/Excel/CalcEngine/Expression.cs b/ClosedXML/Excel/CalcEngine/Expression.cs
index 3e2c9bc..831db51 100644
--- a/ClosedXML/Excel/CalcEngine/Expression.cs
+++ b/ClosedXML/Excel/CalcEngine/Expression.cs
@@ -97,7 +97,7 @@
}
// handle nulls
- if (v == null || v is String)
+ if (v == null || v is string)
{
return 0;
}
diff --git a/ClosedXML/Excel/CalcEngine/Functions/Information.cs b/ClosedXML/Excel/CalcEngine/Functions/Information.cs
new file mode 100644
index 0000000..2af09f9
--- /dev/null
+++ b/ClosedXML/Excel/CalcEngine/Functions/Information.cs
@@ -0,0 +1,193 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+
+namespace ClosedXML.Excel.CalcEngine.Functions
+{
+ internal static class Information
+ {
+ public static void Register(CalcEngine ce)
+ {
+ //TODO: Add documentation
+ ce.RegisterFunction("ERRORTYPE",1,ErrorType);
+ ce.RegisterFunction("ISBLANK", 1,int.MaxValue, IsBlank);
+ ce.RegisterFunction("ISERR",1, int.MaxValue, IsErr);
+ ce.RegisterFunction("ISERROR",1, int.MaxValue, IsError);
+ ce.RegisterFunction("ISEVEN",1, IsEven);
+ ce.RegisterFunction("ISLOGICAL",1,int.MaxValue,IsLogical);
+ ce.RegisterFunction("ISNA",1, int.MaxValue, IsNa);
+ ce.RegisterFunction("ISNONTEXT",1, int.MaxValue, IsNonText);
+ ce.RegisterFunction("ISNUMBER",1, int.MaxValue, IsNumber);
+ ce.RegisterFunction("ISODD",1,IsOdd);
+ ce.RegisterFunction("ISREF",1, int.MaxValue, IsRef);
+ ce.RegisterFunction("ISTEXT", 1, int.MaxValue, IsText);
+ ce.RegisterFunction("N",1,N);
+ ce.RegisterFunction("NA",0,NA);
+ ce.RegisterFunction("TYPE",1,Type);
+ }
+
+ static object ErrorType(List p)
+ {
+ //TODO: Write Code
+ throw new NotSupportedException();;
+ }
+
+ static object IsBlank(List p)
+ {
+ var v = (string) p[0];
+ var isBlank = string.IsNullOrEmpty(v);
+
+
+ if (isBlank && p.Count > 1) {
+ var sublist = p.GetRange(1, p.Count);
+ isBlank = (bool)IsBlank(sublist);
+ }
+
+ return isBlank;
+ }
+
+ //TODO: Support for Error Values
+ static object IsErr(List p)
+ {
+ //TODO: Write Code
+ throw new NotSupportedException();
+ }
+
+ static object IsError(List p)
+ {
+ //TODO: Write Code
+ throw new NotSupportedException();
+ }
+
+ static object IsEven(List p)
+ {
+ var v = p[0].Evaluate();
+ if (v is double)
+ {
+ return Math.Abs((double) v%2) < 1;
+ }
+ //TODO: Error Exceptions
+ throw new ArgumentException("Expression doesn't evaluate to double");
+ }
+
+ static object IsLogical(List p)
+ {
+ var v = p[0].Evaluate();
+ var isLogical = v is bool;
+
+ if (isLogical && p.Count > 1)
+ {
+ var sublist = p.GetRange(1, p.Count);
+ isLogical = (bool) IsLogical(sublist);
+ }
+
+ return isLogical;
+ }
+
+ static object IsNa(List p)
+ {
+ //TODO: Write Code
+ throw new NotSupportedException();;
+ }
+
+ static object IsNonText(List p)
+ {
+ return !(bool) IsText(p);
+ }
+
+ static object IsNumber(List p)
+ {
+ var v = p[0].Evaluate();
+
+ var isNumber = v is double; //Normal number formatting
+ if (!isNumber)
+ {
+ isNumber = v is DateTime; //Handle DateTime Format
+ }
+ if (!isNumber)
+ {
+ //Handle Number Styles
+ try
+ {
+ var stringValue = (string) v;
+ double.Parse(stringValue.TrimEnd('%', ' '), NumberStyles.Any);
+ isNumber = true;
+ }
+ catch (Exception)
+ {
+ isNumber = false;
+ }
+ }
+
+ if (isNumber && p.Count > 1)
+ {
+ var sublist = p.GetRange(1, p.Count);
+ isNumber = (bool)IsNumber(sublist);
+ }
+
+ return isNumber;
+ }
+
+ static object IsOdd(List p)
+ {
+ return !(bool) IsEven(p);
+ }
+
+ static object IsRef(List p)
+ {
+ //TODO: Write Code
+ throw new NotSupportedException();;
+ }
+
+ static object IsText(List p)
+ {
+ //Evaluate Expressions
+ var isText = !(bool) IsBlank(p);
+ if (isText)
+ {
+ isText = !(bool) IsNumber(p);
+ }
+ if (isText)
+ {
+ isText = !(bool) IsLogical(p);
+ }
+ return isText;
+ }
+
+ static object N(List p)
+ {
+ return (double) p[0];
+ }
+
+ static object NA(List p)
+ {
+ //TODO: Write Code
+ throw new NotSupportedException();;
+ }
+
+ static object Type(List p)
+ {
+ if ((bool) IsNumber(p))
+ {
+ return 1;
+ }
+ if ((bool) IsText(p))
+ {
+ return 2;
+ }
+ if ((bool) IsLogical(p))
+ {
+ return 4;
+ }
+ if ((bool) IsError(p))
+ {
+ return 16;
+ }
+ if(p.Count > 1)
+ {
+ return 64;
+ }
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/ClosedXML/Excel/CalcEngine/Functions/Is.cs b/ClosedXML/Excel/CalcEngine/Functions/Is.cs
deleted file mode 100644
index 544f92e..0000000
--- a/ClosedXML/Excel/CalcEngine/Functions/Is.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System;
-using System.Diagnostics;
-using System.Collections.Generic;
-using System.Text;
-
-namespace ClosedXML.Excel.CalcEngine
-{
- internal static class Is
- {
- public static void Register(CalcEngine ce)
- {
- ce.RegisterFunction("ISBLANK", 1, IsBlank);
- }
-
- static object IsBlank(List p)
- {
- var v = (string)p[0];
- return String.IsNullOrEmpty(v);
- }
- }
-}
diff --git a/ClosedXML/Excel/CalcEngine/Functions/Logical.cs b/ClosedXML/Excel/CalcEngine/Functions/Logical.cs
index 1b6695e..18d2a9d 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
{
@@ -15,6 +13,7 @@
ce.RegisterFunction("IF", 2, 3, If);
ce.RegisterFunction("TRUE", 0, True);
ce.RegisterFunction("FALSE", 0, False);
+ ce.RegisterFunction("IFERROR",2,IfError);
}
static object And(List p)
@@ -22,41 +21,55 @@
var b = true;
foreach (var v in p)
{
- b = b && (bool)v;
+ b = b && v;
}
return b;
}
+
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)
{
- return !(bool)p[0];
+ return !p[0];
}
+
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)
{
return true;
}
+
static object False(List p)
{
return false;
}
+
+ static object IfError(List p)
+ {
+ try
+ {
+ return p[0].Evaluate();
+ }
+ catch (ArgumentException)
+ {
+ return p[1].Evaluate();
+ }
+ }
}
-}
+}
\ No newline at end of file
diff --git a/ClosedXML_Net3.5/ClosedXML_Net3.5.csproj b/ClosedXML_Net3.5/ClosedXML_Net3.5.csproj
index 56c7353..7cd14ba 100644
--- a/ClosedXML_Net3.5/ClosedXML_Net3.5.csproj
+++ b/ClosedXML_Net3.5/ClosedXML_Net3.5.csproj
@@ -112,8 +112,8 @@
Excel\CalcEngine\Functions\DateAndTime.cs
-
- Excel\CalcEngine\Functions\Is.cs
+
+ Excel\CalcEngine\Functions\Information.cs
Excel\CalcEngine\Functions\Logical.cs
diff --git a/ClosedXML_Net3.5/Excel/CalcEngine/Functions/Information.cs b/ClosedXML_Net3.5/Excel/CalcEngine/Functions/Information.cs
new file mode 100644
index 0000000..dcb1340
--- /dev/null
+++ b/ClosedXML_Net3.5/Excel/CalcEngine/Functions/Information.cs
@@ -0,0 +1,152 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing.Design;
+
+namespace ClosedXML.Excel.CalcEngine.Functions
+{
+ internal static class Information
+ {
+ public static void Register(CalcEngine ce)
+ {
+ ce.RegisterFunction("ERRORTYPE",1,ErrorType);
+ ce.RegisterFunction("ISBLANK", 1,int.MaxValue, IsBlank);
+ ce.RegisterFunction("ISERR",1, int.MaxValue, IsErr);
+ ce.RegisterFunction("ISERROR",1, int.MaxValue, IsError);
+ ce.RegisterFunction("ISEVEN",1, IsEven);
+ ce.RegisterFunction("ISLOGICAL",1,int.MaxValue,IsLogical);
+ ce.RegisterFunction("ISNA",1, int.MaxValue, IsNa);
+ ce.RegisterFunction("ISNONTEXT",1, int.MaxValue, IsNonText);
+ ce.RegisterFunction("ISNUMBER",1, int.MaxValue, IsNumber);
+ ce.RegisterFunction("ISODD",1,IsOdd);
+ ce.RegisterFunction("ISREF",1, int.MaxValue, IsRef);
+ ce.RegisterFunction("ISTEXT",1, int.MaxValue, IsText);
+ ce.RegisterFunction("N",1,N);
+ ce.RegisterFunction("NA",0,NA);
+ ce.RegisterFunction("TYPE",1,Type);
+ }
+
+ static object ErrorType(List p)
+ {
+ //TODO: Write Code
+ throw new NotSupportedException();;
+ }
+
+ static object IsBlank(List p)
+ {
+ var v = (string) p[0].Evaluate();
+ var isBlank = string.IsNullOrEmpty(v);
+ p.RemoveAt(0);
+
+ if (isBlank && p.Count > 0) {
+ isBlank = (bool)IsBlank(p);
+ }
+
+ return isBlank;
+ }
+
+ //TODO: Support for Error Values
+ static object IsErr(List p)
+ {
+ //TODO: Write Code
+ throw new NotSupportedException();
+ }
+
+ static object IsError(List p)
+ {
+ //TODO: Write Code
+ throw new NotSupportedException();
+ }
+
+ static object IsEven(List p)
+ {
+ var v = p[0].Evaluate();
+ if (v is double)
+ {
+ return Math.Abs((double) v%2) < 0;
+ }
+ throw new ArgumentException("Expression doesn't evaluate to double");
+ }
+
+ static object IsLogical(List p)
+ {
+ var v = p[0].Evaluate();
+ var isLogical = v is bool;
+ p.RemoveAt(0);
+
+ if (isLogical && p.Count > 0)
+ {
+ isLogical = (bool) IsLogical(p);
+ }
+
+ return isLogical;
+ }
+
+ static object IsNa(List p)
+ {
+ //TODO: Write Code
+ throw new NotSupportedException();;
+ }
+
+ static object IsNonText(List p)
+ {
+ return !(bool) IsText(p);
+ }
+
+ static object IsNumber(List p)
+ {
+ var v = p[0].Evaluate();
+ var isNumber = v is double;
+ p.RemoveAt(0);
+
+ if (isNumber && p.Count > 0) {
+ isNumber = (bool)IsNumber(p);
+ }
+
+ return isNumber;
+ }
+
+ static object IsOdd(List p)
+ {
+ return !(bool) IsEven(p);
+ }
+
+ static object IsRef(List p)
+ {
+ //TODO: Write Code
+ throw new NotSupportedException();;
+ }
+
+ static object IsText(List p)
+ {
+ //Evaluate Expressions
+ var isText = !(bool) IsBlank(p);
+ if (isText)
+ {
+ isText = !(bool) IsNumber(p);
+ }
+ if (isText)
+ {
+ isText = !(bool) IsLogical(p);
+ }
+ return isText;
+ }
+
+ static object N(List p)
+ {
+ //TODO: Write Code
+ throw new NotSupportedException();;
+ }
+
+ static object NA(List p)
+ {
+ //TODO: Write Code
+ throw new NotSupportedException();;
+ }
+
+ static object Type(List p)
+ {
+ //TODO: Write Code
+ throw new NotSupportedException();;
+ }
+ }
+}
\ No newline at end of file
diff --git a/ClosedXML_Tests/ClosedXML_Tests.csproj b/ClosedXML_Tests/ClosedXML_Tests.csproj
index aba9560..7ebc711 100644
--- a/ClosedXML_Tests/ClosedXML_Tests.csproj
+++ b/ClosedXML_Tests/ClosedXML_Tests.csproj
@@ -70,7 +70,7 @@
-
+
diff --git a/ClosedXML_Tests/Excel/CalcEngine/InformationTests.cs b/ClosedXML_Tests/Excel/CalcEngine/InformationTests.cs
new file mode 100644
index 0000000..2bb46a3
--- /dev/null
+++ b/ClosedXML_Tests/Excel/CalcEngine/InformationTests.cs
@@ -0,0 +1,331 @@
+using System;
+using System.Runtime.InteropServices;
+using ClosedXML.Excel;
+using NUnit.Framework;
+
+namespace ClosedXML_Tests.Excel.CalcEngine
+{
+
+ [TestFixture]
+ public class InformationTests
+ {
+
+ #region IsBlank Tests
+ [Test]
+ public void IsBlank_Single_true()
+ {
+ var ws = new XLWorkbook().AddWorksheet("Sheet");
+ var actual = ws.Evaluate("=IsBlank(A1)");
+ Assert.AreEqual(true, actual);
+ }
+
+ [Test]
+ public void IsBlank_Single_false()
+ {
+ var ws = new XLWorkbook().AddWorksheet("Sheet");
+ ws.Cell("A1").Value = " ";
+ var actual = ws.Evaluate("=IsBlank(A1)");
+ Assert.AreEqual(false, actual);
+ }
+
+ [Test]
+ public void IsBlank_MultipleAllEmpty_true()
+ {
+ var ws = new XLWorkbook().AddWorksheet("Sheet");
+ var actual = ws.Evaluate("=IsBlank(A1:A3)");
+ Assert.AreEqual(true,actual);
+
+ }
+
+ [Test]
+ public void IsBlank_MultipleAllFill_false()
+ {
+ var ws = new XLWorkbook().AddWorksheet("Sheet");
+ ws.Cell("A1").Value = "1";
+ ws.Cell("A2").Value = "1";
+ ws.Cell("A3").Value = "1";
+ var actual = ws.Evaluate("=IsBlank(A1:A3)");
+ Assert.AreEqual(false,actual);
+ }
+
+ [Test]
+ public void IsBlank_MultipleMixedFill_false()
+ {
+ var ws = new XLWorkbook().AddWorksheet("Sheet");
+ ws.Cell("A1").Value = "1";
+ ws.Cell("A3").Value = "1";
+ var actual = ws.Evaluate("=IsBlank(A1:A3)");
+ Assert.AreEqual(false, actual);
+ }
+ #endregion
+
+ #region IsEven Tests
+
+ [Test]
+ public void IsEven_Single_False()
+ {
+ var ws = new XLWorkbook().AddWorksheet("Sheet");
+
+ ws.Cell("A1").Value = 1;
+ ws.Cell("A2").Value = 1.2;
+ ws.Cell("A3").Value = 3;
+
+ var actual = ws.Evaluate("=IsEven(A1)");
+ Assert.AreEqual(false,actual);
+
+ actual = ws.Evaluate("=IsEven(A2)");
+ Assert.AreEqual(false, actual);
+
+ actual = ws.Evaluate("=IsEven(A3)");
+ Assert.AreEqual(false, actual);
+ }
+
+ [Test]
+ public void IsEven_Single_True()
+ {
+ var ws = new XLWorkbook().AddWorksheet("Sheet");
+
+ ws.Cell("A1").Value = 4;
+ ws.Cell("A2").Value = 0.2;
+ ws.Cell("A3").Value = 12.2;
+
+ var actual = ws.Evaluate("=IsEven(A1)");
+ Assert.AreEqual(true, actual);
+
+ actual = ws.Evaluate("=IsEven(A2)");
+ Assert.AreEqual(true, actual);
+
+ actual = ws.Evaluate("=IsEven(A3)");
+ Assert.AreEqual(true, actual);
+ }
+ #endregion
+
+ #region IsLogical Tests
+
+ [Test]
+ public void IsLogical_Simple_True()
+ {
+ var ws = new XLWorkbook().AddWorksheet("Sheet");
+
+ ws.Cell("A1").Value = true;
+
+ var actual = ws.Evaluate("=IsLogical(A1)");
+ Assert.AreEqual(true, actual);
+ }
+
+ [Test]
+ public void IsLogical_Simpe_False()
+ {
+ var ws = new XLWorkbook().AddWorksheet("Sheet");
+
+ ws.Cell("A1").Value = 123;
+
+ var actual = ws.Evaluate("=IsLogical(A1)");
+ Assert.AreEqual(false, actual);
+ }
+ #endregion
+
+ #region IsNotText Tests
+
+ [Test]
+ public void IsNotText_Simple_true()
+ {
+ var ws = new XLWorkbook().AddWorksheet("Sheet");
+ ws.Cell("A1").Value = "123"; //Double Value
+ ws.Cell("A2").Value = DateTime.Now; //Date Value
+ ws.Cell("A3").Value = "12,235.5"; //Coma Formatting
+ ws.Cell("A4").Value = "$12,235.5"; //Currency Value
+ ws.Cell("A5").Value = true; //Bool Value
+ ws.Cell("A6").Value = "12%"; //Percentage Value
+
+ var actual = ws.Evaluate("=IsNonText(A1)");
+ Assert.AreEqual(true, actual);
+ actual = ws.Evaluate("=IsNonText(A2)");
+ Assert.AreEqual(true, actual);
+ actual = ws.Evaluate("=IsNonText(A3)");
+ Assert.AreEqual(true, actual);
+ actual = ws.Evaluate("=IsNonText(A4)");
+ Assert.AreEqual(true, actual);
+ actual = ws.Evaluate("=IsNonText(A5)");
+ Assert.AreEqual(true, actual);
+ actual = ws.Evaluate("=IsNonText(A6)");
+ Assert.AreEqual(true, actual);
+ }
+
+ [Test]
+ public void IsNotText_Simple_false()
+ {
+ var ws = new XLWorkbook().AddWorksheet("Sheet");
+ ws.Cell("A1").Value = "asd";
+ var actual = ws.Evaluate("=IsNonText(A1)");
+ Assert.AreEqual(false, actual);
+ }
+
+ #endregion
+
+ #region IsNumber Tests
+
+ [Test]
+ public void IsNumber_Simple_true()
+ {
+ var ws = new XLWorkbook().AddWorksheet("Sheet");
+ ws.Cell("A1").Value = "123"; //Double Value
+ ws.Cell("A2").Value = DateTime.Now; //Date Value
+ ws.Cell("A3").Value = "12,235.5"; //Coma Formatting
+ ws.Cell("A4").Value = "$12,235.5"; //Currency Value
+ ws.Cell("A5").Value = "12%"; //Percentage Value
+
+ var actual = ws.Evaluate("=IsNumber(A1)");
+ Assert.AreEqual(true, actual);
+ actual = ws.Evaluate("=IsNumber(A2)");
+ Assert.AreEqual(true, actual);
+ actual = ws.Evaluate("=IsNumber(A3)");
+ Assert.AreEqual(true, actual);
+ actual = ws.Evaluate("=IsNumber(A4)");
+ Assert.AreEqual(true, actual);
+ actual = ws.Evaluate("=IsNumber(A5)");
+ Assert.AreEqual(true, actual);
+ }
+
+ [Test]
+ public void IsNumber_Simple_false()
+ {
+ var ws = new XLWorkbook().AddWorksheet("Sheet");
+ ws.Cell("A1").Value = "asd"; //String Value
+ ws.Cell("A2").Value = true; //Bool Value
+
+ var actual = ws.Evaluate("=IsNumber(A1)");
+ Assert.AreEqual(false, actual);
+ actual = ws.Evaluate("=IsNumber(A2)");
+ Assert.AreEqual(false, actual);
+ }
+ #endregion
+
+ #region IsOdd Test
+
+ [Test]
+ public void IsOdd_Simple_true()
+ {
+ var ws = new XLWorkbook().AddWorksheet("Sheet");
+
+ ws.Cell("A1").Value = 1;
+ ws.Cell("A2").Value = 1.2;
+ ws.Cell("A3").Value = 3;
+
+ var actual = ws.Evaluate("=IsOdd(A1)");
+ Assert.AreEqual(true, actual);
+ actual = ws.Evaluate("=IsOdd(A2)");
+ Assert.AreEqual(true, actual);
+ actual = ws.Evaluate("=IsOdd(A3)");
+ Assert.AreEqual(true, actual);
+ }
+
+ [Test]
+ public void IsOdd_Simple_false()
+ {
+ var ws = new XLWorkbook().AddWorksheet("Sheet");
+
+ ws.Cell("A1").Value = 4;
+ ws.Cell("A2").Value = 0.2;
+ ws.Cell("A3").Value = 12.2;
+
+ var actual = ws.Evaluate("=IsOdd(A1)");
+ Assert.AreEqual(false, actual);
+ actual = ws.Evaluate("=IsOdd(A2)");
+ Assert.AreEqual(false, actual);
+ actual = ws.Evaluate("=IsOdd(A3)");
+ Assert.AreEqual(false, actual);
+ }
+ #endregion
+
+ #region IsText Tests
+ [Test]
+ public void IsText_Simple_true()
+ {
+ var ws = new XLWorkbook().AddWorksheet("Sheet");
+
+ ws.Cell("A1").Value = "asd";
+
+ var actual = ws.Evaluate("=IsText(A1)");
+ Assert.AreEqual(true, actual);
+ }
+
+ [Test]
+ public void IsText_Simple_false()
+ {
+ var ws = new XLWorkbook().AddWorksheet("Sheet");
+ ws.Cell("A1").Value = "123"; //Double Value
+ ws.Cell("A2").Value = DateTime.Now; //Date Value
+ ws.Cell("A3").Value = "12,235.5"; //Coma Formatting
+ ws.Cell("A4").Value = "$12,235.5"; //Currency Value
+ ws.Cell("A5").Value = true; //Bool Value
+ ws.Cell("A6").Value = "12%"; //Percentage Value
+
+ var actual = ws.Evaluate("=IsText(A1)");
+ Assert.AreEqual(false, actual);
+ actual = ws.Evaluate("=IsText(A2)");
+ Assert.AreEqual(false, actual);
+ actual = ws.Evaluate("=IsText(A3)");
+ Assert.AreEqual(false, actual);
+ actual = ws.Evaluate("=IsText(A4)");
+ Assert.AreEqual(false, actual);
+ actual = ws.Evaluate("=IsText(A5)");
+ Assert.AreEqual(false, actual);
+ actual = ws.Evaluate("=IsText(A6)");
+ Assert.AreEqual(false, actual);
+ }
+ #endregion
+
+ #region N Tests
+
+ [Test]
+ public void N_Date_SerialNumber()
+ {
+ var ws = new XLWorkbook().AddWorksheet("Sheet");
+ var testedDate = DateTime.Now;
+ ws.Cell("A1").Value = testedDate;
+ var actual = ws.Evaluate("=N(A1)");
+ Assert.AreEqual(testedDate.ToOADate(),actual);
+ }
+
+ [Test]
+ public void N_Number_Number()
+ {
+ var ws = new XLWorkbook().AddWorksheet("Sheet");
+ var testedValue = 123;
+ ws.Cell("A1").Value = testedValue;
+ var actual = ws.Evaluate("=N(A1)");
+ Assert.AreEqual(testedValue, actual);
+ }
+
+ [Test]
+ public void N_String_Zero()
+ {
+ var ws = new XLWorkbook().AddWorksheet("Sheet");
+ ws.Cell("A1").Value = "asd";
+ var actual = ws.Evaluate("=N(A1)");
+ Assert.AreEqual(0, actual);
+ }
+
+ [Test]
+ public void N_True_One()
+ {
+ var ws = new XLWorkbook().AddWorksheet("Sheet");
+ ws.Cell("A1").Value = true;
+ var actual = ws.Evaluate("=N(A1)");
+ Assert.AreEqual(1, actual);
+ }
+
+ [Test]
+ public void N_False_Zero()
+ {
+ var ws = new XLWorkbook().AddWorksheet("Sheet");
+ ws.Cell("A1").Value = false;
+ var actual = ws.Evaluate("=N(A1)");
+ Assert.AreEqual(0, actual);
+ }
+ #endregion
+
+
+ }
+}
\ No newline at end of file
diff --git a/ClosedXML_Tests/Excel/CalcEngine/IsTests.cs b/ClosedXML_Tests/Excel/CalcEngine/IsTests.cs
deleted file mode 100644
index 2d98c68..0000000
--- a/ClosedXML_Tests/Excel/CalcEngine/IsTests.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using System;
-using ClosedXML.Excel;
-using NUnit.Framework;
-
-namespace ClosedXML_Tests.Excel.CalcEngine
-{
-
- [TestFixture]
- public class IsTests
- {
- [Test]
- public void IsBlank_true()
- {
- var ws = new XLWorkbook().AddWorksheet("Sheet");
- var actual = ws.Evaluate("=IsBlank(A1)");
- Assert.AreEqual(true, actual);
- }
-
- [Test]
- public void IsBlank_false()
- {
- var ws = new XLWorkbook().AddWorksheet("Sheet");
- ws.Cell("A1").Value = " ";
- var actual = ws.Evaluate("=IsBlank(A1)");
- Assert.AreEqual(false, actual);
- }
- }
-}
\ No newline at end of file