# HG changeset patch # User Mike Pavone # Date 1341704916 25200 # Node ID 668f533e52849ad7aabf267cd5a1a7e551c26a48 # Parent 608eb70fe261ce3d85d9ca8bb9123341cce269da Add initial version of C backend diff -r 608eb70fe261 -r 668f533e5284 cbackend.js --- /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); + } +}; diff -r 608eb70fe261 -r 668f533e5284 compiler.js --- a/compiler.js Thu Apr 19 19:20:21 2012 -0700 +++ b/compiler.js Sat Jul 07 16:48:36 2012 -0700 @@ -3,14 +3,32 @@ return str.split('\n').join('\n\t'); } +var toplevel = new topsymbols(); function topsymbols() { this.names = null; + var self = this; + get('/src/', function(data) { + console.log(data); + self.names = {}; + var fakeEl = newEl("div", { + innerHTML: data.response + }); + console.log(fakeEl); + each(qall('a', fakeEl), function(idx, a) { + var tpidx = a.textContent.indexOf('.tp'); + if (tpidx > -1) { + self.names[a.textContent.substr(0, tpidx)] = true; + } + }); + }); } topsymbols.prototype.find = function(name) { + console.log(this.names); if (!this.names) { - + throw new Error('data not ready'); } + console.log('toplevel', name); if (name in this.names) { return { type: 'toplevel', @@ -98,8 +116,13 @@ }; } else if(this.parent) { var ret = this.parent.find(name); - if (ret && ret.type == 'local') { - ret.type = 'upvar'; + if (ret) { + if (ret.type == 'local') { + ret.type = 'upvar'; + ret.depth = 1; + } else if (ret.type == 'upvar') { + ret.depth++; + } } return ret; } diff -r 608eb70fe261 -r 668f533e5284 jsbackend.js --- a/jsbackend.js Thu Apr 19 19:20:21 2012 -0700 +++ b/jsbackend.js Sat Jul 07 16:48:36 2012 -0700 @@ -1,4 +1,5 @@ var mainModule; +var modules = {}; function toobj(val) { @@ -78,6 +79,9 @@ for (var i = 0; i < funinfo.depth; ++i) { pre += '.parent'; } + } else if (info.type == 'toplevel') { + pre = 'modules.'; + modules[name] = false; } return pre + escapeJSName(name); } @@ -209,7 +213,7 @@ } object.prototype.toJSModule = function() { - this.populateSymbols(null); + this.populateSymbols(toplevel); return '(function () {\n\tvar module = ' + indent(this.toJS()) + ';\n\treturn module;\n})' } @@ -236,7 +240,7 @@ return 'function (' + args.join(', ') + ') {\n\t' + (this.symbols.needsSelfVar ? 'var self = this;\n\t' : '') + exprs.join(';\n\t') + '\n}' }; lambda.prototype.toJSModule = function() { - this.populateSymbols(null); + this.populateSymbols(toplevel); return this.toJS(); } diff -r 608eb70fe261 -r 668f533e5284 mquery.js --- a/mquery.js Thu Apr 19 19:20:21 2012 -0700 +++ b/mquery.js Sat Jul 07 16:48:36 2012 -0700 @@ -119,3 +119,10 @@ return el; } +function setText(parent, text) +{ + parent.innerHTML = ''; + parent.appendChild(document.createTextNode(text)); + return parent; +} + diff -r 608eb70fe261 -r 668f533e5284 runtime/object.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/runtime/object.c Sat Jul 07 16:48:36 2012 -0700 @@ -0,0 +1,21 @@ +#include "object.h" +#include +#include + +object * make_object(obj_meta * meta, void * parent, int num_props, ...) +{ + va_list args; + object * newobj = malloc(meta->size); + newobj->meta = meta; + newobj->parent = parent; + + va_start(args, num_props); + object ** curprop = ((object **)(newobj + 1)); + for (; num_props > 0; num_props--) + { + *curprop = va_arg(args, object *); + } + va_end(args); + return newobj; +} + diff -r 608eb70fe261 -r 668f533e5284 runtime/object.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/runtime/object.h Sat Jul 07 16:48:36 2012 -0700 @@ -0,0 +1,36 @@ +#ifndef OBJECT_H_ +#define OBJECT_H_ + +#include + +typedef struct obj_meta obj_meta; + +typedef struct object +{ + obj_meta * meta; + struct object * parent; +} object; + +typedef object * (*method)(uint32_t method_id, uint32_t num_args, object **); + +typedef object * (*closure_func)(void *, uint32_t, object **); + +typedef struct closure +{ + object header; + void * env; + closure_func func; +} closure; + +struct obj_meta +{ + uint32_t size; + method meth_lookup[16]; +}; + +#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, ...); + +#endif //OBJECT_H_ diff -r 608eb70fe261 -r 668f533e5284 runtime/progfoot.inc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/runtime/progfoot.inc Sat Jul 07 16:48:36 2012 -0700 @@ -0,0 +1,23 @@ + +int main(int argc, char ** argv) +{ + object * params[64]; + params[0] = mainModule(); + object * ret = mcall(METHOD_ID_MAIN, 1, params); + printf("%p:%p\n", ret->meta, &obj_int32_meta); + if (ret->meta == &obj_int32_meta) { + obj_int32 * reti32 = (obj_int32 *) ret; + printf("%d\n", reti32->num); + } else if(ret->meta == &lambda_meta) { + puts("returned lambda????"); + } else { + int i = 0; + for(; i < 16; ++i) { + if (ret->meta->meth_lookup[i] != &no_impl) { + printf("slot %d is set\n", i); + } + } + } + return 0; +} + diff -r 608eb70fe261 -r 668f533e5284 runtime/proghead.inc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/runtime/proghead.inc Sat Jul 07 16:48:36 2012 -0700 @@ -0,0 +1,18 @@ +#include "object.h" +#include +#include +#include + +object * no_impl(uint32_t method_id, uint32_t num_args, object ** params) +{ + printf("method %d is not implemented on object\n", method_id); + exit(0); + return NULL; +} + +obj_meta lambda_meta = { + sizeof(closure), + {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} +}; + diff -r 608eb70fe261 -r 668f533e5284 src/editor.tp --- a/src/editor.tp Thu Apr 19 19:20:21 2012 -0700 +++ b/src/editor.tp Sat Jul 07 16:48:36 2012 -0700 @@ -24,27 +24,7 @@ } //kernel definitions -true <- #{ - if:else <- :self trueblock :elseblock { - trueblock: - } -} - -false <- #{ - if:else <- :self trueblock :elseblock { - elseblock: - } -} - -filter <- :arr pred { - output <- arr slice: 0 0 - each: arr :idx el { - if: (pred: el) { - output push: el - } else: {} - } - output -} +import: kernel //editor code editFile <- :path { diff -r 608eb70fe261 -r 668f533e5284 testparse.html --- a/testparse.html Thu Apr 19 19:20:21 2012 -0700 +++ b/testparse.html Sat Jul 07 16:48:36 2012 -0700 @@ -7,12 +7,14 @@ + +
 	
diff -r 608eb70fe261 -r 668f533e5284 testparse.js --- a/testparse.js Thu Apr 19 19:20:21 2012 -0700 +++ b/testparse.js Sat Jul 07 16:48:36 2012 -0700 @@ -21,6 +21,17 @@ q('pre').innerHTML = e.message + '\nLine: ' + e.line + '\nCol: ' + e.column; }*/ } + q('#toc').onclick = function() { + var text = q('textarea').value; + //try { + var parsed = parser.parse(text); + var c = parsed.toCModule(); + setText(q('pre'), c); + console.log(parsed); + /*} catch(e) { + q('pre').innerHTML = e.message + '\nLine: ' + e.line + '\nCol: ' + e.column; + }*/ + } q('#run').onclick = function() { var text = q('textarea').value; //try {