omy.Utils.Expressions.CSyntax 2.0.0-rc.1

This is a prerelease version of omy.Utils.Expressions.CSyntax.
dotnet add package omy.Utils.Expressions.CSyntax --version 2.0.0-rc.1
                    
NuGet\Install-Package omy.Utils.Expressions.CSyntax -Version 2.0.0-rc.1
                    
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="omy.Utils.Expressions.CSyntax" Version="2.0.0-rc.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="omy.Utils.Expressions.CSyntax" Version="2.0.0-rc.1" />
                    
Directory.Packages.props
<PackageReference Include="omy.Utils.Expressions.CSyntax" />
                    
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 omy.Utils.Expressions.CSyntax --version 2.0.0-rc.1
                    
#r "nuget: omy.Utils.Expressions.CSyntax, 2.0.0-rc.1"
                    
#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 omy.Utils.Expressions.CSyntax@2.0.0-rc.1
                    
#: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=omy.Utils.Expressions.CSyntax&version=2.0.0-rc.1&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=omy.Utils.Expressions.CSyntax&version=2.0.0-rc.1&prerelease
                    
Install as a Cake Tool

Utils.Expressions.CSyntax

Utils.Expressions.CSyntax provides a C-like expression compiler that targets LINQ expression trees (System.Linq.Expressions).

The main component is CSyntaxExpressionCompiler in the Utils.Expressions.CSyntax.Runtime namespace.

What it is for

  • Compile textual expressions into .NET Expression trees.
  • Execute dynamic expressions with a symbol context.
  • Declare functions and reuse them in the same source.
  • Share one context instance across multiple expression compilers.
  • Persist runtime symbols (values and static callables) to/from streams.

Installation

This package's first publication is the 2.0.0-rc.1 release candidate - there is no earlier stable version, so dotnet add package requires an explicit version (NuGet does not install a prerelease by default):

dotnet add package omy.Utils.Expressions.CSyntax --version 2.0.0-rc.1

Examples

1) Arithmetic one-liner

using System.Linq.Expressions;
using Utils.Expressions.CSyntax.Runtime;

var compiler = new CSyntaxExpressionCompiler();
Expression expression = compiler.Compile("1 + 2 * 3");
var lambda = Expression.Lambda<Func<double>>(Expression.Convert(expression, typeof(double))).Compile();

double result = lambda(); // 7

2) Boolean one-liner

using System.Linq.Expressions;
using Utils.Expressions.CSyntax.Runtime;

var compiler = new CSyntaxExpressionCompiler();
Expression expression = compiler.Compile("3 > 1 && 4 <= 4");
var lambda = Expression.Lambda<Func<bool>>(Expression.Convert(expression, typeof(bool))).Compile();

bool result = lambda(); // true

3) Compile with symbols

using System.Linq.Expressions;
using Utils.Expressions.CSyntax.Runtime;

var compiler = new CSyntaxExpressionCompiler();
ParameterExpression x = Expression.Parameter(typeof(double), "x");

Expression expression = compiler.Compile("x * 2 + 1", new Dictionary<string, Expression>
{
    ["x"] = x,
});

var lambda = Expression.Lambda<Func<double, double>>(Expression.Convert(expression, typeof(double)), x).Compile();

double result = lambda(4); // 9

4) Compile a strongly typed lambda (one-liner)

using Utils.Expressions.CSyntax.Runtime;

var compiler = new CSyntaxExpressionCompiler();
Expression<Func<int, int>> expression = compiler.Compile<Func<int, int>>("(x) => x + 1");
Func<int, int> function = expression.Compile();

int result = function(41); // 42

5) Functions declared in source

using Utils.Expressions.CSyntax.Runtime;

var compiler = new CSyntaxExpressionCompiler();
var context = new ExpressionCompilerContext();

compiler.CompileSource(
    """
    public double add(double a, double b) { a + b; }
    public double twice(double x) { add(x, x); }
    """,
    context);

