changeset 92:6abbf2454657

merge
author William Morgan <bill@mrgn.org>
date Mon, 23 Jul 2012 01:07:52 -0700
parents 3bf57ace3e0b (current diff) 25bc8a5ab41e (diff)
children 46504d34cb44
files
diffstat 8 files changed, 429 insertions(+), 198 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TASKS	Mon Jul 23 01:07:52 2012 -0700
@@ -0,0 +1,16 @@
+Improve string library
+Add basic UTF-8 support
+Implement import: in C backend
+Implement route:via
+Implement a hash dict based on hash set implementation
+Rejigger how built-in type/modules are handled slightly so we can add array new: which will return an array pre-allocated to a certain size
+Implement immutable objects
+Implement lists
+Add support for actors
+Check for breakage in Javascript backend and fix it
+Port all library stuff from C backend to Javascript backend
+Fix block comments in grammar
+Add dict literals to grammar and compiler
+Re-write compiler in TP
+Finish type system design
+Implement type system
\ No newline at end of file
--- a/cbackend.js	Sun Jul 22 15:10:07 2012 -0700
+++ b/cbackend.js	Mon Jul 23 01:07:52 2012 -0700
@@ -44,6 +44,14 @@
 	var method = optoMeth[this.op];
 	return 'mcall(' + getMethodId(method) + '/* ' + method + ' */, 2, (object *)' + this.left.toC() + ', ' + this.right.toC() + ')\n';
 };
+op.prototype.toCLLExpr = function(vars) {
+	var opmap = {'=': '==', 'xor': '^'};
+	return this.left.toCLLExpr(vars) + (this.op in opmap ? opmap[this.op] : this.op) + this.right.toCLLExpr(vars);
+};
+op.prototype.toCLines = function(vars, needsreturn) {
+	return [ (needsreturn ? 'return (object *)' : '' ) + this.toCLLExpr(vars) + ';'];
+};
+
 
 function escapeCName(name)
 {
@@ -108,6 +116,40 @@
 	}
 	return getSymbolPrefix(info, this.symbols) + escapeCName(name);
 }
+symbol.prototype.toCTypeName = function() {
+	return this.cleanName();
+};
+symbol.prototype.toCLLExpr = function(vars) {
+	var name = this.cleanName();
+	if (name in vars) {
+		return name;
+	}
+	if (name == 'self') {
+		return 'self';
+	}
+	var info = this.symbols.find(name, false, true);
+	var symbols = this.symbols;
+	while (info && info.type == 'local') {
+		symbols = symbols.parent;
+		info = symbols.find(name, false, true);
+	}
+	if (!info) {
+		return name;
+	}
+	if (info.type == 'toplevel') {
+		return toplevel.moduleVar(name);
+	} else if (info.type == 'self') {
+		if (info.isll || !(info.def instanceof lambda)) {
+			return 'self->' + name;
+		} else {
+			return 'mcall(' + getMethodId(name) + '/* ' + name + ' */, 1, self)';
+		}
+	}
+	throw new Error('Unsupported reference type ' + info.type + ' for variable ' + name);
+};
+symbol.prototype.toCLines = function(vars, needsreturn) {
+	return [ (needsreturn ? 'return (object *)' : '' ) + this.toCLLExpr(vars) + ';' ];
+};
 
 var declaredInts = {};
 
@@ -119,10 +161,22 @@
 	}
 	return '((object *)&int32_' + str + ')';
 }
+intlit.prototype.toCLLExpr = function(vars) {
+	return this.val.toString();
+};
+intlit.prototype.toCLines = function(vars, needsreturn) {
+	return [ (needsreturn ? 'return (object *)' : '' ) + this.toCLLExpr(vars) + ';' ];
+};
 
 floatlit.prototype.toC = function() {
 	return 'make_float(' + this.val.toString() + ')';
 }
+floatlit.prototype.toCLLExpr = function(vars) {
+	return this.val.toString();
+};
+floatlit.prototype.toCLines = function(vars, needsreturn) {
+	return [ (needsreturn ? 'return (object *)' : '' ) + this.toCLLExpr(vars) + ';' ];
+};
 
 var declaredStrings = {};
 var nextStringId = 0;
@@ -134,7 +188,13 @@
 		declaredStrings[this.val] = nextStringId++;
 	}
 	return '((object *)&str_' + declaredStrings[this.val] + ')';
-}
+};
+strlit.prototype.toCLLExpr = function(vars) {
+	return '"' + this.val.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n').replace('\r', '\\r') + '"';
+};
+strlit.prototype.toCLines = function(vars, needsreturn) {
+	return [ (needsreturn ? 'return (object *)' : '' ) + this.toCLLExpr(vars) +';' ];
+};
 
 listlit.prototype.toC = function() {
 	var ret = 'make_list(' + this.val.length;
@@ -162,6 +222,8 @@
 		} else {
 			throw new Error("Unexpected AST type for foreign:");
 		}
