# HG changeset patch # User Mike Pavone # Date 1342935021 25200 # Node ID 9811040704ac8bb238f72aa1af09df29b3230fec # Parent fdb9785d2c9343f14b4d6f718bec7e63ad98f9d9 Add support for llMessage:withVars:andCode and llProperty:withType for specifying low level code without having to stick C inside the compiler. Redo array built-in type to use this feature. diff -r fdb9785d2c93 -r 9811040704ac cbackend.js --- a/cbackend.js Sat Jul 21 22:29:16 2012 -0700 +++ b/cbackend.js Sat Jul 21 22:30:21 2012 -0700 @@ -44,6 +44,13 @@ var method = optoMeth[this.op]; return 'mcall(' + getMethodId(method) + '/* ' + method + ' */, 2, (object *)' + this.left.toC() + ', ' + this.right.toC() + ')\n'; }; +op.prototype.toCLLExpr = function(vars) { + return this.left.toCLLExpr(vars) + (this.op == '=' ? '==' : this.op) + this.right.toCLLExpr(vars); +}; +op.prototype.toCLines = function(vars, needsreturn) { + return [ (needsreturn ? 'return (object *)' : '' ) + this.toCLLExpr(vars) + ';']; +}; + function escapeCName(name) { @@ -108,6 +115,40 @@ } return getSymbolPrefix(info, this.symbols) + escapeCName(name); } +symbol.prototype.toCTypeName = function() { + return this.cleanName(); +}; +symbol.prototype.toCLLExpr = 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, ' + (new symbol('self', this.symbols)).toC() + ')'; + } + } + throw new Error('Unsupported reference type ' + info.type + ' for variable ' + name); +}; +symbol.prototype.toCLines = function(vars, needsreturn) { + return [ (needsreturn ? 'return (object *)' : '' ) + this.toCLLExpr(vars) + ';' ]; +}; var declaredInts = {}; @@ -119,10 +160,22 @@ } return '((object *)&int32_' + str + ')'; } +intlit.prototype.toCLLExpr = function(vars) { + return this.val.toString(); +}; +intlit.prototype.toCLines = function(vars, needsreturn) { + return [ (needsreturn ? 'return (object *)' : '' ) + this.toCLLExpr(vars) + ';' ]; +}; floatlit.prototype.toC = function() { return 'make_float(' + this.val.toString() + ')'; } +floatlit.prototype.toCLLExpr = function(vars) { + return this.val.toString(); +}; +floatlit.prototype.toCLines = function(vars, needsreturn) { + return [ (needsreturn ? 'return (object *)' : '' ) + this.toCLLExpr(vars) + ';' ]; +}; var declaredStrings = {}; var nextStringId = 0; @@ -134,7 +187,13 @@ declaredStrings[this.val] = nextStringId++; } return '((object *)&str_' + declaredStrings[this.val] + ')'; -} +}; +strlit.prototype.toCLLExpr = function(vars) { + return '"' + this.val.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n').replace('\r', '\\r') + '"'; +}; +strlit.prototype.toCLines = function(vars, needsreturn) { + return [ (needsreturn ? 'return (object *)' : '' ) + this.toCLLExpr(vars) +';' ]; +}; listlit.prototype.toC = function() { var ret = 'make_list(' + this.val.length; @@ -162,6 +221,8 @@ } 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) { @@ -207,7 +268,107 @@ callpart = 'ccall(' + (new symbol(name, this.symbols)).toC(); } return callpart + ', ' + args.length + args.join('') + ')'; -} +}; +funcall.prototype.toCTypeName = function() { + switch(this.name) + { + case 'ptr:': + case 'ptr': + var receiver = this.receiver ? this.receiver : this.args[0]; + return receiver.toCTypeName() + ' *'; + break; + default: + throw new Error('invalid use of funcall expression where a C type name is expected'); + } +}; +funcall.prototype.toCLines = 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].toCLLExpr(vars) + ') {'); + var blines = this.args[1].toCLines(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].toCLLExpr(vars) + ') {'); + var blines = this.args[1].toCLines(vars, needsreturn); + for (var i in blines) { + lines.push('\t' + blines[i]); + } + lines.push('} else {'); + blines = this.args[2].toCLines(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].toCLLExpr(vars) + ') {'); + var blines = this.args[1].toCLines(vars); + for (var i in blines) { + lines.push('\t' + blines[i]); + } + lines.push('}'); + break; + default: + lines.push( (needsreturn ? 'return (object *)' : '') + this.toCLLExpr(vars) + ';'); + } + return lines; +}; + +funcall.prototype.toCLLExpr = 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) { + return this.receiver.toCLLExpr(vars) + '->' + this.name; + } else if (this.args.length == 1 && name[name.length-1] == '!') { + return this.receiver.toCLLExpr(vars) + '->' + this.name.substr(0, name.length-1) + ' = ' + args[0].toCLLExpr(vars); + } else { + args.splice(0, 0, this.receiver); + } + } + switch(name) + { + case 'if': + case 'if:else': + case 'while:do': + throw new Error('if, if:else and while:do not allow in expression context in llMessage block'); + case 'addr_of': + return '&(' + args[0].toCLLExpr(vars) + ')'; + case 'sizeof': + return 'sizeof(' + args[0].toCTypeName() + ')'; + case 'get': + return args[0].toCLLExpr(vars) + '[' + args[1].toCLLExpr(vars) + ']'; + case 'set': + return args[0].toCLLExpr(vars) + '[' + args[1].toCLLExpr(vars) + '] = ' + args[2].toCLLExpr(vars); + case 'not': + return '!(' + args[0].toCLLExpr(vars) + ')'; + default: + for (var i in args) { + args[i] = args[i].toCLLExpr(vars); + } + return name + '(' + args.join(', ') + ')'; + } +}; function cObject(name) { this.name = name; @@ -355,11 +516,15 @@ var nextobject = 0; -object.prototype.toC = function() { + +object.prototype.toCObject = function() { var messages = this.messages; var values = []; - var imports = [] - var me = new cObject('object_' + nextobject++); + var imports = []; + 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() + ' * '); @@ -381,16 +546,34 @@ importsyms.push(new strlit(el.name)); }); imports.push({symbols: new listlit(importsyms), src: 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].toCTypeName()) + } 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.toCTypeName(); + } + me.addMessage(msgname, { + vars: vars, + lines: messages[i].args[2].toCLines(vars, true) + }); } else { - throw new Error('Only import and import:from calls allowed in object context'); + + 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].toCObject(me); } } - return me.toC(); -} + return me; +}; + +object.prototype.toC = function() { + return this.toCObject().toC(); +}; var toplevelcode; var forwarddec; @@ -459,67 +642,11 @@ function makeArray() { - 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 ' + toplevel.moduleVar('false') + ';' - ] - }); - 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 &(self->header);' - ] - }); - array.addMessage('foreach', { - vars: {index: 'obj_int32 *', i: 'int32_t', clos: 'lambda *'}, - lines: [ - 'clos = va_arg(args, lambda *);', - 'for (i = 0; i < self->size; i++) {', - ' index = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', - ' index->num = i;', - ' ccall(clos, 2, index, self->data[i]);', - '}', - 'return &(self->header);' - ] - }); - array.addMessage('append', { - vars: {tmp: 'object **'}, - lines: [ - 'if (self->storage == self->size) {', - ' self->storage *= 2;', - ' tmp = GC_REALLOC(self->data, self->storage * sizeof(object *));', - ' if (!tmp) {', - ' fputs("Failed to increase array size\\n", stderr);', - ' exit(1);', - ' }', - ' self->data = tmp;', - '}', - 'self->data[self->size++] = va_arg(args, object *);', - 'return &(self->header);' - ] - }); - array.addMessage('length', { - vars: {intret: 'obj_int32 *'}, - lines: [ - 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', - 'intret->num = self->size;', - 'return &(intret->header);' - ] - }); - return array; + var arrayfile = toplevel.names['array']; + var ast = parseFile(arrayfile.path + '/' + arrayfile.file); + ast.name = 'array'; + ast.populateSymbols(toplevel); + return ast.toCObject(); } function makeString() @@ -770,14 +897,12 @@ var alwaysused = ['true', 'false']; var ret = ''; var modulenum = 0; - var newused = Object.keys(toplevel.used); - var allused = newused; var visited = {}; for (var i in alwaysused) { - forwarddec += 'object * ' + toplevel.moduleVar(alwaysused[i]) + ';\n'; - toplevel.names[alwaysused[i]].populateSymbols(toplevel); - visited[alwaysused[i]] = true; + 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'); @@ -794,13 +919,10 @@ } } } - for (var i in alwaysused) { - allused.push(alwaysused[i]); - } for (var i = allused.length-1; i >= 0; i--) { var symbol = allused[i]; - debugprint('//---module', symbol, '--- compile'); + debugprint('//---module', symbol, '(' + i +')--- compile'); ret += '\t' + toplevel.moduleVar(symbol) + ' = ' + toplevel.names[symbol].toC() + ';\n'; } return ret; @@ -808,8 +930,8 @@ function makeCProg(obj) { + forwarddec = toplevelcode = ''; var builtins = builtinTypes(); - forwarddec = toplevelcode = ''; for (var i in builtins) { forwarddec += builtins[i].toEarlyCDef(); toplevelcode += builtins[i].toCDef(); @@ -932,6 +1054,31 @@ }; lambda.prototype.toCModule = function() { return makeCProg(this); +}; +lambda.prototype.toCLines = 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].toCLines(vars, needsreturn && i == this.expressions.length - 1); + for (var j in exprlines) { + lines.push('\t' + exprlines[j]); + } + } + return lines; +} +lambda.prototype.toCLLExpr = 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].toCLLExpr(vars); } assignment.prototype.toC = function() { @@ -982,3 +1129,6 @@ } } }; +assignment.prototype.toCLines = function(vars, needsreturn) { + return [(needsreturn ? 'return ' : '') + this.symbol.toCLLExpr(vars) + ' = ' + this.expression.toCLLExpr(vars) + ';'] +}; diff -r fdb9785d2c93 -r 9811040704ac compiler.js --- a/compiler.js Sat Jul 21 22:29:16 2012 -0700 +++ b/compiler.js Sat Jul 21 22:30:21 2012 -0700 @@ -78,11 +78,12 @@ { this.parent = parent; this.names = {}; + this.llnames = {}; this.needsenv = false; this.typename = null; this.needsparent = false; } -osymbols.prototype.find = function(name, nestedcall) { +osymbols.prototype.find = function(name, nestedcall, allowll) { debugprint('//osymbols.find', name + ', exists?:', name in this.names, ', nested?:', nestedcall); if (name in this.names) { if (this.names[name] instanceof funcall && this.names[name].name == 'foreign:') { @@ -93,11 +94,18 @@ } var ret = { type: 'self', + isll: false, def: this.names[name], selftype: this.typename }; + } else if(allowll && name in this.llnames) { + return { + type: 'self', + isll: true, + selftype: this.typename + }; } else if(this.parent) { - var ret = this.parent.find(name, nestedcall); + var ret = this.parent.find(name, nestedcall, allowll); if (ret) { if(ret.type == 'self') { ret.type = 'parent'; @@ -119,6 +127,9 @@ osymbols.prototype.defineMsg = function(name, def) { this.names[name] = def; } +osymbols.prototype.defineLLProperty = function(name) { + this.llnames[name] = true; +} osymbols.prototype.parentObject = function() { if (!this.parent) { return 'null'; @@ -160,7 +171,7 @@ this.envtype = 'void'; this.needsParentEnv = false; } -lsymbols.prototype.find = function(name, nestedcall) { +lsymbols.prototype.find = function(name, nestedcall, allowll) { debugprint('//lsymbols.find', name + ', exists?:', name in this.names, ', nested?:', nestedcall); if (name in this.names) { if (this.names[name] instanceof funcall && this.names[name].name == 'foreign:') { @@ -187,7 +198,7 @@ } } } else if(this.parent) { - var ret = this.parent.find(name, true); + var ret = this.parent.find(name, true, allowll); if (ret) { if (ret.type == 'closedover') { ret.type = 'upvar'; @@ -317,6 +328,31 @@ } else { throw new Error("Unexpected AST type for foreign:"); } + } else if (this.name == 'llProperty:withType:') { + if (this.args[0] instanceof symbol) { + if ((this.args[1] instanceof symbol) || (this.args[1] instanceof funcall)) { + symbols.defineLLProperty(this.args[0].name); + return; + } else { + throw new Error("Second argument to llProperty:withType: must be a symbol or funcall"); + } + } else { + throw new Error("First argument to llProperty:withType: must be a symbol"); + } + } else if (this.name == 'llMessage:withVars:andCode:') { + if (this.args[0] instanceof symbol) { + if (this.args[1] instanceof lambda) { + if (this.args[2] instanceof lambda) { + symbols.defineMsg(this.args[0].name, this.args[2]); + } else { + throw new Error("Third argument to llMessage:withVars:andCode: must be a lambda"); + } + } else { + throw new Error("Second argument to llMessage:withVars:andCode: must be a lambda"); + } + } else { + throw new Error("First argument to llMessage:withVars:andCode: must be a symbol"); + } } this.symbols = symbols; var name = this.name[this.name.length-1] == ':' ? this.name.substr(0, this.name.length-1) : this.name; @@ -333,11 +369,11 @@ } funcall.prototype.populateSymbolsObject = function(symbols) { - this.populateSymbols(symbols.parent); + this.populateSymbols(symbols); } object.prototype.populateSymbols = function(symbols) { - symbols = new osymbols(symbols); + var symbols = new osymbols(symbols); for (var i in this.messages) { this.messages[i].populateSymbolsObject(symbols); } diff -r fdb9785d2c93 -r 9811040704ac modules/array.tp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/array.tp Sat Jul 21 22:30:21 2012 -0700 @@ -0,0 +1,102 @@ +#{ + llProperty: size withType: uint32_t + llProperty: storage withType: uint32_t + llProperty: data withType: ((object ptr) ptr) + llMessage: get withVars: { + index <- obj_int32 ptr + } andCode: :index { + if: (index num) >= 0 && (index num) < size { + (self data) get: (index num) + } else: { + false + } + } + + llMessage: set withVars: { + index <- obj_int32 ptr + value <- object ptr + } andCode: :index value { + if: (index num) >= 0 && (index num) < size { + data set: (index num) value + } + self + } + + llMessage: foreach withVars: { + clos <- lambda ptr + i <- uint32_t + index <- obj_int32 ptr + } andCode: :clos { + i <- 0 + while: { i < size } do: { + index <- make_object: (addr_of: obj_int32_meta) NULL 0 + index num!: i + ccall: clos 2 index (data get: i) + i <- i + 1 + } + self + } + + llMessage: append withVars: { + value <- object ptr + tmp <- (object ptr) ptr + } andCode: :value { + if: storage = size { + storage <- storage * 2 + tmp <- GC_REALLOC: data storage * (sizeof: (object ptr)) + if: (not: tmp) { + fputs: "Failed to increase array size\n" stderr + exit: 1 + } + data <- tmp + } + data set: size value + size <- size + 1 + self + } + + llMessage: length withVars: { + intret <- obj_int32 ptr + } andCode: { + intret <- make_object: (addr_of: obj_int32_meta) NULL 0 + intret num!: size + intret + } + + fold:with <- :acc :fun { + foreach: self :idx el { + fun: acc el + } + } + + foldr:with <- :acc :fun { + idx <- length - 1 + while: {idx >= 0} do: { + fun: acc (get: idx) + } + } + + map <- :fun { + new <- #[] + foreach: self :idx el { + new append: (fun: el) + } + } + + find:withDefault <- :pred :default{ + idx <- 0 + l <- length + ret <- default + while: {idx < l} do: { + v <- get: idx + if: (pred: v) { + ret <- #{ + key <- idx + value <- v + } + idx <- l + } + } + ret + } +}