NTDLS.ExpressionParser 1.6.0

dotnet add package NTDLS.ExpressionParser --version 1.6.0
                    
NuGet\Install-Package NTDLS.ExpressionParser -Version 1.6.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="NTDLS.ExpressionParser" Version="1.6.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="NTDLS.ExpressionParser" Version="1.6.0" />
                    
Directory.Packages.props
<PackageReference Include="NTDLS.ExpressionParser" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add NTDLS.ExpressionParser --version 1.6.0
                    
#r "nuget: NTDLS.ExpressionParser, 1.6.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package NTDLS.ExpressionParser@1.6.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=NTDLS.ExpressionParser&version=1.6.0
                    
Install as a Cake Addin
#tool nuget:?package=NTDLS.ExpressionParser&version=1.6.0
                    
Install as a Cake Tool

NTDLS.ExpressionParser

📦 Be sure to check out the NuGet package: https://www.nuget.org/packages/NTDLS.ExpressionParser

ExpressionParser is a mathematics parsing engine for .NET. It supports expression nesting, custom variables, custom functions and all standard mathematical operations for integer, decimal (floating point), logic and bitwise.

🔥 Expressions are compiled once and then evaluated without any allocation. A 5 operation expression with variables evaluates in ~41 ns (≈ 24M evaluations/s, ≈ 120M operations/s per core) - about 8× faster than NCalc. See Performance.

In addition to custom functions and variables, out of the box it supports: abs, acos, asin, atan, atan2, avg, ceil, clamp, cos, cosh, count, deg, e, exp, floor, hypot, if, log, log10, logn, max, min, modpow, not, pi, pow, prod, rad, rand, round, sign, sin, sinh, sqrt, sum, tan, tanh, trunc.

Regression Tests

👀 If you came for the C++ version you can find it at: https://github.com/NTDLS/CMathParser

Basic usage:

Simple Example:

In this example we simply call the static function Expression.Evaluate to compute the string expression.

var result = Expression.Evaluate("10 * ((1000 / 5 + (10 * 11)))");
Console.WriteLine($"{result:n2}"); //3,100.00

Simple Example (with work):

In this example we also supply an output parameter, which the parser uses to explain each operation in the order it was performed.

var result = Expression.Evaluate("10 * ((1000 / 5 + (10 * 11)))", out var explanation);

Console.WriteLine($"{result:n2}");
Console.WriteLine(explanation);
3,100.00
{
    1000/5 = 200
    10*11 = 110
    200+110 = 310
    10*310 = 3100
} = 3100

Advanced Example:

In this example we will create an expression that uses two built in functions "Ceil" and "Sum", a custom function called "DoStuff" and one variable called "extra". Names are not case sensitive.

var expression = new Expression("10 * ((5 + extra + DoStuff(11,55) + ( 10 + !0 )) * Ceil(SUM(11.6, 12.5, 14.7, 11.11)) + 60.5) * 10");

//Set a value for the variable called "extra".
expression.SetParameter("extra", 1000);

//Handler for the custom function:
expression.AddFunction("DoStuff", (double[] parameters) =>
{
    double sum = 0;
    foreach (var parameter in parameters)
    {
        sum += parameter;
    }
    return sum;
});

var result = expression.Evaluate();

Console.WriteLine($"{result:n2}"); //5,416,050.00

Reusing an Expression:

An expression is parsed once, when it is constructed. To evaluate it many times, keep the instance and change its parameters - this is the fastest way to use the parser.

var expression = new Expression("price * qty * (1 - discount)");

foreach (var order in orders)
{
    expression.SetParameter("price", order.Price);
    expression.SetParameter("qty", order.Quantity);
    expression.SetParameter("discount", order.Discount);

    Console.WriteLine(expression.Evaluate());
}

Operators

Listed from highest to lowest precedence. Operators on the same row are evaluated left to right. Use parentheses to override.

Operators Description
-x +x !x ~x Negation, unary plus, logical NOT, bitwise NOT
* / % Multiplication, division, modulus
+ - Addition, subtraction
<< >> Bitwise shift
< <= > >= Comparison
= == != <> Equality (= and == are the same, as are != and <>)
& Bitwise AND
^ Bitwise XOR
\| Bitwise OR
&& Logical AND
\|\| Logical OR
  • Comparison and logical operators return 1 (true) or 0 (false). Any non-zero value is true.
  • Bitwise operators (~ << >> & ^ |) truncate their operands to 32 bit integers, e.g. 7.9 | 0 is 7.
  • &=, ^= and |= are accepted as aliases for &, ^ and |.
  • Consecutive signs are allowed, e.g. 1 - -2 is 3.

Functions