+	} else if(name == 'llProperty:withType' || name == 'llProperty:withVars:andCode') {
+		return null;
 	}
 	var args = this.args.slice(0, this.args.length);
 	if (this.receiver) {
@@ -170,7 +232,7 @@
 	var method = false;
 	var funinfo = this.symbols.find(name);
 	var start = 0;
-	if (!funinfo || funinfo.def instanceof setter) {
+	if (!funinfo || funinfo.def instanceof setter || funinfo.type == 'toplevel') {
 		method = true;
 	} else {
 		switch(funinfo.type)
@@ -207,7 +269,113 @@
 		callpart = 'ccall(' + (new symbol(name, this.symbols)).toC();
 	}
 	return callpart + ', ' + args.length + args.join('') + ')';
-}
+};
+funcall.prototype.toCTypeName = function() {
+	switch(this.name)
+	{
+	case 'ptr:':
+	case 'ptr':
+		var receiver = this.receiver ? this.receiver : this.args[0];
+		return receiver.toCTypeName() + ' *';
+		break;
+	default:
+		throw new Error('invalid use of funcall expression where a C type name is expected');
+	}
+};
+funcall.prototype.toCLines = function(vars, needsreturn) {
+	var lines = [];
+	var name = this.name[this.name.length-1] == ':' ? this.name.substr(0, this.name.length-1) : this.name;
+	var args = this.args.slice(0, this.args.length);
+	if (this.receiver) {
+		args.splice(0, 0, [this.receiver]);
+	}
+	switch(name)
+	{
+	case 'if':
+		lines.push('if (' + this.args[0].toCLLExpr(vars) + ') {');
+		var blines = this.args[1].toCLines(vars, needsreturn);
+		for (var i in blines) {
+			lines.push('\t' + blines[i]);
+		}
+		if (needsreturn) {
+			lines.push('} else {');
+			lines.push('\t return module_false;');
+			lines.push('}');
+		} else {
+			lines.push('}');
+		}
+		break;
+	case 'if:else':
+		lines.push('if (' + this.args[0].toCLLExpr(vars) + ') {');
+		var blines = this.args[1].toCLines(vars, needsreturn);
+		for (var i in blines) {
+			lines.push('\t' + blines[i]);
+		}
+		lines.push('} else {');
+		blines = this.args[2].toCLines(vars, needsreturn);
+		for (var i in blines) {
+			lines.push('\t' + blines[i]);
+		}
+		lines.push('}');
+		break;
+	case 'while:do':
+		if (needsreturn) {
+			throw new Error("while:do can't be last statement in llMessage code block");
+		}
+		lines.push('while (' + this.args[0].toCLLExpr(vars) + ') {');
+		var blines = this.args[1].toCLines(vars);
+		for (var i in blines) {
+			lines.push('\t' + blines[i]);
+		}
+		lines.push('}');
+		break;
+	default:
+		lines.push( (needsreturn ? 'return (object *)' : '') + this.toCLLExpr(vars) + ';');
+	}
+	return lines;
+};
+
+funcall.prototype.toCLLExpr = function(vars) {
+	var name = this.name[this.name.length-1] == ':' ? this.name.substr(0, this.name.length-1) : this.name;
+	var args = this.args.slice(0, this.args.length);
+	if (this.receiver) {
+		if(this.args.length == 0) {
+			return this.receiver.toCLLExpr(vars) + '->' + this.name;
+		} else if (this.args.length == 1 && name[name.length-1] == '!') {
+			return this.receiver.toCLLExpr(vars) + '->' + this.name.substr(0, name.length-1) + ' = ' + args[0].toCLLExpr(vars);
+		} else {
+			args.splice(0, 0, this.receiver);
+		}
+	}
+	switch(name)
+	{
+	case 'if':
+		return '((' + args[0].toCLLExpr(vars) + ') ? (' + args[1].toCLLExpr(vars) + ') : 0)';
+	case 'if:else':
+		return '((' + args[0].toCLLExpr(vars) + ') ? (' + args[1].toCLLExpr(vars) + ') : (' + args[2].toCLLExpr(vars) + '))';
+	case 'while:do':
+		throw new Error('while:do not allowed in expression context in llMessage block');
+	case 'addr_of':
+		return '&(' + args[0].toCLLExpr(vars) + ')';
+	case 'sizeof':
+		return 'sizeof(' + args[0].toCTypeName() + ')';
+	case 'get':
+		return args[0].toCLLExpr(vars) + '[' + args[1].toCLLExpr(vars) + ']';
+	case 'set':
+		return args[0].toCLLExpr(vars) + '[' + args[1].toCLLExpr(vars) + '] = ' + args[2].toCLLExpr(vars);
+	case 'not':
+		return '!(' + args[0].toCLLExpr(vars) + ')';
+	case 'mcall':
+		if (args[0] instanceof symbol) {
+			args[0] = new intlit(getMethodId(args[0].name));
+		}
+	default:
+		for (var i in args) {
+			args[i] = args[i].toCLLExpr(vars);
+		}
+		return name + '(' + args.join(', ') + ')';
+	}
+};
 
 function cObject(name) {
 	this.name = name;
@@ -355,11 +523,15 @@
 
 var nextobject = 0;
 
-object.prototype.toC = function() {
+
+object.prototype.toCObject = function() {
 	var messages = this.messages;
 	var values = [];
-	var imports = []
-	var me = new cObject('object_' + nextobject++);
+	var imports = [];
+	if (!this.name) {
+		this.name = 'object_' + nextobject++;
+	}
+	var me = new cObject(this.name);
 	this.symbols.typename = me.name;
 	if (this.symbols.needsenv) {
 		me.addProperty('env', this.symbols.envVar(), 'struct ' + this.symbols.getEnvType() + ' * ');
@@ -381,16 +553,34 @@
 					importsyms.push(new strlit(el.name));
 				});
 				imports.push({symbols: new listlit(importsyms), src: messages[i].args[1]});
+			} else if(messages[i].name == 'llProperty:withType:' && messages[i].args.length == 2) {
+				me.addProperty(messages[i].args[0].name, null, messages[i].args[1].toCTypeName())
+			} else if(messages[i].name == 'llMessage:withVars:andCode:' && messages[i].args.length == 3) {
+				var msgname = messages[i].args[0].name
+				var rawvars = messages[i].args[1].expressions;
+				var vars = {};
+				for(var v in rawvars) {
+					vars[rawvars[v].symbol.name] = rawvars[v].expression.toCTypeName();
+				}
+				me.addMessage(msgname, {
+					vars: vars,
+					lines: messages[i].args[2].toCLines(vars, true)
+				});
 			} else {
-				throw new Error('Only import and import:from calls allowed in object context');
+				
+				throw new Error('Only import and import:from calls allowed in object context. ' + messages[i].name + 'with ' + messages[i].args.length + ' arguments found instead.');
 			}
 		} else {
 			messages[i].toCObject(me);
 		}
 	}
 
