Aj​Script: Intérprete tipo Javascript en C# (2) Expresiones

Published on Author lopez1 Comment

Anterior Post

Como en otros intérpretes, una pieza clave es la Expression, algo para evaluar durante la ejecución de un programa. En Aj?Script (repositorio de código) tengo una IExpression:

public interface IExpression
{
    object Evaluate(IContext context);
}

Estas son las que tengo implementadas en el código actual:

Veamos un ejemplo de expresión simple, ConstantExpression:

public class ConstantExpression : IExpression
{
    private object value;
    public ConstantExpression(object value)
    {
        this.value = value;
    }
    public object Value { get { return this.value; } }
    public object Evaluate(IContext context)
    {
        return this.value;
    }
}

Un poco más interesante, VariableExpression, dado un nombre y un contexto devuelve el valor de la variable según su nombre:

public class VariableExpression : IExpression
{
    private string name;
    public VariableExpression(string name)
    {
        this.name = name;
    }
    public string Name { get { return this.name; } }
    public object Evaluate(IContext context)
    {
        return context.GetValue(this.name);
    }
}

A veces una expresión tiene uno o dos parámetros. Tengo UnaryExpression y BinaryExpression como clases abstractas. El código de BinaryExpression:

public abstract class BinaryExpression : IExpression
{
    private IExpression leftExpression;
    private IExpression rigthExpression;
    public BinaryExpression(IExpression left, IExpression right)
    {
        this.leftExpression = left;
        this.rigthExpression = right;
    }
    public IExpression LeftExpression { get { return this.leftExpression; } }
    public IExpression RightExpression { get { return this.rigthExpression; } }
    public abstract object Apply(object leftValue, object rightValue);
    public object Evaluate(IContext context)
    {
        object leftValue = this.leftExpression.Evaluate(context);
        object rightValue = this.rigthExpression.Evaluate(context);
        return this.Apply(leftValue, rightValue);
    }
}

Noten que el método Apply es abstracto. Una implementación concreta:

using Microsoft.VisualBasic.CompilerServices;
public class ConcatenateExpression : BinaryExpression
{
    public ConcatenateExpression(IExpression left, IExpression right)
        : base(left, right)
    {
    }
    public override object Apply(object leftValue, object rightValue)
    {
        if (leftValue == null)
            leftValue = string.Empty;
        if (rightValue == null)
            rightValue = string.Empty;
        return Operators.ConcatenateObject(leftValue, rightValue);
    }
}

Soy un programador perezoso: vean que uso los operadores de Visual Basic. Podría escribir una implementación directa en C#. Tengo todos los tests, listos en su lugar, para refactorizas esta expresión y otras.

No escribí todas estas expresiones de una vez. Un ejemplo: Escribí una clase Context usando tests, y entonces la clase VariableExpression usando tests. Agregué entonces el soporte de variables en el Parser, y evalué expresiones simples que usaran constantes y variables, dentro de nuevos tests. Uso ese patrón muchas veces: agregar una nueva clase de base (Context, Function…) con tests, agregar una nueva expresión (suma, invocación…. ) con tests, agrego el soporte de esa nueva expresión en el Lexer y en el Parser, con tests, y termino usando la nueva expresión en evaluciones de expresiones en tests.

Próximos tópicos: comandos, implementación de funciones, clausuras, “hoisting”, acceso a objetos nativos. Y ejemplos con AjScript.

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 *