AjTalk: un intérprete tipo Smalltalk

Hace unas semanas, estuve trabajando en mi proyecto de código abierto AjTalk, un intérprete tipo Smalltalk, escrito en .NET (con C#, para facilitar la migración a Mono, si alguien lo necesita), y ahora, quiero presentar el estado actual de ese trabajo. Desde los tempranos ochenta del siglo pasado, me ha interesado Smalltalk y siempre vuelvo al tema, aunque sólo en mi tiempo libre, no como desarrollador profesional. No es la primera vez que escribo un intérprete de este tipo (el primero que escribí en aquellos años era muy simple: en C, sin recolector de basura, interface solo texto), pero ahora quiero implementar uno más, esta vez más completo, apoyándome en la librería de clases .NET.

La versión actual es mínima, pero va tomando forma. La idea es tener objetos dinámicos, como en Smalltalk, y, en algún momento, agregarle el soporte de prototipos. Los objetos y el intérprete accederían a todo el framework .NET y otras librerías. Tengo la experiencia previa de haber logrado eso en mi intérprete AjBasic, que uso en mi proyecto de generación de código AjGenesis.

La versión inicial de AjTalk está publicada en Google Code:

http://code.google.com/p/ajtalk/

La solución

 Estoy usando Visual Studio 2005 Professional. La solución contiene cuatro proyectos.

AjTalk es el proyecto principal, una librería de clases conteniendo el núcleo del sistema.

AjTalk.Test01 y AjTalk.Test02 son aplicaciones de consola, para usar en pruebas manuales.

AjTalk.Tests contiene los unit tests, escritos usando NUnit framework 2.2.8.

Mucho del sistema núcleo consiste en interfaces, que definen las conductas de base, y clases implementando esas interfaces.

El objeto

El proyecto contiene una interface para representar cualquier objeto:

using System; namespace AjTalk { public interface IObject { IClass Class { get; } object this[int n] { get; set;} object SendMessage(string msgname, object [] args); } }

Podría hacer que un mensaje sea un objeto (del tipo Message), pero por ahora, el mensaje es sólo un nombre y un arreglo de argumentos.

El índice this[int n] accesde a las variables de instancia. Cada valor en AjTalk puede apuntar a cualquier objeto .NET, no solamente a objetos que implementen IObject. De esta forma, puedo manejar int, long, String, DataSet, desde objetos IObject.

La clase

Estoy implementando una interface simple IClass, sin distinción entre clase, behavior, y otras clases, como sería en el Smalltalk clásico. Separaré esas clases en una futura versión, cuando llegue a ser necesario. Por ahora, estoy manejando sólo una interface:

using System; using System.Collections; using System.Collections.Generic; namespace AjTalk { public interface IClass : IObject { IClass SuperClass { get; } string Name { get; } void DefineClassMethod(IMethod method); void DefineInstanceMethod(IMethod method); void DefineClassVariable(string varname); void DefineInstanceVariable(string varname); IObject NewObject(); IMethod GetClassMethod(string mthname); IMethod GetInstanceMethod(string mthname); int GetClassVariableOffset(string varname); int GetInstanceVariableOffset(string varname); } }

Hay un diccionaro para los métodos de clase y otro para los de instancia, y listas de variables de instancia y de clase. No soporta todavía variables indexadas. La interface IClass está implementada en la clase BaseClass.

El método

Hay una interface IMethod:

using System; namespace AjTalk { public interface IMethod { string Name { get; } IClass Class { get; } object Execute(IObject receiver, object [] args); } }

La clase concreta Método implementa esa interfaces. Su método Execute tiene:

public object Execute(IObject receiver, object[] args) { return (new ExecutionBlock(receiver,receiver,this,args)).Execute(); }

Los Execution blocks tienen variables locales y argumentos. El Execute de un bloque de ejecución toma instrucciones (bytecodes) desde métodos “compilados”, y los ejecuta. Aquí, me aparto de nuevo de Smalltalk original: acá el bloque de ejecución no es un objeto AjTalk. De esta forma, yo puedo ejecutar este intérprete sin necesidad de implementar gran cantidad de clases de base. Tengo que investigar las ventajas y problemas que esta decisión podría tener sobre el diseño e implementación de esta solución.

Los bytecodes

Tengo que meditar sobre usar un árbol de objetos (como en el patrón Interpreter) o bytecodes. En esta versión, uso bytecodes. Son las instrucciones básicas que mi “máquina virtul” entiende y ejecuta paso a paso.

Esta es su enumeración:

namespace AjTalk { public enum ByteCode : byte { Nop = 0, GetVariable = 1, SetVariable = 2, GetArgument = 3, SetArgument = 4, GetConstant = 5, GetLocal = 6, SetLocal = 7, GetClassVariable = 8, SetClassVariable = 9, GetSelf = 20, GetClass = 21, GetSuperClass = 22, NewObject = 23, Pop = 24, ReturnSub = 25, ReturnPop = 26, Add = 40, Substract = 41, Multiply = 42, Divide = 43, Send = 50 } }

Los bytecodes contenidos en un método, son interpretados y ejecutados en un Execution Blog. Un fragmento de ese código:

 

while (ip < method.ByteCodes.Length) { ByteCode bc = (ByteCode) method.ByteCodes[ip]; Byte arg; switch (bc) { case ByteCode.ReturnSub: return null; case ByteCode.ReturnPop: return Top; case ByteCode.GetConstant: ip++; arg = method.ByteCodes[ip]; Push(method.GetConstant(arg)); break; case ByteCode.GetArgument: ip++; arg = method.ByteCodes[ip]; Push(arguments[arg]); break; ....


Todo con Test


El código inicial había sido escrito en VB.NET. El año pasado lo comencé a pasar a C#. Este año cambié a modo “TDD”, tarde pero seguro, y agregué varios tests para el NUnit:



Bootstraping


Uso un archivo de texto, con un formato ad’hoc, para inyectar las definiciones iniciales de clases y objetos. Ahora, en el proyectos AjTest.Test02, hay un ejemplo de ese formato en el archivo Definitions.txt:


class Point
variables x y

method
x ^x.

method
y ^y.

class Rectangle
variables point1 point2
class Square Rectangle


Próximos pasos


Hay una pila de trabajo pendiente:


- Completar la jerarquía de clases base (Behavior, Class, ….)


- Implementar más bytecodes


- Soporte de variables locales en métodos


- Un archivo de text estándard


- Acceso a objetos .NET nativos


- Usar AjTalk desde aplicaciones .NET (no veo necesario entonces implementar una interface de ventanas en AjTalk, solamente consumirlo adecuadamente desde cualquier aplicación .NET)


- Definir las clases y métodos para una implementación mínima


- Serialización/deserialización de la imagen en memoria


- Soporte de agregado de variables en una clase que ya tenga instancias creadas (puede ser un tema difícil)


- Soporte de become: (el problema de todo intérprete)


- Y mil más….


Pero tengo confianza en la forma que va tomando el proyecto. Estoy aplicando “baby steps”, para mejorar el código de base y su funcionalidad.


Este artículo en Anglish (Angel’s English):


AjTalk: a Smalltalk-like interpreter


Nos leemos!


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

This entry was posted in 11722, 1389, 3462, 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>