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