Mercurial > repos > tabletprog
view interp.js @ 209:4b3b57f39f10
Implement zeroPlus macro
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Wed, 27 Nov 2013 23:36:24 -0800 |
parents | a1b4a2bc8d72 |
children | d0848563f25d |
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_asStringChar = function() { return String.fromCharCode(this); }; Number.prototype['tpmeth_isString?'] = function() { return false; }; 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_length = function() { return this.length; }; String.prototype.tpmeth_byte_length = function() { return this.length; }; String.prototype.tpmeth_byte = function(index) { return this.charCodeAt(index); }; String.prototype['tpmeth_from:withLength'] = function(pos, length) { return this.substr(pos, length); }; String.prototype['tpmeth_isString?'] = function() { return true; }; String.prototype.tpmeth_print = function() { print(this); }; Function.prototype['tpmeth_while:do'] = function(body) { var ret = null; for (;;) { var res = this.call(null); if (res != tptrue) { break; } ret = body.call(null); } return ret; }; 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; }, findQuoteTrans: function(name) { return null; } } function environment(parent) { this.parent = parent; this.syms = {}; this.macros = {}; this.quotetrans = {}; } 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; }, findQuoteTrans: function(name) { if (name in this.quotetrans) { return this.quotetrans[name]; } if (this.parent) { return this.parent.findQuoteTrans(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 strlit(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 name = this.op; if (name == '&&' || name == '||') { var r = (new lambda([], [this.right])).eval(env); } else { var r = this.right.eval(env); } var fun = env.findNoTop(name); var ret; if (fun) { ret = fun(l,r) } else { if (name == '&&') { name = 'if' } else if (name == '||') { name = 'ifnot' } 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; }; op.prototype.quote = function(env) { var left = this.left.quote(env); var right = this.right.quote(env); return new op(left, this.op, right); }; op.prototype.tpmeth_nodeType = function() { return "op"; }; op.prototype.tpmeth_left = function() { return this.left; }; op.prototype.tpmeth_right = function() { return this.right; }; op.prototype.tpmeth_opName = function() { return this.op; }; var quote_prefix = 0; symbol.prototype.eval = function(env) { var res = env.find(this.name); if (res === null) { throw new Error('Symbol ' + this.name + ' is not bound'); } return res; }; symbol.prototype.quote = function(env) { var val = env.find(this.name); if (val) { var newnode = makeASTNode(val); if (!(newnode instanceof symbol)) { newnode = newnode.quote(env); } return newnode; } else { var hygenic = env.findQuoteTrans(this.name); return hygenic ? new symbol(hygenic, this.symbols) : this; } }; symbol.prototype.tpmeth_nodeType = function() { return "symbol"; }; symbol.prototype.tpmeth_name = function() { return this.cleanName(); }; 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; }; intlit.prototype.quote = floatlit.prototype.quote = strlit.prototype.quote = arraylit.prototype.quote = listlit.prototype.quote = function(env) { return this; }; intlit.prototype.tpmeth_nodeType = function() { return "intlit"; }; floatlit.prototype.tpmeth_nodeType = function() { return "floatlit"; }; strlit.prototype.tpmeth_nodeType = function() { return "strlit"; }; arraylit.prototype.tpmeth_nodeType = function() { return "arraylit"; }; listlit.prototype.tpmeth_nodeType = function() { return "strlit"; }; intlit.prototype.tpmeth_value = floatlit.prototype.tpmeth_value = strlit.prototype.tpmeth_value = arraylit.prototype.tpmeth_value = listlit.prototype.tpmeth_value = function() { 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 (name == 'quote') { if (this.receiver) { return this.receiver.quote(env); } if (this.args.length) { var cur = env; return this.args[0].quote(env); } 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]); } /*} 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)); }; funcall.prototype.quote = function(env) { var receiver = this.receiver ? this.receiver.quote(env) : null; var args = []; for (var i = 0; i < this.args.length; i++) { args.push(this.args[i].quote(env)); } var name = this.name; if (name[name.length-1] == ":") { name = name.substr(0, name.length-1); } var fun = env.find(name); if (fun) { fun = makeASTNode(fun); if (fun instanceof symbol) { name = fun.cleanName(); } else if (fun instanceof lambda) { throw new Error('FIXME'); } } else { var hygenic = env.findQuoteTrans(this.name); if (hygenic) { name = hygenic; } } var ret = new funcall(name, args); ret.receiver = receiver; return ret; }; funcall.prototype.tpmeth_nodeType = function() { return "funcall"; }; 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; }; object.prototype.quote = function(parentenv) { var env = new environment(parentenv); var outmessages = []; for (var i = 0; i < this.messages.length; i++) { var msg = this.messages[i]; if (msg instanceof assignment) { //Make sure method names don't get renamed for hygene env.syms[msg.symbol.name] = null; env.quotetrans[msg.symbol.name] = msg.symbol.name; if (msg.expression instanceof lambda) { env.syms[msg.symbol.name + '!'] = null; env.quotetrans[msg.symbol.name + '!'] = msg.symbol.name + '!'; } } } for (var i = 0; i < this.messages.length; i++) { var msg = this.messages[i]; if (msg instanceof assignment) { outmessages.push(new assignment(msg.symbol, msg.expression.quote(env))); } else { outmessages.push(msg.quote(env)); } } return new object(outmessages); }; object.prototype.tpmeth_nodeType = function() { return "object"; }; 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; }; lambda.prototype.quote = function(parentenv) { var args = []; var expressions = []; var env = new environment(parentenv); for (var i = 0; i < this.args.length; i++) { env.syms[this.args[i].cleanName()] = null; var hygenic = '' + quote_prefix + this.args[i].cleanName(); env.quotetrans[this.args[i].cleanName()] = hygenic; args.push(new symbol(hygenic, this.args[i].symbols)); } for (var i = 0; i < this.expressions.length; i++) { expressions.push(this.expressions[i].quote(env)); } return new lambda(args, expressions); }; lambda.prototype.tpmeth_nodeType = function() { return "lambda"; }; 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; }; assignment.prototype.quote = function(env) { var name = this.symbol.cleanName(); env.syms[name] = null; var hygenic = '' + quote_prefix + name; env.quotetrans[name] = hygenic; return new assignment(new symbol(hygenic, this.symbol.symbols), this.expression.quote(env)); }; assignment.prototype.tpmeth_nodeType = function() { return "assignment"; };