Angel \”Java\” Lopez on Blog

March 22, 2012

AjTalk in Javascript (1) First implementations

Some days ago, I moved my AjTalk project from Google Code to GitHub:

https://github.com/ajlopez/AjTalk

Last year, I added the compilation of Smalltalk fileouts to Javascript, into that project. Past Sunday, I created a new project at:

https://github.com/ajlopez/AjTalkJs

as code kata for another approach: implementing an AjTalk virtual machine, from scratch, in JavaScript. The idea is to compile Smalltalk code to methods in bytecodes, and to have an interpreter for those methods. The code is at´

https://github.com/ajlopez/AjTalkJs/blob/master/lib/ajtalk.js

In that implementation, I’m using a sendMessage method and a custom lookup for methods. Excerpt (lib/ajtalk.js):

BaseObject.prototype.sendMessage = function(selector, args)
{
    var method = this.lookup(selector);
    return method.apply(this, args);
};

function BaseClass(name, instvarnames, clsvarnames, supercls) {
    this.name = name;
    this.instvarnames = instvarnames;
    this.clsvarnames = clsvarnames;
    this.supercls = supercls;
    this.methods = {};
};

BaseClass.prototype.__proto__ = BaseObject.prototype;

BaseClass.prototype.defineMethod = function (selector, method)
{
    this.methods[selector] = method;
};

BaseClass.prototype.getInstanceSize = function() {
    var result = this.instvarnames.length;
    if (this.supercls)
        result += this.supercls.getInstanceSize();

    return result;
};

BaseClass.prototype.lookupInstanceMethod = function (selector)
{
    var result = this.methods[selector];
    if (result == null && this.supercls)
        return this.supercls.lookupInstanceMethod(selector);
    return result;
};

At Monday, I had new ideas for implementing the virtual maching. I could use the prototype features of Javascript. Then, I wrote some new code (lib/ajtalknew.js):

function createClass(name, superklass, instvarnames, clsvarnames)
{
    var protoklass = new Function();

    if (superklass)
    {
        // Chain class prototypes
        protoklass.prototype.__proto__ = superklass.proto;
    }
    else
    {
        // First class methods
        protoklass.prototype.basicNew = function()
        {
            var obj = new this.func;
            obj.klass = this;
            return obj;
        }

        protoklass.prototype.defineSubclass = function(name, instvarnames, clsvarnames)
        {
            return createClass(name, this, instvarnames, clsvarnames);
        }

        protoklass.prototype.defineMethod = function(name, method)
        {
            var mthname = name.replace(/:/g, '_');
            if (typeof method == "function")
                this.func.prototype[mthname] = method;
            else
                this.func.prototype[mthname] = method.toFunction();
        }

        protoklass.prototype.defineClassMethod = function(name, method)
        {
            var mthname = name.replace(/:/g, '_');
            if (typeof method == "function")
                this.proto[mthname] = method;
            else
                this.proto[mthname] = method.toFunction();
        }

        // TODO Quick hack. It should inherits from Object prototype
        protoklass.prototype.sendMessage = function(selector, args)
        {
            return this[selector].apply(this, args);
        }
    }

    var klass = new protoklass;

    // Function with prototype of this klass instances
    klass.func = new Function();
    klass.proto = protoklass.prototype;
    klass.name = name;
    klass.super = superklass;
    klass.instvarnames = instvarnames;
    klass.clsvarnames = clsvarnames;

    klass.func.prototype.klass = klass;

    if (superklass)
    {
        // Chaining instances prototypes
        klass.func.prototype.__proto__ = superklass.func.prototype;
    }
    else
    {
        // First instance methods
        klass.func.prototype.sendMessage = function(selector, args)
        {
            return this[selector].apply(this, args);
        }
    }

    Smalltalk[name] = klass;

    return klass;
}

createClass('Object');

Smalltalk.Object.defineClassMethod('compileMethod:', function(text)
    {
        var compiler = new Compiler();
        var method = compiler.compileMethod(text, this);
        this.defineMethod(method.name, method);
        return method;
    });

Smalltalk.Object.defineClassMethod('compileClassMethod:', function(text)
    {
        var compiler = new Compiler();
        var method = compiler.compileMethod(text, this);
        this.defineClassMethod(method.name, method);
        return method;
    });

Ok, it’s a bit tricky, but it works!

Notably, I wrote test using Node builtin assert module (I had no Internet connection, and had no NodeUnit in my machine). The code was nurtured using TDD and it was a good experience for me.

I have a javascript Compiler function/”class” that I can use to compile Smalltalk code to my bytecode version. Only a few bytecodes are implemented:

var ByteCodes = {
    GetValue: 0,
    GetArgument: 1,
    GetLocal: 2,
    GetInstanceVariable: 3,
    GetGlobalVariable: 4,
    GetSelf: 5,
    SetLocal: 10,
    SetInstanceVariable: 11,
    SetGlobalVariable: 12,
    Add: 20,
    Subtract: 21,
    Multiply: 22,
    Divide: 23,
    SendMessage: 40,
    Return: 50
};

Pending work: implement class variables, metaclasses, with TDD. Improve hmtl samples

Keep tuned!

Angel “Java” Lopez

http://www.ajlopez.com

http://twitter.com/ajlopez

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: