Est-il possible d'interpréter un arbre d'expression C # pour émettre du JavaScript?

c# expression-trees javascript transformation

Question

Par exemple, si vous avez une expression comme celle-ci:

Expression<Func<int, int>> fn = x => x * x;

Y a-t-il quelque chose qui traverse l'arbre d'expression et génère ceci?

"function(x) { return x * x; }"

Réponse acceptée

Ce n'est probablement pas facile, mais oui, c'est tout à fait réalisable. Les ORM comme Entity Framework ou Linq to SQL le font pour traduire les requêtes Linq en SQL, mais vous pouvez générer tout ce que vous voulez à partir de l'arborescence des expressions ...

Vous devez implémenter un ExpressionVisitor pour analyser et transformer l'expression.


EDIT: voici une implémentation très basique qui convient à votre exemple:

Expression<Func<int, int>> fn = x => x * x;
var visitor = new JsExpressionVisitor();
visitor.Visit(fn);
Console.WriteLine(visitor.JavaScriptCode);

...

class JsExpressionVisitor : ExpressionVisitor
{
    private readonly StringBuilder _builder;

    public JsExpressionVisitor()
    {
        _builder = new StringBuilder();
    }

    public string JavaScriptCode
    {
        get { return _builder.ToString(); }
    }

    public override Expression Visit(Expression node)
    {
        _builder.Clear();
        return base.Visit(node);
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        _builder.Append(node.Name);
        base.VisitParameter(node);
        return node;
    }

    protected override Expression VisitBinary(BinaryExpression node)
    {
        base.Visit(node.Left);
        _builder.Append(GetOperator(node.NodeType));
        base.Visit(node.Right);
        return node;
    }

    protected override Expression VisitLambda<T>(Expression<T> node)
    {
        _builder.Append("function(");
        for (int i = 0; i < node.Parameters.Count; i++)
        {
            if (i > 0)
                _builder.Append(", ");
            _builder.Append(node.Parameters[i].Name);
        }
        _builder.Append(") {");
        if (node.Body.Type != typeof(void))
        {
            _builder.Append("return ");
        }
        base.Visit(node.Body);
        _builder.Append("; }");
        return node;
    }

    private static string GetOperator(ExpressionType nodeType)
    {
        switch (nodeType)
        {
            case ExpressionType.Add:
                return " + ";
            case ExpressionType.Multiply:
                return " * ";
            case ExpressionType.Subtract:
                return " - ";
            case ExpressionType.Divide:
                return " / ";
            case ExpressionType.Assign:
                return " = ";
            case ExpressionType.Equal:
                return " == ";
            case ExpressionType.NotEqual:
                return " != ";

            // TODO: Add other operators...
        }
        throw new NotImplementedException("Operator not implemented");
    }
}

Il ne gère que lambdas avec une seule instruction, mais le compilateur C # ne peut de toute façon pas générer d'arborescence d'expression pour un bloc lambda.

Bien sûr, il reste encore beaucoup de travail à faire, il s’agit d’une implémentation très minimale ... vous devrez probablement ajouter des appels de méthode ( VisitMethodCall ), un accès aux propriétés et aux champs ( VisitMember ), etc.


Réponse populaire

Le script # est utilisé par les développeurs internes de Microsoft pour faire exactement cela.



Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow