Mercurial > repos > tabletprog
view ilbackend.js @ 293:2b045d5b673b
Add binding for sdl mouse motion event
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Thu, 24 Jul 2014 09:43:18 -0700 |
parents | 6fe9343b1400 |
children | d1dc2d70bdfd |
line wrap: on
line source
var mainModule; var modules = {}; var methodIds = {}; var methodNames = []; var assignNames; function register(num) { this.num = num; } register.prototype = { valueOf: function() { return 'r' + this.num; }, get isRegister() { return true; }, get isArgument() { return false; } }; function argument(num) { this.num = num; } argument.prototype = { valueOf: function() { return 'a' + this.num; }, get isRegister() { return true; }, get isArgument() { return true; } }; function retreg(size) { this.size = size; this.reg = NULL; } retreg.prototype = { valueOf: function() { if (!this.reg) { return 'retr'; } return this.reg.valueOf(); }, clobber: function(code) { this.reg = code.getReg(); code.addInst('mov', 'retr', this.reg, this.size); }, get isRegister() { return true; }, get isArgument() { return false; } } function indexed(base, offset, index, size) { this.base = base; this.offset = offset; this.index = index; this.size = size; } indexed.prototype = { valueOf: function() { return this.offset + '[' + this.base + ' ' + this.index + '*' + this.size + ']'; }, get isRegister() { return false; }, get isArgument() { return false; } }; function offset(base, offset) { this.base = base; this.offset = offset; } offset.prototype = { valueOf: function() { var out = ''; if (this.offset) { out += this.offset; } return out + '[' + this.base + ']'; }, get isRegister() { return false; }, get isArgument() { return false; } }; function funCode(name) { this.name = name; this.instructions = []; this.nextReg = 0; this.toclobber = []; this.envreg = null; } funCode.prototype = { addInst: function() { var inst = ''; for (var i = 0; i < arguments.length; i++) { if (arguments[0] == 'call') { for (var i = 0; i < this.toclobber.length; i++) { this.toclobber[i].clobber(); } this.toclobber = []; } if (inst) { inst += ' '; } inst += arguments[i]; } this.instructions.push(inst); }, getReg: function() { return new register(this.nextReg++); }, getRetReg: function(size) { var reg = new retreg(size); this.toclobber.push(reg); return reg; }, getEnvReg: function() { if (!this.envreg) { this.envreg = this.getReg(); } return this.envreg; }, valueOf: function() { return this.name + ':\n\t' + this.instructions.join('\n\t') + '\n'; } }; function getMethodId(methodName) { if (!(methodName in methodIds)) { methodIds[methodName] = methodNames.length; methodNames.push(methodName); } return methodIds[methodName]; } function getOpMethodName(opname) { var optoMeth = {'+': 'ADD_', '-': 'SUB_', '*': 'MUL_', '/': 'DIV_', '%': 'MOD_', '=': 'EQ_', '!=': 'NEQ_', '<': 'LT_', '>': 'GT_', '>=': 'GEQ_', '<=': 'LEQ_', '.': 'CAT_', '&&':'if', '||':'ifnot', '|': 'CONS_'}; if (opname in optoMeth) { return optoMeth[opname]; } else { return opname; } } var slot_arr_offset = 8; function getMethodSlot(code, base, methodid) { var reg; if (!base.isRegister()) { reg = code.getReg(); code.addInst('mov', base, reg, 'q'); base = reg; } reg = code.getReg(); code.addInst('mov', new offset(base, 0), reg, 'q'); return new offset(reg, slot_arr_offset + (methodid & 0xF)*8); } op.prototype.toIL = function(code, isReceiver) { var method = getOpMethodName(this.op); var left = this.left.toIL(code); var right = this.right.toIL(code); var methId = getMethodId(method); if (this.op == '|') { code.addInst('call', getMethodSlot(code, right, methId), methId, right, left); } else { code.addInst('call', getMethodSlot(code, left, methId), methId, left, right); } return code.getRetReg(); }; op.prototype.toILLLExpr = function(vars) { var opmap = {'=': '==', 'xor': '^', 'or': '|', 'and': '&'}; return this.left.toILLLExpr(vars) + (this.op in opmap ? opmap[this.op] : this.op) + this.right.toILLLExpr(vars); }; op.prototype.toILLines = function(vars, needsreturn) { return [ (needsreturn ? 'return (object *)' : '' ) + this.toILLLExpr(vars) + ';']; }; function escapeCName(name) { if (name == 'self') { return name; } name = name.replace(/_/g, "UN_").replace(/:/g, "CN_").replace(/!/g, "EX_").replace(/\?/g, 'QS_').replace(/@/g, 'AT_'); name = 'tp_' + name; return name; } function getSymbolPrefix(info, symbols) { var pre = ''; switch(info.type) { case 'self': pre = (new symbol('self', symbols)).toIL() + '->'; break; case 'parent': pre = (new symbol('self', symbols)).toIL() + '->header.'; for (var i = 0; i < info.depth; ++i) { pre += (i ? '->' : '') + 'parent'; } pre = '((' + info.selftype + ' *)' + pre + ')->'; break; case 'upvar': pre = 'env->'; for (var i = info.startdepth; i < info.depth; ++i) { pre += 'parent->'; } break; case 'recupvar': if (info.subtype == 'object') { pre = 'self->env->'; } else { //TODO: fill this case in if necessary } pre += getSymbolPrefix(info.parent); case 'closedover': pre = 'myenv->'; } return pre; } symbol.prototype.toIL = function(code) { var name = this.cleanName(); var info = this.symbols.find(name); if (!info) { throw new Error('symbol ' + name + ' not found in ' + assignNames.join('<-')); } switch (info.type) { case 'toplevel': return toplevel.moduleVar(name); case 'self': if (info.def instanceof lambda) { var self = (new symbol('self', this.symbols)).toIL(code); var methId = getMethodId(name); code.addInst('call', getMethodSlot(code, self, methId), methId, self); return code.getRetReg(); } else { throw new Error('implement me'); } case 'parent': if (info.def instanceof lambda) { throw new Error('implement me'); var obj = (new symbol('self', this.symbols)).toIL() + '->header.'; for (var i = 0; i < info.depth; ++i) { obj += (i ? '->' : '') + 'parent'; } return 'mcall(' + getMethodId(name) + '/* ' + name + ' */, 1, ' + obj + ')'; } else { throw new Error('implement me'); } case 'closedover': var env = code.getEnvReg(); return new offset(env, info.offset) default: throw new Error('implement ' + info.type); } }; symbol.prototype.toILTypeName = function() { return this.cleanName(); }; symbol.prototype.toILLLExpr = 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.toILLines = function(vars, needsreturn) { return [ (needsreturn ? 'return (object *)' : '' ) + this.toILLLExpr(vars) + ';' ]; }; var declaredInts = {}; intlit.prototype.toIL = function() { var intType = (this.unsigned ? 'u' : '') + 'int' + this.bits; var str = intType + '_' + (this.val < 0 ? 'neg_' + (0-this.val).toString() : this.val.toString()); if (!(str in declaredInts)) { toplevelcode += 'obj_' + intType + ' ' + str + ' = {{&obj_' + intType + '_meta, NULL}, ' + this.val.toString() + '};\n'; declaredInts[str] = true; } return '((object *)&' + str + ')'; } intlit.prototype.toILLLExpr = function(vars) { return this.val.toString(); }; intlit.prototype.toILLines = function(vars, needsreturn) { return [ (needsreturn ? 'return (object *)' : '' ) + this.toILLLExpr(vars) + ';' ]; }; floatlit.prototype.toIL = function() { return 'make_float(' + this.val.toString() + ')'; } floatlit.prototype.toILLLExpr = function(vars) { return this.val.toString(); }; floatlit.prototype.toILLines = function(vars, needsreturn) { return [ (needsreturn ? 'return (object *)' : '' ) + this.toILLLExpr(vars) + ';' ]; }; var declaredStrings = {}; var nextStringId = 0; strlit.prototype.toIL = function() { if (!(this.val in declaredStrings)) { //TODO: get the proper byte length toplevelcode += 'string str_' + nextStringId + ' = {{&string_meta, NULL}, ' + this.val.length + ', ' + this.val.length + ', "' + this.val.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n').replace(/\r/g, '\\r') + '"};\n'; declaredStrings[this.val] = nextStringId++; } return '((object *)&str_' + declaredStrings[this.val] + ')'; }; strlit.prototype.toILLLExpr = function(vars) { return '"' + this.val.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n').replace(/\r/g, '\\r') + '"'; }; strlit.prototype.toILLines = function(vars, needsreturn) { return [ (needsreturn ? 'return (object *)' : '' ) + this.toILLLExpr(vars) +';' ]; }; listlit.prototype.toIL = function() { var ret = 'make_list(' + this.val.length; for (var i = this.val.length-1; i >= 0; i--) { ret += ', ' + this.val[i].toIL(); } return ret + ')'; } arraylit.prototype.toIL = function() { var ret = 'make_array(' + this.val.length; for (var i = 0; i < this.val.length; i++) { ret += ', ' + this.val[i].toIL(); } return ret + ')'; } funcall.prototype.toIL = 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:"); } } else if(name == 'llProperty:withType' || name == 'llProperty:withVars:andCode') { return null; } 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); var start = 0; if (!funinfo || funinfo.def instanceof setter || funinfo.type == 'toplevel') { method = true; } else { switch(funinfo.type) { case 'self': case 'parent': var defargs = funinfo.def.args.length; if (!defargs || funinfo.def.args[0].name != 'self') { defargs ++ } if (args.length < defargs) { if (funinfo.type == 'self') { args.splice(0, 0, new symbol('self', this.symbols)); } else { var obj = (new symbol('self', this.symbols)).toIL() + '->header.'; for (var i = 0; i < funinfo.depth; ++i) { obj += (i ? '->' : '') + 'parent'; } args.splice(0, 0, ', ' + obj); start = 1; } } else if(!(args[0] instanceof symbol) || args[0].name != 'self') { funinfo = null; } method = true; break; } } for (var i = start; i < args.length; i++) { args[i] = ', ' + (!i && method ? '(object *)(' : '') + args[i].toIL() + (!i && method ? ')' : ''); } var callpart; if (method) { if (funinfo && funinfo.type == 'self' && funinfo.def.name) { callpart = funinfo.def.name + '(' + (funinfo.def.symbols.parent.needsenv ? (new symbol('self', this.symbols)).toIL() + '->env' : 'NULL' ); } else { callpart = 'mcall(' + getMethodId(name) + '/* ' + name + ' */'; } } else { var closVar = (new symbol(name, this.symbols)).toIL(); callpart = '((lambda *)' + closVar + ')->func(((lambda *)' + closVar + ')->env'; } return callpart + ', ' + args.length + args.join('') + ')'; }; funcall.prototype.toILTypeName = function() { switch(this.name) { case 'ptr:': case 'ptr': var receiver = this.receiver ? this.receiver : this.args[0]; return receiver.toILTypeName() + ' *'; break; case 'struct:': case 'struct': var receiver = this.receiver ? this.receiver : this.args[0]; return 'struct ' + receiver.toILTypeName(); break; case 'funptr:': case 'funptr': var rettype; var firstArg; if (this.receiver) { rettype = this.receiver; firstArg = 0; } else { rettype = this.args[0]; firstArg = 1; } var argtypes = ''; for (var i = firstArg; i < this.args.length; i++) { if (argtypes) { argtypes += ', ' } argtypes += this.args[i].toILTypeName(); } return [rettype.toILTypeName() + '(*', ')(' + argtypes + ')']; break; default: throw new Error('invalid use of funcall expression where a C type name is expected'); } }; funcall.prototype.toILLines = 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].toILLLExpr(vars) + ') {'); var blines = this.args[1].toILLines(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].toILLLExpr(vars) + ') {'); var blines = this.args[1].toILLines(vars, needsreturn); for (var i in blines) { lines.push('\t' + blines[i]); } lines.push('} else {'); blines = this.args[2].toILLines(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].toILLLExpr(vars) + ') {'); var blines = this.args[1].toILLines(vars); for (var i in blines) { lines.push('\t' + blines[i]); } lines.push('}'); break; default: lines.push( (needsreturn ? 'return (object *)' : '') + this.toILLLExpr(vars) + ';'); } return lines; }; funcall.prototype.toILLLExpr = 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) { if (this.receiver instanceof symbol && this.receiver.name in vars && vars[this.receiver.name].substr(-1) != "*") { return this.receiver.toILLLExpr(vars) + '.' + this.name; } else { return this.receiver.toILLLExpr(vars) + '->' + this.name; } } else if (this.args.length == 1 && name[name.length-1] == '!') { if (this.receiver instanceof symbol && this.receiver.name in vars && vars[this.receiver.name].substr(-1) != "*") { return this.receiver.toILLLExpr(vars) + '.' + this.name.substr(0, name.length-1) + ' = ' + args[0].toILLLExpr(vars); } else { return this.receiver.toILLLExpr(vars) + '->' + this.name.substr(0, name.length-1) + ' = ' + args[0].toILLLExpr(vars); } } else { args.splice(0, 0, this.receiver); } } switch(name) { case 'if': return '((' + args[0].toILLLExpr(vars) + ') ? (' + args[1].toILLLExpr(vars) + ') : 0)'; case 'if:else': return '((' + args[0].toILLLExpr(vars) + ') ? (' + args[1].toILLLExpr(vars) + ') : (' + args[2].toILLLExpr(vars) + '))'; case 'while:do': throw new Error('while:do not allowed in expression context in llMessage block'); case 'addr_of': return '&(' + args[0].toILLLExpr(vars) + ')'; case 'sizeof': return 'sizeof(' + args[0].toILTypeName() + ')'; case 'get': return args[0].toILLLExpr(vars) + '[' + args[1].toILLLExpr(vars) + ']'; case 'set': return args[0].toILLLExpr(vars) + '[' + args[1].toILLLExpr(vars) + '] = ' + args[2].toILLLExpr(vars); case 'not': return '!(' + args[0].toILLLExpr(vars) + ')'; case 'castTo': return '((' + args[1].toILTypeName() + ')(' + args[0].toILLLExpr(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].toILLLExpr(vars); } return name + '(' + args.join(', ') + ')'; } }; function cObject(name) { this.name = name; this.slots = {}; this.properties = []; this.values = []; this.slotvars = {}; this.includes = {}; this.parent = 'NULL'; this.init = []; this.initmsgadded = false; } cObject.prototype.addInclude = function(includefile) { this.includes[includefile] = true; } 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]); if (value !== null) { this.values.push(value); } } else { var escaped = escapeCName(propname); this.addMessage(propname, { vars: {}, lines: [ 'return self->' + escaped + ';' ]}); this.addMessage(propname + '!', { vars: {}, lines: [ 'self->' + escaped + ' = va_arg(args, object *);', 'return (object *)self;' ]}); this.properties.push(escaped); this.values.push(value); } } cObject.prototype.addInit = function(statement) { this.init.push(statement); }; cObject.prototype.addImport = function(symbols, source) { this.imported.push(source); var importNum = imported.length - 1; if (symbols) { each(symbols, function(i, sym) { this.addMessage(sym.name, { vars: {}, lines: [ 'return self->import_' + importNum + '->meta->meth_lookup[method_id & 0xF](method_id, num_args, self->import_' + importNum + ', args);' ] }); }); } else { //TODO: handle proxying for unimplemented methods } }; cObject.prototype.checkInitMsg = function() { if (!this.initmsgadded && this.init.length) { var init = this.init.slice(0, this.init.length); init.push('return (object *)self;'); this.addMessage('_init', { vars: {}, lines: init }); this.initmsgadded = true; } } cObject.prototype.populateSymbols = function() {}; cObject.prototype.toEarlyCDef = function() { this.checkInitMsg(); var includes = ''; for (var file in this.includes) { includes += '#include ' + file + '\n'; } var objdef = 'typedef struct ' + this.name + ' {\n\tobject header;\n'; for (var i in this.properties) { if (this.properties[i] instanceof Array) { if (this.properties[i][1] instanceof Array) { objdef += '\t' + this.properties[i][1][0] + this.properties[i][0] + this.properties[i][1][1] + ';\n'; } else { 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 includes + objdef; } cObject.prototype.toILDef = function() { this.checkInitMsg(); 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]) { if (this.slotvars[i][varname] instanceof Array) { slotdefs += '/*foo*/\t' + this.slotvars[i][varname][0] + ' ' + varname + this.slotvars[i][varname][1] + ';\n'; } else { slotdefs += '/*bar*/\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' + '\t\t\treturn no_impl(method_id, num_params, (object *)self, args);\n\t}\n}\n'; } metadef += this.name + '_slot_' + i; } else { metadef += 'no_impl'; } } metadef += '}};\n'; return slotdefs + metadef; } cObject.prototype.toILInstance = function() { this.checkInitMsg(); var ret = 'make_object(&' + this.name + '_meta, ' + this.parent + ', ' + this.values.length + (this.values.length ? ', ' : '') + this.values.join(', ') + ')'; if (this.initmsgadded) { ret = 'mcall(' + getMethodId('_init') + ', 1, ' + ret + ')' } return ret; } cObject.prototype.toIL = function() { forwarddec += this.toEarlyCDef(); toplevelcode += this.toILDef(); return this.toILInstance(); } var nextobject = 0; object.prototype.toILObject = function() { var messages = this.messages; 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() + ' * '); me.hasenv = true; } if (this.symbols.needsparent && !(this.symbols.parent instanceof osymbols)) { me.parent = '(object *)(' + (new symbol('self', this.symbols.parent)).toIL() + ')'; } for (var i in messages) { if (messages[i] instanceof funcall) { if (messages[i].name == 'import:' && messages[i].args.length == 1) { me.addImport(false, 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(el); }); me.addImport(importsyms, 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].toILTypeName()); } 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.toILTypeName(); } me.addMessage(msgname, { vars: vars, lines: messages[i].args[2].toILLines(vars, true) }); } else if(messages[i].name == 'includeSystemHeader:' && messages[i].args.length == 1) { me.addInclude("<" + messages[i].args[0].val + ">"); } else { 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].toILObject(me); } } return me; }; object.prototype.toIL = function() { return this.toILObject().toIL(); }; 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->header);' ] }); } 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 ' + toplevel.moduleVar('true') + ';', '}', 'return ' + toplevel.moduleVar('false') + ';', ] }); } function makeInt(bits, unsigned) { var typename = 'obj_' + (unsigned ? 'u' : '') + 'int' + bits; var intObj = new cObject(typename); intObj.addProperty('num', null, (unsigned ? 'u' : '') + 'int' + bits +'_t'); addBinaryOp(intObj, 'ADD_', '+', typename); addBinaryOp(intObj, 'SUB_', '-', typename); addBinaryOp(intObj, 'MUL_', '*', typename); addBinaryOp(intObj, 'DIV_', '/', typename); addBinaryOp(intObj, 'MOD_', '%', typename); addBinaryOp(intObj, 'or', '|', typename); addBinaryOp(intObj, 'xor', '^', typename); addBinaryOp(intObj, 'and', '&', typename); addBinaryOp(intObj, 'lshift:by', '<<', typename); addBinaryOp(intObj, 'rshift:by', '>>', typename); addCompOp(intObj, 'LT_', '<', typename); addCompOp(intObj, 'GT_', '>', typename); addCompOp(intObj, 'EQ_', '==', typename); addCompOp(intObj, 'NEQ_', '!=', typename); addCompOp(intObj, 'GEQ_', '>=', typename); addCompOp(intObj, 'LEQ_', '<=', typename); intObj.addInclude('<string.h>'); //-9223372036854775808 //01234567890123456789 intObj.addMessage('string', { vars: {str: 'string *'}, lines: [ 'str = (string *)make_object(&string_meta, NULL, 0);', 'str->data = GC_MALLOC(' + (bits == 64 ? 21 : 12) + ');', 'sprintf(str->data, "%' + (bits == 64 ? 'l' : '') + (unsigned ? 'u' : 'd') + '", self->num);', 'str->len = str->bytes = strlen(str->data);', 'return &(str->header);' ] }); //7FFFFFFFFFFFFFFF //01234567890123456789 intObj.addMessage('hex', { vars: {str: 'string *'}, lines: [ 'str = (string *)make_object(&string_meta, NULL, 0);', 'str->data = GC_MALLOC(' + (bits == 64 ? 17 : 9) + ');', 'sprintf(str->data, "%' + (bits == 64 ? 'l' : '') +'X", self->num);', 'str->len = str->bytes = strlen(str->data);', 'return &(str->header);' ] }); intObj.addMessage('isInteger?', { vars: {}, lines: [ 'return ' + toplevel.moduleVar('true') + ';' ] }); intObj.addMessage('hash', { vars: {}, lines: [ 'return &(self->header);' ] }); intObj.addMessage('signed?', { vars: {}, lines: [ 'return ' + toplevel.moduleVar(unsigned ? 'false' : 'true') + ';' ] }); var sizes = [8, 16, 32, 64]; var destunsigned = [false, true]; for (var i = 0; i < sizes.length; i++) { size = sizes[i]; for (var j = 0; j < destunsigned.length; j++) { uns = destunsigned[j]; if (uns == unsigned && size == bits) { intObj.addMessage((uns ? 'u' : '') + 'int' + size, { vars: {}, lines: [ 'return &(self->header);' ] }); } else { var retType = 'obj_' + (uns ? 'u' : '') + 'int' + size; var retName = 'ret' + (uns ? 'u' : '') + size; var vars = {}; vars[retName] = retType + ' *'; intObj.addMessage((uns ? 'u' : '') + 'int' + size, { vars: vars, lines: [ retName + ' = ('+retType+' *)make_object(&' + retType +'_meta, NULL, 0);', retName + '->num = self->num;', 'return &(' + retName + '->header);' ] }); } } } return intObj; } function makeArray() { var arrayfile = toplevel.names['array']; var ast = parseFile(arrayfile.path + '/' + arrayfile.file); ast.name = 'array'; ast.populateSymbols(toplevel); return ast.toILObject(); } function makeString() { var arrayfile = toplevel.names['string']; var ast = parseFile(arrayfile.path + '/' + arrayfile.file); ast.name = 'string'; ast.populateSymbols(toplevel); return ast.toILObject(); } function makelambda() { var clos = new cObject('lambda'); clos.addProperty('env', null, 'void *'); clos.addProperty('func', null, 'closure_func'); clos.addMessage('while:do', { vars: {action: 'lambda *', ret: 'object *'}, lines: [ 'action = va_arg(args, lambda *);', 'ret = ' + toplevel.moduleVar('true') + ';', 'while(' + toplevel.moduleVar('true') + ' == ccall(self, 0)) {', ' ccall(action, 0);', '}', 'return ret;' ] }); return clos; } function builtinTypes() { return [makeInt(64, false), makeInt(32, false), makeInt(16, false), makeInt(8, false), makeInt(64, true) , makeInt(32, true), makeInt(16, true), makeInt(8, true), makeArray(), makeString(), makelambda()]; } function addBuiltinModules(toplevel) { var os = new cObject('mod_obj_os'); os.addInclude('<sys/stat.h>'); os.addInclude('<fcntl.h>'); os.addInclude('<stdlib.h>'); os.addInclude('<time.h>'); os.addInclude('<unistd.h>'); os.addMessage('write', { vars: {str: 'string *', intret: 'obj_int32 *', filedes: 'obj_int32 *'}, lines: [ 'filedes = va_arg(args, obj_int32 *);', 'str = va_arg(args, string *);', 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', 'intret->num = write(filedes->num, str->data, str->bytes);', 'return &(intret->header);' ] }); os.addMessage('read', { vars: {str: 'string *', size: 'obj_int32 *', filedes: 'obj_int32 *'}, lines: [ 'filedes = va_arg(args, obj_int32 *);', 'size = va_arg(args, obj_int32 *);', 'str = (string *)make_object(&string_meta, NULL, 0);', 'str->data = GC_MALLOC_ATOMIC(size->num + 1);', 'str->len = str->bytes = read(filedes->num, str->data, size->num);', 'if (str->bytes < 0) { str->bytes = str->len = 0; }', 'str->data[str->bytes] = 0;', 'return &(str->header);' ] }); os.addMessage('open', { vars: {str: 'string *', flags: 'obj_int32 *', filedes: 'obj_int32 *', perm: 'obj_int32 *'}, lines: [ 'str = va_arg(args, string *);', 'flags = va_arg(args, obj_int32 *);', 'filedes = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', 'if (num_params == 3) {', ' filedes->num = open(str->data, flags->num);', '} else if (num_params == 4) {', ' perm = va_arg(args, obj_int32 *);', ' filedes->num = open(str->data, flags->num, perm->num);', '} else {', ' filedes->num = -1;', '}', 'return &(filedes->header);' ] }); os.addMessage('close', { vars: {filedes: 'obj_int32 *', intret: 'obj_int32 *'}, lines: [ 'filedes = va_arg(args, obj_int32 *);', 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', 'intret->num = close(filedes->num);', 'return &(intret->header);' ] }); os.addMessage('O_RDONLY', { vars: {intret: 'obj_int32 *'}, lines: [ 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', 'intret->num = O_RDONLY;', 'return &(intret->header);' ] }); os.addMessage('O_WRONLY', { vars: {intret: 'obj_int32 *'}, lines: [ 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', 'intret->num = O_WRONLY;', 'return &(intret->header);' ] }); os.addMessage('O_RDWR', { vars: {intret: 'obj_int32 *'}, lines: [ 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', 'intret->num = O_RDWR;', 'return &(intret->header);' ] }); os.addMessage('O_CREAT', { vars: {intret: 'obj_int32 *'}, lines: [ 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', 'intret->num = O_CREAT;', 'return &(intret->header);' ] }); os.addMessage('O_APPEND', { vars: {intret: 'obj_int32 *'}, lines: [ 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', 'intret->num = O_APPEND;', 'return &(intret->header);' ] }); os.addMessage('O_TRUNC', { vars: {intret: 'obj_int32 *'}, lines: [ 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', 'intret->num = O_TRUNC;', 'return &(intret->header);' ] }); os.addMessage('rand', { vars: {intret: 'obj_int32 *'}, lines: [ 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', 'intret->num = rand();', 'return &(intret->header);' ] }); os.addMessage('rand64', { vars: {intret64: 'obj_int64 *'}, lines: [ 'intret64 = (obj_int64 *)make_object(&obj_int64_meta, NULL, 0);', 'intret64->num = (((int64_t)rand()) << 32 ) | rand();', 'return &(intret64->header);' ] }); os.addMessage('srand', { vars: {oseed: 'object *', seed: 'obj_int32 *'}, lines: [ 'oseed = va_arg(args, object *);', 'seed = mcall(' + getMethodId("int32") + ', 1, oseed);', 'srand(seed->num);', 'return &(seed->header);' ] }); os.addMessage('time', { vars: {intret64: 'obj_int64 *'}, lines: [ 'intret64 = (obj_int64 *)make_object(&obj_int64_meta, NULL, 0);', 'intret64->num = time(NULL);', 'return &(intret64->header);' ] }); os.addMessage('sleep', { vars: {osecs: 'object *', secs: 'obj_int32 *', intret: 'obj_int32 *'}, lines: [ 'osecs = va_arg(args, object *);', 'secs = mcall(' + getMethodId("int32") + ', 1, osecs);', 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', 'intret->num = sleep(secs->num);', 'return &(intret->header);' ] }); toplevel.names['os'] = os; } modulefile.prototype.toIL = function(){ return this.ast.toILModuleInstance(); }; function processUsedToplevel(toplevel) { var alwaysused = ['true', 'false', 'list']; 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; while (newused.length) { for (var i in newused) { debugprint('//---module', newused[i], '--- populate symbols'); forwarddec += 'object * ' + toplevel.moduleVar(newused[i]) + ';\n'; toplevel.names[newused[i]].populateSymbols(toplevel); visited[newused[i]] = true; } newused = []; for (var symbol in toplevel.used) { if (!(symbol in visited)) { debugprint('//found new usage of module', symbol); newused.push(symbol); allused.push(symbol); } } } for (var i = allused.length-1; i >= 0; i--) { var symbol = allused[i]; debugprint('//---module', symbol, '(' + i +')--- compile'); assignNames.push(symbol); ret += '\t' + toplevel.moduleVar(symbol) + ' = ' + toplevel.names[symbol].toIL() + ';\n'; assignNames.pop(); } return ret; } function makeCProg(obj) { forwarddec = toplevelcode = ''; assignNames = []; var builtins = builtinTypes(); for (var i in builtins) { forwarddec += builtins[i].toEarlyCDef(); toplevelcode += builtins[i].toILDef(); } addBuiltinModules(toplevel); debugprint('//------POPULATING SYMBOLS-----'); obj.populateSymbols(toplevel); var moduleinit = processUsedToplevel(toplevel); debugprint('//------COMPILING AST-----'); var rest = 'object * mainModule() {\n' + moduleinit + '\tmain_module = ' + obj.toILModuleInstance() + ';\n\treturn main_module;\n}\n'; var mnames = 'char * methodNames[] = {\n'; for (var i = 0; i < methodNames.length; i++) { mnames += '\t"' + methodNames[i].replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n") + '",\n'; } mnames += '};\n'; return '#include "runtime/proghead.inc"\n' + '#define METHOD_ID_MAIN ' + getMethodId('main') + '\n' + '#define METHOD_ID_EMPTY ' + getMethodId('empty') + '\n' + '#define METHOD_ID_CONS ' + getMethodId(getOpMethodName('|')) + '\n' + mnames + forwarddec + toplevelcode + rest + '#include "runtime/progfoot.inc"\n'; } object.prototype.toILModule = function() { return makeCProg(this); } object.prototype.toILModuleInstance = function() { return this.toIL(); } lambda.prototype.toIL = function(parentcode) { var code = new funCode(this.name); var args = this.args ? this.args.slice(0, this.args.length) : []; var exprs = this.expressions; var assignPath = assignNames.join('<-'); debugprint('//', this.name, assignPath); var addedTypeDef = false; if (Object.keys(this.symbols.closedover).length) { this.symbols.envtype = this.name + '_env'; forwarddec += 'typedef struct ' + this.symbols.envtype + ' ' + this.symbols.envtype + ';\n' var addedTypeDef = true; } 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].toIL(code); args[i] = (argname.indexOf('->') < 0 ? '\tobject * ' : '\t') + argname + ' = va_arg(args, object *);\n'; } var compiled = [] for (var i in exprs) { var js = exprs[i].toIL(code); if (js) { compiled.push(indent(js)); } } if (compiled.length) { if (exprs[exprs.length - 1] instanceof assignment) { compiled.push('return ' + exprs[exprs.length - 1].symbol.toIL() + ';'); } else { compiled[compiled.length-1] = 'return (object *)(' + compiled[compiled.length-1] + ');'; } } exprs = compiled; if (Object.keys(this.symbols.closedover).length) { if (!addedTypeDef) { for (var key in this.symbols.closedover) { print(key, ": ", this.symbols.closedover[key]); } throw new Error('this.symbols.closedover is not empty, but it was when compilation of ' + this.name + ' "' + assignPath + '" started'); } forwarddec += 'struct ' + this.name + '_env {\n'; if (this.symbols.needsParentEnv) { forwarddec += '\tstruct ' + this.symbols.parentEnvType() + ' * parent;\n'; } for (var varname in this.symbols.closedover) { if (varname == 'self' && this.selftype) { forwarddec += '\tstruct ' + this.selftype + ' * self;\n'; } else { forwarddec += '\tobject * ' + escapeCName(varname) + ';\n'; } } forwarddec += '};\n' var myenvinit = '\t' + this.name + '_env * myenv = GC_MALLOC(sizeof(' + this.name + '_env));\n'; if (this.symbols.needsParentEnv) { myenvinit += '\tmyenv->parent = env;\n'; } this.symbols.envtype = this.name + '_env'; } else { var myenvinit = ''; } forwarddec += 'object *' + this.name + ' (' + this.symbols.parentEnvType() + ' * env, uint32_t num_args, ...);\n'; toplevelcode += '//' + assignPath + "\n"; toplevelcode += 'object * ' + this.name + ' ( ' + 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)).toIL(); 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.selftype) { return this.name; } else { if (this.symbols.parentEnvType() != 'void') { if (this.symbols.parent.passthruenv) { var envvar = 'env'; } else { var envvar = 'myenv'; } debugprint('//' + this.name, 'has envvar:', envvar, 'num vars closed over:', Object.keys(this.symbols.closedover).length); return 'make_lambda(' + envvar + ', (closure_func)' + this.name + ')'; } else { toplevelcode += 'lambda ' + this.name + '_obj = {{&lambda_meta, NULL}, NULL, ' + this.name + '};\n'; return '((object *)&' + this.name + '_obj)'; } } }; lambda.prototype.toILModuleInstance = function() { this.toIL(); return this.name + '(NULL, 0)'; }; lambda.prototype.toILObject = function(typename) { this.selftype = typename; return this.toIL(); }; lambda.prototype.toILModule = function() { return makeCProg(this); }; lambda.prototype.toILLines = 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].toILLines(vars, needsreturn && i == this.expressions.length - 1); for (var j in exprlines) { lines.push('\t' + exprlines[j]); } } return lines; } lambda.prototype.toILLLExpr = 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].toILLLExpr(vars); } assignment.prototype.toIL = function() { debugprint('//assignment', this.symbol.name); var existing = this.symbols.find(this.symbol.name); var prefix = ''; assignNames.push(this.symbol.name); var val = this.expression.toIL(); assignNames.pop(this.symbol.name); if (val === null) { return null; } if (existing.type == 'local' && !existing.isdeclared) { prefix = 'object *'; this.symbols.declareVar(this.symbol.name); debugprint('//declared var', this.symbol.name); } return prefix + this.symbol.toIL() + ' = ' + val; }; assignment.prototype.toILObject = function(cobj) { debugprint('//message definition', this.symbol.name); assignNames.push('#' + this.symbol.name); if (this.expression.toILObject) { var val = this.expression.toILObject(cobj.name); } else { var val = this.expression.toIL(); } assignNames.pop(); 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(getOpMethodName(this.symbol.name), { vars: messagevars, lines: [paramget + 'return ' + val + '(' + (cobj.hasenv ? 'self->env' : 'NULL') + ', ' + params.length + (params.length ? ', ' : '') + params.join(', ') + ');'] }); } else { cobj.addProperty(this.symbol.name, val); if (this.expression instanceof object && this.expression.symbols.needsparent) { cobj.addInit('self->' + escapeCName(this.symbol.name) + '->parent = (object *)self;'); } } }; assignment.prototype.toILLines = function(vars, needsreturn) { return [(needsreturn ? 'return ' : '') + this.symbol.toILLLExpr(vars) + ' = ' + this.expression.toILLLExpr(vars) + ';'] };