# HG changeset patch # User Mike Pavone # Date 1382580603 25200 # Node ID b4a9d4e405c5eef9dcfe0a3d6078300cb00194d5 # Parent 6fe9343b140045b0bf227c835296cf60d82280d6 Implemented a simple interpreter to be used for macro expansion and a driver for testing it diff -r 6fe9343b1400 -r b4a9d4e405c5 interp.js --- /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; +}; diff -r 6fe9343b1400 -r b4a9d4e405c5 tpi.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tpi.js Wed Oct 23 19:10:03 2013 -0700 @@ -0,0 +1,103 @@ +var module = {exports: {}}; +var console = {log: function(text){}}; +var PEG; + +var file = null; +var argtype = 'normal'; +var includes = ['.']; +var basedir = ''; +var debugmode = false; +for (var i = 0; i < arguments.length; i++) { + switch (argtype) { + case 'normal': + switch (arguments[i]) { + case '-basedir': + case '-i': + case '-interpdebug': + debugmode = true; + break; + default: + if (arguments[i].charAt(0) == '-') { + print("unrecognized switch", arguments[i]); + quit(1); + } + file = arguments[i]; + } + break; + case '-basedir': + if (basedir == '') { + basedir = arguments[i]; + argtype = 'normal'; + } else { + print("only one -basedir option allowed"); + quit(1); + } + break; + case '-i': + includes.push(arguments[i]); + argtype = 'normal'; + break; + case '-backend': + backend = arguments[i]; + argtype = 'normal'; + break; + } +} +if (argtype != 'normal') { + print("switch", argtype, "expects a parameter"); + quit(1); +} + +if (!file) { + print('usage: d8 tpi.js -- filename'); + quit(1); +} +includes.push(basedir + 'modules'); +interpFile(file, basedir, includes, debugmode); + + +function parseFile(filename) +{ + debugprint('//parsing', filename); + var text = read(filename); + try { + var parsed = parser.parse(text); + } catch (error) { + print('SyntaxError on at', error.line, ',', error.column, ':', error.message); + var lines = text.split('\n'); + print(lines[error.line-1]); + var spacer = ''; + for (var i = 1; i < error.column; i++) { + if (lines[error.line-1].charAt(i-1) == '\t') { + spacer += ' '; + } else { + spacer += ' '; + } + } + print(spacer + '^'); + quit(1); + } + return parsed; +} + + +function interpFile(filename, basedir, includes, debugmode) +{ + load(basedir + 'mquery.js'); + load(basedir + 'peg.js'); + PEG = module.exports; + load(basedir + 'parser.js'); + load(basedir + 'interp.js'); + + if (debugmode) { + debugprint = print; + } else { + debugprint = function() {}; + } + var parsed = parseFile(filename); + + toplevel = new topenv(includes); + + var mainModule = parsed.eval(toplevel); + return mainModule.tpmeth_main(); +}