Dynamic Language Runtime (Parte 2) Hello Expressions!

Published on Author lopez1 Comment

Próximo Post
Anterior Post

En mi anterior post escribí un simple programa “Hello, world”. Esta vez, quiero comenzar a explorar las Expression de DLR. El código de esta serie de posts se mantiene en 
http://code.google.com/p/ajcodekatas/ en el directorio trunk/Dlr.

Primero, una Expression en DLR no es una System.Linq.Expression (uno de mis primeros errores, en este ejemplo, fue usar la version Expression de Linq, en vez de la de DLR). DLR tiene su propia implementación de Expression, con más metodos. Esta implementación está en el namespace  Microsoft.Scripting.Ast. AST viene de  Abstract Syntax Tree: cada expresión, en DLR como en Linq, es una estructura de datos en memoria, que representa algo como 4, 5+a, llamadas a métodos, etc…

La clase Expression de DLR class tiene métodos estáticos para crear objetos Expression. Por ejemplo

var expr = Expression.Constant(“I’m a string”);

 

 

es la forma de crear una expresión constante. Pero necesitamos no solamente constantes, sino también variables:

var expr = Expression.Variable(typeof(string), “foo”);

 

 

Este comando crea una expresión variable, que representa una variable con nombre “foo” y tipo string.

¿Cómo invocar una expresión? Recordemos, los objetos Expression son árboles, datos en memoria, no código compilado. La magia de DLR (y de Linq también) es ser capaz de COMPILAR la expresión a un delegado, y entonces, poder invocarlo:

expr.Compile().Invoke(); // you can use Invoke with parameters, too

 

 

(Generalmente en mis intérpretes, no llego a compilar, recorro y ejecuto las acciones de los nodos del árbol de comandos y expresiones)

Entonces, para reproducir el  “Hello, World”, escribí este código de ejemplo:

// Console.WriteLine("Hello, Method!");
MethodInfo method = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) });
Expression callExpression = Expression.Call(method, Expression.Constant("Hello Method!"));
Expression.Lambda<Action>(callExpression).Compile().Invoke();

 

 

Precedí el código con un comentario que contiene el código C# equivalente.

Nota: necesitamos un Expression.Lambda (en este caso, un lambda Action) para crear un delegado alrededor de una expresión. Una acción es algo para invocar, sin retornar un valor (una rutina, en vez de una función). Un Action<string> es una rutina que recibe un parámetro string. Un Action sin nada más, no tendría parámetros.

¿Cómo usamos una variable y le asignamos un valor? Veamos un ejemplo:

// string x = "Hello, Assign!";
// Console.WriteLine(x);
ParameterExpression x = Expression.Variable(typeof(string), "x");
Expression blockExpression = Expression.Block(
    new ParameterExpression[] { x }, 
    Expression.Assign(x, Expression.Constant("Hello, Assign!")),
    Expression.Call(method, x));
Expression.Lambda<Action>(blockExpression).Compile().Invoke();

 

 

Noten que el Expression.Block recibe un enumerable de ParameterExpressions, y luego un param array (una cantidad variable de argumentos) con Expressions que constituyen el cuerpo del bloque que vamos a ejecutar.

En el próximo ejemplo, implementé un código C# (descripto en el comentario, pero código inválido), que luego implemento en DLR: un condicional que retorna un valor:

// x = "Hello";
// if (true)
//     x.ToLower();
MethodInfo toLowerMethod = typeof(string).GetMethod("ToLower", new Type[] {});
ParameterExpression y = Expression.Variable(typeof(string), "y");
ParameterExpression z = Expression.Variable(typeof(string), "z");
Expression blockExpression2 = Expression.Block(
    new ParameterExpression[] { x, y},
    Expression.Assign(x, Expression.Constant("Hello")),
    Expression.Condition(Expression.Constant(true),
        Expression.Call(x, toLowerMethod),
        Expression.Default(typeof(string)),
        typeof(string))
        );
string result = Expression.Lambda<Func<string>>(blockExpression2).Compile().Invoke();
Console.WriteLine(result);

 

 

La primera expresión en el bloque es la asignación de la constante “Hello” a la variable x. Noten que cualquier variable es inyectada como parámetro en un bloque. Esta es la forma de declarar variables en bloques de expresiones DLR. Hay una Expression.Condition que toma como parámetros a una expresión booleana, una Expression que se evalúa si la condición es verdadera, una Expression que se evalúa si la condición es falsa,  y el tipo del valor a retorna. Este tipo de condición es similar al operador ternario ?: de C#.

Principal fuente consultada para este ejemplo: “Pro DLR in .NET 4” de Chaur Wu.

Próximos paso: compilar expresiones más complejas, expresiones binarias, expresiones IfThenElse, y más.

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Leave a Reply

Your email address will not be published. Required fields are marked *