Archive for the '16298' Category

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

Monday, September 26th, 2011

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

Aj Script: Intérprete tipo Javascript en C# (1) Interfaces de Base

Monday, September 19th, 2011

Estoy trabajando en implementar un interéprete C# llamado AjScript: un lenguaje tipo Javascript. El código (en progreso) está siendo publicado en:

https://github.com/ajlopez/AjScript

(el título de este post es Aj Script con un blanco, el Community Server me rechaza el título conteniendo AjScript al tomarlo por un script y lo bloquea).

Comenzó hace unos meses dentro de mi AjCodeKatas project en Google Code, pero hace unas semanas lo moví a GitHab: me permite tener más proyectos. En cambio, Google Code tiene un límite por número de proyectos. Por eso he puesto muchos pequeños proyectos dentro de AjCodeKata. Ahora, con GitHub no tengo esa limitación (hay límites de espacio no de cantidad de proyectos). Y con los forks públicos y los pull request, GitHub tiene su encanto.

La idea es tener un intéprete que tenga las “good parts” de Javascript. Estoy discutiendo conmigo mismo cuáles serán, por ejemplo, cómo estabilizar el tema de la herencia. Pero un punto clave a soportar es: acceso a objetos .NET nativos. Pueden escribir

var obj = new System.IO.DirectoryInfo(‘.’);

directamente en AjScript. Y pueden invocar código AjScript desde una aplicación .NET.

Pero ahora, quiero presentar las interfaces de base de este intérprete. Primero, IObject, la interfaz que todo objeto AjScript debe implementar:

 

public interface IObject
{
    IFunction Function { get; }
    object GetValue(string name);
    void SetValue(string name, object value);
    ICollection<string> GetNames();
    object Invoke(string name, object[] parameters);
    object Invoke(ICallable method, object[] parameters);
}

Vean que cada objeto tiene una propiedad Function: es la “clase” del objeto, la función usada al crearlo. Puede que lo cambie por Prototype.

Para mantener los nombres de las variables y sus valores, tengo una interfaz IContext:

public interface IContext
{
    IContext RootContext { get; }
    ReturnValue ReturnValue { get; set;  }
    void SetValue(string name, object value);
    object GetValue(string name);
    void DefineVariable(string name);
}

Noten que un contexto puede tener un contexto padre, para de esta forma tener contextos anidados. La propiedad ReturnValue es usada para detectar si hay un comando return ejecutado varios niveles adendro en la ejecución actual.

Cualquier método AjScript implementa ICallable:

public interface ICallable
{
    int Arity { get; }
    IContext Context { get; }
    object Invoke(IContext context, object @this, object[] arguments);
}

Noten que las funciones son IObject y ICallable con el poder adicional de poder crear un nuevo objeto:

public interface IFunction : ICallable, IObject
{
    object NewInstance(object[] parameters);
}

Próximos posts: ejemplos, closures, manejo de objetos nativos, lexer y parser, tests con TDD (escribí este intérprete usando TDD, tiene >80% de code coverage, podría mejorarlo).

Trabajo pendiente: implementar Function como “clase” con argumentos, prototype con propiedad .constructor, typeof, y las “clases” Javascript originales como Number, Date y otras; decidir qué características de  Javascript quedan “dentro” y cuáles quedan “fuera”. ¿Sugerencias?

Nos leemos!

Angel “Java” Lopez

http://www.ajlopez.com

http://twitter.com/ajlopez