diff cbackend.js @ 31:668f533e5284

Add initial version of C backend
author Mike Pavone <pavone@retrodev.com>
date Sat, 07 Jul 2012 16:48:36 -0700
parents
children 64f1d516fbfd
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cbackend.js	Sat Jul 07 16:48:36 2012 -0700
@@ -0,0 +1,368 @@
+var mainModule;
+var modules = {};
+
+var nextmethodId = 0;
+var methodIds = {};
+function getMethodId(methodName)
+{
+	if (!(methodName in methodIds)) {
+		methodIds[methodName] = nextmethodId++;
+		
+	}
+	return methodIds[methodName];
+}
+
+function importSym(obj, src, key)
+{
+	if(!(key in src)) {
+		throw new Error(key +' not found in source object for import');
+	}
+	if(key in obj) {
+		throw new Error(key +' already exists in target object for import')
+	}
+	obj[key] = src[key];
+}
+
+function doImport(obj, src, symlist)
+{
+	if (symlist === undefined) {
+		each(src, function(key,val) {
+			if (key != 'parent') {
+				importSym(obj, src, key);
+			}
+		});
+	} else {
+		for (var i = 0; i < symlist.length; ++i) {
+			importSym(obj, src, symlist[i]);
+		}
+	}
+	return obj;
+}
+
+op.prototype.toC = function(isReceiver) {
+	var optoMeth = {'+': 'ADD_', '-': 'SUB_', '*': 'MUL_', '/': 'DIV_', '=': 'EQ_', '!=': 'NEQ_', '<': 'LT_', '>': 'GT_', '>=': 'GEQ_', '<=': 'LEQ_'};
+	var method = optoMeth[this.op];
+	var ret = '(params[0] = ' + this.left.toC() + ',\n';
+	ret += 'params[1] = ' + this.right.toC() + ',\n';
+	ret += 'mcall(' + getMethodId(method) + ', 2, params))\n';
+	return ret;
+};
+
+function escapeCName(name)
+{
+	name = name.replace("_", "UN_").replace(":", "CN_").replace("!", "EX_").replace('?', 'QS_').replace('@', 'AT_');
+	name = 'tp_' + name;
+	return name;
+}
+
+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();
+		for (var i = 0; i < funinfo.depth; ++i) {
+			pre += '->parent';
+		}
+	} else if (info.type == 'toplevel') {
+		pre = 'modules.';
+		modules[name] = false;
+	}
+	return pre + escapeCName(name);
+}
+
+intlit.prototype.toC = function() {
+	var str = this.val.toString();
+	toplevelcode += 'obj_int32 int32_' + str + ' = {{&obj_int32_meta, NULL}, ' + str + '};\n';
+	return '((object *)&int32_' + str + ')';
+}
+
+floatlit.prototype.toC = function() {
+	return 'make_float(' + this.val.toString() + ')';
+}
+
+strlit.prototype.toC = function() {
+	return 'make_str("' + this.val.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n').replace('\r', '\\r') + '")';
+}
+
+listlit.prototype.toC = function() {
+	var ret = 'make_list(';
+	each(this.val, function(idx, el) {
+		ret += (idx ? ', ' : '') + el.toC();
+	});
+	return ret + ')';
+}
+
+funcall.prototype.toC = function() {
+	var name = this.name[this.name.length-1] == ':' ? this.name.substr(0, this.name.length-1) : this.name;
+	if (name == 'foreign') {
+		if ((this.args[0] instanceof lambda) || (this.args[0] instanceof object)) {
+			return null;
+		} else if(this.args[0] instanceof symbol) {
+			return this.args[0].name;
+		} else {
+			throw new Error("Unexpected AST type for foreign:");
+		}
+	}
+	var args = this.args.slice(0, this.args.length);
+	if (this.receiver) {
+		args.splice(0, 0, this.receiver);
+	}
+		var method = false;
+	var funinfo = this.symbols.find(name);
+	if (!funinfo || funinfo.def instanceof setter) {
+		method = true;
+	} else {
+		switch(funinfo.type)
+		{
+		case 'self':
+			if (args.length < funinfo.def.args.length || funinfo.def.args[0].name != 'self') {
+				args.splice(0, 0, new symbol('self', this.symbols));
+			} else {
+				args.splice(0, 1);
+			}
+			method = true;
+			break;
+		case 'parent':
+			ret = 'self';
+			for (var i = 0; i < funinfo.depth; ++i) {
+				ret += '->parent';
+			}
+			break;
+		}
+	}
+	for (var i in args) {
+		args[i] = 'params[' + i + '] = ' + args[i].toC() + ',\n';
+	}
+	var callpart;
+	if (method) {
+		callpart = 'mcall(' + getMethodId(name);
+	} else {
+		callpart = 'ccall(' + escapeCName(name);
+	}
+	return '(' + args.join('') + callpart + ', ' + args.length + ', ' + 'params))';
+}
+
+function cObject(name) {
+	this.name = name;
+	this.slots = {};
+	this.properties = [];
+	this.values = [];
+}
+
+cObject.prototype.addMessage = function(msgname, implementation) {
+	var methodid = getMethodId(msgname);
+	var trunc = methodid & 0xF;
+	if (!(trunc in this.slots)) {
+		this.slots[trunc] = [];
+	}
+	this.slots[trunc].push([methodid, implementation, msgname]);
+}
+
+cObject.prototype.addProperty = function(propname, value, type) {
+	if (type != undefined) {
+		this.properties.push([propname, type]);
+	} else {
+		var escaped = escapeCName(propname);
+		this.addMessage(propname, 'return self->' + escaped + ';');
+		this.addMessage(propname + '!', 'self->' + escaped + ' = params[1]; return params[0];');
+		this.properties.push(escaped);
+		this.values.push(value);
+	}
+}
+
+cObject.prototype.toEarlyCDef = function() {
+	var objdef =  'typedef struct {\n\tobject header;\n';
+	for (var i in this.properties) {
+		if (this.properties[i] instanceof Array) {
+			objdef += '\t' + this.properties[i][1] + ' ' + this.properties[i][0] + ';\n';
+		} else {
+			objdef += '\tobject * ' + this.properties[i] + ';\n'
+		}
+	}
+	objdef += '} ' + this.name + ';\nobj_meta ' + this.name + '_meta;\n';
+	return objdef;
+}
+
+cObject.prototype.toCDef = function() {
+	var slotdefs = '';
+	var metadef = 'obj_meta ' + this.name + '_meta = {sizeof(' + this.name +'), {';
+	for (var i = 0; i < 16; i++) {
+		if (i) {
+			metadef += ', ';
+		}
+		if (i in this.slots) {
+			slotdefs += 'object * ' + this.name + '_slot_' + i + '(uint32_t method_id, uint32_t num_params, object ** params) {\n\t' +
+				this.name + ' *self = (' + this.name + ' *)params[0];';
+			if (this.slots[i].length == 1) {
+				slotdefs += '\tif (method_id == ' + this.slots[i][0][0] + ') { /* ' + this.slots[i][0][2] + '*/\n' +
+					'\t\t' + this.slots[i][0][1] + '\n' + 
+					'\t}\n' +
+					'\treturn no_impl(method_id, num_params, params);\n}\n';
+			} else {
+				slotdefs += '\tswitch(method_id) {\n';
+				for (j in this.slots[i]) {
+					slotdefs += '\t\tcase ' + this.slots[i][j][0] + ': /* ' + this.slots[i][j][2] + '*/\n' +
+						'\t\t\t' + this.slots[i][j][1] + '\n';
+				}
+				slotdefs += '\t\tdefault:\n\treturn no_impl(method_id, num_params, params);\n}\n';
+			}
+			metadef += this.name + '_slot_' + i;
+		} else {
+			metadef += 'no_impl';
+		}
+	}
+	metadef += '}};\n';
+	return slotdefs + metadef;
+}
+
+cObject.prototype.toCInstance = function() {
+	return 'make_object(&' + this.name + '_meta, NULL, ' + this.values.length + (this.values.length ? ', ' : '') + this.values.join(', ') + ')';
+}
+
+var nextobject = 0;
+
+object.prototype.toC = function() {
+	var messages = this.messages;
+	var values = [];
+	var imports = []
+	var me = new cObject('object_' + nextobject++);
+	for (var i in messages) {
+		if (messages[i] instanceof funcall) {
+			if (messages[i].name == 'import:' && messages[i].args.length == 1) {
+				imports.push({symbols: false, src: messages[i].args[0]});
+			} else if(messages[i].name == 'import:from:' && messages[i].args.length == 2) {
+				var importsyms = [];
+				each(messages[i].args[0].val, function(i, el) {
+					if (!(el instanceof symbol)) {
+						throw new Error('Names in import:from statement must be symbols');
+					}
+					importsyms.push(new strlit(el.name));
+				});
+				imports.push({symbols: new listlit(importsyms), src: messages[i].args[1]});
+			} else {
+				throw new Error('Only import and import:from calls allowed in object context');
+			}
+		} else {
+			messages[i].toCObject(me);
+		}
+	}
+	forwarddec += me.toEarlyCDef();
+	toplevelcode += me.toCDef();
+	return me.toCInstance();
+}
+
+var toplevelcode;
+var forwarddec;
+
+function makeCProg(obj)
+{
+	var int32 = new cObject('obj_int32');
+	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];');
+	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';
+}
+
+object.prototype.toCModule = function() {
+	return makeCProg(this);
+}
+
+var lambdanum = 0;
+
+lambda.prototype.toC = function() {
+	var args = this.args ? this.args.slice(0, this.args.length) : [];
+	var exprs = this.expressions;
+	if (this.selftype) {
+		if (args[0] && args[0].cleanName() == 'self') {
+			args.splice(0, 1);
+		}
+		var offset = 1;
+	} else {
+		var offset = 0;
+	}
+	for (var i = 0; i < args.length; ++i) {
+		args[i] = '\tobject * ' + args[i].toC() + ' = params[' + (offset + i) + '];\n';
+	}
+	var compiled = []
+	for (var i in exprs) {
+		var js = exprs[i].toC();
+		if (js) {
+			compiled.push(indent(js));
+		}
+	}
+	exprs = compiled;
+	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 (this.selftype) {
+		toplevelcode += '\t' + this.selftype + ' * self = (' + 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 + ')';
+};
+lambda.prototype.toCObject = function(typename) {
+	this.selftype = typename;
+	return this.toC();
+};
+lambda.prototype.toCModule = function() {
+	return makeCProg(this);
+}
+
+assignment.prototype.toC = function() {
+	var existing = this.symbols.find(this.symbol.name);
+	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) {
+		return null;
+	}
+	return prefix + this.symbol.toC() + ' = ' + val;
+};
+assignment.prototype.toCObject = function(cobj) {
+	if (this.expression.toCObject) {
+		var val = this.expression.toCObject(cobj.name);
+	} else {
+		var val = this.expression.toC();
+	}
+	if (val === null) {
+		return;
+	}
+	if (this.expression instanceof lambda) {
+		cobj.addMessage(this.symbol.name, 'return ccall(' + val + ', num_params, params);');
+	} else {
+		cobj.addProperty(this.symbol.name, val);
+	}
+};