Table of Contents

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

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.