NCalc: a .NET expression evaluator that grew a parser, a lambda compiler and a package split
NCalc is a fast and lightweight expression evaluator library for .NET, designed for flexibility and high performance. It supports a wide range of mathematical and logical operations.
At a glance
- What is it?
- NCalc parses and evaluates mathematical and logical expressions with literals, operators, parameters, built-in functions and custom functions, and the current release line adds compiled lambdas, plugins and DI. The interesting engineering decision is that the parser is now Parlot, and the old package names are redirect shims to a consolidated core.
- Who is it for?
- NCalc fits a .NET application that needs to let a user or a configuration file express a formula, a rule, or a threshold, and that needs the same expression language in a UI, a service and a test. It does not fit a project that wants to sandbox untrusted expressions, since the language exposes built-in functions and the evaluation is not described as a security boundary, and it does not fit a team that only needs arithmetic on doubles and would rather call a compiled method.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly C#, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The expression language, and the four things it accepts
NCalc is described as a fast, lightweight expression evaluator for .NET that parses and evaluates mathematical and logical expressions with literals, operators, parameters, built-in functions, and custom functions.
That sentence is the API surface, and each of the four things in it is a distinct extension point.
Literals and operators are the base language. Parameters are the substitution mechanism, and they use square brackets so that a name in an expression is unambiguously a parameter rather than a function call. Built-in functions cover the maths you would expect. Custom functions are the extension point, and the mechanism is a dictionary on the expression object where the key is the function name and the value is a delegate receiving the argument list.
The quick example shows the base case.
var result = new Expression("2 + 3 * 5").Evaluate();
// 17And the parameter example shows how a formula becomes a function of inputs, with a Round and a Pow composed around two parameters.
var expression = new Expression("Round(Pow([x], 2) + [offset], 2)");
expression.Parameters["x"] = 3.14;
expression.Parameters["offset"] = 10;The custom function example is the one worth reading twice, because the arguments are not plain values. The delegate receives an args collection and calls args.Evaluate(0) or args.Evaluate(1) to get each argument, which means the arguments arrive as expressions you evaluate, not as numbers you read. That is what lets a custom function take an expression as an argument and compose, and it is also why the delegate signature is more involved than a normal two argument function.
The documentation covers a wider set: data types, operators, built-in functions, async evaluation, caching, expression serialization, and advanced configuration.
Three package names that are now redirects to one core
The install section contains an important note that is easy to skim, because it is set in a callout rather than in the package list.
NCalc, NCalcSync and NCalcAsync are redirect packages that currently point directly to NCalc.Core, preserving compatibility from before the community efforts were consolidated into the same repository.
So there are two package identities here, and the second is the real one. NCalc.Core is what everything resolves to. NCalc is what most people install. The other two exist so that code written against the old split keeps working.
That is a normal and careful way to consolidate, but it has a practical consequence for anyone reading the documentation. If you search for NCalc documentation, you will find material written when the project was split into sync and async variants, and the version of the API described there may not be the API you have after installing the package. The repository layout reflects the consolidation: a src/ directory, a single solution file NCalc.slnx, Directory.Build.props and Directory.Packages.props for centralised build and package settings, a global.json pinning the SDK, version.json for the versioning data, and a NCalc.snk signing key.
The versioning is done with Nerdbank.GitVersioning, and the stated benefit is that each library build can be traced back to the original git commit. That is the right mechanism for a library distributed as NuGet packages where the version number is the only thing a consumer sees.
Parlot for the parser, FastExpressionCompiler for the lambda
The related projects table is where the architecture becomes visible, because NCalc names the two libraries it builds on and why.
Parlot is described as fast and lightweight parser creation tools by Sebastien Ros, and NCalc uses Parlot in its default parser. That is a significant choice. A hand written recursive descent parser gives you full control and a fair amount of work; a parser generator library gives you a grammar and the code to handle it, and the result is usually both smaller and faster than what you would write by hand.
The other dependency is FastExpressionCompiler, described as a fast compiler for C# expression trees, which NCalc uses for lambda compilation. The two together are the shape of the library. Parlot handles turning text into something the evaluator can walk, and FastExpressionCompiler turns the expression into a .NET expression tree that the runtime can compile into a delegate.
And the two features are optional, which is the point. The lambda compilation is a separate NuGet package.
dotnet add package NCalc.LambdaCompilation
dotnet add package NCalc.DependencyInjectionSo the default install evaluates an expression by walking it, and you opt into compiling it when you need a delegate. The example in the README makes the difference concrete.
using NCalc.LambdaCompilation;
var expression = new Expression("1 + 2");
Func<int> function = expression.ToLambda<int>();Evaluating an expression every time you call it and compiling it once and calling a delegate are different performance profiles, and the fact that the compiled path is a separate package with its own dependency is the library telling you that honestly rather than making compilation the default and the interpretation the exception.
Targeting three framework generations at once
NCalc targets modern .NET, .NET Standard 2.0, and .NET Framework 4.8. Three target frameworks, and they are not arbitrary.
Modern .NET is where new work goes. .NET Standard 2.0 is the compatibility floor that lets the library be consumed from .NET Framework 4.6.1 and later, and from Mono and Xamarin style targets. .NET Framework 4.8 is the last version of the old framework, and shipping it means a library that still supports applications which will never be migrated.
The interesting consequence is not the target list, it is the async story. The library has separate NCalcSync and NCalcAsync package identities, which existed as a genuine split in the codebase before the consolidation, and the documentation covers async evaluation as a first class feature. Both are now redirect packages, so the modern install gets one engine that can do both.
For an adopter, the practical question is which of your target frameworks the expression evaluation has to run on. If it has to run inside an old .NET Framework application on a Windows Server, the 4.8 target is what makes the library usable at all. If everything is on current .NET, the older targets cost nothing but they also do not buy you anything.
The repository also carries a SECURITY.md, a CODE_OF_CONDUCT.md, a CONTRIBUTING.md, an AGENTS.md and a CHANGELOG.md, and the test/ directory with a Directory.Build.props at the root, so the build configuration is centralised across the project rather than repeated per project.
A browser playground, and a documentation set split by concern
Before you install anything, the project offers a way to try it. The NCalc playground runs expressions directly in your browser powered by Blazor WASM.
That is a small thing and a useful one, because an expression evaluator has a syntax you can only learn by trying. The alternative is writing a console app, adding a package and a rebuild, and the feedback loop is bad enough that people guess at the syntax instead. A page in the browser that evaluates what you type removes that whole step, and it also tells you the project intends the library to be usable outside .NET server applications, since the playground is a WASM build.
The documentation is organised by what you are trying to do rather than by API surface, and the four entry points are the language reference, the evaluation options, the runtime features and extensibility and plugins. That split is a reasonable signal of the four questions a user has: what can I write, how do I make it evaluate the way I want, what does the runtime do, and how do I extend it.
The extensibility and plugins page is worth noting separately. It means the library has a plugin concept beyond custom functions, and a plugin concept in an expression evaluator raises the question of what a plugin can reach. The documentation is the place to check that, and the project does not make claims in the README about sandboxing.
For bug reports the project asks for something specific: open an issue with the expression, the expected result, the actual result, and your NCalc version. That is a reasonable ask for a parser, since the version determines the grammar and the defaults, and reproducing an expression bug with the version stated is what makes it fixable.
What NCalc is the wrong tool for
Two cases, and the first is the one that catches people out.
The first is untrusted expressions. NCalc is an expression evaluator for .NET, and .NET expressions in general, and the language includes built-in functions and a plugin mechanism. The project does not describe the evaluation as a sandbox, and a language that can call functions is a language that can call whatever the host has exposed. If your users are untrusted, an expression evaluator is a code execution surface and you need an allowlist of what the expression may reach, which is a design task you own rather than a setting you turn on. For a configuration value typed by an administrator, the same library is entirely appropriate, and the difference between the two cases is about who can type, not about the library.
The second is when you do not need a language. If the formula is a constant, or there are two or three of them and their shape is fixed, a compiled method with ordinary arguments is faster to write, faster to run and easier to test. An expression engine earns its cost when the set of formulas is open ended and authored by someone other than the developer, or when the formula is stored in data and edited without a rebuild.
There is a third, quieter case. The library targets .NET Framework 4.8 as well as modern .NET, and the ecosystem around it has been consolidating, with the old package names now redirecting to a core. A team starting fresh is adopting a codebase that has been through at least one restructuring, and the documentation is being rewritten alongside the code. That is a normal state for a mature library and not a fault, but it does mean the older articles and Stack Overflow answers about NCalc may describe an API you no longer get. Check the version before you copy an answer.
Editorial conclusion
NCalc fits a .NET application that needs to let a user or a configuration file express a formula, a rule, or a threshold, and that needs the same expression language in a UI, a service and a test. It does not fit a project that wants to sandbox untrusted expressions, since the language exposes built-in functions and the evaluation is not described as a security boundary, and it does not fit a team that only needs arithmetic on doubles and would rather call a compiled method. Check three things before you commit. Check which package you actually need, because NCalc, NCalcSync and NCalcAsync are redirect packages that currently point at NCalc.Core, and the optional features live in separate packages such as NCalc.LambdaCompilation and NCalc.DependencyInjection. Check that your target framework is covered, since the library targets modern .NET, .NET Standard 2.0 and .NET Framework 4.8. And check whether you need the compiled path at all, because evaluating an expression and compiling it to a delegate are different costs and different risks, and the lambda compilation is an optional package for a reason. The last push was on 2026-10-13, the newest release listed is v7.2.0 from 2026-09-13, and the license is MIT.
Frequently asked questions
How do I install NCalc in a .NET project?
Use dotnet add package NCalc for the expression engine. The optional packages are added separately, with dotnet add package NCalc.LambdaCompilation for compiled lambdas and dotnet add package NCalc.DependencyInjection for the DI integration.
What is the difference between the NCalc, NCalcSync and NCalcAsync packages?
All three are redirect packages that currently point directly to NCalc.Core, preserving compatibility from before the community efforts were consolidated into the same repository. The separate identities existed when the project was split into sync and async variants.
How do I add a custom function to an NCalc expression?
You assign a delegate into the expression's Functions dictionary under the function name, and the delegate receives the argument list where each argument is retrieved by evaluating it, for example args.Evaluate(0) and args.Evaluate(1).
How do I compile an NCalc expression to a delegate?
Install the NCalc.LambdaCompilation package, import NCalc.LambdaCompilation, and call expression.ToLambda<int>() on the expression, which returns a Func<int> you can invoke.
Which target frameworks does NCalc support?
NCalc targets modern .NET, .NET Standard 2.0, and .NET Framework 4.8. The related projects table notes that NCalc uses the Parlot parser in its default parser and FastExpressionCompiler for lambda compilation.
What license is NCalc released under?
MIT, with a LICENSE file at the root of the repository. The newest listed release is v7.2.0 from 2026-09-13, and the last push was on 2026-10-13.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/ncalc-ncalc)Was this useful?