RubySharp, implementando Ruby en C# (3)

Anterior Post

En RubySharp puedo definir nuevas funciones (en realidad métodos del objeto actual) e invocarlas. Hay algunas funciones que ya están predefinidas en C#:

Veamos primero que toda función tiene que cumplir con la interfaz:

public interface IFunction
{
    object Apply(DynamicObject self, Context context, IList<object> values);
}


donde cada función, al aplicarse, recibe el objeto sobre la que es invocada como método, el contexto de variables (locales, argumentos, otros… ) actual, y una lista (posiblemente vacía) de argumentos.



Por ejemplo, la función puts está implementada así:



public class PutsFunction : IFunction
{
    private TextWriter writer;

    public PutsFunction(TextWriter writer)
    {
        this.writer = writer;
    }

    public object Apply(DynamicObject self, Context context, IList<object> values)
    {
        foreach (var value in values)
            this.writer.WriteLine(value);

        return null;
    }
}


Recibe: el objeto al que pertenece como método, el contexto, y una lista de argumentos. Cada uno de los argumentos ya lo recibe evaluado, y simplemente lo envía a un TextWriter, un argumento por línea. Vean que no imprime a la consola: el TextWriter se lo entregan cuando se crea esta función (veremos más adelante que se crea al crearse un objeto Machine). Esta inyección por constructor facilita el test de esta función predefinida, ejemplo:



[TestMethod]
public void PutsTwoIntegers()
{
    StringWriter writer = new StringWriter();
    PutsFunction function = new PutsFunction(writer);

    Assert.IsNull(function.Apply(null, null, new object[] { 123, 456 }));

    Assert.AreEqual("123\r\n456\r\n", writer.ToString());
}


Pero fue algo que nació no sólo de armar un test, sino de seguir el flujo de trabajo de TDD.



Veamos otro ejemplo de función predefinida, el require:



public class RequireFunction : IFunction
{
    private Machine machine;

    public RequireFunction(Machine machine)
    {
        this.machine = machine;
    }

    public object Apply(DynamicObject self, Context context, IList<object> values)
    {
        string filename = (string)values[0];
        return this.machine.RequireFile(filename);
    }
}


Esta vez, delega el trabajo de cargar un archivo de programa al objeto Machine que se le inyectó al comienzo.



Y finalmente, veamos el código de una función definida, con un cuerpo ya compilado de RubySharp:



public class DefinedFunction : IFunction
{
    private IExpression body;
    private IList<string> parameters;
    private Context context;

    public DefinedFunction(IExpression body, IList<string> parameters, Context context)
    {
        this.body = body;
        this.context = context;
        this.parameters = parameters;
    }

    public object Apply(DynamicObject self, Context context, IList<object> values)
    {
        Context newcontext = new Context(self, this.context);

        int k = 0;
        int cv = values.Count;

        foreach (var parameter in this.parameters) 
        {
            newcontext.SetLocalValue(parameter, values[k]);
            k++;
        }

        return this.body.Evaluate(newcontext);
    }
}


En este caso, se arma un nuevo contexto para que contenga a los argumentos de la función definida, con los valores que se pasaron ahora, y que recuerde igual al contexto desde donde fue definida. Observen que el contexto desde donde fue invocada (el Context context que recibe como parámetro Apply) no es usado. Hay una “closure” con el contexto en el que fue construida la función (el Context context que recibe en el constructor)



Hay más temas para explorar, como implementación de algunos comandos, la Machine que arranca todo, el REPL, etc.



Nos leemos!



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

This entry was posted in 11699, 1389, 15035, 16397, 17933, 5374. Bookmark the permalink.

Leave a Reply

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


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>