Mercurial > repos > tabletprog
view cbackend.js @ 38:e7be612fd3ae
Very basic array support
author | Mike Pavone <pavone@retrodev.com> |
---|---|
date | Tue, 10 Jul 2012 23:09:44 -0700 |
parents | a6bf4869fcbe |
children | a997e42b9051 |
line wrap: on
line source
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]; return 'mcall(' + getMethodId(method) + ', 2, ' + this.left.toC() + ', ' + this.right.toC() + ')\n'; }; 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; } symbol.prototype.toC = function() { var name = this.cleanName(); var info = this.symbols.find(name); if (!info) { throw new Error('symbol ' + name + ' not found'); } var pre = ''; switch(info.type) { case 'self': pre = 'self->'; break; case 'parent': pre = 'self->'; for (var i = 0; i < info.depth; ++i) { pre += 'parent->'; } break; case 'upvar': pre = 'env->'; for (var i = 1; i < info.depth; ++i) { pre += 'parent->'; } 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(); if (!(this.val in declaredInts)) { toplevelcode += 'obj_int32 int32_' + str + ' = {{&obj_int32_meta, NULL}, ' + str + '};\n'; declaredInts[this.val] = true; } 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(' + this.val.length + ', '; for (var i = 0; i < this.val.length; i++) { ret += (i ? ', ' : '') + this.val[i].toC(); } return ret + ')'; } arraylit.prototype.toC = function() { var ret = 'make_array(' + this.val.length + ', '; for (var i = 0; i < this.val.length; i++) { ret += (i ? ', ' : '') + this.val[i].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] = ', ' + args[i].toC(); } var callpart; if (method) { callpart = 'mcall(' + getMethodId(name); } else { callpart = 'ccall(' + escapeCName(name); } return callpart + ', ' + args.length + args.join('') + ')'; } function cObject(name) { this.name = name; this.slots = {}; this.properties = []; this.values = []; this.slotvars = {}; } 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, '\t\t' + implementation.lines.join('\n\t\t') + '\n', msgname]); if (!(trunc in this.slotvars)) { this.slotvars[trunc] = {}; } for (var varname in implementation.vars) { this.slotvars[trunc][varname] = implementation.vars[varname]; } } cObject.prototype.addProperty = function(propname, value, type) { if (type != undefined) { this.properties.push([propname, type]); } else { var escaped = escapeCName(propname); this.addMessage(propname, { vars: {}, lines: [ 'va_end(args);', 'return self->' + escaped + ';' ]}); this.addMessage(propname + '!', { vars: {setval: 'object *'}, lines: [ 'setval = va_arg(args, object *);', 'va_end(args);', 'self->' + escaped + ' = setval;', 'return (object *)self;' ]}); 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 * oself, va_list args) {\n\t' + this.name + ' *self = (' + this.name + ' *)oself;\n'; for (var varname in this.slotvars[i]) { slotdefs += '\t' + this.slotvars[i][varname] + ' ' + varname + ';\n'; } 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, (object *)self, args);\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, args);\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 addBinaryOp(cobject, opname, cop, objtype) { cobject.addMessage(opname, { vars: {ret: objtype + ' *', argb: objtype +' *'}, lines: [ 'argb = va_arg(args, ' + objtype + ' *);', 'ret = (' + objtype + ' *)make_object(&' + objtype + '_meta, NULL, 0);', 'ret->num = self->num ' + cop + ' argb->num;', 'return ret;' ] }); } function addCompOp(cobject, opname, cop, objtype) { cobject.addMessage(opname, { vars: {argb: objtype + ' *'}, lines: [ 'argb = va_arg(args, ' + objtype + ' *);', 'if (self->num ' + cop + ' argb->num) {', ' return mcall(METHOD_ID_TRUE, 1, main_module);', '}', 'return mcall(METHOD_ID_FALSE, 1, main_module);' ] }); } function makeCProg(obj) { var int32 = new cObject('obj_int32'); int32.addProperty('num', null, 'int32_t'); addBinaryOp(int32, 'ADD_', '+', 'obj_int32'); addBinaryOp(int32, 'SUB_', '-', 'obj_int32'); addBinaryOp(int32, 'MUL_', '*', 'obj_int32'); addBinaryOp(int32, 'DIV_', '/', 'obj_int32'); addCompOp(int32, 'LT_', '<', 'obj_int32'); addCompOp(int32, 'GT_', '>', 'obj_int32'); addCompOp(int32, 'EQ_', '==', 'obj_int32'); addCompOp(int32, 'NEQ_', '!=', 'obj_int32'); addCompOp(int32, 'GEQ_', '>=', 'obj_int32'); addCompOp(int32, 'LEQ_', '<=', 'obj_int32'); 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 mcall(METHOD_ID_FALSE, 1, main_module);' ] }); 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 (object *)self;' ] }); forwarddec = toplevelcode = ''; forwarddec += int32.toEarlyCDef() + array.toEarlyCDef(); toplevelcode += int32.toCDef() + array.toCDef(); obj.populateSymbols(toplevel); 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() { 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; 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); } var offset = 1; } else { var offset = 0; } for (var i = 0; i < args.length; ++i) { var argname = args[i].toC(); args[i] = (argname.indexOf('->') < 0 ? '\tobject * ' : '\t') + argname + ' = va_arg(args, object *);\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] + ';'; } 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, ...) {\n\tva_list args;\n' + myenvinit + '\tva_start(args, num_args);\n'; if (this.selftype) { var selfvar = (new symbol('self', this.symbols)).toC(); if (selfvar == 'self') { toplevelcode += '\t' + this.selftype + ' * self = va_arg(args, ' + this.selftype + ' *);\n'; } else { toplevelcode += '\t' + selfvar + ' = va_arg(args, ' + this.selftype + ' *);\n'; } } toplevelcode += args.join('') + '\tva_end(args);\n' + exprs.join(';\n\t') + '\n}\n'; 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; return this.toC(); }; lambda.prototype.toCModule = function() { return makeCProg(this); } assignment.prototype.toC = function() { var existing = this.symbols.find(this.symbol.name); var prefix = ''; var val = this.expression.toC(); if (val === null) { return null; } if (existing.type == 'local' && !existing.isdeclared) { prefix = 'object *'; this.symbols.declareVar(this.symbol.name); } 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) { var params = ['((object *)self)']; var paramget = ''; var messagevars = {}; for (var i in this.expression.args) { var escaped = escapeCName(this.expression.args[i].cleanName()); if (escaped != 'self') { messagevars[escaped] = 'object *'; params.push(escaped); paramget += escaped + ' = va_arg(args, object *); '; } } cobj.addMessage(this.symbol.name, { vars: messagevars, lines: [paramget + 'return ccall(' + val + ', ' + params.length + (params.length ? ', ' : '') + params.join(', ') + ');'] }); } else { cobj.addProperty(this.symbol.name, val); } };