Intro
NCalc is a mathematical expression evaluator in .NET. NCalc can parse any expression or a group of expressions and evaluate the result, including static or dynamic parameters and custom functions.
Table of Contents
- Operators: Available standard operators and structures.
- Values: Authorized values like types and functions.
- Advanced Value Formats: Advanced Value Formats and Operations
- Functions: List of already implemented functions.
- Parameters/Variables: How to use parameters (variables) in expressions.
- Handling Errors: How to handle errors.
- Case Sensitivity: Options in how to handle case sensitivity.
- Overflow Handling: How to handle overflow with binary arithmetic operations
- Async Support: How and when to use
async. - Caching: How caching works.
- Improve performance: How to use compilation of expressions to CLR lambdas.
- Dependency Injection: Bring expressions to the next level with dependency injection.
- Architecture: Check this article to learn how NCalc works.
- Benchmarks: Check some numbers about the speed of some NCalc components.
NCalc versions
Allied Bits NCalc comes in two versions - regular and AOT-compatible. The second can be used in AOT-enabled applications. Both versions are included in the same package, and the AOT-compatible assembly is named "NCalc.AOT".
Functionalities
Expressions and statements
Allied Bits NCalc handles expressions which include variable assignment statements (which are also expressions as they return the assigned value) and groups of expressions. Expressions in a group are separated with a semicolon. One or several expressions may be wrapped with curly braces '{' and '}' to make a single expression, which is useful when a group of expressions should be treated as one expression (e.g., in function parameters). To enable expression sequences, include the UseStatementSequences flag in ExpressionOptions passed when creating an instance of the Expression or AsyncExpression class:
var expression = new NCalc.Expression("{ 1; 2 }", ExpressionOptions.UseStatementSequences);
Simple Expressions
var expression = new Expression("2 + 3 * 5");
Debug.Assert(17 == expression.Evaluate());
Conditional Statements
Allied Bits NCalc supports if statements with a common C-style syntax. To enable them, include the UseIfStatement flag in ExpressionOptions passed when creating an instance of the Expression or AsyncExpression class:
var expression = new NCalc.Expression("a = 1; if (a < 5) { a += 1; }; a", ExpressionOptions.UseIfStatement | ExpressionOptions.UseAssignments | ExpressionOptions.UseCStyleAssignments | ExpressionOptions.UseStatementSequences);
Loops
Allied Bits NCalc supports while loops with a common C-style syntax.
To enable them, include the UseLoops flag in ExpressionOptions passed when creating an instance of the Expression or AsyncExpression class:
var expression = new NCalc.Expression("a = 1; while (a < 5) { a += 1; }", ExpressionOptions.UseLoops | ExpressionOptions.UseAssignments | ExpressionOptions.UseCStyleAssignments | ExpressionOptions.UseStatementSequences);
Loop flow control
You can use break and continue keywords in loops; they do the same job as in C/C++ and C#.
Returning of Value
An expression always evaluates to some value. In complex expressions which include conditional statements and loops, it may be desired to return a value without evaluating the expression completely. For this, one can use a common return keyword followed by the value to return.
.NET Data Types
Debug.Assert(123456 == new Expression("123456").Evaluate()); // integers
Debug.Assert(new DateTime(2001, 01, 01) == new Expression("#01/01/2001#").Evaluate()); // datetime
Debug.Assert(123.456 == new Expression("123.456").Evaluate()); // floating point numbers
Debug.Assert(true == new Expression("true").Evaluate()); // booleans
Debug.Assert("azerty" == new Expression("'azerty'").Evaluate()); // strings
Mathematical functional from System.Math**
Debug.Assert(0 == new Expression("Sin(0)").Evaluate());
Debug.Assert(2 == new Expression("Sqrt(4)").Evaluate());
Debug.Assert(0 == new Expression("Tan(0)").Evaluate());
Custom Functions
var expression = new Expression("SecretOperation(3, 6)");
expression.Functions["SecretOperation"] = (args) => {
return (int)args[0].Evaluate() + (int)args[1].Evaluate();
};
Debug.Assert(9 == expression.Evaluate());
Unicode Characters
Debug.Assert("経済協力開発機構" == new Expression("'経済協力開発機構'").Evaluate());
Debug.Assert("Hello" == new Expression(@"'\u0048\u0065\u006C\u006C\u006F'").Evaluate());
Debug.Assert("だ" == new Expression(@"'\u3060'").Evaluate());
Debug.Assert("\u0100" == new Expression(@"'\u0100'").Evaluate());
Parameters (variables) - Static and Dynamic
var expression = new Expression("Round(Pow([Pi], 2) + Pow([Pi2], 2) + [X], 2)");
expression.Parameters["Pi2"] = new Expression("Pi * [Pi]");
expression.Parameters["X"] = 10;
expression.DynamicParameters["Pi"] = _ => {
Console.WriteLine("I'm evaluating π!");
return 3.14;
};
Debug.Assert(117.07 == expression.Evaluate());
Lambda Expressions
var expression = new Expression("1 + 2");
Func<int> function = expression.ToLambda<int>();
Debug.Assert(function()); //3
Comments
Allied Bits NCalc supports line and block C-style comments and Python line comments. To enable them, include the SupportCStyleComments or SupportPythonComments flag in ExpressionOptions passed when creating an instance of the Expression or AsyncExpression class:
var expression = new NCalc.Expression("{ 1 /* this produces 1 as a result */ }", ExpressionOptions.SupportCStyleComments | ExpressionOptions.SupportPythonComments);
Note that when C-style comments are enabled, the "//" operator used in Python for integer division, cannot be used for this purpose. If C-style comments are disabled, this "//" integer division operator is supported.
Recursive and non-recursive evaluation
By default, the evaluator works by recursively traversing the expression tree. Note that even linear expressions like "1+2+...+99+100" would be parsed into a tree with plenty of nodes (about 200 nodes in the given example). This can cause a StackOverflow error. If you need to evaluate such long or deeply nested expressions, you can enable the non-recursive evaluator by setting the UseNonRecursiveEvaluator flag in ExpressionOptions passed when creating an instance of the Expression or AsyncExpression class. This non-recursive evaluator is slower than the recursive one, so it is disabled by default.