changeset 208:a1b4a2bc8d72

Initial work on pattern match macrosfor the new parser
author Mike Pavone <pavone@retrodev.com>
date Fri, 22 Nov 2013 19:37:25 -0800
parents 60eff5f81d9a
children 4b3b57f39f10
files interp.js modules/parser.tp
diffstat 2 files changed, 254 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/interp.js	Tue Nov 19 22:02:11 2013 -0800
+++ b/interp.js	Fri Nov 22 19:37:25 2013 -0800
@@ -25,6 +25,9 @@
 Number.prototype['tpmeth_<='] = function(other) {
 	return this <= other ? tptrue : tpfalse;
 };
+Number.prototype.tpmeth_asStringChar = function() {
+	return String.fromCharCode(this);
+};
 Number.prototype.tpmeth_string = function() {
 	return '' + this;
 };
@@ -38,9 +41,33 @@
 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_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;
@@ -93,6 +120,9 @@
 	findMacro: function(name) {
 		return null;
 	},
+	findQuoteTrans: function(name) {
+		return null;
+	}
 }
 
 function environment(parent)
@@ -100,6 +130,7 @@
 	this.parent = parent;
 	this.syms = {};
 	this.macros = {};
+	this.quotetrans = {};
 }
 
 environment.prototype = {
@@ -150,6 +181,15 @@
 		}
 		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;
@@ -162,7 +202,7 @@
 		return new intlit(val);
 	}
 	if (typeof val == 'string') {
-		return new stringlit(val);
+		return new strlit(val);
 	}
 	if (val instanceof Array) {
 		return new arraylit(val);
@@ -196,8 +236,30 @@
 	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);
+};
+
+var quote_prefix = 0;
+
 symbol.prototype.eval = function(env) {
-	return env.find(this.name);
+	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) {
+		return makeASTNode(val);
+	} else {
+		var hygenic = env.findQuoteTrans(this.name);
+		return hygenic ? new symbol(hygenic, this.symbols) : this;
+	}
 };
 
 intlit.prototype.eval =
@@ -216,6 +278,14 @@
 	return this;
 };
 
+intlit.prototype.quote =
+	floatlit.prototype.quote =
+	strlit.prototype.quote =
+	arraylit.prototype.quote =
+	listlit.prototype.quote = function(env) {
+	return this;
+};
+
 funcall.prototype.eval = function(env) {
 	var args = [];
 	var name = this.name;
@@ -224,10 +294,10 @@
 	}
 	if (name == 'quote') {
 		if (this.receiver) {
-			return this.receiver;
+			return this.receiver.quote(env);
 		}
 		if (this.args.length) {
-			return this.args[0];
+			return this.args[0].quote(env);
 		}
 		throw new Error('quote takes an argument');
 	}
@@ -253,11 +323,11 @@
 		return fun.apply(null, args);
 	} else {
 		//if (typeof args[0]'tpmeth_'+name in args[0]) {
-			//try {
+			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])));
-			}*/
+			} 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]));
 		}*/
@@ -289,6 +359,21 @@
 	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 = env.findQuoteTrans(this.name);
+	if (!name) {
+		name = this.name;
+	}
+	var ret = new funcall(name, args);
+	ret.receiver = receiver;
+	return ret;
+};
+
 object.prototype.eval = function(parentenv) {
 	var env = new environment(parentenv);
 	var obj = {env: env};
@@ -369,6 +454,32 @@
 	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);
+};
+
 lambda.prototype.eval = function(parentenv) {
 	var args = this.args;
 	var exprs = this.expressions;
@@ -380,7 +491,7 @@
 			}
 			env.syms[args[j].cleanName()] = arguments[i];
 		}
-		if (this != null && (args.length == 0 || args[0].cleanName() != 'self')) {
+		if (this != null && !(args.length == 0 || args[0].cleanName() != 'self')) {
 			env.syms['self'] = this;
 		}
 		var res = null;
@@ -412,6 +523,22 @@
 	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);
+};
+
 assignment.prototype.eval = function(env) {
 	var val = this.expression.eval(env);
 	env.findSet(this.symbol.name, val);
@@ -422,3 +549,11 @@
 	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));
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/parser.tp	Fri Nov 22 19:37:25 2013 -0800
@@ -0,0 +1,109 @@
+#{
+	expandClass <- :chars {
+		if: (chars length) > 0 {
+			pos <- 0
+			inverted <- false
+			if: (chars byte: 0) = ("^" byte: 0) {
+				pos <- 1
+				inverted <- true
+			}
+			state_begin <- 0
+			state_normal <- 1
+			state_rangeend <- 2
+			state <- state_begin
+			out <- ""
+			while: { pos < (chars byte_length)} do: {
+				if: state = state_begin {
+					out <- out . (chars from: pos withLength: 1)
+					state <- state_normal
+				} else: {
+					if: state = state_normal {
+						if: (chars byte: pos) = ("-" byte: 0) {
+							state <- state_rangeend
+						} else: {
+							out <- out . (chars from: pos withLength: 1)
+						}
+					} else: {
+						rangestart <- out byte: ((out byte_length) - 1)
+						rangeend <- chars byte: pos
+						if: rangeend < rangestart {
+							tmp <- rangeend
+							rangeend <- rangestart
+							rangestart <- tmp
+						}
+						out <- out from: 0 withLength: ((out length) - 1)
+						while: { rangestart <= rangeend } do: {
+							out <- out . (rangestart asStringChar)
+							rangestart <- rangestart + 1
+						}
+						state <- state_begin
+					}
+				}
+				pos <- pos + 1
+			}
+			if: inverted {
+				old <- out
+				out <- ""
+				cur <- 0
+				while: { cur < 256 } do: {
+					out <- out . (cur asStringChar)
+					cur <- cur + 1
+				}
+			}
+			out
+		} else: {
+			""
+		}
+	}
+	charClass <- macro: :rawchars {
+		eval: rawchars :chars {
+			chars <- expandClass: chars
+			//TODO: Use a more sophisticated approach for large classes
+			quote: :tomatch {
+				if: (tomatch isString?) {
+					check <- 0
+
+					nomatch <- true
+					while: { nomatch && check < (chars byte_length) } do: {
+						if: (tomatch byte: 0) = (chars byte: check) {
+							nomatch <- false
+						}
+						check <- check + 1
+					}
+					if: nomatch {
+						#{
+							matched? <- { false }
+						}
+					} else: {
+						#{
+							matched? <- { true }
+							matchlen <- { 1 }
+						}
+					}
+				} else: {
+					#{
+						matched? <- { false }
+					}
+				}
+			}
+		} else: {
+			print: "uh oh"
+		}
+	}
+	alpha <- charClass: "a-zA-Z"
+
+	main <- {
+		cmatch <- alpha: "c0123"
+		zeromatch <- alpha: "01234"
+		if: (cmatch matched?) {
+			print: "c0123 matched with length " . (cmatch matchlen) . "\n"
+		} else: {
+			print: "c0123 didn't match\n"
+		}
+		if: (zeromatch matched?) {
+			print: "0123 matched with length " . (zeromatch matchlen) . "\n"
+		} else: {
+			print: "0123 didn't match\n"
+		}
+	}
+}