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;
+};