changeset 34:a10f1b049193

Working closures, but need to rethink method call strategy
author Mike Pavone <pavone@retrodev.com>
date Mon, 09 Jul 2012 08:57:50 -0700
parents 96e21f525b78
children bf5e88f6419d
files cbackend.js compiler.js runtime/object.c runtime/object.h runtime/proghead.inc
diffstat 5 files changed, 134 insertions(+), 38 deletions(-) [+]
line wrap: on
line diff
--- a/cbackend.js	Sun Jul 08 12:32:24 2012 -0700
+++ b/cbackend.js	Mon Jul 09 08:57:50 2012 -0700
@@ -50,6 +50,9 @@
 
 function escapeCName(name)
 {
+	if (name == 'self') {
+		return name;
+	}
 	name = name.replace("_", "UN_").replace(":", "CN_").replace("!", "EX_").replace('?', 'QS_').replace('@', 'AT_');
 	name = 'tp_' + name;
 	return name;
@@ -57,36 +60,46 @@
 
 symbol.prototype.toC = function() {
 	var name = this.cleanName();
-	if (name == 'self') {
-		return name;
-	}
 	var info = this.symbols.find(name);
 	if (!info) {
 		throw new Error('symbol ' + name + ' not found');
 	}
 	var pre = '';
-	if (info.type == 'self') {
-		pre = this.symbols.selfVar() + '->';
-	} else if(info.type == 'parent') {
-		pre = this.symbols.selfVar() + '->';
+	console.log('symbol', info);
+	switch(info.type) {
+	case 'self':
+		pre = 'self->';
+		break;
+	case 'parent':
+		pre = 'self->';
 		for (var i = 0; i < info.depth; ++i) {
 			pre += 'parent->';
 		}
-	} else if(info.type == 'upvar') {
+		break;
+	case 'upvar':
 		pre = 'env->';
 		for (var i = 1; i < info.depth; ++i) {
 			pre += 'parent->';
 		}
-	} else if (info.type == 'toplevel') {
+		break;
+	case 'toplevel':
 		pre = 'modules.';
 		modules[name] = false;
+		break;
+	case 'closedover':
+		pre = 'myenv->';
 	}
 	return pre + escapeCName(name);
 }
 
+var declaredInts = {};
+
 intlit.prototype.toC = function() {
 	var str = this.val.toString();
-	toplevelcode += 'obj_int32 int32_' + str + ' = {{&obj_int32_meta, NULL}, ' + str + '};\n';
+	if (!(this.val in declaredInts)) {
+		toplevelcode += 'obj_int32 int32_' + str + ' = {{&obj_int32_meta, NULL}, ' + str + '};\n';
+		declaredInts[this.val] = true;
+	}
 	return '((object *)&int32_' + str + ')';
 }
 
@@ -274,12 +287,17 @@
 	int32.addProperty('num', null, 'int32_t');
 	int32.addMessage('ADD_', 'params[0] = make_object(&obj_int32_meta, NULL, 0); ((obj_int32 *)params[0])->num = self->num + ((obj_int32 *)params[1])->num; return params[0];');
 	int32.addMessage('SUB_', 'params[0] = make_object(&obj_int32_meta, NULL, 0); ((obj_int32 *)params[0])->num = self->num - ((obj_int32 *)params[1])->num; return params[0];');
+	int32.addMessage('LT_', 'params[0] = main_module; if (self->num < ((obj_int32 *)params[1])->num) { return mcall(METHOD_ID_TRUE, 1, params); } return mcall(METHOD_ID_FALSE, 1, params);');
 	forwarddec = toplevelcode = '';
 	forwarddec += int32.toEarlyCDef();
 	toplevelcode += int32.toCDef();
 	obj.populateSymbols(toplevel);
-	var rest = 'object * mainModule() {\n\treturn ' + obj.toC() + ';\n}\n';
-	return '#include "runtime/proghead.inc"\n#define METHOD_ID_MAIN ' + getMethodId('main') + '\n' + forwarddec + toplevelcode + rest + '#include "runtime/progfoot.inc"\n';
+	var rest = 'object * mainModule() {\n\tmain_module = ' + obj.toC() + ';\n\treturn main_module;\n}\n';
+	return '#include "runtime/proghead.inc"\n' +
+		'#define METHOD_ID_MAIN ' + getMethodId('main') + '\n' +
+		'#define METHOD_ID_TRUE ' + getMethodId('true') + '\n' +
+		'#define METHOD_ID_FALSE ' + getMethodId('false') + '\n' +
+		forwarddec + toplevelcode + rest + '#include "runtime/progfoot.inc"\n';
 }
 
 object.prototype.toCModule = function() {
@@ -291,7 +309,12 @@
 lambda.prototype.toC = function() {
 	var args = this.args ? this.args.slice(0, this.args.length) : [];
 	var exprs = this.expressions;
+	var mynum = lambdanum++;
+	if (Object.keys(this.symbols.closedover).length) {
+		this.symbols.envtype = 'lambda_' + mynum + '_env';
+	}
 	if (this.selftype) {
+		this.symbols.defineVar('self', this.selftype);
 		if (args[0] && args[0].cleanName() == 'self') {
 			args.splice(0, 1);
 		}
@@ -300,7 +323,9 @@
 		var offset = 0;
 	}
 	for (var i = 0; i < args.length; ++i) {
-		args[i] = '\tobject * ' + args[i].toC() + ' = params[' + (offset + i) + '];\n';
+		var argname = args[i].toC();
+		
+		args[i] = (argname.indexOf('->') < 0 ? '\tobject * ' : '\t') + argname + ' = params[' + (offset + i) + '];\n';
 	}
 	var compiled = []
 	for (var i in exprs) {
@@ -313,15 +338,43 @@
 	if (exprs.length) {
 		exprs[exprs.length-1] = 'return ' + exprs[exprs.length-1] + ';';
 	}
-	var mynum = lambdanum++;
-	toplevelcode +=  'object * lambda_' + mynum + ' (void * env, uint32_t num_args, object ** params) {\n';
+	
+	if (Object.keys(this.symbols.closedover).length) {
+		forwarddec += 'typedef struct {\n';
+		for (var varname in this.symbols.closedover) {
+			forwarddec += '\tobject * ' + escapeCName(varname) + ';\n';
+		}
+		forwarddec += '} lambda_' + mynum + '_env;\n'
+		
+		var myenvinit = '\tlambda_' + mynum + '_env * myenv = malloc(sizeof(lambda_' + mynum + '_env));\n';
+		this.symbols.envtype = 'lambda_' + mynum + '_env';
+	} else {
+		var myenvinit = '';
+	}
+	
+	toplevelcode +=  'object * lambda_' + mynum + ' (' + this.symbols.parentEnvType() + ' * env, uint32_t num_args, object ** params) {\n' + myenvinit;
 	if (this.selftype) {
-		toplevelcode += '\t' + this.selftype + ' * self = (' + this.selftype + ' *)params[0];\n';
+		var selfvar = (new symbol('self', this.symbols)).toC();
+		if (selfvar == 'self') {
+			toplevelcode += '\t' + this.selftype + ' * self = (' + this.selftype + ' *)params[0];\n';
+		} else {
+			toplevelcode += '\t' + selfvar  + ' = (' + this.selftype + ' *)params[0];\n';
+		}
+		
 	}
 	toplevelcode += args.join('') + exprs.join(';\n\t') + '\n}\n';
-		
-	toplevelcode += 'closure lambda_obj_' + mynum + ' = {{&lambda_meta, NULL}, NULL, lambda_' + mynum + '};\n';
-	return '((object *)&lambda_obj_' + mynum + ')';
+	
+	if (this.symbols.parentEnvType() != 'void') {
+		if (this.symbols.passthruenv) {
+			var envvar = 'env';
+		} else {
+			var envvar = 'myenv';
+		}
+		return 'make_closure(' + envvar + ', lambda_' + mynum + ')';
+	} else {	
+		toplevelcode += 'closure lambda_obj_' + mynum + ' = {{&lambda_meta, NULL}, NULL, lambda_' + mynum + '};\n';
+		return '((object *)&lambda_obj_' + mynum + ')';
+	}
 };
 lambda.prototype.toCObject = function(typename) {
 	this.selftype = typename;
@@ -336,19 +389,6 @@
 	var prefix = '';
 	if (!existing) {
 		prefix =  'object * ';
-	} else {
-		switch (existing.type)
-		{
-		case 'self':
-			prefix = 'self->';
-			break;
-		case 'parent':
-			prefix = 'self->header.';
-			for (var i = 0; i < existing.depth; ++i) {
-				prefix += 'parent->';
-			}
-			break;
-		}
 	}
 	var val = this.expression.toC();
 	if (val === null) {
--- a/compiler.js	Sun Jul 08 12:32:24 2012 -0700
+++ b/compiler.js	Mon Jul 09 08:57:50 2012 -0700
@@ -37,6 +37,9 @@
 	}
 	return null;
 }
+topsymbols.prototype.getEnvType = function() {
+	return 'void';
+}
 
 function osymbols(parent)
 {
@@ -95,14 +98,22 @@
 	}
 	return curlist;
 }
+osymbols.prototype.getEnvType = function() {
+	console.log('osymbol parent', this.parent);
+	return this.parent.getEnvType();
+}
 
 function lsymbols(parent)
 {
 	this.parent = parent;
 	this.names = {};
+	this.closedover = {};
 	this.needsSelfVar = false;
+	this.passthruenv = false;
+	this.envtype = 'void';
 }
-lsymbols.prototype.find = function(name) {
+lsymbols.prototype.find = function(name, nestedcall) {
+	console.log('find', name, nestedcall);
 	if (name in this.names) {
 		if (this.names[name] instanceof funcall && this.names[name].name == 'foreign:') {
 			return {
@@ -110,18 +121,32 @@
 				def: this.names[name]
 			};
 		}
+		if (nestedcall) {
+			console.log('closedover', name);
+			this.closedover[name] = true;
+		} 
+		if (name in this.closedover) {
+			return {
+				type: 'closedover',
+				def: this.names[name]
+			};
+		}
 		return {
 			type: 'local',
 			def: this.names[name]
 		};
 	} else if(this.parent) {
-		var ret = this.parent.find(name);
+		var ret = this.parent.find(name, true);
 		if (ret) {
-			if (ret.type == 'local') {
+			if (ret.type == 'closedover') {
 				ret.type = 'upvar';
 				ret.depth = 1;
 			} else if (ret.type == 'upvar') {
-				ret.depth++;
+				if (Object(this.closedover).keys.length) {
+					ret.depth++;
+				} else {
+					this.passthruenv = true;
+				}
 			}
 		}
 		return ret;
@@ -147,6 +172,20 @@
 	}
 };
 lsymbols.prototype.allSymbols = osymbols.prototype.allSymbols;
+lsymbols.prototype.parentEnvType = function() {
+	if (!this.parent) {
+		return 'void';
+	}
+	return this.parent.getEnvType();
+};
+lsymbols.prototype.getEnvType = function() {
+	if (this.passthruenv) {
+		console.log('lsymbol parent', this.parent);
+		return this.parent.getEnvType();
+	} else {
+		return this.envtype;
+	}
+}
 
 var mainModule;
 
@@ -173,6 +212,7 @@
 
 symbol.prototype.populateSymbols = function(symbols) {
 	this.symbols = symbols;
+	symbols.find(this.cleanName());
 }
 
 intlit.prototype.populateSymbols = function(symbols) {
--- a/runtime/object.c	Sun Jul 08 12:32:24 2012 -0700
+++ b/runtime/object.c	Mon Jul 09 08:57:50 2012 -0700
@@ -1,6 +1,7 @@
 #include "object.h"
 #include <stdarg.h>
 #include <stdlib.h>
+#include <stddef.h>
 
 object * make_object(obj_meta * meta, void * parent, int num_props, ...)
 {
@@ -14,8 +15,18 @@
 	for (; num_props > 0; num_props--)
 	{
 		*curprop = va_arg(args, object *);
+		curprop++;
 	}
 	va_end(args);
 	return newobj;
 }
 
+object * make_closure(void * env, closure_func func)
+{
+	closure * ret = malloc(sizeof(closure));
+	ret->header.meta = &lambda_meta;
+	ret->header.parent = NULL;
+	ret->env = env;
+	ret->func = func;
+	return (object *) ret;
+}
--- a/runtime/object.h	Sun Jul 08 12:32:24 2012 -0700
+++ b/runtime/object.h	Mon Jul 09 08:57:50 2012 -0700
@@ -28,9 +28,12 @@
 	method   meth_lookup[16];
 };
 
+extern obj_meta lambda_meta;
+
 #define mcall(method_id, num_args, args) (args[0])->meta->meth_lookup[method_id & 0xF](method_id, num_args, args)
 #define ccall(clos, num_args, args) (((closure *)clos)->func(((closure *)clos)->env, num_args, args))
 
 object * make_object(obj_meta * meta, void * parent, int num_props, ...);
+object * make_closure(void * env, closure_func func);
 
 #endif //OBJECT_H_
--- a/runtime/proghead.inc	Sun Jul 08 12:32:24 2012 -0700
+++ b/runtime/proghead.inc	Mon Jul 09 08:57:50 2012 -0700
@@ -3,9 +3,12 @@
 #include <stdlib.h>
 #include <stdio.h>
 
+object * main_module;
+
 object * no_impl(uint32_t method_id, uint32_t num_args, object ** params)
 {
-	printf("method %d is not implemented on object\n", method_id);
+	printf("method %d is not implemented on object %p\n", method_id, params[0]);
+	printf("main_module %p\n", main_module);
 	exit(0);
 	return NULL;
 }
@@ -15,4 +18,3 @@
 	{no_impl, no_impl, no_impl, no_impl, no_impl, no_impl, no_impl, no_impl, 
 	 no_impl, no_impl, no_impl, no_impl, no_impl, no_impl, no_impl, no_impl}
 };
-