-	return me.toC();
-}
+	return me;
+};
+
+object.prototype.toC = function() {
+	return this.toCObject().toC();
+};
 
 var toplevelcode;
 var forwarddec;
@@ -444,7 +634,7 @@
 			'str = (string *)make_object(&string_meta, NULL, 0);',
 			'str->data = GC_MALLOC(12);',
 			'sprintf(str->data, "%d", self->num);',
-			'str->length = str->bytes = strlen(str->data);',
+			'str->len = str->bytes = strlen(str->data);',
 			'return &(str->header);'
 		]
 	});
@@ -459,169 +649,20 @@
 
 function makeArray()
 {
-	var array = new cObject('array');
-	array.addProperty('size', null, 'uint32_t');
-	array.addProperty('storage', null, 'uint32_t');
-	array.addProperty('data', null, 'object **');
-	array.addMessage('get', {
-		vars: {index: 'obj_int32 *'},
-		lines: [
-			'index = va_arg(args, obj_int32 *);',
-			'if (index->num >= 0 && index->num < self->size) {',
-			'	return self->data[index->num];',
-			'}',
-			'return ' + toplevel.moduleVar('false') + ';'
-		]
-	});
-	array.addMessage('set', {
-		vars: {index: 'obj_int32 *'},
-		lines: [
-			'index = va_arg(args, obj_int32 *);',
-			'if (index->num >= 0 && index->num < self->size) {',
-			'	self->data[index->num] = va_arg(args, object *);',
-			'}',
-			'return &(self->header);'
-		]
-	});
-	array.addMessage('foreach', {
-		vars: {index: 'obj_int32 *', i: 'int32_t', clos: 'lambda *'},
-		lines: [
-			'clos = va_arg(args, lambda *);',
-			'for (i = 0; i < self->size; i++) {',
-			'	index = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);',
-			'	index->num = i;',
-			'	ccall(clos, 2, index, self->data[i]);',
-			'}',
-			'return &(self->header);'
-		]
-	});
-	array.addMessage('append', {
-		vars: {tmp: 'object **'},
-		lines: [
-			'if (self->storage == self->size) {',
-			'	self->storage *= 2;',
-			'	tmp = GC_REALLOC(self->data, self->storage * sizeof(object *));',
-			'	if (!tmp) {',
-			'		fputs("Failed to increase array size\\n", stderr);',
-			'		exit(1);',
-			'	}',
-			'	self->data = tmp;',
-			'}',
-			'self->data[self->size++] = va_arg(args, object *);',
-			'return &(self->header);'
-		]
-	});
-	array.addMessage('length', {
-		vars: {intret: 'obj_int32 *'},
-		lines: [
-			'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);',
-			'intret->num = self->size;',
-			'return &(intret->header);'
-		]
-	});
-	return array;
+	var arrayfile = toplevel.names['array'];
+	var ast = parseFile(arrayfile.path + '/' + arrayfile.file);
+	ast.name = 'array';
+	ast.populateSymbols(toplevel);
+	return ast.toCObject();
 }
 
 function makeString()
 {
-	var string = new cObject('string');
-	string.addProperty('length', null, 'uint32_t');
-	string.addProperty('bytes', null, 'uint32_t');
-	string.addProperty('data', null, 'char *');
-	string.addMessage('length', {
-		vars: {intret: 'obj_int32 *'},
-		lines: [
-			'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);',
-			'intret->num = self->length;',
-			'return &(intret->header);'
-		]
-	});
-	string.addMessage('byte_length', {
-		vars: {intret: 'obj_int32 *'},
-		lines: [
-			'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);',
-			'intret->num = self->bytes;',
-			'return &(intret->header);'
-		]
-	});
-	string.addMessage('EQ_', {
-		vars: {argb: 'string *'},
-		lines: [
-			'argb = va_arg(args, string *);',
-			'if (self->length == argb->length && self->bytes == argb->bytes && !memcmp(self->data, argb->data, self->bytes)) {',
-			'	return ' + toplevel.moduleVar('true') + ';',
-			'}',
-			'return ' + toplevel.moduleVar('false') + ';',
-		]
-	});
-	string.addMessage('NEQ_', {
-		vars: {argb: 'string *'},
-		lines: [
-			'argb = va_arg(args, string *);',
-			'if (self->length != argb->length || self->bytes != argb->bytes || memcmp(self->data, argb->data, self->bytes)) {',
-			'	return ' + toplevel.moduleVar('true') + ';',
-			'}',
-			'return ' + toplevel.moduleVar('false') + ';',
-		]
-	});
-	string.addMessage('print', {
-		vars: {},
-		lines: [
-			'fwrite(self->data, 1, self->bytes, stdout);',
-			'return &(self->header);'
-		]
-	});
-	string.addMessage('string', {
-		vars: {},
-		lines: [ 'return &(self->header);' ]
-	});
-	string.addMessage('CAT_', {
-		vars: {argbo: 'object *', argb: 'string *', out: 'string *'},
-		lines: [
-			'argbo = va_arg(args, object *);',
-			'argb = (string *)mcall(' + getMethodId('string') + ', 1, argbo);',
-			'out = (string *)make_object(&string_meta, NULL, 0);',
-			'out->bytes = self->bytes + argb->bytes;',
-			'out->length = self->length + argb->length;',
-			'out->data = GC_MALLOC_ATOMIC(out->bytes+1);',
-			'memcpy(out->data, self->data, self->bytes);',
-			'memcpy(out->data + self->bytes, argb->data, argb->bytes + 1);',
-			'return &(out->header);'
-		]
-	});
-	string.addMessage('byte', {
-		vars: {index: 'obj_int32 *', intret: 'obj_int32 *'},
-		lines: [
-			'index = va_arg(args, obj_int32 *);',
-			'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);',
-			'intret->num = index->num < self->bytes ? self->data[index->num] : 0;',
-			'return &(intret->header);'
-		]
-	});
-	string.addMessage('int32', {
-		vars: {intret: 'obj_int32 *'},
-		lines: [
-			'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);',
-			'intret->num = atoi(self->data);',
-			'return &(intret->header);'
-		]
-	});
-	string.addMessage('hash', {
-		vars: {intret: 'obj_int32 *', i: 'uint32_t'},
-		lines: [
-			'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);',
-			'intret->num = 0;',
-			'if (self->bytes) {',
-			'	intret->num = self->data[0] << 7;',
-			'	for (i = 0; i < self->bytes; i++) {',
-			'		intret->num = (1000003 * intret->num) ^ self->data[i];',
-			'	}',
-			'	intret->num = intret->num ^ self->bytes;',
-			'}',
-			'return &(intret->header);'
-		]
-	});
-	return string;
+	var arrayfile = toplevel.names['string'];
+	var ast = parseFile(arrayfile.path + '/' + arrayfile.file);
+	ast.name = 'string';
+	ast.populateSymbols(toplevel);
+	return ast.toCObject();
 }
 
 function makelambda()
