Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to convert Expression into CSharpCompilation or CSharpSyntaxTree?

Tags:

c#

.net

roslyn

How to convert:

System.Linq.Expression.Expression

Into:

Microsoft.CodeAnalysis.CSharp.CSharpCompilation

Or into:

Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree

I need next specific cases to work like one of the options:

  • I can compile Expression and CSharpSyntaxTree to the same behavior of the executable code

  • When I look at C# expression typed manually then I can get CSharpSyntaxTree and it will generate the same code.

    public void MultipleStatementsBlockTest()
    {
        var p = Expression.Parameter(typeof(int), "p");
        Expression assignment = Expression.Assign(p, Expression.Constant(1));
        Expression addAssignment = Expression.AddAssign(p, Expression.Constant(5));
        // Convert addAssignment  to Roslyn tree here
    }
    
    class HasIndexers
    {
        public object this[string s] => null;
    
        public object this[int i] => null;
    }
    public void CanPrettyPrintVariousIndexers()
    {
        Expression<Func<Bool>> expr = () => new HasIndexers()[3] == new HasIndexers()["three"];
        // Convert expr to Roslyn tree here
    }
    

UPDATE:

Approach Expression -> string -> Roslyn in unacceptable. Conversion should be direct.

UPDATE2: Possible usages:

  1. DI/IoC container or ORM or message bus or other library based on expressions of run time into compile time library with code generation.

a. Faster start

b. Compile time errors, not runtime errors.

c. Possibly faster runtime.

d. Allow C# to live longer by eating F# pie.

e. Possibly more hybrid libraries, e.g. for matrix(images) manipulation which allow to copy and paste resulted tree created on Server/Desktop as code to be used on IoT.

  1. Expression lib (e.g. for debugging) which converts these into C# code.

a. More options of output of code (namespaces, spaces, tabs). b. More correctness with less manual code of generation. c. Support for different output language rather than C#.

like image 222
Dzmitry Lahoda Avatar asked Feb 14 '18 15:02

Dzmitry Lahoda


2 Answers

See here syntax transformation

Because the syntax trees are immutable, the Syntax API provides no direct mechanism for modifying an existing syntax tree after construction. However, the Syntax API does provide methods for producing new trees based on specified changes to existing ones. Each concrete class that derives from SyntaxNode defines With* methods which you can use to specify changes to its child properties.

Additionally, the ReplaceNode extension method can be used to replace a descendent node in a subtree. Without this method updating a node would also require manually updating its parent to point to the newly created child and repeating this process up the entire tree

  • a process known as re-spining the tree.

Example - Transformations using the With* and ReplaceNode methods:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;

namespace ConstructionCS
{
    class Program
    {
        static void Main(string[] args)
        {
            NameSyntax name = IdentifierName("System");
            name = QualifiedName(name, IdentifierName("Collections"));
            name = QualifiedName(name, IdentifierName("Generic"));

            SyntaxTree tree = CSharpSyntaxTree.ParseText(
@"using System;
using System.Collections;
using System.Linq;
using System.Text;

namespace HelloWorld
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(""Hello, World!"");
        }
    }
}");

            var root = (CompilationUnitSyntax)tree.GetRoot();

            var oldUsing = root.Usings[1];
            var newUsing = oldUsing.WithName(name);

            root = root.ReplaceNode(oldUsing, newUsing);
        }
    }
}

Try it out live at: http://roslynquoter.azurewebsites.net/

like image 135
stefan Avatar answered Nov 15 '22 18:11

stefan


The easiest way of converting Expression to Roslyn SyntaxTree would be:

  1. Convert Expression to corresponding source code.
  2. Parse expression source code with CSharpSyntaxTree.ParseText().

We basically reduced a problem to converting Expression to its source code. Such question was already asked on SO. Among answers Steve Wilkes proposed his library AgileObjects.ReadableExpressions. It basically provides one extension method ToReadableString() on Expression:

// str will contain source code of expression
var str = expression.ToReadableString();

I've tried this library with different expressions and it works perfectly fine.

So returning to you problem, solution based on this library will be very simple:

  1. Install AgileObjects.ReadableExpressions NuGet package.
  2. Define following extension method on Expression:

    public static class ExpressionExtensions
    {
          public static SyntaxTree ToSyntaxTree(this Expression expression)
          {
                var expressionCode = expression.ToReadableString();
                return CSharpSyntaxTree.ParseText(expressionCode);
          }
    }
    

Now you could try it:

var p = Expression.Parameter(typeof(int), "p");
Expression assignment = Expression.Assign(p, Expression.Constant(1));
Expression addAssignment = Expression.AddAssign(p, Expression.Constant(5));
BlockExpression addAssignmentBlock = Expression.Block(
    new ParameterExpression[] { p },
    assignment, addAssignment);

SyntaxTree tree = addAssignmentBlock.ToSyntaxTree();

Such expression will be converted to the following code:

var p = 1;
p += 5;

which is successfully parsed to corresponding SyntaxTree.

like image 40
CodeFuller Avatar answered Nov 15 '22 18:11

CodeFuller