Expr 0.1.0
dotnet add package Expr --version 0.1.0
NuGet\Install-Package Expr -Version 0.1.0
<PackageReference Include="Expr" Version="0.1.0" />
<PackageVersion Include="Expr" Version="0.1.0" />
<PackageReference Include="Expr" />
paket add Expr --version 0.1.0
#r "nuget: Expr, 0.1.0"
#:package Expr@0.1.0
#addin nuget:?package=Expr&version=0.1.0
#tool nuget:?package=Expr&version=0.1.0
Expr for .NET
Expr is a safe, statically checked expression language for modern .NET. Embed user-authored rules, filters, policies, and computed values without compiling or executing arbitrary C#.
- Compile once and evaluate concurrently with isolated runtime state.
- Validate names, types, functions, and return values before execution.
- Work with an immutable, public AST that can be walked, printed, and rewritten.
- Bound evaluation with work, memory, collection, stack, regex, and cancellation limits.
- Run on Native AOT and trimmed applications with reflection-free environment schemas.
- Ship a single library with no third-party runtime dependencies.
Expr targets .NET 10 and C# 14.
Install
dotnet add package Expr
Quick start
For a one-off expression with no host environment:
using Expr;
object? result = ExprEngine.Evaluate("all([2, 3, 5], # > 0)");
// result is true
For repeated evaluation, describe the values visible to the expression, compile
once, and reuse the resulting CompiledExpression:
using System.Collections.Generic;
using Expr;
using Expr.Configuration;
using Expr.Runtime;
using Expr.Types;
var schema = new ExprEnvironmentSchemaBuilder<OrderContext>()
.Member("customer", static value => value.Customer, ExprTypes.String)
.ArrayMember("prices", static value => value.Prices, ExprTypes.Float)
.Build();
ExprConfiguration configuration = ExprConfiguration.Default
.WithEnvironment(schema)
.WithExpectedType(ExprTypes.Boolean);
CompiledExpression expression = ExprEngine.Compile(
"customer == 'Ada' && sum(prices) >= 100.0",
configuration);
bool accepted = (bool)expression.Run(
new OrderContext("Ada", [45.0, 60.0]))!;
// accepted is true
public sealed record OrderContext(
string Customer,
IReadOnlyList<double> Prices);
The schema is strict: misspelled names and invalid operations fail during
Compile, not in a later production evaluation. Explicit schemas are also safe
for Native AOT and trimming. Conventional applications can instead create a
cached reflection-based schema with ExprEnvironmentSchema.Reflect<T>().
Add application functions
Functions declare their Expr-visible signature independently of their runtime implementation, so calls remain statically checked:
using System;
using Expr;
using Expr.Configuration;
using Expr.Runtime;
using Expr.Types;
var isPreferred = new ExprFunction(
"isPreferred",
[new ExprFunctionOverload([ExprTypes.String], ExprTypes.Boolean)],
static arguments => ((string)arguments[0]!).StartsWith("vip-", StringComparison.Ordinal));
ExprConfiguration configuration = ExprConfiguration.Default
.WithFunction(isPreferred);
CompiledExpression expression = ExprEngine.Compile(
"isPreferred('vip-123')",
configuration);
Inspect and adapt the AST
Parsing, traversal, canonical printing, rewriting, static checking, compilation, and execution are all public APIs:
using System;
using Expr;
using Expr.Syntax;
SyntaxTree tree = ExprEngine.Parse("price * quantity");
foreach (SyntaxNode node in SyntaxWalker.Traverse(tree.Root))
{
Console.WriteLine(node.GetType().Name);
}
SyntaxNode rewritten = new RenamePrice().Visit(tree.Root);
Console.WriteLine(SyntaxPrinter.Print(rewritten));
// unitPrice * quantity
sealed class RenamePrice : SyntaxRewriter
{
protected override SyntaxNode VisitNode(SyntaxNode node) =>
node is IdentifierNode { Name: "price" } identifier
? identifier with { Name = "unitPrice" }
: node;
}
CompiledExpression exposes its checked SyntaxTree, SemanticModel, and
immutable bytecode Program for integrations that need more than evaluation.
Bound untrusted evaluations
Every invocation accepts a cancellation token and independent runtime budgets:
using System;
using Expr.Execution;
object? result = expression.Run(
environment,
new ExprEvaluationOptions
{
WorkBudget = 100_000,
MemoryBudget = 1_000_000,
MaximumCollectionLength = 10_000,
RegularExpressionTimeout = TimeSpan.FromMilliseconds(100),
},
cancellationToken);
See the security model before accepting expressions from an untrusted boundary.
Where to go next
- Expr language definition covers literals, operators, collections, predicates, and built-ins.
- Native AOT sample demonstrates a fully reflection-free schema and explicit evaluation budgets.
- Compatibility policy describes language and .NET integration behavior.
- Security model and security review document the trust boundary and deployment contract.
- Architecture explains the parser-to-bytecode pipeline.
- Benchmarks documents the reproducible performance suite.
The project is pre-1.0 while its public API matures. Compatibility with the Expr language is continuously checked against its upstream test corpus.
Build and contribute
dotnet restore expr-dotnet.slnx
dotnet format expr-dotnet.slnx --verify-no-changes --no-restore
dotnet build expr-dotnet.slnx --configuration Release --no-restore
dotnet test expr-dotnet.slnx --configuration Release --no-build --no-restore
See CONTRIBUTING.md for the development workflow. Maintainers can find the publication process in docs/releasing.md.
License
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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. |
-
net10.0
- No dependencies.
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 |
|---|---|---|
| 0.1.0 | 102 | 8/10/2026 |
| 0.1.0-alpha.1 | 50 | 8/10/2026 |