@@ -770,14 +811,12 @@
 	var alwaysused = ['true', 'false'];
 	var ret = '';
 	var modulenum = 0;
+	var visited = {};
+	for (var i in alwaysused) {
+		toplevel.used[alwaysused[i]] = true;
+	}
 	var newused = Object.keys(toplevel.used);
 	var allused = newused;
-	var visited = {};
-	for (var i in alwaysused) {
-		forwarddec += 'object * ' + toplevel.moduleVar(alwaysused[i]) + ';\n';
-		toplevel.names[alwaysused[i]].populateSymbols(toplevel);
-		visited[alwaysused[i]] = true;
-	}
 	while (newused.length) {
 		for (var i in newused) {
 			debugprint('//---module', newused[i], '--- populate symbols');
@@ -794,13 +833,10 @@
 			}
 		}
 	}
-	for (var i in alwaysused) {
-		allused.push(alwaysused[i]);
-	}
 		
 	for (var i = allused.length-1; i >= 0; i--) {
 		var symbol = allused[i];
-		debugprint('//---module', symbol, '--- compile');
+		debugprint('//---module', symbol, '(' + i +')--- compile');
 		ret += '\t' + toplevel.moduleVar(symbol) + ' = ' + toplevel.names[symbol].toC() + ';\n';
 	}
 	return ret;