if (!context.TryGet("twice", out object? twiceSymbol) || twiceSymbol is not Func<double, double> twice)
{
    throw new InvalidOperationException("Unable to resolve function 'twice'.");
}

double result = twice(5d); // 10

6) Lambda in context + invocation

using System.Linq.Expressions;
using Utils.Expressions.CSyntax.Runtime;

var compiler = new CSyntaxExpressionCompiler();
var context = new ExpressionCompilerContext();
context.Set("increment", (Func<double, double>)(x => x + 1));

Expression expression = compiler.Compile("increment(41)", context);
var lambda = Expression.Lambda<Func<double>>(Expression.Convert(expression, typeof(double))).Compile();

double result = lambda(); // 42

7) Standard control flow: if / else

using Utils.Expressions.CSyntax.Runtime;

var compiler = new CSyntaxExpressionCompiler();
var context = new ExpressionCompilerContext();

compiler.CompileSource(
    """
    public double abs(double x)
    {
        if (x >= 0) x
        else -x
    }
    """,
    context);

if (!context.TryGet("abs", out object? absSymbol) || absSymbol is not Func<double, double> abs)
{
    throw new InvalidOperationException("Unable to resolve function 'abs'.");
}

double a = abs(3);   // 3
double b = abs(-3);  // 3

8) Standard control flow: for

using Utils.Expressions.CSyntax.Runtime;

var compiler = new CSyntaxExpressionCompiler();
var context = new ExpressionCompilerContext();

compiler.CompileSource(
    """
    public double sumTo(int n)
    {
        int i = 0;
        double sum = 0;
        for (i = 1; i <= n; i = i + 1) sum = sum + i;
        sum
    }
    """,
    context);

if (!context.TryGet("sumTo", out object? sumToSymbol) || sumToSymbol is not Func<int, double> sumTo)
{
    throw new InvalidOperationException("Unable to resolve function 'sumTo'.");
}

double result = sumTo(4); // 10

9) Standard control flow: foreach

using Utils.Expressions.CSyntax.Runtime;

var compiler = new CSyntaxExpressionCompiler();
var context = new ExpressionCompilerContext();
context.Set("values", new[] { 1, 2, 3, 4 });

compiler.CompileSource(
    """
    public int sumValues()
    {
        int sum = 0;
        foreach (int item in values) sum = sum + item;
        sum
    }
    """,
    context);

if (!context.TryGet("sumValues", out object? sumValuesSymbol) || sumValuesSymbol is not Func<int> sumValues)
{
    throw new InvalidOperationException("Unable to resolve function 'sumValues'.");
}

int result = sumValues(); // 10

10) Persist and restore a shared context

using Utils.Expressions;
using Utils.Expressions.CSyntax.Runtime;

var compiler = new CSyntaxExpressionCompiler();
var context = new ExpressionCompilerContext();
context.Set("add", (Func<int, int, int>)((a, b) => a + b));
compiler.CompileSource("public int twice(int x) { add(x, x); }", context);

using MemoryStream stream = new();
context.WriteToStream(stream);
stream.Position = 0;

ExpressionCompilerContext restored = ExpressionCompilerContext.ReadFromStream(stream);
Expression expression = compiler.Compile("twice(21)", restored);
int result = Expression.Lambda<Func<int>>(Expression.Convert(expression, typeof(int))).Compile()(); // 42

Notes

  • The compiler accepts C-like syntax (arithmetic operations, blocks, if, for, foreach, functions, etc.).
  • The final expression type depends on context and generated LINQ conversions.
  • For advanced scenarios, use ExpressionCompilerContext to register symbols, overloaded callables, and persisted runtime values.
  • Unused variable elimination — local variable declarations that are never read are silently removed from the compiled block. If the initializer expression has observable side effects (e.g. a method call), the initializer is kept as a standalone statement and the variable itself is dropped. Pure initializers (constants, parameter references) are discarded entirely.

Versioned API documentation

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 was computed.  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 was computed.  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
2.0.0-rc.1 97 8/28/2026