Mercurial > repos > tabletprog
diff 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 diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/interp.js Wed Oct 23 19:10:03 2013 -0700 @@ -0,0 +1,245 @@ +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; +};