@@ -808,8 +844,8 @@
 
 function makeCProg(obj)
 {
+	forwarddec = toplevelcode = '';
 	var builtins = builtinTypes();
-	forwarddec = toplevelcode = '';
 	for (var i in builtins) {
 		forwarddec += builtins[i].toEarlyCDef();
 		toplevelcode += builtins[i].toCDef();
@@ -932,6 +968,31 @@
 };
 lambda.prototype.toCModule = function() {
 	return makeCProg(this);
+};
+lambda.prototype.toCLines = function(vars, needsreturn) {
+	var lines = [];
+	for (var i in this.args) {
+		var name = this.args[i].name;
+		if (name[0] == ':') {
+			name = name.substr(1);
+		}
+		if(name != 'self') {
+			lines.push(name + ' = va_arg(args, ' + vars[name] + ');');
+		}
+	}
+	for (var i in this.expressions) {
+		var exprlines = this.expressions[i].toCLines(vars, needsreturn && i == this.expressions.length - 1);
+		for (var j in exprlines) {
+			lines.push('\t' + exprlines[j]);
+		}
+	}
+	return lines;
+}
+lambda.prototype.toCLLExpr = function(vars) {
+	if (this.expressions.length != 1) {
+		throw new Error('lambda in expression context must have a single statement in llMessage block');
+	}
+	return this.expressions[0].toCLLExpr(vars);
 }
 
 assignment.prototype.toC = function() {
@@ -982,3 +1043,6 @@
 		}
 	}
 };
+assignment.prototype.toCLines = function(vars, needsreturn) {
+	return [(needsreturn ? 'return ' : '') + this.symbol.toCLLExpr(vars) + ' = ' + this.expression.toCLLExpr(vars) + ';']
+};
--- a/compiler.js	Sun Jul 22 15:10:07 2012 -0700
+++ b/compiler.js	Mon Jul 23 01:07:52 2012 -0700
@@ -78,11 +78,12 @@
 {
 	this.parent = parent;
 	this.names = {};
+	this.llnames = {};
 	this.needsenv = false;
 	this.typename = null;
 	this.needsparent = false;
 }
