lvm.js

Mon, 05 Apr 2010 17:03:06 +0100

author
Matthew Wild <mwild1@gmail.com>
date
Mon, 05 Apr 2010 17:03:06 +0100
changeset 2
253863ece36f
parent 1
f32a9c1be7c6
child 3
6f338fbf0abc
permissions
-rw-r--r--

Implement OP_MOVE, OP_LOADNIL and OP_RETURN. Also change the way OP_CALL is implemented, and update the test code with a more complicated (kind of) sample.


var OP_MOVE = 0;
var OP_LOADK = 1;
var OP_LOADNIL = 3;
var OP_GETGLOBAL = 5;
var OP_CALL = 28;
var OP_RETURN = 30;

function LValue(type, value)
{
	this.type = type||"nil";
	this.value = value||null;
}

LValue.prototype = {
	call: function (args)
	{
		return this.value;
	},
	index: function (key)
	{
		if(this.type == "table")
		{
			return this.value[key.value];
		}
	},
	setIndex: function (key, value)
	{
		if(this.type == "table")
		{
			this.value[key.value] = value;
		}
	}
};

function LValueFromString(string)
{
	return new LValue("string", string);
}

function LValueFromFunction(func)
{
	return new LValue("function", func);
}

var default_environment = new LValue("table", {});

var print;
if(typeof(document) == "object")
	print = function (a) { document.write(a+"<br/>") }; // Browser
else
	print = require("sys").puts; // Nodejs

default_environment.setIndex(LValueFromString("print"), LValueFromFunction(print));

function LFunction()
{
	this.constants = [];
	this.instructions = [];
	this.environment = default_environment;
	return this;
}

LFunction.prototype = {
	addInstruction: function (instruction)
	{
		this.instructions.push(instruction);
	},
	addConstant: function (constant)
	{
		this.constants.push(constant);
	}
};

function LVM()
{
	this.callstack = [];
	this.stack = [];
	return this;
}

LVM.prototype = {
	run: function (lfFunction)
	{
		this.frame = {f:lfFunction,pc:0,reg:[]};
		this.callstack.push(this.frame);
		var instruction;
		while(this.callstack.length>0)
		{
			instruction = this.frame.f.instructions[this.frame.pc++];
			switch(instruction[0])
			{
			case OP_MOVE:
				this.frame.reg[instruction[1]] = this.frame.reg[instruction[2]];
				break;
			case OP_LOADNIL:
				for(var i = instruction[1];i<=instruction[2];i++)
					this.frame.reg[i] = new LValue("nil", null);
				break;
			case OP_GETGLOBAL:
				var name = this.frame.f.constants[instruction[2]];
				this.frame.reg[instruction[1]] = this.frame.f.environment.index(name);
				break;
			case OP_LOADK:
				var value = this.frame.f.constants[instruction[2]];
				this.frame.reg[instruction[1]] = value;
				break;
			case OP_CALL:
				var f = this.frame.reg[instruction[1]].call(); // return JS or LValue
				var args = this.frame.reg.splice(instruction[1]+1, instruction[1]+(instruction[2]-1));
				if(typeof(f) == "function")
				{
					// JS native function
					var ret = f.apply(null, args.map(function (a) { return a.value; }));
				}
				else
				{
					// Lua function
				}
				break;
			case OP_RETURN:
				this.callstack.pop();
				break;
			default:
			}
		}
	}
};

var testvm = new LVM();
var mycode = new LFunction();

mycode.addConstant(LValueFromString("hello"));
mycode.addConstant(LValueFromString("print"));

mycode.addInstruction([OP_LOADK, 0, 0]);
mycode.addInstruction([OP_LOADNIL, 1, 1]);
mycode.addInstruction([OP_MOVE, 1, 0]);
mycode.addInstruction([OP_GETGLOBAL, 2, 1]);
mycode.addInstruction([OP_MOVE, 3, 1]);
mycode.addInstruction([OP_CALL, 2, 2, 1]);
mycode.addInstruction([OP_RETURN, 0, 1]);



testvm.run(mycode);

mercurial