Mercurial > repos > tabletprog
diff interp.js @ 217:adad61ea2f3a
Switched to a less hacky implementation of hygiene and exposed more AST properties to macros
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sat, 21 Dec 2013 12:07:51 -0800 |
parents | e00a8bc6361b |
children | b70be565d54c |
line wrap: on
line diff
--- a/interp.js Sat Dec 21 12:07:00 2013 -0800 +++ b/interp.js Sat Dec 21 12:07:51 2013 -0800 @@ -119,8 +119,12 @@ } if (name in this.names) { var parsed = parseFile(this.names[name]); - this.modules[name] = parsed.macroexpand(this).eval(this); - return this.modules[name]; + var ret = parsed.macroexpand(this).eval(this); + if (typeof ret == 'function') { + ret = ret(); + } + this.modules[name] = ret; + return ret; } return null; }, @@ -246,6 +250,8 @@ name = 'if' } else if (name == '||') { name = 'ifnot' + } else if (name == '|') { + return r['tpmeth_|'](l); } ret = l['tpmeth_'+name](r); } @@ -264,6 +270,12 @@ return new op(left, this.op, right); }; +op.prototype.makeHygienic = function(env) { + var left = this.left.makeHygienic(env); + var right = this.right.makeHygienic(env); + return new op(left, this.op, right); +}; + op.prototype.tpmeth_nodeType = function() { return "op"; }; @@ -294,17 +306,24 @@ var val = env.find(this.name); if (val) { var newnode = makeASTNode(val); - if (!(newnode instanceof symbol)) { - if ('quote' in newnode) { - newnode = newnode.quote(env); - } else { - throw new Error('Symbol ' + this.name + ' is not bound to a valid AST value, instead it is bound to an object with keys ' + JSON.stringify(Object.keys(newnode))); - } - } return newnode; } else { - var hygenic = env.findQuoteTrans(this.name); - return hygenic ? new symbol(hygenic, this.symbols) : this; + this.dirty = true; + return this; + } +}; + +symbol.prototype.makeHygienic = function(env) { + if (this.dirty) { + var hygenic = env.findQuoteTrans(this.cleanName()); + if (hygenic) + { + return new symbol(hygenic, this.symbols); + } else { + return this; + } + } else { + return this; } }; @@ -315,6 +334,10 @@ symbol.prototype.tpmeth_name = function() { return this.cleanName(); }; +symbol.prototype['tpmeth_name!'] = function(val) { + this.name = val; + return this; +}; intlit.prototype.eval = floatlit.prototype.eval = @@ -323,6 +346,14 @@ return this.val; }; +listlit.prototype.eval = function(env) { + var cur = tplist.tpmeth_empty(); + for (var idx = this.val.length - 1; idx >= 0; --idx) { + cur = tplist['tpmeth_node:withTail'](this.val[idx].eval(env), cur); + } + return cur; +}; + symbol.prototype.macroexpand = intlit.prototype.macroexpand = floatlit.prototype.macroexpand = @@ -347,6 +378,14 @@ return this; }; +intlit.prototype.makeHygienic = + floatlit.prototype.makeHygienic = + strlit.prototype.makeHygienic = + arraylit.prototype.makeHygienic = + listlit.prototype.makeHygienic = function(env) { + return this; +}; + intlit.prototype.tpmeth_nodeType = function() { return "intlit"; }; @@ -457,7 +496,10 @@ for (var i = 0; i < this.args.length; i++) { args.push(this.args[i]); } - return makeASTNode(macro.apply(null, args)); + var ret = makeASTNode(macro.apply(null, args)); + ret = ret.makeHygienic(env); + quote_prefix++; + return ret; }; funcall.prototype.quote = function(env) { @@ -479,10 +521,26 @@ throw new Error('FIXME'); } } else { - var hygenic = env.findQuoteTrans(this.name); - if (hygenic) { - name = hygenic; - } + this.dirty = true; + } + var ret = new funcall(name, args); + ret.receiver = receiver; + return ret; +}; + +funcall.prototype.makeHygienic = function(env) { + var receiver = this.receiver ? this.receiver.makeHygienic(env) : null; + var args = []; + for (var i = 0; i < this.args.length; i++) { + args.push(this.args[i].makeHygienic(env)); + } + var name = this.name; + if (name[name.length-1] == ":") { + name = name.substr(0, name.length-1); + } + var hygienic = env.findQuoteTrans(name); + if (hygienic) { + name = hygienic; } var ret = new funcall(name, args); ret.receiver = receiver; @@ -493,6 +551,11 @@ return "funcall"; }; +funcall.prototype['tpmeth_args!'] = function(arglist) { + this.args = []; + //TODO: Finish this +}; + object.prototype.eval = function(parentenv) { var env = new environment(parentenv); var obj = {env: env}; @@ -581,12 +644,9 @@ 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) { + if (!(msg.expression instanceof lambda)) { env.syms[msg.symbol.name + '!'] = null; - env.quotetrans[msg.symbol.name + '!'] = msg.symbol.name + '!'; } } } @@ -601,10 +661,41 @@ return new object(outmessages); }; +object.prototype.makeHygienic = 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) { + env.syms[msg.symbol.name] = null; + //make sure method names don't get translated for hygiene + env.quotetrans[msg.symbol.name] = null; + if (!(msg.expression instanceof lambda)) { + env.syms[msg.symbol.name + '!'] = null; + env.quotetrans[msg.symbol.name + '!'] = null; + } + } + } + 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.makeHygienic(env))); + } else { + outmessages.push(msg.makeHygienic(env)); + } + } + return new object(outmessages); +}; + object.prototype.tpmeth_nodeType = function() { return "object"; }; +object.prototype.tpmeth_addMessage = function(message) { + this.messages.push(makeASTNode(message)); + return this; +}; + lambda.prototype.eval = function(parentenv) { var args = this.args; var exprs = this.expressions; @@ -654,9 +745,9 @@ 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)); + var sym = new symbol(this.args[i].name, this.args[i].symbols); + sym.dirty = true; + args.push(sym); } for (var i = 0; i < this.expressions.length; i++) { expressions.push(this.expressions[i].quote(env)); @@ -664,6 +755,26 @@ return new lambda(args, expressions); }; +lambda.prototype.makeHygienic = 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; + if (this.args[i].dirty) { + var hygienic = '' + quote_prefix + this.args[i].cleanName(); + env.quotetrans[this.args[i].cleanName()] = hygienic; + args.push(new symbol(hygienic, this.args[i].symbols)); + } else { + args.push(this.args[i]); + } + } + for (var i = 0; i < this.expressions.length; i++) { + expressions.push(this.expressions[i].makeHygienic(env)); + } + return new lambda(args, expressions); +}; + lambda.prototype.numArgs = function() { var num = this.args.length; if (num && (this.args[0].cleanName() == 'self')) { @@ -676,6 +787,37 @@ return "lambda"; }; +lambda.prototype.tpmeth_args = function() { + var cur = tplist.tpmeth_empty(); + for (var idx = this.args.length - 1; idx >= 0; --idx) { + cur = tplist['tpmeth_node:withTail'](this.args[idx], cur); + } + return cur; +}; + +lambda.prototype.tpmeth_expressions = function() { + var cur = tplist.tpmeth_empty(); + for (var idx = this.expressions.length - 1; idx >= 0; --idx) { + cur = tplist['tpmeth_node:withTail'](this.expressions[idx], cur); + } + return cur; +}; + +lambda.prototype['tpmeth_expressions!'] = function(exprs) { + var expressions = []; + while (!exprs['tpmeth_empty?']().valueOf()) { + expressions.push(makeASTNode(exprs.tpmeth_value())); + exprs = exprs.tpmeth_tail(); + } + this.expressions = expressions; + return this; +}; + +lambda.prototype.tpmeth_addExpression = function(expr) { + this.expressions.push(makeASTNode(expr)); + return this; +}; + assignment.prototype.eval = function(env) { var val = this.expression.eval(env); env.findSet(this.symbol.name, val); @@ -689,12 +831,49 @@ assignment.prototype.quote = function(env) { var name = this.symbol.cleanName(); + var val = env.find(name); + var dirty = true; + if (val) { + var node = makeASTNode(val); + if (!(node instanceof symbol)) { + throw new Error('Left hand side of assignment expression must be a symbol!'); + } + name = node.cleanName(); + dirty = node.dirty; + } 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)); + var sym = new symbol(name, this.symbol.symbols); + sym.dirty = dirty; + return new assignment(sym, this.expression.quote(env)); +}; + +assignment.prototype.makeHygienic = function(env) { + var name = this.symbol.cleanName(); + env.syms[name] = null; + if (this.symbol.dirty) { + name = env.quotetrans[name] = '' + quote_prefix + name; + } + return new assignment(new symbol(name, this.symbol.symbols), this.expression.makeHygienic(env)); }; assignment.prototype.tpmeth_nodeType = function() { return "assignment"; }; + +assignment.prototype.tpmeth_symbol = function() { + return this.symbol; +}; + +assignment.prototype['tpmeth_symbol!'] = function(val) { + this.symbol = makeASTNode(val); + return this; +}; + +assignment.prototype.tpmeth_expression = function() { + return this.expression; +}; + +assignment.prototype['tpmeth_expression!'] = function(val) { + this.expression = makeASTNode(val); + return this; +};