-osymbols.prototype.find = function(name, nestedcall) {
+osymbols.prototype.find = function(name, nestedcall, allowll) {
 	debugprint('//osymbols.find', name + ', exists?:', name in this.names, ', nested?:', nestedcall);
 	if (name in this.names) {
 		if (this.names[name] instanceof funcall && this.names[name].name == 'foreign:') {
@@ -93,11 +94,18 @@
 		}
 		var ret = {
 			type: 'self',
+			isll: false,
 			def: this.names[name],
 			selftype: this.typename
 		};
+	} else if(allowll && name in this.llnames) {
+		return {
+			type: 'self',
+			isll: true,
+			selftype: this.typename
+		};
 	} else if(this.parent) {
-		var ret = this.parent.find(name, nestedcall);
+		var ret = this.parent.find(name, nestedcall, allowll);
 		if (ret) {
 			if(ret.type == 'self') {
 				ret.type = 'parent';
@@ -119,6 +127,9 @@
 osymbols.prototype.defineMsg = function(name, def) {
 	this.names[name] = def;
 }
+osymbols.prototype.defineLLProperty = function(name) {
+	this.llnames[name] = true;
+}
 osymbols.prototype.parentObject = function() {
 	if (!this.parent) {
 		return 'null';
@@ -160,7 +171,7 @@
 	this.envtype = 'void';
 	this.needsParentEnv = false;
 }
-lsymbols.prototype.find = function(name, nestedcall) {
+lsymbols.prototype.find = function(name, nestedcall, allowll) {
 	debugprint('//lsymbols.find', name + ', exists?:', name in this.names, ', nested?:', nestedcall);
 	if (name in this.names) {
 		if (this.names[name] instanceof funcall && this.names[name].name == 'foreign:') {
@@ -187,7 +198,7 @@
 			}
 		}
 	} else if(this.parent) {
-		var ret = this.parent.find(name, true);
+		var ret = this.parent.find(name, true, allowll);
 		if (ret) {
 			if (ret.type == 'closedover') {
 				ret.type = 'upvar';
@@ -317,6 +328,31 @@
 		} else {
 			throw new Error("Unexpected AST type for foreign:");
 		}
+	} else if (this.name == 'llProperty:withType:') {
+		if (this.args[0] instanceof symbol) {
+			if ((this.args[1] instanceof symbol) || (this.args[1] instanceof funcall)) {
+				symbols.defineLLProperty(this.args[0].name);
+				return;
+			} else {
+				throw new Error("Second argument to llProperty:withType: must be a symbol or funcall");
+			}
+		} else {
+			throw new Error("First argument to llProperty:withType: must be a symbol");
+		}
+	} else if (this.name == 'llMessage:withVars:andCode:') {
+		if (this.args[0] instanceof symbol) {
+			if (this.args[1] instanceof lambda) {
+				if (this.args[2] instanceof lambda) {
+					symbols.defineMsg(this.args[0].name, this.args[2]);
+				} else {
+					throw new Error("Third argument to llMessage:withVars:andCode: must be a lambda");
+				}
+			} else {
+				throw new Error("Second argument to llMessage:withVars:andCode: must be a lambda");
+			}
+		} else {
+			throw new Error("First argument to llMessage:withVars:andCode: must be a symbol");
+		}
 	}
 	this.symbols = symbols;
 	var name = this.name[this.name.length-1] == ':' ? this.name.substr(0, this.name.length-1) : this.name;
@@ -333,11 +369,11 @@
 }
 
 funcall.prototype.populateSymbolsObject = function(symbols) {
-	this.populateSymbols(symbols.parent);
+	this.populateSymbols(symbols);
 }
 
 object.prototype.populateSymbols = function(symbols) {
-	symbols = new osymbols(symbols);
+	var symbols = new osymbols(symbols);
 	for (var i in this.messages) {
 		this.messages[i].populateSymbolsObject(symbols);
 	}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/array.tp	Mon Jul 23 01:07:52 2012 -0700
@@ -0,0 +1,102 @@
+#{
+	llProperty: size withType: uint32_t
+	llProperty: storage withType: uint32_t
+	llProperty: data withType: ((object ptr) ptr)
+	llMessage: get withVars: {
+		index <- obj_int32 ptr
+	} andCode: :index {
+		if: (index num) >= 0 && (index num) < size {
+			(self data) get: (index num)
+		} else: {
+			false
+		}
+	}
+	
+	llMessage: set withVars: {
+		index <- obj_int32 ptr
+		value <- object ptr
+	} andCode: :index value {
+		if: (index num) >= 0 && (index num) < size {
+			data set: (index num) value
+		}
+		self
+	}
+	
+	llMessage: foreach withVars: {
+		clos <- lambda ptr
+		i <- uint32_t
+		index <- obj_int32 ptr
+	} andCode: :clos {
+		i <- 0
+		while: { i < size } do: {
+			index <- make_object: (addr_of: obj_int32_meta) NULL 0
+			index num!: i
+			ccall: clos 2 index (data get: i)
+			i <- i + 1
+		}
+		self
+	}
+	
+	llMessage: append withVars: {
+		value <- object ptr
+		tmp <- (object ptr) ptr
+	} andCode: :value {
+		if: storage = size {
+			storage <- storage * 2
+			tmp <- GC_REALLOC: data storage * (sizeof: (object ptr))
+			if: (not: tmp) {
+				fputs: "Failed to increase array size\n" stderr
+				exit: 1
+			}
+			data <- tmp
+		}
+		data set: size value
+		size <- size + 1
+		self
+	}
+	
+	llMessage: length withVars: {
+		intret <- obj_int32 ptr
+	} andCode: {
+		intret <- make_object: (addr_of: obj_int32_meta) NULL 0
+		intret num!: size
+		intret
+	}
+	
+	fold:with <- :acc :fun {
+		foreach: self :idx el {
+			fun: acc el
+		}
+	}
+	
+	foldr:with <- :acc :fun {
+		idx <- length - 1
+		while: {idx >= 0} do: {
+			fun: acc (get: idx)
+		}
+	}
+	
+	map <- :fun {
+		new <- #[]
+		foreach: self :idx el {
+			new append: (fun: el)
+		}
+	}
+	
+	find:withDefault <- :pred :default{
+		idx <- 0
+		l <- length
+		ret <- default
+		while: {idx < l} do: {
+			v <- get: idx
+			if: (pred: v) {
+				ret <- #{
+					key <- idx
+					value <- v
+				}
+				idx <- l
+			}
+		}
+		ret
+	}
+}
--- a/modules/sets.tp	Sun Jul 22 15:10:07 2012 -0700
+++ b/modules/sets.tp	Mon Jul 23 01:07:52 2012 -0700
@@ -3,17 +3,20 @@
 		empty <- #{
 			empty? <- { true }
 		}
+		size <- 0
+		hashdiffs <- #[0]
 		#{
 			buckets <- #[empty empty empty empty]
 			contains? <- :object {
 				hv <- object hash
 				
 				notdone <- true
-				hashes <- #[hv (hv + 1) (hv - 1)]
+				
+				basehash <- hv
 				i <- 0
 				ret <- false
-				while: { if: notdone { i < 3 } } do: {
-					hv <- hashes get: i
+				while: { if: notdone { i < (hashdiffs length) } } do: {
+					hv <- basehash + (hashdiffs get: i)
 					trunc <- hv % (buckets length)
 					if: trunc < 0 { trunc <- 0 - trunc }
 					bucketval <- (buckets get: trunc)	
@@ -41,14 +44,15 @@
 					}
 				}
 				notdone <- true
-				hashes <- #[hv (hv + 1) (hv - 1)]
+				basehash <- hv
 				i <- 0
-				while: { if: notdone { i < 3 } } do: {
-					hv <- hashes get: i
+				while: { if: notdone { i < (hashdiffs length) } } do: {
+					hv <- basehash + (hashdiffs get: i)
 					trunc <- hv % (buckets length)
 					if: trunc < 0 { trunc <- 0 - trunc }
 					bucketval <- (buckets get: trunc)	
 					if: (bucketval empty?) {
+						size <- size + 1
 						buckets set: trunc (makeBucket: hv)
 						notdone <- false
 					} else: {
@@ -59,7 +63,13 @@
 					i <- i + 1
 				}
 				if: notdone {
-					newsize <- (buckets length) * 3
+					newsize <- (buckets length) * 3 + 1
+					lastdiff <- hashdiffs get: ((hashdiffs length) - 1)
+					if: lastdiff <= 0 {
+						hashdiffs append: ((0 - lastdiff) + 1)
+					} else: {
+						hashdiffs append: (0 - lastdiff)
+					}
 					newbucks <- #[]
 					while: { (newbucks length) < newsize } do: {
 						newbucks append: empty
--- a/parser.js	Sun Jul 22 15:10:07 2012 -0700
+++ b/parser.js	Mon Jul 23 01:07:52 2012 -0700
@@ -50,6 +50,7 @@
 function object(messages)
 {
 	this.messages = messages;
+	this.name = null;
 }
 
 function lambda(args, expressions)
@@ -74,8 +75,9 @@
 'ws = ([ \\t\\n\\r] / "//" [^\\n]* "\\n")*;' +
 'hws = ([ \\t] / "/*" ([^*] / "*" ! "/")* "*/" )*;' +
 'expr = e:(funcall / methcall / opexpr) ws { return e; };' +
-'opexpr = left:addsub pieces:(hws ("<=" / ">=" / "<" / ">" / "=" / "!=") hws addsub)* { if (pieces.length) { var cur = new op(left, pieces[0][1], pieces[0][3]); for (var i = 1; i < pieces.length; i++) { cur = new op(cur, pieces[i][1], pieces[i][3]); } return cur; } else { return left; } };'+
-'addsub = left:muldiv pieces:(hws ("+"/"-"/".") hws muldiv)* { if (pieces.length) { var cur = new op(left, pieces[0][1], pieces[0][3]); for (var i = 1; i < pieces.length; i++) { cur = new op(cur, pieces[i][1], pieces[i][3]); } return cur; } else { return left; } };'+
+'opexpr = left:compareop pieces:(hws ("&&" / "||") hws compareop)* { if (pieces.length) { var cur = new op(left, pieces[0][1], pieces[0][3]); for (var i = 1; i < pieces.length; i++) { cur = new op(cur, pieces[i][1], pieces[i][3]); } return cur; } else { return left; } };'+
+'compareop = left:addsub pieces:(hws ("<=" / ">=" / "<" / ">" / "=" / "!=") hws addsub)* { if (pieces.length) { var cur = new op(left, pieces[0][1], pieces[0][3]); for (var i = 1; i < pieces.length; i++) { cur = new op(cur, pieces[i][1], pieces[i][3]); } return cur; } else { return left; } };'+
+'addsub = left:muldiv pieces:(hws ("+"/"-"/"xor"/".") hws muldiv)* { if (pieces.length) { var cur = new op(left, pieces[0][1], pieces[0][3]); for (var i = 1; i < pieces.length; i++) { cur = new op(cur, pieces[i][1], pieces[i][3]); } return cur; } else { return left; } };'+
 'muldiv = left:primlitsym pieces:(hws ("*"/"/"/"%") hws primlitsym)* { if (pieces.length) { var cur = new op(left, pieces[0][1], pieces[0][3]); for (var i = 1; i < pieces.length; i++) { cur = new op(cur, pieces[i][1], pieces[i][3]); } return cur; } else { return left; } };'+
 'primlitsym = hws val:(float / hex / binary / int / string / symbol / object / array / list / lambda / "(" ws expr:expr hws ")" { return expr; }) { return val; };' +
 'symbol = chars:[a-zA-Z_!?@]+ trailing:(":"? [a-zA-Z_!?@0-9])* ! ":" { for (var i in trailing) { trailing[i] = trailing[i].join(""); } return new symbol(chars.join("") + trailing.join("")); };' +
@@ -86,12 +88,13 @@
 'string = "\\"" text:(strpart/escape)* "\\"" { return new strlit(text.join("")); };' +
 'strpart = text:[^\\"\\\\]+ { return text.join(""); };' + 
 'escape = "\\\\" char:[nt\\"r\\\\] { if (char == "n") { return "\\n"; } if (char == "r") { return "\\r"; } return char; };' +
-'object = "#{" ws messages:(assignment / funcall)* "}" { return new object(messages); };' +
+'object = "#{" ws messages:(assignment / funexpr)* "}" { return new object(messages); };' +
 'array = "#[" ws els:opexpr* "]" { return new arraylit(els); };' +
 'list = "[" ws els:opexpr* "]" { return new listlit(els); };' +
 'assignment = ws sym:symbol hws "<-" expr:expr ws { return new assignment(sym, expr); }' +
 'lambda = args:((& ":") argname+  )? "{" ws exprs:(assignment / expr)* "}" { return new lambda(args[1], exprs); };' +
 'argname = init:":"? chars:[a-zA-Z_!?@]+ trailing:[a-zA-Z_!?@0-9]* hws { return new symbol(init + chars.join("") + trailing.join("")); };' +
+'funexpr = f: funcall ws { return f; };' +
 'funcall = hws parts: funcallpart+ { var fun = ""; var args = []; for (var i in parts) { fun += parts[i].name; args = args.concat(parts[i].args); } return new funcall(fun, args); };' +
 'funcallpart = fun:funpart args:opexpr* hws { return { name: fun, args: args}; };' +
 'funpart = chars:[a-zA-Z_!?@]+ middle:[a-zA-Z_!?@0-9]* ":" & [ \\t\\n\\r] { return chars.join("") + middle.join("") + ":"; };' +
--- a/runtime/progfoot.inc	Sun Jul 22 15:10:07 2012 -0700
+++ b/runtime/progfoot.inc	Mon Jul 23 01:07:52 2012 -0700
@@ -43,7 +43,7 @@
 	for (i = 0; i < argc; ++i) {
 		arg = (string *)make_object(&string_meta, NULL, 0);
 		arg->data = argv[i];
-		arg->bytes = arg->length = strlen(argv[i]);
+		arg->bytes = arg->len = strlen(argv[i]);
 		arr->data[i] = &(arg->header);
 	}
 	object * ret = mcall(METHOD_ID_MAIN, 2, mainModule(), &(arr->header));
--- a/samples/testarray.tp	Sun Jul 22 15:10:07 2012 -0700
+++ b/samples/testarray.tp	Mon Jul 23 01:07:52 2012 -0700
@@ -12,7 +12,7 @@
 		bar append: 30
 		bar append: 28
 		bar append: 42
-		(sum: foo) + (sum: bar)
+		print: "" . ((sum: foo) + (sum: bar)) . "\n"
 	}
 }