¿Es posible interpretar un árbol de expresiones C # para emitir JavaScript?

c# expression-trees javascript transformation

Pregunta

Por ejemplo, si tienes una expresión como esta:

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

¿Hay algo que atravesará el árbol de expresiones y generará esto?

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

Respuesta aceptada

Probablemente no sea fácil, pero sí, es absolutamente factible. Los ORM como Entity Framework o Linq to SQL lo hacen para traducir las consultas de Linq a SQL, pero en realidad puedes generar lo que quieras del árbol de expresiones ...

Debe implementar un ExpressionVisitor para analizar y transformar la expresión.


EDIT: aquí hay una implementación muy básica que funciona para su ejemplo:

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");
    }
}

Solo maneja lambdas con una sola instrucción, pero de todos modos el compilador de C # no puede generar un árbol de expresiones para un bloque lambda.

Todavía hay mucho trabajo que hacer, por supuesto, esta es una implementación mínima ... probablemente necesite agregar llamadas a métodos ( VisitMethodCall ), acceso a propiedades y campos ( VisitMember ), etc.


Respuesta popular

Script # es utilizado por los desarrolladores internos de Microsoft para hacer exactamente esto.



Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow