view interp.js @ 206:b4a9d4e405c5

Implemented a simple interpreter to be used for macro expansion and a driver for testing it
author Mike Pavone <pavone@retrodev.com>
date Wed, 23 Oct 2013 19:10:03 -0700
parents
children 60eff5f81d9a
line wrap: on
line source

Number.prototype['tpmeth_+'] = function(other) {
	return this + other;
};
Number.prototype['tpmeth_-'] = function(other) {
	return this - other;
};
Number.prototype['tpmeth_*'] = function(other) {
	return this * other;
};
Number.prototype['tpmeth_/'] = function(other) {
	return this / other;
};
Number.prototype['tpmeth_='] = function(other) {
	return this == other ? tptrue : tpfalse;
};
Number.prototype['tpmeth_>'] = function(other) {
	return this > other ? tptrue : tpfalse;
};
Number.prototype['tpmeth_<'] = function(other) {
	return this < other ? tptrue : tpfalse;
};
Number.prototype['tpmeth_>='] = function(other) {
	return this >= other ? tptrue : tpfalse;
};
Number.prototype['tpmeth_<='] = function(other) {
	return this <= other ? tptrue : tpfalse;
};
Number.prototype.tpmeth_string = function() {
	return '' + this;
};
Number.prototype.tpmeth_print = function() {
	print(this);
};

String.prototype['tpmeth_.'] = function(other) {
	return this + other;
};
String.prototype['tpmeth_='] = function(other) {
	return this == other ? tptrue : tpfalse;
};
String.prototype.tpmeth_print = function() {
	print(this);
}

var tptrue = null;
var tpfalse = null;

function topenv(moduledirs)
{
	this.names = {};
	this.modules = {};
	for (var dirnum in moduledirs) {
		var results = os.system("ls", [moduledirs[dirnum]]).split('\n');
		for (var i in results) {
			var tpidx = results[i].indexOf('.tp')
			if (tpidx > 0 && tpidx == results[i].length - 3) {
				this.names[results[i].substr(0, tpidx)] = moduledirs[dirnum] + "/" + results[i];
			}
		}
	}
	if (!tptrue) {
		tptrue = this.find('true');
		tptrue.valueOf = function() {
			return true;
		};
	}
	if (!tpfalse) {
		tpfalse = this.find('false');
		tpfalse.valueOf = function() {
			return false;
		};
	}
}

topenv.prototype = {
	find: function(name) {
		if (name in this.modules) {
			return this.modules[name];
		}
		if (name in this.names) {
			var parsed = parseFile(this.names[name]);
			this.modules[name] = parsed.eval(this);
			return this.modules[name];
		}
		return null;
	},
	findNoTop: function(name) {
		return null;
	},
	findSetPresent: function(name, val) {
		return false;
	}
}

function environment(parent)
{
	this.parent = parent;
	this.syms = {};
}

environment.prototype = {
	find: function(name) {
		if (name in this.syms) {
			return this.syms[name];
		}
		if (this.parent) {
			return this.parent.find(name);
		}
		return null;
	},
	findNoTop: function(name) {
		if (name in this.syms) {
			return this.syms[name];
		}
		if (this.parent) {
			return this.parent.findNoTop(name);
		}
		return null;
	},
	findSet: function(name, val) {
		if (name in this.syms
		    || !this.parent
			|| !this.parent.findSetPresent(name, val)) {
			this.syms[name] = val;
		}
	},
	findSetPresent: function(name, val) {
		if (name in this.syms) {
			this.syms[name] = val;
			return true;
		}
		if (this.parent) {
			return this.parent.findSetPresent(name, val);
		}
		return false;
	}
};

op.prototype.eval = function(env) {
	var l = this.left.eval(env);
	var r = this.right.eval(env);
	var name = this.op;
	var fun = env.findNoTop(name);
	var ret;
	if (fun) {
		ret = fun(l,r)
	} else {
		ret = l['tpmeth_'+name](r);
	}
	return ret;
};

symbol.prototype.eval = function(env) {
	return env.find(this.name);
};

intlit.prototype.eval =
	floatlit.prototype.eval =
	strlit.prototype.eval =
	arraylit.prototype.eval = function(env) {
	return this.val;
};

funcall.prototype.eval = function(env) {
	var args = [];
	var name = this.name;
	if (name[name.length-1] == ":") {
		name = name.substr(0, name.length-1);
	}
	if (this.receiver) {
		args.push(this.receiver.eval(env));
	}
	for (var i = 0; i < this.args.length; i++) {
		args.push(this.args[i].eval(env));
	}
	var fun = env.findNoTop(name);
	if (fun) {
		return fun.apply(null, args);
	} else {
		return args[0]['tpmeth_'+name].apply(args[0], args.slice(1));
	}
};

object.prototype.eval = function(parentenv) {
	var env = new environment(parentenv);
	var obj = {env: env};
	for (var i = 0; i < this.messages.length; i++) {
		var msg = this.messages[i];
		if (msg instanceof assignment) {
			if (msg.expression instanceof lambda) {
				obj['tpmeth_' + msg.symbol.name] = msg.expression.eval(env);
				(function(name) {
					env.syms[name] = function() {
						return obj['tpmeth_' + name].apply(obj, arguments);
					};
				})(msg.symbol.name);
			} else {
				var makeProp = function(obj, name) {
					obj['tprop_' + name] = msg.expression.eval(env);
					name = 'tpmeth_' + name;
					obj[name] = function() {
						return this[name];
					};
					var setname = name+'!';
					obj[setname] = function(val) {
						this[setname] = val;
						return this;
					};
				};
				makeProp(obj, msg.symbol.name);
			}
		} else {
			throw new Error('pseudo function calls in object definitions not implemented yet');
		}
	}
	return obj;
};

lambda.prototype.eval = function(parentenv) {
	var args = this.args;
	var exprs = this.expressions;
	return function() {
		var env = new environment(parentenv);
		for (var i = 0, j = 0; i < arguments.length && j < args.length; i++, j++) {
			while (j < args.length && args[j].cleanName() == 'self') {
				j++;
			}
			env.syms[args[j].cleanName()] = arguments[i];
		}
		if (this != null && (args.length == 0 || args[0].cleanName() != 'self')) {
			env.syms['self'] = this;
		}
		var res = null;
		for (var i = 0; i < exprs.length; i++) {
			res = exprs[i].eval(env);
		}
		return res;
	};
};

assignment.prototype.eval = function(env) {
	var val = this.expression.eval(env);
	env.findSet(this.symbol.name, val);
	return val;
};