This project has moved. For the latest updates, please go here.

Compile to Expression

Aug 6, 2015 at 1:59 AM
Hi everyone,

Thanks so much for the great library, this has been badly needed for a while!

A simple question: I noticed Expression Evaluator uses expression trees internally to generate the delegates and it's even possible to access the cached Expression after you call Compile.

However, I am most interested in just the Expression, I don't really care about the delegate. Is there a way to simply Parse the expression string and return the Expression instance directly? Or just initialize the Expression cache? I looked at GenerateLambda, but it requires knowing the return type a priori, which I don't.

It seems that this must be there somewhere, but for some reason this feature has not been exposed to the public API. This would be invaluable for completely dynamic and code generation scenarios.

Thanks again!
Aug 6, 2015 at 2:39 AM
Ok, it seems I mostly got it after I found out about the Parser classes.

AntlrParser seems to do most of what I wanted, but there's a tiny catch. I realize it is possible to pass in a scope variable as an input Expression to the parser which will be used to resolve identifiers. This virtual identifier is exactly what I wanted, except for the situation where I want to reference the scope variable itself.

For example, imagine scope is a Point. You can easily do "X * Y" and get an int that is the multiplication of both components. But what if I want to operate on the whole Point structure? Is there an identifier that will be replaced by the scope variable itself? I tried the obvious "scope" and "this" and nothing worked.

Thanks again and congrats again on the great library!
Sep 15, 2015 at 10:35 PM
By the way, just to answer my own question, the way to do this is to use the ExternalParameters property, like this:
var parameter = global::System.Linq.Expressions.Expression.Parameter(typeof(int), "it");
var parser = new AntlrParser(Expression);
parser.ExternalParameters = new List<ParameterExpression> { parameter };
var expression = parser.Parse(parameter);
This will both include the parameter as "scope" for evaluating the expression and will assign it to the named parameter "it".
Neat!
Marked as answer by Chance on 9/15/2015 at 2:35 PM
Developer
Sep 16, 2015 at 2:23 AM
Glad to know you got it to work for you.

Perhaps we could add some unique, non-C# compliant (and therefore Expression-Evaluator specific) token in order to access the current scope?

Since scope was highly inspired by AngularJS, maybe we could go with $scope?
Developer
Sep 16, 2015 at 2:33 AM
Edited Sep 16, 2015 at 2:37 AM
You can try this out for now:

Update ExprEval.g3 (primary_expression_start, starts at line 206)

Add the line
    | '$scope' { $value = Scope; } 
anywhere in there, for example, before the this expression:
    public primary_expression_start returns [Expression value]:
    predefined_type { var type = GetType($predefined_type.text); $value = Expression.Constant(type, typeof(Type)); }           
    | (identifier    generic_argument_list) => identifier   generic_argument_list
    | identifier { $value = $identifier.value; }
    | '$scope' { $value = Scope; } 
    | 'this' 
    | 'base'
    | paren_expression  { $value = $paren_expression.value; }
    | typeof_expression  { $value = $typeof_expression.value; }          // typeof(Foo).Name
    | literal { $value = $literal.value; }
    ;
The following unit tests work fine:
        [TestMethod]
        public void ScopeToken()
        {
            var str = "$scope";
            var scope = new Scope() { Property1 = 1, Property2 = 2 };
            var c = new CompiledExpression<Scope>(str) { TypeRegistry = new TypeRegistry() };
            var ret = c.ScopeCompile<Scope>()(scope);
            Assert.AreEqual(scope, ret);
            Assert.AreEqual(scope.Property1, ret.Property1);
            Assert.AreEqual(scope.Property2, ret.Property2);
        }

        [TestMethod]
        public void ScopeAccessor()
        {
            var str = "$scope.Property1 + $scope.Property2";
            var scope = new Scope() { Property1 = 1, Property2 = 2 };
            var c = new CompiledExpression<int>(str) { TypeRegistry = new TypeRegistry() };
            var ret = c.ScopeCompile<Scope>()(scope);
            Assert.AreEqual(3, ret);
        }
Using $scope in any place that isn't the start of a primary expression should throw an exception.
Sep 16, 2015 at 9:40 AM
It's nice that this is easy to add. However, I'm not so sure I like the "$scope" keyword. I guess it's both too long and it doesn't feel like C#. One of the things I like about ExpressionEvaluator is how close you're trying to make it to C#, so I guess I wouldn't change strategies now.

I'm fine with using the external parameters list, indeed I think that may be the best solution rather than assuming some arbitrary keyword (e.g. in Dynamic LINQ they used "it", which I never really understood, guess it stands for "iterator"?).