GameDevWare.Dynamic.Expressions 2.2.5

C# expression parsing and execution library.
Designed for AOT runtimes (Unity's IL2CPP, .NET Native, Mono AOT).

Install-Package GameDevWare.Dynamic.Expressions -Version 2.2.5
dotnet add package GameDevWare.Dynamic.Expressions --version 2.2.5
<PackageReference Include="GameDevWare.Dynamic.Expressions" Version="2.2.5" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add GameDevWare.Dynamic.Expressions --version 2.2.5
The NuGet Team does not provide support for this client. Please contact its maintainers for support.

Introduction

Attention! This is a paid package, you can use it anywhere if you purchased it.

This package provides the API for parsing and expression execution written in C#. It is specially designed to work with the Unity on various platforms. Since it is written in C# 3.5 and has no additional dependencies, it should work with any version of Unity (and .NET framework).

It is tested to work on:

  • IOS
  • Android
  • WebGL
  • PC/Mac

It should work on any other platforms. For AOT execution platforms (iOS, WebGL) a link.xml should be added to project's root directory. Read more about IL code stripping in official documentation.

API

  • CSharpExpression
    • Evaluate
    • Parse
  • AotCompilation
    • RegisterFunc
    • RegisterForFastCall (optional stuff)

Example

Parsing C# expression into System.Linq.Expression.Expression[T]:

var mathExpr = "Math.Max(x, y)";
var exprTree = CSharpExpression.Parse<double, double, double>(mathExpr, arg1Name: "x", arg2Name: "y") 
// exprTree -> Expression<Func<double, double, double>>

Evaluating C# expression:

var arifExpr = "2 * (2 + 3) << 1 + 1 & 7 | 25 ^ 10";
var result = CSharpExpression.Evaluate<int>(arifExpr); 
// result -> 19

Parser

The parser recognizes the C# 4 grammar only. It includes:

Nullable types are supported.
Generics are supported.
Enumerations are supported.
Type inference is not available and your should always specify generic parameters on types and methods.

Known Types

For security reasons the parser does not provide access to any types except:

  • argument types
  • primitive types
  • Math, Array, Func<> (up to 4 arguments) types

To access other types your should pass typeResolver parameter in Parse and Evaluate method:

var typeResolver = new KnownTypeResolver(typeof(Mathf), typeof(Time));
CSharpExpression.Evaluate<int>("Mathf.Clamp(Time.time, 1.0f, 3.0f)", typeResolver); 

If you want to access all types in UnityEngine you can pass custom AssemblyTypeResolver as typeResolver parameter.

var typeResolver = new AssemblyTypeResolver(typeof(UnityEngine.Application).Assembly);

For security reasons any member invocation on System.Type will throw exceptions until System.Type is added as known type.

AOT Execution

You can compile and evaluate expression created by System.Linq.Expression and execute it in AOT environment where it is usually impossible.

var expr = (Expression<Func<Vector3>>)(() => new Vector3(1.0f, 1.0f, 1.0f));
var fn = expr.CompileAot();

fn; // -> Func<Vector3>
fn(); // -> Vector3(1.0f, 1.0f, 1.0f)

iOS, WebGL and most console platforms use AOT compilation which imposes following restrictions on the dynamic code execution:

  • only Expression&lt;Func&lt;...&gt;&gt; could be used with CompileAot() and Lambda types
  • only static methods using primitives (int, float, string, object ...) are optimized for fast calls
  • all used classes/methods/properties should be visible to Unity's static code analyser
  • :grey_exclamation: An additional preparation should be made for AOT execution platforms. This link.xml should be added in project's root folder. Read more about IL code stripping in official documentation.

See Also

WebGL and iOS

  • Only Func<> (up to 4 arguments) Lambdas are supported
  • Instance methods invocation performs slowly due reflection
  • Moderate boxing for value types (see roadmap)

You can ensure that your generic Func<> pass AOT compilation by registering it with AotCompilation.RegisterFunc

AotCompilation.RegisterFunc<int, bool>(); // will enable Func<int, bool> lambdas anywhere in expressions

Improving Performance

You can improve the performance of methods invocation by registering their signatures in AotCompilation.RegisterForFastCall().

// Supports up to 3 arguments.
// First generic argument is your class type.
// Last generic argument is return type.

AotCompilation.RegisterForFastCall<InstanceT, ResultT>()
AotCompilation.RegisterForFastCall<InstanceT, Arg1T, ResultT>()
AotCompilation.RegisterForFastCall<InstanceT, Arg1T, Arg2T, ResultT>()
AotCompilation.RegisterForFastCall<InstanceT, Arg1T, Arg2T, Arg3T, ResultT>()

Example:

public class MyVectorMath
{
    public Vector4 Dot(Vector4 vector, Vector4 vector);
    public Vector4 Cross(Vector4 vector, Vector4 vector);
    public Vector4 Scale(Vector4 vector, float scale);    
}

// register Dot and Cross method signatures
AotCompilation.RegisterForFastCall<MyVectorMath, Vector4, Vector4, Vector4>();
// register Scale method signature
AotCompilation.RegisterForFastCall<MyVectorMath, Vector4, float, Vector4>();

Introduction

Attention! This is a paid package, you can use it anywhere if you purchased it.

This package provides the API for parsing and expression execution written in C#. It is specially designed to work with the Unity on various platforms. Since it is written in C# 3.5 and has no additional dependencies, it should work with any version of Unity (and .NET framework).

It is tested to work on:

  • IOS
  • Android
  • WebGL
  • PC/Mac

It should work on any other platforms. For AOT execution platforms (iOS, WebGL) a link.xml should be added to project's root directory. Read more about IL code stripping in official documentation.

API

  • CSharpExpression
    • Evaluate
    • Parse
  • AotCompilation
    • RegisterFunc
    • RegisterForFastCall (optional stuff)

Example

Parsing C# expression into System.Linq.Expression.Expression[T]:

var mathExpr = "Math.Max(x, y)";
var exprTree = CSharpExpression.Parse<double, double, double>(mathExpr, arg1Name: "x", arg2Name: "y") 
// exprTree -> Expression<Func<double, double, double>>

Evaluating C# expression:

var arifExpr = "2 * (2 + 3) << 1 + 1 & 7 | 25 ^ 10";
var result = CSharpExpression.Evaluate<int>(arifExpr); 
// result -> 19

Parser

The parser recognizes the C# 4 grammar only. It includes:

Nullable types are supported.
Generics are supported.
Enumerations are supported.
Type inference is not available and your should always specify generic parameters on types and methods.

Known Types

For security reasons the parser does not provide access to any types except:

  • argument types
  • primitive types
  • Math, Array, Func<> (up to 4 arguments) types

To access other types your should pass typeResolver parameter in Parse and Evaluate method:

var typeResolver = new KnownTypeResolver(typeof(Mathf), typeof(Time));
CSharpExpression.Evaluate<int>("Mathf.Clamp(Time.time, 1.0f, 3.0f)", typeResolver); 

If you want to access all types in UnityEngine you can pass custom AssemblyTypeResolver as typeResolver parameter.

var typeResolver = new AssemblyTypeResolver(typeof(UnityEngine.Application).Assembly);

For security reasons any member invocation on System.Type will throw exceptions until System.Type is added as known type.

AOT Execution

You can compile and evaluate expression created by System.Linq.Expression and execute it in AOT environment where it is usually impossible.

var expr = (Expression<Func<Vector3>>)(() => new Vector3(1.0f, 1.0f, 1.0f));
var fn = expr.CompileAot();

fn; // -> Func<Vector3>
fn(); // -> Vector3(1.0f, 1.0f, 1.0f)

iOS, WebGL and most console platforms use AOT compilation which imposes following restrictions on the dynamic code execution:

  • only Expression&lt;Func&lt;...&gt;&gt; could be used with CompileAot() and Lambda types
  • only static methods using primitives (int, float, string, object ...) are optimized for fast calls
  • all used classes/methods/properties should be visible to Unity's static code analyser
  • :grey_exclamation: An additional preparation should be made for AOT execution platforms. This link.xml should be added in project's root folder. Read more about IL code stripping in official documentation.

See Also

WebGL and iOS

  • Only Func<> (up to 4 arguments) Lambdas are supported
  • Instance methods invocation performs slowly due reflection
  • Moderate boxing for value types (see roadmap)

You can ensure that your generic Func<> pass AOT compilation by registering it with AotCompilation.RegisterFunc

AotCompilation.RegisterFunc<int, bool>(); // will enable Func<int, bool> lambdas anywhere in expressions

Improving Performance

You can improve the performance of methods invocation by registering their signatures in AotCompilation.RegisterForFastCall().

// Supports up to 3 arguments.
// First generic argument is your class type.
// Last generic argument is return type.

AotCompilation.RegisterForFastCall<InstanceT, ResultT>()
AotCompilation.RegisterForFastCall<InstanceT, Arg1T, ResultT>()
AotCompilation.RegisterForFastCall<InstanceT, Arg1T, Arg2T, ResultT>()
AotCompilation.RegisterForFastCall<InstanceT, Arg1T, Arg2T, Arg3T, ResultT>()

Example:

public class MyVectorMath
{
    public Vector4 Dot(Vector4 vector, Vector4 vector);
    public Vector4 Cross(Vector4 vector, Vector4 vector);
    public Vector4 Scale(Vector4 vector, float scale);    
}

// register Dot and Cross method signatures
AotCompilation.RegisterForFastCall<MyVectorMath, Vector4, Vector4, Vector4>();
// register Scale method signature
AotCompilation.RegisterForFastCall<MyVectorMath, Vector4, float, Vector4>();

Release Notes

# 2.2.5
renamed PropertyOfFieldBinder to MemberBinder
changed 'propertyOrFieldName' attribute to 'name' in SyntaxTreeNode
changed 'PropertyOrField' expression type to 'MemberResolve' in SyntaxTreeNode
added backward compatibility checks in all related classes
renamed ParseTreeNode.Lexeme to .Token
renamed few member of TokenType for better clarity
added documentation file in Unity project assets

  • .NETCoreApp 2.0

    • No dependencies.
  • .NETFramework 3.5

    • No dependencies.
  • .NETFramework 4.5

    • No dependencies.
  • .NETStandard 1.3

Version History

Version Downloads Last updated
2.2.5 170 4/20/2019
2.2.4 246 9/3/2018
2.2.2 286 5/16/2018
2.2.1 389 12/19/2017
2.2.0 388 11/30/2017
2.1.4 311 10/22/2017
1.0.1.10 488 11/18/2016