AjTalk y Javascript (Parte 2) Compilando una Clase Simple

Published on Author lopezLeave a comment

Post Anterior

En este post explicaré con un ejemplo simple cómo código Smalltalk puede ser compilado a Javascript usando el proyecto AjTalk.Compiler, el nombre del ejecutable es ajtalkc. El compilador está escrito desde cero en C#. La forma de compilar (la salida) fue fruto de investigar temas dinámicos de Javascript, y para Smalltalk en particular, leí los posts que mencioné en  Smalltalk y Javascript sin ver la implementación interna, sólo lo publicado en post. En cada proyecto trato de ver cómo implementar lo que quiero, sin “infectarme” de otras implementaciones, simplemente para ver hasta donde puedo llegar desde las ideas básicas. Y usando TDD, la implementación interna va surgiendo a medida que se va desarrollando.

Como es usual, el código está en mi repo:

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

Yo hice “fileOut” de una clase simple de Pharo: Point. El fragmento del principio:

Object subclass: #Point
	instanceVariableNames: 'x y'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Graphics-Primitives'!
!Point commentStamp: '<historical>' prior: 0!
I represent an x-y pair of numbers usually 
designating a location on the screen.!
!Point methodsFor: '*Polymorph-Geometry' stamp: 'gvc 10/31/2006 11:01'!
directionToLineFrom: p1 to: p2
	"Answer the direction of the line from the receiver
	position.
	< 0 => left (receiver to right)
	= => on line
	> 0 => right (receiver to left)."
	^((p2 x - p1 x) * (self y - p1 y)) -
		((self x - p1 x) * (p2 y - p1 y))! !
....

Lo compilé usando el programa de comando de línea ajtalkc.exe (vean el proyecto de consola AjTalk.Compiler en la solución AjTalk; tiene un directorio CodeFiles con varios fileouts):

ajtalkc CodeFiles\PharoCorePoint.st

Se crea un nuevo archivo: Program.js. Exploremos cómo una clase Smalltalk puede ser representada en Javascript. Estas líneas definen la clase Point como una función Javascript, y su clase como otra función Javascript:

function PointClass()
{
}
function Point()
{
}

Nada especial. Las siguientes dos líneas son:

Point.prototype.__class = PointClass.prototype;
Point.classPrototype = PointClass.prototype;

De esta manera, cada instancia de la clase Point tiene una variable __class que apunta al singleton PointClass.prototype. Y la función/”clase”  Point tiene una variable classPrototype para apuntar al mismo singleton, si es necesario.

¿Cómo creamos una nueva instancia de Point? Usando un método de PointClass, en este caso definiendo su método basicNew:

PointClass.prototype['_basicNew'] = function() { return new Point(); };

Agregué un carácter _ para evitar la colisión de nombres con cualquier método Javascript.

Ahora, ¿Cómo asegurar que todas las instancias tengan sus variables de instanca? Definiéndolas en su prototipo:

Point.prototype.x = null;
Point.prototype.y = null;

Y la parte más interesante: ¿Cómo traducir un método Javascript a Smalltalk? Sea el método min en Smalltalk:

min
	"Answer a number that is the minimum
	of the x and y of the receiver."
	^self x min: self y! !

Su traducción a un método del prototipo de Point:

Point.prototype['_min'] = function()
{
    var self = this;
    return send(send(self, '_x'), '_min_', [send(self, '_y')]);
};

Noten que definí la variable self para ser usado por el resto del método (especialmente como variable libre en cualquier closure Javascript que encuentre, donde this podría apuntar a otro objeto). Y el uso de un método ayudante send que recibe: un objeto destino, el nombre del método a invocar, y un arreglo con sus argumentos.

Todo esto es trabajo en progreso. Ahora, estoy generando las líneas adicionales para usar Program.js desde Node.js (pueden ver el repo, con un ejemplo de Node.js), y también con otras líneas adicionales, usarlo desde el browser (ver el repo, ya hay un ejemplo cliente). Debería escribir diferentes compiladores para el server y el cliente, todos usando el mismo núcleo: el compilador de NodeJs ahora es una subclase del compilador básico de Javascript. Lo mismo el compilador para el browser. Tengo planeado compilar a Ruby. Todo esto es una prueba de concepto para mostrar el poder de tener un modelos abstracto del programa a ejecutar/compilar. Hasta podría usar ese modelo abstracto para ejecutar un programa, en lugar de compilar a bytecodes. O podría usar ese modelo abstracto, para compilar a bytecodes de AjTalk.

Próximos temas a visitar en posts: opciones de compilación, ejemplo para el browser, ejemplo para Node.js. Cómo acceder desde el código generado a objectos Javascript. Las funciones utilitarias. Implementación de herencia, bloques y clausuras.

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 *