pavone@31: var mainModule; pavone@31: var modules = {}; pavone@31: pavone@31: var methodIds = {}; pavone@182: var methodNames = []; pavone@155: var assignNames; pavone@31: function getMethodId(methodName) pavone@31: { pavone@31: if (!(methodName in methodIds)) { pavone@182: methodIds[methodName] = methodNames.length; pavone@182: methodNames.push(methodName); pavone@31: } pavone@31: return methodIds[methodName]; pavone@31: } pavone@31: pavone@122: function getOpMethodName(opname) pavone@122: { pavone@170: var optoMeth = {'+': 'ADD_', '-': 'SUB_', '*': 'MUL_', '/': 'DIV_', '%': 'MOD_', pavone@170: '=': 'EQ_', '!=': 'NEQ_', '<': 'LT_', '>': 'GT_', '>=': 'GEQ_', '<=': 'LEQ_', pavone@170: '.': 'CAT_', '&&':'if', '||':'ifnot', '|': 'CONS_'}; pavone@122: if (opname in optoMeth) { pavone@122: return optoMeth[opname]; pavone@122: } else { pavone@122: return opname; pavone@122: } pavone@122: } pavone@122: pavone@122: op.prototype.toC = function(isReceiver) { pavone@122: var method = getOpMethodName(this.op); pavone@170: if (this.op == '|') { pavone@170: return 'mcall(' + getMethodId(method) + '/* operator ' + method + ' */, 2, (object *)' + this.right.toC() + ', ' + this.left.toC() + ')\n'; pavone@170: } else { pavone@170: return 'mcall(' + getMethodId(method) + '/* operator ' + method + ' */, 2, (object *)' + this.left.toC() + ', ' + this.right.toC() + ')\n'; pavone@170: } pavone@31: }; pavone@84: op.prototype.toCLLExpr = function(vars) { pavone@177: var opmap = {'=': '==', 'xor': '^', 'or': '|', 'and': '&'}; pavone@87: return this.left.toCLLExpr(vars) + (this.op in opmap ? opmap[this.op] : this.op) + this.right.toCLLExpr(vars); pavone@84: }; pavone@84: op.prototype.toCLines = function(vars, needsreturn) { pavone@84: return [ (needsreturn ? 'return (object *)' : '' ) + this.toCLLExpr(vars) + ';']; pavone@84: }; pavone@84: pavone@31: pavone@31: function escapeCName(name) pavone@31: { pavone@34: if (name == 'self') { pavone@34: return name; pavone@34: } pavone@172: name = name.replace(/_/g, "UN_").replace(/:/g, "CN_").replace(/!/g, "EX_").replace(/\?/g, 'QS_').replace(/@/g, 'AT_'); pavone@31: name = 'tp_' + name; pavone@31: return name; pavone@31: } pavone@31: pavone@54: function getSymbolPrefix(info, symbols) pavone@42: { pavone@31: var pre = ''; pavone@34: switch(info.type) { pavone@34: case 'self': pavone@121: pavone@54: pre = (new symbol('self', symbols)).toC() + '->'; pavone@34: break; pavone@34: case 'parent': pavone@57: pre = (new symbol('self', symbols)).toC() + '->header.'; pavone@32: for (var i = 0; i < info.depth; ++i) { pavone@57: pre += (i ? '->' : '') + 'parent'; pavone@32: } pavone@57: pre = '((' + info.selftype + ' *)' + pre + ')->'; pavone@34: break; pavone@34: case 'upvar': pavone@32: pre = 'env->'; pavone@57: for (var i = info.startdepth; i < info.depth; ++i) { pavone@32: pre += 'parent->'; pavone@31: } pavone@34: break; pavone@42: case 'recupvar': pavone@42: if (info.subtype == 'object') { pavone@42: pre = 'self->env->'; pavone@42: } else { pavone@42: //TODO: fill this case in if necessary pavone@42: } pavone@42: pre += getSymbolPrefix(info.parent); pavone@34: case 'closedover': pavone@34: pre = 'myenv->'; pavone@31: } pavone@42: return pre; pavone@42: } pavone@42: pavone@42: symbol.prototype.toC = function() { pavone@42: var name = this.cleanName(); pavone@42: var info = this.symbols.find(name); pavone@42: if (!info) { pavone@172: throw new Error('symbol ' + name + ' not found in ' + assignNames.join('<-')); pavone@42: } pavone@48: if (info.type == 'toplevel') { pavone@68: return toplevel.moduleVar(name); pavone@69: } else if (info.type == 'self' && info.def instanceof lambda) { pavone@69: return 'mcall(' + getMethodId(name) + '/* ' + name + ' */, 1, ' + (new symbol('self', this.symbols)).toC() + ')'; pavone@69: } else if (info.type == 'parent' && info.def instanceof lambda) { pavone@69: var obj = (new symbol('self', this.symbols)).toC() + '->header.'; pavone@69: for (var i = 0; i < info.depth; ++i) { pavone@69: obj += (i ? '->' : '') + 'parent'; pavone@69: } pavone@69: return 'mcall(' + getMethodId(name) + '/* ' + name + ' */, 1, ' + obj + ')'; pavone@48: } pavone@54: return getSymbolPrefix(info, this.symbols) + escapeCName(name); pavone@31: } pavone@84: symbol.prototype.toCTypeName = function() { pavone@84: return this.cleanName(); pavone@84: }; pavone@84: symbol.prototype.toCLLExpr = function(vars) { pavone@84: var name = this.cleanName(); pavone@84: if (name in vars) { pavone@84: return name; pavone@84: } pavone@84: if (name == 'self') { pavone@84: return 'self'; pavone@84: } pavone@84: var info = this.symbols.find(name, false, true); pavone@84: var symbols = this.symbols; pavone@84: while (info && info.type == 'local') { pavone@84: symbols = symbols.parent; pavone@84: info = symbols.find(name, false, true); pavone@84: } pavone@84: if (!info) { pavone@84: return name; pavone@84: } pavone@84: if (info.type == 'toplevel') { pavone@84: return toplevel.moduleVar(name); pavone@84: } else if (info.type == 'self') { pavone@84: if (info.isll || !(info.def instanceof lambda)) { pavone@84: return 'self->' + name; pavone@84: } else { pavone@87: return 'mcall(' + getMethodId(name) + '/* ' + name + ' */, 1, self)'; pavone@84: } pavone@84: } pavone@84: throw new Error('Unsupported reference type ' + info.type + ' for variable ' + name); pavone@84: }; pavone@84: symbol.prototype.toCLines = function(vars, needsreturn) { pavone@84: return [ (needsreturn ? 'return (object *)' : '' ) + this.toCLLExpr(vars) + ';' ]; pavone@84: }; pavone@31: pavone@34: var declaredInts = {}; pavone@34: pavone@31: intlit.prototype.toC = function() { pavone@155: var intType = (this.unsigned ? 'u' : '') + 'int' + this.bits; pavone@135: var str = intType + '_' + (this.val < 0 ? 'neg_' + (0-this.val).toString() : this.val.toString()); pavone@135: if (!(str in declaredInts)) { pavone@135: toplevelcode += 'obj_' + intType + ' ' + str + ' = {{&obj_' + intType + '_meta, NULL}, ' + this.val.toString() + '};\n'; pavone@135: declaredInts[str] = true; pavone@34: } pavone@135: return '((object *)&' + str + ')'; pavone@31: } pavone@84: intlit.prototype.toCLLExpr = function(vars) { pavone@84: return this.val.toString(); pavone@84: }; pavone@84: intlit.prototype.toCLines = function(vars, needsreturn) { pavone@84: return [ (needsreturn ? 'return (object *)' : '' ) + this.toCLLExpr(vars) + ';' ]; pavone@84: }; pavone@31: pavone@31: floatlit.prototype.toC = function() { pavone@31: return 'make_float(' + this.val.toString() + ')'; pavone@31: } pavone@84: floatlit.prototype.toCLLExpr = function(vars) { pavone@84: return this.val.toString(); pavone@84: }; pavone@84: floatlit.prototype.toCLines = function(vars, needsreturn) { pavone@84: return [ (needsreturn ? 'return (object *)' : '' ) + this.toCLLExpr(vars) + ';' ]; pavone@84: }; pavone@31: pavone@41: var declaredStrings = {}; pavone@41: var nextStringId = 0; pavone@41: pavone@31: strlit.prototype.toC = function() { pavone@41: if (!(this.val in declaredStrings)) { pavone@41: //TODO: get the proper byte length pavone@146: 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'; pavone@41: declaredStrings[this.val] = nextStringId++; pavone@41: } pavone@41: return '((object *)&str_' + declaredStrings[this.val] + ')'; pavone@84: }; pavone@84: strlit.prototype.toCLLExpr = function(vars) { pavone@146: return '"' + this.val.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n').replace(/\r/g, '\\r') + '"'; pavone@84: }; pavone@84: strlit.prototype.toCLines = function(vars, needsreturn) { pavone@84: return [ (needsreturn ? 'return (object *)' : '' ) + this.toCLLExpr(vars) +';' ]; pavone@84: }; pavone@31: pavone@31: listlit.prototype.toC = function() { pavone@40: var ret = 'make_list(' + this.val.length; pavone@170: for (var i = this.val.length-1; i >= 0; i--) { pavone@40: ret += ', ' + this.val[i].toC(); pavone@38: } pavone@38: return ret + ')'; pavone@38: } pavone@38: pavone@38: arraylit.prototype.toC = function() { pavone@40: var ret = 'make_array(' + this.val.length; pavone@38: for (var i = 0; i < this.val.length; i++) { pavone@40: ret += ', ' + this.val[i].toC(); pavone@38: } pavone@31: return ret + ')'; pavone@31: } pavone@31: pavone@31: funcall.prototype.toC = function() { pavone@31: var name = this.name[this.name.length-1] == ':' ? this.name.substr(0, this.name.length-1) : this.name; pavone@31: if (name == 'foreign') { pavone@31: if ((this.args[0] instanceof lambda) || (this.args[0] instanceof object)) { pavone@31: return null; pavone@31: } else if(this.args[0] instanceof symbol) { pavone@31: return this.args[0].name; pavone@31: } else { pavone@31: throw new Error("Unexpected AST type for foreign:"); pavone@31: } pavone@211: } else if(name == 'llProperty:withType' || name == 'llProperty:withVars:andCode' || name == 'quote') { pavone@84: return null; pavone@31: } pavone@31: var args = this.args.slice(0, this.args.length); pavone@31: if (this.receiver) { pavone@31: args.splice(0, 0, this.receiver); pavone@31: } pavone@42: var method = false; pavone@31: var funinfo = this.symbols.find(name); pavone@72: var start = 0; pavone@87: if (!funinfo || funinfo.def instanceof setter || funinfo.type == 'toplevel') { pavone@31: method = true; pavone@31: } else { pavone@31: switch(funinfo.type) pavone@31: { pavone@31: case 'self': pavone@72: case 'parent': pavone@72: var defargs = funinfo.def.args.length; pavone@241: if (!defargs || funinfo.def.args[0].cleanName() != 'self') { pavone@72: defargs ++ pavone@72: } pavone@72: if (args.length < defargs) { pavone@72: if (funinfo.type == 'self') { pavone@72: args.splice(0, 0, new symbol('self', this.symbols)); pavone@72: } else { pavone@72: var obj = (new symbol('self', this.symbols)).toC() + '->header.'; pavone@72: for (var i = 0; i < funinfo.depth; ++i) { pavone@72: obj += (i ? '->' : '') + 'parent'; pavone@72: } pavone@72: args.splice(0, 0, ', ' + obj); pavone@72: start = 1; pavone@72: } pavone@137: } else if(!(args[0] instanceof symbol) || args[0].name != 'self') { pavone@137: funinfo = null; pavone@31: } pavone@31: method = true; pavone@31: break; pavone@31: } pavone@31: } pavone@72: for (var i = start; i < args.length; i++) { pavone@72: args[i] = ', ' + (!i && method ? '(object *)(' : '') + args[i].toC() + (!i && method ? ')' : ''); pavone@31: } pavone@31: var callpart; pavone@31: if (method) { pavone@96: if (funinfo && funinfo.type == 'self' && funinfo.def.name) { pavone@96: callpart = funinfo.def.name + '(' + (funinfo.def.symbols.parent.needsenv ? (new symbol('self', this.symbols)).toC() + '->env' : 'NULL' ); pavone@96: } else { pavone@96: callpart = 'mcall(' + getMethodId(name) + '/* ' + name + ' */'; pavone@96: } pavone@31: } else { pavone@172: var closVar = (new symbol(name, this.symbols)).toC(); pavone@172: callpart = '((lambda *)' + closVar + ')->func(((lambda *)' + closVar + ')->env'; pavone@31: } pavone@35: return callpart + ', ' + args.length + args.join('') + ')'; pavone@84: }; pavone@84: funcall.prototype.toCTypeName = function() { pavone@84: switch(this.name) pavone@84: { pavone@84: case 'ptr:': pavone@84: case 'ptr': pavone@84: var receiver = this.receiver ? this.receiver : this.args[0]; pavone@84: return receiver.toCTypeName() + ' *'; pavone@84: break; pavone@143: case 'struct:': pavone@143: case 'struct': pavone@143: var receiver = this.receiver ? this.receiver : this.args[0]; pavone@143: return 'struct ' + receiver.toCTypeName(); pavone@143: break; pavone@177: case 'funptr:': pavone@177: case 'funptr': pavone@177: var rettype; pavone@177: var firstArg; pavone@177: if (this.receiver) { pavone@177: rettype = this.receiver; pavone@177: firstArg = 0; pavone@177: } else { pavone@177: rettype = this.args[0]; pavone@177: firstArg = 1; pavone@177: } pavone@177: var argtypes = ''; pavone@177: for (var i = firstArg; i < this.args.length; i++) { pavone@177: if (argtypes) { pavone@177: argtypes += ', ' pavone@177: } pavone@177: argtypes += this.args[i].toCTypeName(); pavone@177: } pavone@177: return [rettype.toCTypeName() + '(*', ')(' + argtypes + ')']; pavone@177: break; pavone@84: default: pavone@84: throw new Error('invalid use of funcall expression where a C type name is expected'); pavone@84: } pavone@84: }; pavone@84: funcall.prototype.toCLines = function(vars, needsreturn) { pavone@84: var lines = []; pavone@84: var name = this.name[this.name.length-1] == ':' ? this.name.substr(0, this.name.length-1) : this.name; pavone@84: var args = this.args.slice(0, this.args.length); pavone@84: if (this.receiver) { pavone@84: args.splice(0, 0, [this.receiver]); pavone@84: } pavone@84: switch(name) pavone@84: { pavone@84: case 'if': pavone@84: lines.push('if (' + this.args[0].toCLLExpr(vars) + ') {'); pavone@84: var blines = this.args[1].toCLines(vars, needsreturn); pavone@84: for (var i in blines) { pavone@84: lines.push('\t' + blines[i]); pavone@84: } pavone@84: if (needsreturn) { pavone@84: lines.push('} else {'); pavone@84: lines.push('\t return module_false;'); pavone@84: lines.push('}'); pavone@84: } else { pavone@84: lines.push('}'); pavone@84: } pavone@84: break; pavone@84: case 'if:else': pavone@84: lines.push('if (' + this.args[0].toCLLExpr(vars) + ') {'); pavone@84: var blines = this.args[1].toCLines(vars, needsreturn); pavone@84: for (var i in blines) { pavone@84: lines.push('\t' + blines[i]); pavone@84: } pavone@84: lines.push('} else {'); pavone@84: blines = this.args[2].toCLines(vars, needsreturn); pavone@84: for (var i in blines) { pavone@84: lines.push('\t' + blines[i]); pavone@84: } pavone@84: lines.push('}'); pavone@84: break; pavone@84: case 'while:do': pavone@84: if (needsreturn) { pavone@84: throw new Error("while:do can't be last statement in llMessage code block"); pavone@84: } pavone@84: lines.push('while (' + this.args[0].toCLLExpr(vars) + ') {'); pavone@84: var blines = this.args[1].toCLines(vars); pavone@84: for (var i in blines) { pavone@84: lines.push('\t' + blines[i]); pavone@84: } pavone@84: lines.push('}'); pavone@84: break; pavone@84: default: pavone@84: lines.push( (needsreturn ? 'return (object *)' : '') + this.toCLLExpr(vars) + ';'); pavone@84: } pavone@84: return lines; pavone@84: }; pavone@84: pavone@84: funcall.prototype.toCLLExpr = function(vars) { pavone@84: var name = this.name[this.name.length-1] == ':' ? this.name.substr(0, this.name.length-1) : this.name; pavone@84: var args = this.args.slice(0, this.args.length); pavone@84: if (this.receiver) { pavone@84: if(this.args.length == 0) { pavone@143: if (this.receiver instanceof symbol && this.receiver.name in vars && vars[this.receiver.name].substr(-1) != "*") { pavone@143: return this.receiver.toCLLExpr(vars) + '.' + this.name; pavone@143: } else { pavone@143: return this.receiver.toCLLExpr(vars) + '->' + this.name; pavone@143: } pavone@84: } else if (this.args.length == 1 && name[name.length-1] == '!') { pavone@143: if (this.receiver instanceof symbol && this.receiver.name in vars && vars[this.receiver.name].substr(-1) != "*") { pavone@143: return this.receiver.toCLLExpr(vars) + '.' + this.name.substr(0, name.length-1) + ' = ' + args[0].toCLLExpr(vars); pavone@143: } else { pavone@143: return this.receiver.toCLLExpr(vars) + '->' + this.name.substr(0, name.length-1) + ' = ' + args[0].toCLLExpr(vars); pavone@143: } pavone@84: } else { pavone@84: args.splice(0, 0, this.receiver); pavone@84: } pavone@84: } pavone@84: switch(name) pavone@84: { pavone@84: case 'if': pavone@87: return '((' + args[0].toCLLExpr(vars) + ') ? (' + args[1].toCLLExpr(vars) + ') : 0)'; pavone@84: case 'if:else': pavone@87: return '((' + args[0].toCLLExpr(vars) + ') ? (' + args[1].toCLLExpr(vars) + ') : (' + args[2].toCLLExpr(vars) + '))'; pavone@84: case 'while:do': pavone@87: throw new Error('while:do not allowed in expression context in llMessage block'); pavone@84: case 'addr_of': pavone@84: return '&(' + args[0].toCLLExpr(vars) + ')'; pavone@84: case 'sizeof': pavone@84: return 'sizeof(' + args[0].toCTypeName() + ')'; pavone@84: case 'get': pavone@84: return args[0].toCLLExpr(vars) + '[' + args[1].toCLLExpr(vars) + ']'; pavone@84: case 'set': pavone@84: return args[0].toCLLExpr(vars) + '[' + args[1].toCLLExpr(vars) + '] = ' + args[2].toCLLExpr(vars); pavone@84: case 'not': pavone@84: return '!(' + args[0].toCLLExpr(vars) + ')'; pavone@177: case 'castTo': pavone@177: return '((' + args[1].toCTypeName() + ')(' + args[0].toCLLExpr(vars) + '))'; pavone@87: case 'mcall': pavone@87: if (args[0] instanceof symbol) { pavone@87: args[0] = new intlit(getMethodId(args[0].name)); pavone@87: } pavone@84: default: pavone@84: for (var i in args) { pavone@84: args[i] = args[i].toCLLExpr(vars); pavone@84: } pavone@84: return name + '(' + args.join(', ') + ')'; pavone@84: } pavone@84: }; pavone@31: pavone@31: function cObject(name) { pavone@31: this.name = name; pavone@31: this.slots = {}; pavone@31: this.properties = []; pavone@31: this.values = []; pavone@35: this.slotvars = {}; pavone@48: this.includes = {}; pavone@59: this.parent = 'NULL'; pavone@59: this.init = []; pavone@59: this.initmsgadded = false; pavone@48: } pavone@48: pavone@48: cObject.prototype.addInclude = function(includefile) { pavone@48: this.includes[includefile] = true; pavone@31: } pavone@31: pavone@31: cObject.prototype.addMessage = function(msgname, implementation) { pavone@31: var methodid = getMethodId(msgname); pavone@31: var trunc = methodid & 0xF; pavone@31: if (!(trunc in this.slots)) { pavone@31: this.slots[trunc] = []; pavone@31: } pavone@37: this.slots[trunc].push([methodid, '\t\t' + implementation.lines.join('\n\t\t') + '\n', msgname]); pavone@37: if (!(trunc in this.slotvars)) { pavone@37: this.slotvars[trunc] = {}; pavone@37: } pavone@37: for (var varname in implementation.vars) { pavone@37: this.slotvars[trunc][varname] = implementation.vars[varname]; pavone@37: } pavone@31: } pavone@31: pavone@31: cObject.prototype.addProperty = function(propname, value, type) { pavone@31: if (type != undefined) { pavone@31: this.properties.push([propname, type]); pavone@42: if (value !== null) { pavone@42: this.values.push(value); pavone@42: } pavone@31: } else { pavone@31: var escaped = escapeCName(propname); pavone@37: this.addMessage(propname, { pavone@37: vars: {}, pavone@37: lines: [ pavone@37: 'return self->' + escaped + ';' pavone@37: ]}); pavone@37: this.addMessage(propname + '!', { pavone@59: vars: {}, pavone@37: lines: [ pavone@59: 'self->' + escaped + ' = va_arg(args, object *);', pavone@37: 'return (object *)self;' pavone@37: ]}); pavone@31: this.properties.push(escaped); pavone@31: this.values.push(value); pavone@31: } pavone@31: } pavone@31: pavone@59: cObject.prototype.addInit = function(statement) { pavone@59: this.init.push(statement); pavone@140: }; pavone@140: pavone@140: cObject.prototype.addImport = function(symbols, source) { pavone@140: this.imported.push(source); pavone@140: var importNum = imported.length - 1; pavone@140: if (symbols) { pavone@140: each(symbols, function(i, sym) { pavone@140: this.addMessage(sym.name, { pavone@140: vars: {}, pavone@140: lines: [ pavone@140: 'return self->import_' + importNum + '->meta->meth_lookup[method_id & 0xF](method_id, num_args, self->import_' + importNum + ', args);' pavone@140: ] pavone@140: }); pavone@140: }); pavone@140: } else { pavone@140: //TODO: handle proxying for unimplemented methods pavone@140: } pavone@140: }; pavone@59: pavone@59: cObject.prototype.checkInitMsg = function() { pavone@59: if (!this.initmsgadded && this.init.length) { pavone@59: var init = this.init.slice(0, this.init.length); pavone@59: init.push('return (object *)self;'); pavone@59: this.addMessage('_init', { pavone@59: vars: {}, pavone@59: lines: init pavone@121: }); pavone@59: this.initmsgadded = true; pavone@59: } pavone@59: } pavone@59: pavone@68: cObject.prototype.populateSymbols = function() {}; pavone@59: pavone@31: cObject.prototype.toEarlyCDef = function() { pavone@59: this.checkInitMsg(); pavone@48: var includes = ''; pavone@48: for (var file in this.includes) { pavone@48: includes += '#include ' + file + '\n'; pavone@48: } pavone@54: var objdef = 'typedef struct ' + this.name + ' {\n\tobject header;\n'; pavone@31: for (var i in this.properties) { pavone@31: if (this.properties[i] instanceof Array) { pavone@177: if (this.properties[i][1] instanceof Array) { pavone@177: objdef += '\t' + this.properties[i][1][0] + this.properties[i][0] + this.properties[i][1][1] + ';\n'; pavone@177: } else { pavone@177: objdef += '\t' + this.properties[i][1] + ' ' + this.properties[i][0] + ';\n'; pavone@177: } pavone@31: } else { pavone@31: objdef += '\tobject * ' + this.properties[i] + ';\n' pavone@31: } pavone@31: } pavone@31: objdef += '} ' + this.name + ';\nobj_meta ' + this.name + '_meta;\n'; pavone@48: return includes + objdef; pavone@31: } pavone@31: pavone@31: cObject.prototype.toCDef = function() { pavone@59: this.checkInitMsg(); pavone@31: var slotdefs = ''; pavone@31: var metadef = 'obj_meta ' + this.name + '_meta = {sizeof(' + this.name +'), {'; pavone@31: for (var i = 0; i < 16; i++) { pavone@31: if (i) { pavone@31: metadef += ', '; pavone@31: } pavone@31: if (i in this.slots) { pavone@35: slotdefs += 'object * ' + this.name + '_slot_' + i + '(uint32_t method_id, uint32_t num_params, object * oself, va_list args) {\n\t' + pavone@35: this.name + ' *self = (' + this.name + ' *)oself;\n'; pavone@35: for (var varname in this.slotvars[i]) { pavone@177: if (this.slotvars[i][varname] instanceof Array) { pavone@177: slotdefs += '/*foo*/\t' + this.slotvars[i][varname][0] + ' ' + varname + this.slotvars[i][varname][1] + ';\n'; pavone@177: } else { pavone@177: slotdefs += '/*bar*/\t' + this.slotvars[i][varname] + ' ' + varname + ';\n'; pavone@177: } pavone@35: } pavone@31: if (this.slots[i].length == 1) { pavone@31: slotdefs += '\tif (method_id == ' + this.slots[i][0][0] + ') { /* ' + this.slots[i][0][2] + '*/\n' + pavone@121: '\t\t' + this.slots[i][0][1] + '\n' + pavone@31: '\t}\n' + pavone@35: '\treturn no_impl(method_id, num_params, (object *)self, args);\n}\n'; pavone@31: } else { pavone@31: slotdefs += '\tswitch(method_id) {\n'; pavone@31: for (j in this.slots[i]) { pavone@31: slotdefs += '\t\tcase ' + this.slots[i][j][0] + ': /* ' + this.slots[i][j][2] + '*/\n' + pavone@31: '\t\t\t' + this.slots[i][j][1] + '\n'; pavone@31: } pavone@35: slotdefs += '\t\tdefault:\n' + pavone@57: '\t\t\treturn no_impl(method_id, num_params, (object *)self, args);\n\t}\n}\n'; pavone@121: pavone@31: } pavone@31: metadef += this.name + '_slot_' + i; pavone@31: } else { pavone@31: metadef += 'no_impl'; pavone@31: } pavone@31: } pavone@31: metadef += '}};\n'; pavone@31: return slotdefs + metadef; pavone@31: } pavone@31: pavone@31: cObject.prototype.toCInstance = function() { pavone@59: this.checkInitMsg(); pavone@59: var ret = 'make_object(&' + this.name + '_meta, ' + this.parent + ', ' + this.values.length + (this.values.length ? ', ' : '') + this.values.join(', ') + ')'; pavone@59: if (this.initmsgadded) { pavone@59: ret = 'mcall(' + getMethodId('_init') + ', 1, ' + ret + ')' pavone@59: } pavone@59: return ret; pavone@31: } pavone@31: pavone@48: cObject.prototype.toC = function() { pavone@48: forwarddec += this.toEarlyCDef(); pavone@48: toplevelcode += this.toCDef(); pavone@48: return this.toCInstance(); pavone@48: } pavone@48: pavone@31: var nextobject = 0; pavone@31: pavone@84: pavone@84: object.prototype.toCObject = function() { pavone@31: var messages = this.messages; pavone@84: if (!this.name) { pavone@84: this.name = 'object_' + nextobject++; pavone@84: } pavone@84: var me = new cObject(this.name); pavone@57: this.symbols.typename = me.name; pavone@42: if (this.symbols.needsenv) { pavone@42: me.addProperty('env', this.symbols.envVar(), 'struct ' + this.symbols.getEnvType() + ' * '); pavone@42: me.hasenv = true; pavone@42: } pavone@59: if (this.symbols.needsparent && !(this.symbols.parent instanceof osymbols)) { pavone@59: me.parent = '(object *)(' + (new symbol('self', this.symbols.parent)).toC() + ')'; pavone@59: } pavone@31: for (var i in messages) { pavone@31: if (messages[i] instanceof funcall) { pavone@31: if (messages[i].name == 'import:' && messages[i].args.length == 1) { pavone@140: me.addImport(false, messages[i].args[0]); pavone@31: } else if(messages[i].name == 'import:from:' && messages[i].args.length == 2) { pavone@31: var importsyms = []; pavone@31: each(messages[i].args[0].val, function(i, el) { pavone@31: if (!(el instanceof symbol)) { pavone@31: throw new Error('Names in import:from statement must be symbols'); pavone@31: } pavone@140: importsyms.push(el); pavone@31: }); pavone@140: me.addImport(importsyms, messages[i].args[1]); pavone@84: } else if(messages[i].name == 'llProperty:withType:' && messages[i].args.length == 2) { pavone@177: me.addProperty(messages[i].args[0].name, null, messages[i].args[1].toCTypeName()); pavone@84: } else if(messages[i].name == 'llMessage:withVars:andCode:' && messages[i].args.length == 3) { pavone@84: var msgname = messages[i].args[0].name pavone@84: var rawvars = messages[i].args[1].expressions; pavone@84: var vars = {}; pavone@84: for(var v in rawvars) { pavone@84: vars[rawvars[v].symbol.name] = rawvars[v].expression.toCTypeName(); pavone@84: } pavone@84: me.addMessage(msgname, { pavone@84: vars: vars, pavone@84: lines: messages[i].args[2].toCLines(vars, true) pavone@84: }); pavone@143: } else if(messages[i].name == 'includeSystemHeader:' && messages[i].args.length == 1) { pavone@143: me.addInclude("<" + messages[i].args[0].val + ">"); pavone@31: } else { pavone@121: pavone@84: throw new Error('Only import and import:from calls allowed in object context. ' + messages[i].name + 'with ' + messages[i].args.length + ' arguments found instead.'); pavone@31: } pavone@31: } else { pavone@31: messages[i].toCObject(me); pavone@31: } pavone@31: } pavone@48: pavone@84: return me; pavone@84: }; pavone@84: pavone@84: object.prototype.toC = function() { pavone@84: return this.toCObject().toC(); pavone@84: }; pavone@31: pavone@31: var toplevelcode; pavone@31: var forwarddec; pavone@31: pavone@37: function addBinaryOp(cobject, opname, cop, objtype) pavone@37: { pavone@37: cobject.addMessage(opname, { pavone@37: vars: {ret: objtype + ' *', argb: objtype +' *'}, pavone@37: lines: [ pavone@37: 'argb = va_arg(args, ' + objtype + ' *);', pavone@37: 'ret = (' + objtype + ' *)make_object(&' + objtype + '_meta, NULL, 0);', pavone@37: 'ret->num = self->num ' + cop + ' argb->num;', pavone@52: 'return &(ret->header);' pavone@37: ] pavone@37: }); pavone@37: } pavone@37: pavone@37: function addCompOp(cobject, opname, cop, objtype) pavone@37: { pavone@37: cobject.addMessage(opname, { pavone@37: vars: {argb: objtype + ' *'}, pavone@37: lines: [ pavone@37: 'argb = va_arg(args, ' + objtype + ' *);', pavone@37: 'if (self->num ' + cop + ' argb->num) {', pavone@68: ' return ' + toplevel.moduleVar('true') + ';', pavone@37: '}', pavone@68: 'return ' + toplevel.moduleVar('false') + ';', pavone@37: ] pavone@37: }); pavone@37: } pavone@37: pavone@155: function makeInt(bits, unsigned) pavone@31: { pavone@155: var typename = 'obj_' + (unsigned ? 'u' : '') + 'int' + bits; pavone@135: var intObj = new cObject(typename); pavone@155: intObj.addProperty('num', null, (unsigned ? 'u' : '') + 'int' + bits +'_t'); pavone@135: addBinaryOp(intObj, 'ADD_', '+', typename); pavone@135: addBinaryOp(intObj, 'SUB_', '-', typename); pavone@135: addBinaryOp(intObj, 'MUL_', '*', typename); pavone@135: addBinaryOp(intObj, 'DIV_', '/', typename); pavone@135: addBinaryOp(intObj, 'MOD_', '%', typename); pavone@135: addBinaryOp(intObj, 'or', '|', typename); pavone@135: addBinaryOp(intObj, 'xor', '^', typename); pavone@135: addBinaryOp(intObj, 'and', '&', typename); pavone@135: addBinaryOp(intObj, 'lshift:by', '<<', typename); pavone@135: addBinaryOp(intObj, 'rshift:by', '>>', typename); pavone@135: addCompOp(intObj, 'LT_', '<', typename); pavone@135: addCompOp(intObj, 'GT_', '>', typename); pavone@135: addCompOp(intObj, 'EQ_', '==', typename); pavone@135: addCompOp(intObj, 'NEQ_', '!=', typename); pavone@135: addCompOp(intObj, 'GEQ_', '>=', typename); pavone@135: addCompOp(intObj, 'LEQ_', '<=', typename); pavone@135: intObj.addInclude(''); pavone@135: //-9223372036854775808 pavone@135: //01234567890123456789 pavone@135: intObj.addMessage('string', { pavone@43: vars: {str: 'string *'}, pavone@43: lines: [ pavone@43: 'str = (string *)make_object(&string_meta, NULL, 0);', pavone@135: 'str->data = GC_MALLOC(' + (bits == 64 ? 21 : 12) + ');', pavone@155: 'sprintf(str->data, "%' + (bits == 64 ? 'l' : '') + (unsigned ? 'u' : 'd') + '", self->num);', pavone@87: 'str->len = str->bytes = strlen(str->data);', pavone@52: 'return &(str->header);' pavone@43: ] pavone@43: }); pavone@135: //7FFFFFFFFFFFFFFF pavone@135: //01234567890123456789 pavone@135: intObj.addMessage('hex', { pavone@135: vars: {str: 'string *'}, pavone@135: lines: [ pavone@135: 'str = (string *)make_object(&string_meta, NULL, 0);', pavone@135: 'str->data = GC_MALLOC(' + (bits == 64 ? 17 : 9) + ');', pavone@135: 'sprintf(str->data, "%' + (bits == 64 ? 'l' : '') +'X", self->num);', pavone@135: 'str->len = str->bytes = strlen(str->data);', pavone@135: 'return &(str->header);' pavone@135: ] pavone@135: }); pavone@135: intObj.addMessage('isInteger?', { pavone@121: vars: {}, pavone@121: lines: [ pavone@121: 'return ' + toplevel.moduleVar('true') + ';' pavone@121: ] pavone@121: }); pavone@135: intObj.addMessage('hash', { pavone@79: vars: {}, pavone@79: lines: [ pavone@79: 'return &(self->header);' pavone@79: ] pavone@79: }); pavone@172: intObj.addMessage('signed?', { pavone@172: vars: {}, pavone@172: lines: [ pavone@172: 'return ' + toplevel.moduleVar(unsigned ? 'false' : 'true') + ';' pavone@172: ] pavone@172: }); pavone@155: var sizes = [8, 16, 32, 64]; pavone@155: var destunsigned = [false, true]; pavone@155: for (var i = 0; i < sizes.length; i++) { pavone@155: size = sizes[i]; pavone@155: for (var j = 0; j < destunsigned.length; j++) { pavone@155: uns = destunsigned[j]; pavone@155: if (uns == unsigned && size == bits) { pavone@155: intObj.addMessage((uns ? 'u' : '') + 'int' + size, { pavone@155: vars: {}, pavone@155: lines: [ pavone@155: 'return &(self->header);' pavone@155: ] pavone@155: }); pavone@155: } else { pavone@155: var retType = 'obj_' + (uns ? 'u' : '') + 'int' + size; pavone@161: var retName = 'ret' + (uns ? 'u' : '') + size; pavone@161: var vars = {}; pavone@161: vars[retName] = retType + ' *'; pavone@155: intObj.addMessage((uns ? 'u' : '') + 'int' + size, { pavone@155: pavone@161: vars: vars, pavone@155: lines: [ pavone@161: retName + ' = ('+retType+' *)make_object(&' + retType +'_meta, NULL, 0);', pavone@161: retName + '->num = self->num;', pavone@161: 'return &(' + retName + '->header);' pavone@155: ] pavone@155: }); pavone@155: } pavone@155: } pavone@155: } pavone@155: pavone@135: return intObj; pavone@45: } pavone@45: pavone@45: function makeArray() pavone@45: { pavone@84: var arrayfile = toplevel.names['array']; pavone@84: var ast = parseFile(arrayfile.path + '/' + arrayfile.file); pavone@84: ast.name = 'array'; pavone@84: ast.populateSymbols(toplevel); pavone@84: return ast.toCObject(); pavone@45: } pavone@45: pavone@45: function makeString() pavone@45: { pavone@87: var arrayfile = toplevel.names['string']; pavone@87: var ast = parseFile(arrayfile.path + '/' + arrayfile.file); pavone@87: ast.name = 'string'; pavone@87: ast.populateSymbols(toplevel); pavone@87: return ast.toCObject(); pavone@45: } pavone@45: pavone@45: function makelambda() pavone@45: { pavone@45: var clos = new cObject('lambda'); pavone@45: clos.addProperty('env', null, 'void *'); pavone@45: clos.addProperty('func', null, 'closure_func'); pavone@45: clos.addMessage('while:do', { pavone@68: vars: {action: 'lambda *', ret: 'object *'}, pavone@45: lines: [ pavone@45: 'action = va_arg(args, lambda *);', pavone@68: 'ret = ' + toplevel.moduleVar('true') + ';', pavone@68: 'while(' + toplevel.moduleVar('true') + ' == ccall(self, 0)) {', pavone@45: ' ccall(action, 0);', pavone@45: '}', pavone@45: 'return ret;' pavone@45: ] pavone@45: }); pavone@45: return clos; pavone@45: } pavone@45: pavone@45: function builtinTypes() pavone@45: { pavone@155: return [makeInt(64, false), makeInt(32, false), makeInt(16, false), makeInt(8, false), pavone@155: makeInt(64, true) , makeInt(32, true), makeInt(16, true), makeInt(8, true), pavone@155: makeArray(), makeString(), makelambda()]; pavone@45: } pavone@45: pavone@48: function addBuiltinModules(toplevel) pavone@48: { pavone@48: var os = new cObject('mod_obj_os'); pavone@48: os.addInclude(''); pavone@48: os.addInclude(''); pavone@162: os.addInclude(''); pavone@162: os.addInclude(''); pavone@168: os.addInclude(''); pavone@48: os.addMessage('write', { pavone@48: vars: {str: 'string *', intret: 'obj_int32 *', filedes: 'obj_int32 *'}, pavone@48: lines: [ pavone@48: 'filedes = va_arg(args, obj_int32 *);', pavone@48: 'str = va_arg(args, string *);', pavone@59: 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', pavone@48: 'intret->num = write(filedes->num, str->data, str->bytes);', pavone@59: 'return &(intret->header);' pavone@48: ] pavone@48: }); pavone@48: os.addMessage('read', { pavone@48: vars: {str: 'string *', size: 'obj_int32 *', filedes: 'obj_int32 *'}, pavone@48: lines: [ pavone@48: 'filedes = va_arg(args, obj_int32 *);', pavone@48: 'size = va_arg(args, obj_int32 *);', pavone@59: 'str = (string *)make_object(&string_meta, NULL, 0);', pavone@78: 'str->data = GC_MALLOC_ATOMIC(size->num + 1);', pavone@162: 'str->len = str->bytes = read(filedes->num, str->data, size->num);', pavone@162: 'if (str->bytes < 0) { str->bytes = str->len = 0; }', pavone@58: 'str->data[str->bytes] = 0;', pavone@59: 'return &(str->header);' pavone@48: ] pavone@48: }); pavone@48: os.addMessage('open', { pavone@49: vars: {str: 'string *', flags: 'obj_int32 *', filedes: 'obj_int32 *', perm: 'obj_int32 *'}, pavone@48: lines: [ pavone@48: 'str = va_arg(args, string *);', pavone@48: 'flags = va_arg(args, obj_int32 *);', pavone@59: 'filedes = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', pavone@49: 'if (num_params == 3) {', pavone@49: ' filedes->num = open(str->data, flags->num);', pavone@49: '} else if (num_params == 4) {', pavone@49: ' perm = va_arg(args, obj_int32 *);', pavone@49: ' filedes->num = open(str->data, flags->num, perm->num);', pavone@49: '} else {', pavone@49: ' filedes->num = -1;', pavone@49: '}', pavone@59: 'return &(filedes->header);' pavone@48: ] pavone@48: }); pavone@48: os.addMessage('close', { pavone@48: vars: {filedes: 'obj_int32 *', intret: 'obj_int32 *'}, pavone@48: lines: [ pavone@48: 'filedes = va_arg(args, obj_int32 *);', pavone@59: 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', pavone@48: 'intret->num = close(filedes->num);', pavone@59: 'return &(intret->header);' pavone@48: ] pavone@48: }); pavone@48: os.addMessage('O_RDONLY', { pavone@48: vars: {intret: 'obj_int32 *'}, pavone@48: lines: [ pavone@59: 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', pavone@48: 'intret->num = O_RDONLY;', pavone@59: 'return &(intret->header);' pavone@48: ] pavone@48: }); pavone@48: os.addMessage('O_WRONLY', { pavone@48: vars: {intret: 'obj_int32 *'}, pavone@48: lines: [ pavone@59: 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', pavone@48: 'intret->num = O_WRONLY;', pavone@59: 'return &(intret->header);' pavone@48: ] pavone@48: }); pavone@48: os.addMessage('O_RDWR', { pavone@48: vars: {intret: 'obj_int32 *'}, pavone@48: lines: [ pavone@59: 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', pavone@48: 'intret->num = O_RDWR;', pavone@59: 'return &(intret->header);' pavone@48: ] pavone@48: }); pavone@48: os.addMessage('O_CREAT', { pavone@48: vars: {intret: 'obj_int32 *'}, pavone@48: lines: [ pavone@59: 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', pavone@48: 'intret->num = O_CREAT;', pavone@59: 'return &(intret->header);' pavone@48: ] pavone@48: }); pavone@48: os.addMessage('O_APPEND', { pavone@48: vars: {intret: 'obj_int32 *'}, pavone@48: lines: [ pavone@59: 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', pavone@48: 'intret->num = O_APPEND;', pavone@59: 'return &(intret->header);' pavone@48: ] pavone@48: }); pavone@48: os.addMessage('O_TRUNC', { pavone@48: vars: {intret: 'obj_int32 *'}, pavone@48: lines: [ pavone@59: 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', pavone@48: 'intret->num = O_TRUNC;', pavone@59: 'return &(intret->header);' pavone@48: ] pavone@48: }); pavone@162: os.addMessage('rand', { pavone@162: vars: {intret: 'obj_int32 *'}, pavone@162: lines: [ pavone@162: 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', pavone@162: 'intret->num = rand();', pavone@162: 'return &(intret->header);' pavone@162: ] pavone@162: }); pavone@162: os.addMessage('rand64', { pavone@162: vars: {intret64: 'obj_int64 *'}, pavone@162: lines: [ pavone@162: 'intret64 = (obj_int64 *)make_object(&obj_int64_meta, NULL, 0);', pavone@162: 'intret64->num = (((int64_t)rand()) << 32 ) | rand();', pavone@162: 'return &(intret64->header);' pavone@162: ] pavone@162: }); pavone@162: os.addMessage('srand', { pavone@162: vars: {oseed: 'object *', seed: 'obj_int32 *'}, pavone@162: lines: [ pavone@162: 'oseed = va_arg(args, object *);', pavone@162: 'seed = mcall(' + getMethodId("int32") + ', 1, oseed);', pavone@162: 'srand(seed->num);', pavone@162: 'return &(seed->header);' pavone@162: ] pavone@162: }); pavone@162: os.addMessage('time', { pavone@162: vars: {intret64: 'obj_int64 *'}, pavone@162: lines: [ pavone@162: 'intret64 = (obj_int64 *)make_object(&obj_int64_meta, NULL, 0);', pavone@162: 'intret64->num = time(NULL);', pavone@162: 'return &(intret64->header);' pavone@162: ] pavone@162: }); pavone@168: os.addMessage('sleep', { pavone@168: vars: {osecs: 'object *', secs: 'obj_int32 *', intret: 'obj_int32 *'}, pavone@168: lines: [ pavone@168: 'osecs = va_arg(args, object *);', pavone@168: 'secs = mcall(' + getMethodId("int32") + ', 1, osecs);', pavone@168: 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', pavone@168: 'intret->num = sleep(secs->num);', pavone@168: 'return &(intret->header);' pavone@168: ] pavone@168: }); pavone@48: toplevel.names['os'] = os; pavone@48: } pavone@48: pavone@66: modulefile.prototype.toC = function(){ pavone@68: return this.ast.toCModuleInstance(); pavone@66: }; pavone@66: pavone@48: function processUsedToplevel(toplevel) pavone@121: { pavone@170: var alwaysused = ['true', 'false', 'list']; pavone@48: var ret = ''; pavone@48: var modulenum = 0; pavone@68: var visited = {}; pavone@68: for (var i in alwaysused) { pavone@84: toplevel.used[alwaysused[i]] = true; pavone@66: } pavone@84: var newused = Object.keys(toplevel.used); pavone@84: var allused = newused; pavone@68: while (newused.length) { pavone@68: for (var i in newused) { pavone@68: debugprint('//---module', newused[i], '--- populate symbols'); pavone@68: forwarddec += 'object * ' + toplevel.moduleVar(newused[i]) + ';\n'; pavone@68: toplevel.names[newused[i]].populateSymbols(toplevel); pavone@68: visited[newused[i]] = true; pavone@68: } pavone@68: newused = []; pavone@68: for (var symbol in toplevel.used) { pavone@68: if (!(symbol in visited)) { pavone@68: debugprint('//found new usage of module', symbol); pavone@68: newused.push(symbol); pavone@68: allused.push(symbol); pavone@68: } pavone@68: } pavone@68: } pavone@121: pavone@68: for (var i = allused.length-1; i >= 0; i--) { pavone@68: var symbol = allused[i]; pavone@84: debugprint('//---module', symbol, '(' + i +')--- compile'); pavone@155: assignNames.push(symbol); pavone@68: ret += '\t' + toplevel.moduleVar(symbol) + ' = ' + toplevel.names[symbol].toC() + ';\n'; pavone@155: assignNames.pop(); pavone@48: } pavone@48: return ret; pavone@48: } pavone@48: pavone@45: function makeCProg(obj) pavone@45: { pavone@84: forwarddec = toplevelcode = ''; pavone@155: assignNames = []; pavone@45: var builtins = builtinTypes(); pavone@45: for (var i in builtins) { pavone@45: forwarddec += builtins[i].toEarlyCDef(); pavone@45: toplevelcode += builtins[i].toCDef(); pavone@45: } pavone@48: addBuiltinModules(toplevel); pavone@54: debugprint('//------POPULATING SYMBOLS-----'); pavone@31: obj.populateSymbols(toplevel); pavone@48: var moduleinit = processUsedToplevel(toplevel); pavone@54: debugprint('//------COMPILING AST-----'); pavone@60: var rest = 'object * mainModule() {\n' + moduleinit + '\tmain_module = ' + obj.toCModuleInstance() + ';\n\treturn main_module;\n}\n'; pavone@182: var mnames = 'char * methodNames[] = {\n'; pavone@182: for (var i = 0; i < methodNames.length; i++) { pavone@182: mnames += '\t"' + methodNames[i].replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n") + '",\n'; pavone@182: } pavone@182: mnames += '};\n'; pavone@34: return '#include "runtime/proghead.inc"\n' + pavone@34: '#define METHOD_ID_MAIN ' + getMethodId('main') + '\n' + pavone@170: '#define METHOD_ID_EMPTY ' + getMethodId('empty') + '\n' + pavone@170: '#define METHOD_ID_CONS ' + getMethodId(getOpMethodName('|')) + '\n' + pavone@182: mnames + forwarddec + toplevelcode + rest + '#include "runtime/progfoot.inc"\n'; pavone@31: } pavone@31: pavone@31: object.prototype.toCModule = function() { pavone@31: return makeCProg(this); pavone@31: } pavone@31: pavone@60: object.prototype.toCModuleInstance = function() { pavone@60: return this.toC(); pavone@60: } pavone@60: pavone@31: lambda.prototype.toC = function() { pavone@31: var args = this.args ? this.args.slice(0, this.args.length) : []; pavone@31: var exprs = this.expressions; pavone@155: var assignPath = assignNames.join('<-'); pavone@155: debugprint('//', this.name, assignPath); pavone@155: var addedTypeDef = false; pavone@34: if (Object.keys(this.symbols.closedover).length) { pavone@96: this.symbols.envtype = this.name + '_env'; pavone@96: forwarddec += 'typedef struct ' + this.symbols.envtype + ' ' + this.symbols.envtype + ';\n' pavone@155: var addedTypeDef = true; pavone@34: } pavone@31: if (this.selftype) { pavone@34: this.symbols.defineVar('self', this.selftype); pavone@31: if (args[0] && args[0].cleanName() == 'self') { pavone@31: args.splice(0, 1); pavone@31: } pavone@31: var offset = 1; pavone@31: } else { pavone@31: var offset = 0; pavone@31: } pavone@31: for (var i = 0; i < args.length; ++i) { pavone@34: var argname = args[i].toC(); pavone@251: this.symbols.declareVar(args[i].cleanName()); pavone@35: args[i] = (argname.indexOf('->') < 0 ? '\tobject * ' : '\t') + argname + ' = va_arg(args, object *);\n'; pavone@31: } pavone@31: var compiled = [] pavone@31: for (var i in exprs) { pavone@31: var js = exprs[i].toC(); pavone@31: if (js) { pavone@31: compiled.push(indent(js)); pavone@31: } pavone@31: } pavone@172: if (compiled.length) { pavone@172: if (exprs[exprs.length - 1] instanceof assignment) { pavone@172: compiled.push('return ' + exprs[exprs.length - 1].symbol.toC() + ';'); pavone@172: } else { pavone@172: compiled[compiled.length-1] = 'return (object *)(' + compiled[compiled.length-1] + ');'; pavone@172: } pavone@172: } pavone@31: exprs = compiled; pavone@121: pavone@34: if (Object.keys(this.symbols.closedover).length) { pavone@155: if (!addedTypeDef) { pavone@155: for (var key in this.symbols.closedover) { pavone@155: print(key, ": ", this.symbols.closedover[key]); pavone@155: } pavone@156: throw new Error('this.symbols.closedover is not empty, but it was when compilation of ' + this.name + ' "' + assignPath + '" started'); pavone@155: } pavone@96: forwarddec += 'struct ' + this.name + '_env {\n'; pavone@57: if (this.symbols.needsParentEnv) { pavone@57: forwarddec += '\tstruct ' + this.symbols.parentEnvType() + ' * parent;\n'; pavone@57: } pavone@34: for (var varname in this.symbols.closedover) { pavone@54: if (varname == 'self' && this.selftype) { pavone@54: forwarddec += '\tstruct ' + this.selftype + ' * self;\n'; pavone@54: } else { pavone@54: forwarddec += '\tobject * ' + escapeCName(varname) + ';\n'; pavone@54: } pavone@34: } pavone@96: forwarddec += '};\n' pavone@121: pavone@96: var myenvinit = '\t' + this.name + '_env * myenv = GC_MALLOC(sizeof(' + this.name + '_env));\n'; pavone@58: if (this.symbols.needsParentEnv) { pavone@58: myenvinit += '\tmyenv->parent = env;\n'; pavone@58: } pavone@96: this.symbols.envtype = this.name + '_env'; pavone@34: } else { pavone@34: var myenvinit = ''; pavone@34: } pavone@96: forwarddec += 'object *' + this.name + ' (' + this.symbols.parentEnvType() + ' * env, uint32_t num_args, ...);\n'; pavone@121: pavone@155: toplevelcode += '//' + assignPath + "\n"; pavone@96: toplevelcode += 'object * ' + this.name + ' ( ' + this.symbols.parentEnvType() + ' * env, uint32_t num_args, ...) {\n\tva_list args;\n' + myenvinit + '\tva_start(args, num_args);\n'; pavone@31: if (this.selftype) { pavone@34: var selfvar = (new symbol('self', this.symbols)).toC(); pavone@34: if (selfvar == 'self') { pavone@35: toplevelcode += '\t' + this.selftype + ' * self = va_arg(args, ' + this.selftype + ' *);\n'; pavone@34: } else { pavone@35: toplevelcode += '\t' + selfvar + ' = va_arg(args, ' + this.selftype + ' *);\n'; pavone@34: } pavone@121: pavone@31: } pavone@35: toplevelcode += args.join('') + '\tva_end(args);\n' + exprs.join(';\n\t') + '\n}\n'; pavone@121: pavone@42: if (this.selftype) { pavone@60: return this.name; pavone@42: } else { pavone@42: if (this.symbols.parentEnvType() != 'void') { pavone@55: if (this.symbols.parent.passthruenv) { pavone@42: var envvar = 'env'; pavone@42: } else { pavone@42: var envvar = 'myenv'; pavone@42: } pavone@96: debugprint('//' + this.name, 'has envvar:', envvar, 'num vars closed over:', Object.keys(this.symbols.closedover).length); pavone@60: return 'make_lambda(' + envvar + ', (closure_func)' + this.name + ')'; pavone@121: } else { pavone@143: toplevelcode += 'lambda ' + this.name + '_obj = {{&lambda_meta, NULL}, NULL, ' + this.name + '};\n'; pavone@96: return '((object *)&' + this.name + '_obj)'; pavone@34: } pavone@34: } pavone@31: }; pavone@60: lambda.prototype.toCModuleInstance = function() { pavone@60: this.toC(); pavone@60: return this.name + '(NULL, 0)'; pavone@60: }; pavone@31: lambda.prototype.toCObject = function(typename) { pavone@31: this.selftype = typename; pavone@31: return this.toC(); pavone@31: }; pavone@31: lambda.prototype.toCModule = function() { pavone@31: return makeCProg(this); pavone@84: }; pavone@84: lambda.prototype.toCLines = function(vars, needsreturn) { pavone@84: var lines = []; pavone@84: for (var i in this.args) { pavone@84: var name = this.args[i].name; pavone@84: if (name[0] == ':') { pavone@84: name = name.substr(1); pavone@84: } pavone@84: if(name != 'self') { pavone@84: lines.push(name + ' = va_arg(args, ' + vars[name] + ');'); pavone@84: } pavone@84: } pavone@84: for (var i in this.expressions) { pavone@84: var exprlines = this.expressions[i].toCLines(vars, needsreturn && i == this.expressions.length - 1); pavone@84: for (var j in exprlines) { pavone@84: lines.push('\t' + exprlines[j]); pavone@84: } pavone@84: } pavone@84: return lines; pavone@84: } pavone@84: lambda.prototype.toCLLExpr = function(vars) { pavone@84: if (this.expressions.length != 1) { pavone@84: throw new Error('lambda in expression context must have a single statement in llMessage block'); pavone@84: } pavone@84: return this.expressions[0].toCLLExpr(vars); pavone@31: } pavone@31: pavone@31: assignment.prototype.toC = function() { pavone@54: debugprint('//assignment', this.symbol.name); pavone@31: var existing = this.symbols.find(this.symbol.name); pavone@31: var prefix = ''; pavone@155: assignNames.push(this.symbol.name); pavone@31: var val = this.expression.toC(); pavone@155: assignNames.pop(this.symbol.name); pavone@31: if (val === null) { pavone@31: return null; pavone@31: } pavone@38: if (existing.type == 'local' && !existing.isdeclared) { pavone@38: prefix = 'object *'; pavone@38: this.symbols.declareVar(this.symbol.name); pavone@54: debugprint('//declared var', this.symbol.name); pavone@38: } pavone@31: return prefix + this.symbol.toC() + ' = ' + val; pavone@31: }; pavone@31: assignment.prototype.toCObject = function(cobj) { pavone@54: debugprint('//message definition', this.symbol.name); pavone@156: assignNames.push('#' + this.symbol.name); pavone@31: if (this.expression.toCObject) { pavone@31: var val = this.expression.toCObject(cobj.name); pavone@31: } else { pavone@31: var val = this.expression.toC(); pavone@31: } pavone@156: assignNames.pop(); pavone@31: if (val === null) { pavone@31: return; pavone@31: } pavone@31: if (this.expression instanceof lambda) { pavone@35: var params = ['((object *)self)']; pavone@35: var paramget = ''; pavone@37: var messagevars = {}; pavone@35: for (var i in this.expression.args) { pavone@35: var escaped = escapeCName(this.expression.args[i].cleanName()); pavone@35: if (escaped != 'self') { pavone@37: messagevars[escaped] = 'object *'; pavone@35: params.push(escaped); pavone@35: paramget += escaped + ' = va_arg(args, object *); '; pavone@35: } pavone@35: } pavone@122: cobj.addMessage(getOpMethodName(this.symbol.name), { pavone@37: vars: messagevars, pavone@42: lines: [paramget + 'return ' + val + '(' + (cobj.hasenv ? 'self->env' : 'NULL') + ', ' + params.length + (params.length ? ', ' : '') + params.join(', ') + ');'] pavone@37: }); pavone@31: } else { pavone@31: cobj.addProperty(this.symbol.name, val); pavone@59: if (this.expression instanceof object && this.expression.symbols.needsparent) { pavone@72: cobj.addInit('self->' + escapeCName(this.symbol.name) + '->parent = (object *)self;'); pavone@59: } pavone@31: } pavone@31: }; pavone@84: assignment.prototype.toCLines = function(vars, needsreturn) { pavone@84: return [(needsreturn ? 'return ' : '') + this.symbol.toCLLExpr(vars) + ' = ' + this.expression.toCLLExpr(vars) + ';'] pavone@84: };