Function Description
abs(x) sign(x) Absolute value, sign (-1, 0 or 1)
ceil(x) floor(x) trunc(x) round(x) round(x, digits) Rounding
sqrt(x) pow(x, y) exp(x) modpow(base, exponent, modulus) Powers and roots
log(x) log10(x) logn(x, base) Logarithms
sin cos tan asin acos atan sinh cosh tanh (one parameter), atan2(y, x) Trigonometry, in radians
deg(radians) rad(degrees) Angle conversion
min(...) max(...) sum(...) avg(...) prod(...) count(...) hypot(...) Aggregates of one or more values (count also accepts none)
clamp(x, min, max) Limits x to the range min..max
if(condition, whenTrue, whenFalse) Returns whenTrue if condition is non-zero, otherwise whenFalse
not(x) Logical NOT, same as !x
pi() e() Constants
rand() Random number from 0 to 1

Custom functions are added with AddFunction and receive their parameters as a double[]. A built in function can not be replaced by a custom function of the same name.

Nulls

The keyword null can be used in expressions and variables can be set to null.

  • An operation with a null operand results in null, e.g. null + 1 is null.
  • A function with a null parameter results in null, and a custom function is not called.
  • Set ExpressionOptions.DefaultNullValue to use a value in place of null - for null literals, variables set to null, and custom functions that return null.
  • Expression.EvaluateNotNull returns 0 for a null result, and can report whether the result was null.
Expression.Evaluate("null + 1");                                                      //null
Expression.Evaluate("null + 1", new ExpressionOptions { DefaultNullValue = 0 });      //1
Expression.EvaluateNotNull("null + 1", out bool wasNull);                             //0, wasNull = true

Options

Pass an ExpressionOptions to the constructor or to the static methods.

Option Default Description
UseCompileCache true Caches each compiled expression, so constructing another Expression with the same text skips parsing. Entries expire after 5 minutes unused.
DefaultNullValue null The value used in place of null (see Nulls).
Precision 17 Significant digits used to format numbers when showing the work. Calculations always use full double precision.
UseFastFloatingPointParser true Uses a faster parser for number literals. It is correctly rounded, falling back to double.Parse for numbers it can not parse exactly.
CustomHash null A key to cache the compiled expression under, instead of its text. Only reuse a key for identical expression text.

Errors

Errors are raised as exceptions:

  • When constructing an Expression: malformed expressions, e.g. Syntax error: missing operand at end of expression at position 2 of '2+'. The position refers to the expression after whitespace is removed, which the message includes.
  • When evaluating: undefined variables or functions, the wrong number of parameters for a built in function, division or modulus by zero, and operators that produce an infinite or NaN result.

Built in functions follow System.Math, so for example sqrt(-1) returns NaN rather than raising an error.

Performance

Expressions are compiled to a compact program when constructed. Evaluating it does not allocate, and:

  • Parts of an expression that do not depend on variables are computed once, when compiled (rand() excluded). An expression without variables evaluates in a few nanoseconds.
  • Variables are resolved by SetParameter, so Evaluate never looks a name up.
  • Compiled expressions are cached (see UseCompileCache), so even the static Expression.Evaluate(text) only parses an expression the first time it sees it.
  • OperationCount reports how many operations an expression performs, e.g. for calculating operations per second.

Measured with the PerfTest project in this repository - 10 * ((a + 1000 + ( b )) * c) * 10, 5 operations with 3 variables, evaluated 100,000 times per run, against NCalcSync 7.2.0 on .NET 10:

Avg per 100,000 Per evaluation Operations/μs
NTDLS.ExpressionParser 4.07 ms ~41 ns ~123
NCalc 32.74 - 36.04 ms ~330 - 360 ns ~14 - 15

Thread safety

  • The static methods (Expression.Evaluate, Expression.EvaluateNotNull) are thread safe.
  • Separate Expression instances can be used on separate threads, including for the same expression text - the compile cache and the compiled expressions in it are thread safe.
  • A single instance can be evaluated (Evaluate, including with show work) from multiple threads at once, as long as nothing modifies it at the same time.
  • Modifying an instance (SetParameter, RemoveParameter, ClearParameters, AddFunction, RemoveFunction, ClearFunctions) is not thread safe: it must not happen at the same time as any other use of that instance.

Parameters belong to the instance, so to evaluate with different values on different threads, use one instance per thread.

License

MIT

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.6.0 0 10/7/2026
1.5.9 118 9/5/2026
1.5.8 249 5/18/2026
1.5.7 136 5/18/2026
1.5.6 138 5/18/2026
1.5.5 137 5/16/2026
1.5.4 588 11/13/2025
1.5.3 305 10/30/2025
1.5.2 300 10/30/2025
1.5.1 313 10/28/2025
1.5.0 309 10/28/2025
1.4.2 301 10/27/2025
1.4.1 299 10/27/2025
1.4.0 292 10/26/2025
1.3.2 250 10/26/2025
1.3.1 256 10/26/2025
1.3.0 248 10/26/2025
1.2.0 223 10/25/2025
1.1.5 331 12/31/2024
1.1.4 306 10/3/2024
Loading failed

Compiled expression optimizations.