Coder Social home page Coder Social logo

deniszykov / csharp-eval-unity3d Goto Github PK

View Code? Open in Web Editor NEW
195.0 11.0 24.0 2.1 MB

C# Expression Parser for Unity3D

License: Other

C# 99.03% TypeScript 0.97%
unity unity3d unity3d-plugin unity-asset unity-scripts ast eval expression-evaluator expression-parser

csharp-eval-unity3d's Introduction

Actions Status

Licensing

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

Introduction

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, IL2CPP) 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:

  • types used in arguments
  • primitive types
  • Math, Array, Func<> 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 method/property calls 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<Func<...>> delegate type could be used with CompileAot().
  • only static methods using primitives (int, float, string, object ...) are optimized for fast calls, others are called with reflection.
  • all used classes/methods/properties should be visible to Unity's static code analyser
  • ⚠️ 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 with CompileAot().
  • Instance methods calls performs slowly due reflection
  • Moderate boxing for value types (see roadmap)

You can prepare Func<> lambda for AOT compilation/execution by registering it with AotCompilation.RegisterFunc.

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

Unity 2020.3.2 Issues

This version of Unity has runtime bug related to lambda compilation. Please use following workaround for IL2CPP/AOT runtime:

#if ((UNITY_WEBGL || UNITY_IOS || ENABLE_IL2CPP) && !UNITY_EDITOR)
GameDevWare.Dynamic.Expressions.AotCompilation.IsAotRuntime = true;
#endif

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>();

Roadmap

You can send suggestions at [email protected]

  • Expression serialization (in-progress)
  • Void expressions (System.Action delegates) (done)
  • Parser: Delegate construction from method reference
  • Parser: Type inference for generics
  • Parser: Full C#6 syntax
  • Parser: Extension methods
  • Parser: Type initializers, List initializers
  • Custom editor with auto-completion for Unity

Changes

2.3.0

  • fix: fixed netcore related error with enumerable.empty
  • feature: added optional global parameter to all CSharpExpression methods to allow specify global object for expression.
  • test: removed flaky test with double.tostring() comparison
  • fix: fixed typo in arg4Name in CSharpExpression.ParseFunc{4} and CSharpExpression.ParseAction{4}

2.2.9

  • fix: fixed error with instantiated generic method on types (which is impossible in normal conditions, but fine for Unity AOT runtime).

2.2.8

  • feature:made AotCompilation.IsAotRuntime is mutable, this will allow to signal for AOT runtime and suppress further checks.

2.2.7

  • feature: added public CSharpExpression.Format method for SyntaxTreeNode

2.2.6

  • changed order or SyntaxTreeNode fields and added "original C# expression" field to parsed AST.
  • refactored C# expression rendering to support null-propagation expressions, type aliases (int, byte, object ...),
  • renamed "Render" methods to "FormatAsCSharp". Now it is "formatting"
  • moved c# "formatting" methods to CSharpExpression class
  • mark old "Parse" functions as errors
  • mark old "Render" methods as obsolete
  • renamed CSharpExpressionFormatter to CSharpExpressionFormatter
  • fixed indexer experssion rendering
  • refactored NameUtils to properly render C# type names

2.2.5

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

2.2.4

  • added protection against wrong expressions like 'a b' which later bound as 'b'
  • fixed some tokenization errors:
    • 'issa'scanned as 'is'[Operator] and 'sa'[Identifier], now as 'issa'
    • '.09' scanned as '.'[Operator] and '09'[Number], now as '0.09'
    • '0.1x' scanned as '0.1'[Number] and 'x'[Identifier], now cause error
  • added method calls on numbers (example 1.ToString())
  • added short number notation (example '.9' for '0.9')
  • added '@' prefix for identifiers (example '@is') https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/verbatim
  • done small Tokenizer optimization (reduced string allocation during scanning)

2.2.3

  • added ExpressionPacker type. This type allows packing/unpacking expressions into primitive structures (Dictionaries, Arrays...). These structures could be serialized and wired by network or stored for future use.
  • added better error message for some binding cases
  • denying call to 'Type.InvokeMember' if 'Type' is not within 'known types'.

2.2.2

  • fixed conditional operator (a ? b : c) parsing with method call in place of 'b'

2.2.1

  • fixed IL2CPP compilation error due _Attribute interface complilation failure
  • added few interfaces to AOT.cs file for better AOT coverage

2.2.0

Features

  • added support for void expressions (Action<> delegates)
  • added support of '.NET Standart 1.3' and '.NET Core 2.0' platforms

2.1.4

  • Release version, no actual changes except readme.md

2.1.2-rc

Features

  • added more descriptive message for member binding error
  • added autodoc comments for public members
  • hid ReadOnlyDictionary from public access
  • removed Unity WebGL #if for unsigned types. Unity's bug was fixed.
  • added support for generic types and generic methods
  • added nullable types and ? suffix support
CSharpExpression.Evaluate<int?>("default(int?)"); // -> null
  • added lambda expressions. Syntax is () => x and new Func(a => x)
  • added support for expression's parameter re-mapping with lambda expression syntax:
CSharpExpression.Evaluate<int, int, int>("(x,y) => x + y", 2, 2); // -> 4
  • added support for Func<> lambdas for AOT environments
  • added additional constructor to Binder class
