Mercurial > repos > tabletprog
view interp.js @ 207:60eff5f81d9a
Basic implementation of macros is now working
author | Mike Pavone <pavone@retrodev.com> |
---|---|
date | Tue, 19 Nov 2013 22:02:11 -0800 |
parents | b4a9d4e405c5 |
children | a1b4a2bc8d72 |
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.macroexpand(this).eval(this); return this.modules[name]; } return null; }, findNoTop: function(name) { return null; }, findSetPresent: function(name, val) { return false; }, findMacro: function(name) { return null; }, } function environment(parent) { this.parent = parent; this.syms = {}; this.macros = {}; } 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; }, findMacro: function(name) { if (name in this.syms) { if (name in this.macros) { return this.syms[name]; } return null; } if (this.parent) { return this.parent.findMacro(name); } return null; }, defMacro: function(name, def) { this.syms[name] = def; this.macros[name] = true; } }; function makeASTNode(val) { if (typeof val == 'number') { return new intlit(val); } if (typeof val == 'string') { return new stringlit(val); } if (val instanceof Array) { return new arraylit(val); } if (val == tptrue) { return new symbol('true'); } if (val == tpfalse) { return new symbol('false'); } return val; } 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; }; op.prototype.macroexpand = function(env) { this.left = this.left.macroexpand(env); this.right = this.right.macroexpand(env); return this; }; 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; }; symbol.prototype.macroexpand = intlit.prototype.macroexpand = floatlit.prototype.macroexpand = strlit.prototype.macroexpand = arraylit.prototype.macroexpand = listlit.prototype.macroexpand = function(env) { return this; }; funcall.prototype.eval = function(env) { var args = []; var name = this.name; if (name[name.length-1] == ":") { name = name.substr(0, name.length-1); } if (name == 'quote') { if (this.receiver) { return this.receiver; } if (this.args.length) { return this.args[0]; } throw new Error('quote takes an argument'); } if (name == 'macro') { return null; } 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)); } if (name == 'eval:else') { try { var res = args[0].eval(env); } catch(e) { return args[2].call(null); } return args[1].call(null, res); } var fun = env.findNoTop(name); if (fun) { return fun.apply(null, args); } else { //if (typeof args[0]'tpmeth_'+name in args[0]) { //try { return args[0]['tpmeth_'+name].apply(args[0], args.slice(1)); /*} catch(e) { throw new Error('Error, \n\t' + e.message.split('\n').join('\n\t') + '\ninvoking method ' + name + ' on object ' + args[0] + ' ' + JSON.stringify(Object.keys(args[0]))); }*/ /*} else {JSON.stringify throw new Error('No method named ' + name + ' on object ' + JSON.stringify(args[0])); }*/ } }; funcall.prototype.macroexpand = function(env) { var name = this.name; if (name[name.length-1] == ":") { name = name.substr(0, name.length-1); } var macro = env.findMacro(name); if (this.receiver) { this.receiver = this.receiver.macroexpand(env); } for (var i = 0; i < this.args.length; i++) { this.args[i] = this.args[i].macroexpand(env); } if (!macro) { return this; } var args = []; if (this.receiver) { args.push(this.receiver); } for (var i = 0; i < this.args.length; i++) { args.push(this.args[i]); } return makeASTNode(macro.apply(null, args)); }; 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; }; object.prototype.macroexpand = function(parentenv) { var env = new environment(parentenv); var obj = {env: env}; var outmessages = []; for (var i = 0; i < this.messages.length; i++) { var msg = this.messages[i].macroexpand(env); if (msg instanceof assignment) { if (msg.expression instanceof lambda) { (function(name, expr) { env.syms[name] = function() { if (!obj['tpmeth_' + name]) { obj['tpmeth_' + name] = expr.eval(env); } return obj['tpmeth_' + name].apply(obj, arguments); }; })(msg.symbol.name, msg.expression); outmessages.push(msg); } else if (msg.expression instanceof funcall && msg.expression.name == 'macro:') { env.defMacro(msg.symbol.name, msg.expression.args[0].eval(env)); } else { outmessages.push(msg); /* 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 { outmessages.push(msg); //throw new Error('pseudo function calls in object definitions not implemented yet'); } } this.messages = outmessages; return this; }; 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; }; }; lambda.prototype.macroexpand = function(parentenv) { var env = new environment(parentenv); for (var i = 0; i < this.args.length; i++) { env.syms[this.args[i].cleanName()] = {}; } for (var i = 0; i < this.expressions.length; i++) { var expr = this.expressions[i]; if (expr instanceof assignment) { if (expr.expression instanceof funcall && expr.expression.name == 'macro:') { env.defMacro(expr.symbol.name, exp.expression.args[0].eval(env)); } else { env.syms[expr.symbol.cleanName()] = {}; this.expressions[i] = expr.macroexpand(env); } } else { this.expressions[i] = expr.macroexpand(env); } } return this; }; assignment.prototype.eval = function(env) { var val = this.expression.eval(env); env.findSet(this.symbol.name, val); return val; }; assignment.prototype.macroexpand = function(env) { this.expression = this.expression.macroexpand(env); return this; };