public Binder(Type lambdaType, ITypeResolver typeResolver = null);
  • added ArgumentsTree.ToString() method
  • added aliases for build-in types. Aliases resolved during binding phase inside Binder.Bind() method.
CSharpExpression.Evaluate<int>("int.MaxValue");

Bugs

  • fixed error with wrongly resolved types (only by name) in KnownTypeResolver
  • fixed bug with ACCESS_VIOLATION on iOS (Unity 5.x.x IL2CPP)
  • fixed few Unity 3.4 related errors in code
  • fixed 'new' expression parsed with error on chained calls new a().b().c()
  • fixed some cases of lifted binary/unary/conversion operations
  • fixed some AOT'ed operations on System.Boolean type
  • fixed null-propagation chains generate invalid code
  • fixed some edge cases of resolving nested generic types
  • fixed error with types without type.FullName value
  • fixed Condition operator types promotion
  • fixed Power operator types promotion and null-lifting
  • fixed enum constants threated as underlying types during binary/unary operations

Breaking changes in 2.0

  • ParserNode renamed to ParseTreeNode
  • ExpressionTree renamed to SyntaxTreeNode
  • ExpressionBuilder renamed to Binder
  • ITypeResolutionService renamed to ITypeResolver
  • ITypeResolver.GetType removed
  • ITypeResolver now could be configured with TypeDiscoveryOptions

1.0.1.11

  • fixed error them creating nullable types via "new" keyword
  • fixed Embedded Resource addressing problem on IL2CPP WebGL (localized error messages)
  • fixed some cases of nullable types binding
  • fixed Enum member resolution
  • added Power(**) operator into C# syntax
CSharpExpression.Evaluate<int>("2 ** 2"); // -> 4
  • added TypeResolutionService chaining for better KnownTypes re-use

1.0.1.10

  • fixed error with nulled constants after first call for AOT-ted expression.
  • added ToString() impl for ExpressionTree

1.0.1.9

  • added Null-conditional Operators (example: a.?b.?.c)
  • fixed array indexing expressions
  • added array types support in type expressions ('convert', 'typeof', 'is' etc.)

Installation

Nuget:

Install-Package GameDevWare.Dynamic.Expressions

Unity:

Package at Unity Asset Store

Contacts

Please send any questions at [email protected]

License

Asset Store Terms of Service and EULA

csharp-eval-unity3d's People

Contributors

deniszykov avatar yellowisher avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

csharp-eval-unity3d's Issues

error when export variable name start with "as" or "is"

I use KnownTypeResolver for export variable to Expression.
When the export variable name start with "as" or "is" such as "S.aso"
I will throw exception "ExpressionParserException: A 'Resolve' operator requires a second operand."
Wrong:
S.aso
S.asp
S.iso
S.isp
Right
S.oas
S.ois
Maybe some error occur when parse key word "is" and "as".

CSharpExpression.Execute doesn't recognize passed-in known types

Now I have created a custom class with a public static method:

using UnityEngine;

public class GFunc
{
    public static void Foo()
    {
        Debug.Log("Foo!");
    }
}

And I have already passed the type of GFunc through the typeResolve parameter to CSharpExpression.Execute:

KnownTypeResolver typeResolver = new KnownTypeResolver(typeof(GFunc));
CSharpExpression.Execute("GFunc.Foo()", typeResolver);

However, CSharpExpression.Execute still doesn't recognize GFunc and therefore generates a parser exception:

ExpressionParserException: Unable to resolve 'Random'. There is no formal parameter with this name.

Of course, currently I can work around it by making Foo() return an int, and then using CSharpExpression.Evaluate instead of Execute.

Exception

Catched exception on the first call CSharpExpression.Evaluate

NotSupportedException: /Users/builduser/buildslave/unity/build/External/il2cpp/il2cpp/libil2cpp/icalls/mscorlib/System.Reflection.Emit/DynamicMethod.cpp(24) : Unsupported internal call for IL2CPP:DynamicMethod::destroy_dynamic_method - System.Reflection.Emit is not supported.

return void, set field value

does it support any those?

var typeResolver = new KnownTypeResolver(typeof(Test), typeof(Console));
var test = new Test();
var exprTree = CSharpExpression.Evaluate<Test,int>("test.te=23", test,typeResolver:typeResolver);
throws error Unexpected symbol '='

var exprTree = CSharpExpression.Evaluate<System.void>("Console.Write(123)",typeResolver:typeResolver);
will not work because cannot use System.void

Allow for more than 4 arguments?

Hello, I was wondering if there are any plans/if it is possible to parse functions with more than 4 arguments or, even better, for a generic number of arguments.

MethodCallSignature hash code issues

public MethodCallSignature(Type parameter1Type, string parameter1Name, Type returnType)
: this(returnType)
etc...

due to the order of the constructor call, the same hash code will be calculated

Matching functionality with Linq Eval

Hey, was really hoping you could help. I picked up this plugin to try and match functionality we had in place before moving over to the IL2CPP scripting backend.

The function I'm trying to replicate is as below.

LambdaParser.Linq.LambdaParse.Eval(string expr, IDictionary<string,object> vars) T Output = Assets.Scripts.SysExtensions.Extensions.TryCast<T>(res);

Using the above function with correct fed variables was matching my needed functionality perfectly. How would I go about achieving this in this plugin?

Thanks,
Ezzuna.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.