comparison cbackend.js @ 84:9811040704ac

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.
author Mike Pavone <pavone@retrodev.com>
date Sat, 21 Jul 2012 22:30:21 -0700
parents 7f635666c73d
children 25bc8a5ab41e
comparison
equal deleted inserted replaced
83:fdb9785d2c93 84:9811040704ac
42 op.prototype.toC = function(isReceiver) { 42 op.prototype.toC = function(isReceiver) {
43 var optoMeth = {'+': 'ADD_', '-': 'SUB_', '*': 'MUL_', '/': 'DIV_', '%': 'MOD_', '=': 'EQ_', '!=': 'NEQ_', '<': 'LT_', '>': 'GT_', '>=': 'GEQ_', '<=': 'LEQ_', '.': 'CAT_'}; 43 var optoMeth = {'+': 'ADD_', '-': 'SUB_', '*': 'MUL_', '/': 'DIV_', '%': 'MOD_', '=': 'EQ_', '!=': 'NEQ_', '<': 'LT_', '>': 'GT_', '>=': 'GEQ_', '<=': 'LEQ_', '.': 'CAT_'};
44 var method = optoMeth[this.op]; 44 var method = optoMeth[this.op];
45 return 'mcall(' + getMethodId(method) + '/* ' + method + ' */, 2, (object *)' + this.left.toC() + ', ' + this.right.toC() + ')\n'; 45 return 'mcall(' + getMethodId(method) + '/* ' + method + ' */, 2, (object *)' + this.left.toC() + ', ' + this.right.toC() + ')\n';
46 }; 46 };
47 op.prototype.toCLLExpr = function(vars) {
48 return this.left.toCLLExpr(vars) + (this.op == '=' ? '==' : this.op) + this.right.toCLLExpr(vars);
49 };
50 op.prototype.toCLines = function(vars, needsreturn) {
51 return [ (needsreturn ? 'return (object *)' : '' ) + this.toCLLExpr(vars) + ';'];
52 };
53
47 54
48 function escapeCName(name) 55 function escapeCName(name)
49 { 56 {
50 if (name == 'self') { 57 if (name == 'self') {
51 return name; 58 return name;
106 } 113 }
107 return 'mcall(' + getMethodId(name) + '/* ' + name + ' */, 1, ' + obj + ')'; 114 return 'mcall(' + getMethodId(name) + '/* ' + name + ' */, 1, ' + obj + ')';
108 } 115 }
109 return getSymbolPrefix(info, this.symbols) + escapeCName(name); 116 return getSymbolPrefix(info, this.symbols) + escapeCName(name);
110 } 117 }
118 symbol.prototype.toCTypeName = function() {
119 return this.cleanName();
120 };
121 symbol.prototype.toCLLExpr = function(vars) {
122 var name = this.cleanName();
123 if (name in vars) {
124 return name;
125 }
126 if (name == 'self') {
127 return 'self';
128 }
129 var info = this.symbols.find(name, false, true);
130 var symbols = this.symbols;
131 while (info && info.type == 'local') {
132 symbols = symbols.parent;
133 info = symbols.find(name, false, true);
134 }
135 if (!info) {
136 return name;
137 }
138 if (info.type == 'toplevel') {
139 return toplevel.moduleVar(name);
140 } else if (info.type == 'self') {
141 if (info.isll || !(info.def instanceof lambda)) {
142 return 'self->' + name;
143 } else {
144 return 'mcall(' + getMethodId(name) + '/* ' + name + ' */, 1, ' + (new symbol('self', this.symbols)).toC() + ')';
145 }
146 }
147 throw new Error('Unsupported reference type ' + info.type + ' for variable ' + name);
148 };
149 symbol.prototype.toCLines = function(vars, needsreturn) {
150 return [ (needsreturn ? 'return (object *)' : '' ) + this.toCLLExpr(vars) + ';' ];
151 };
111 152
112 var declaredInts = {}; 153 var declaredInts = {};
113 154
114 intlit.prototype.toC = function() { 155 intlit.prototype.toC = function() {
115 var str = this.val < 0 ? 'neg_' + (0-this.val).toString() : this.val.toString(); 156 var str = this.val < 0 ? 'neg_' + (0-this.val).toString() : this.val.toString();
117 toplevelcode += 'obj_int32 int32_' + str + ' = {{&obj_int32_meta, NULL}, ' + this.val.toString() + '};\n'; 158 toplevelcode += 'obj_int32 int32_' + str + ' = {{&obj_int32_meta, NULL}, ' + this.val.toString() + '};\n';
118 declaredInts[this.val] = true; 159 declaredInts[this.val] = true;
119 } 160 }
120 return '((object *)&int32_' + str + ')'; 161 return '((object *)&int32_' + str + ')';
121 } 162 }
163 intlit.prototype.toCLLExpr = function(vars) {
164 return this.val.toString();
165 };
166 intlit.prototype.toCLines = function(vars, needsreturn) {
167 return [ (needsreturn ? 'return (object *)' : '' ) + this.toCLLExpr(vars) + ';' ];
168 };
122 169
123 floatlit.prototype.toC = function() { 170 floatlit.prototype.toC = function() {
124 return 'make_float(' + this.val.toString() + ')'; 171 return 'make_float(' + this.val.toString() + ')';
125 } 172 }
173 floatlit.prototype.toCLLExpr = function(vars) {
174 return this.val.toString();
175 };
176 floatlit.prototype.toCLines = function(vars, needsreturn) {
177 return [ (needsreturn ? 'return (object *)' : '' ) + this.toCLLExpr(vars) + ';' ];
178 };
126 179
127 var declaredStrings = {}; 180 var declaredStrings = {};
128 var nextStringId = 0; 181 var nextStringId = 0;
129 182
130 strlit.prototype.toC = function() { 183 strlit.prototype.toC = function() {
132 //TODO: get the proper byte length 185 //TODO: get the proper byte length
133 toplevelcode += 'string str_' + nextStringId + ' = {{&string_meta, NULL}, ' + this.val.length + ', ' + this.val.length + ', "' + this.val.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n').replace('\r', '\\r') + '"};\n'; 186 toplevelcode += 'string str_' + nextStringId + ' = {{&string_meta, NULL}, ' + this.val.length + ', ' + this.val.length + ', "' + this.val.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n').replace('\r', '\\r') + '"};\n';
134 declaredStrings[this.val] = nextStringId++; 187 declaredStrings[this.val] = nextStringId++;
135 } 188 }
136 return '((object *)&str_' + declaredStrings[this.val] + ')'; 189 return '((object *)&str_' + declaredStrings[this.val] + ')';
137 } 190 };
191 strlit.prototype.toCLLExpr = function(vars) {
192 return '"' + this.val.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n').replace('\r', '\\r') + '"';
193 };
194 strlit.prototype.toCLines = function(vars, needsreturn) {
195 return [ (needsreturn ? 'return (object *)' : '' ) + this.toCLLExpr(vars) +';' ];
196 };
138 197
139 listlit.prototype.toC = function() { 198 listlit.prototype.toC = function() {
140 var ret = 'make_list(' + this.val.length; 199 var ret = 'make_list(' + this.val.length;
141 for (var i = 0; i < this.val.length; i++) { 200 for (var i = 0; i < this.val.length; i++) {
142 ret += ', ' + this.val[i].toC(); 201 ret += ', ' + this.val[i].toC();
160 } else if(this.args[0] instanceof symbol) { 219 } else if(this.args[0] instanceof symbol) {
161 return this.args[0].name; 220 return this.args[0].name;
162 } else { 221 } else {
163 throw new Error("Unexpected AST type for foreign:"); 222 throw new Error("Unexpected AST type for foreign:");
164 } 223 }
224 } else if(name == 'llProperty:withType' || name == 'llProperty:withVars:andCode') {
225 return null;
165 } 226 }
166 var args = this.args.slice(0, this.args.length); 227 var args = this.args.slice(0, this.args.length);
167 if (this.receiver) { 228 if (this.receiver) {
168 args.splice(0, 0, this.receiver); 229 args.splice(0, 0, this.receiver);
169 } 230 }
205 callpart = 'mcall(' + getMethodId(name) + '/* ' + name + ' */'; 266 callpart = 'mcall(' + getMethodId(name) + '/* ' + name + ' */';
206 } else { 267 } else {
207 callpart = 'ccall(' + (new symbol(name, this.symbols)).toC(); 268 callpart = 'ccall(' + (new symbol(name, this.symbols)).toC();
208 } 269 }
209 return callpart + ', ' + args.length + args.join('') + ')'; 270 return callpart + ', ' + args.length + args.join('') + ')';
210 } 271 };
272 funcall.prototype.toCTypeName = function() {
273 switch(this.name)
274 {
275 case 'ptr:':
276 case 'ptr':
277 var receiver = this.receiver ? this.receiver : this.args[0];
278 return receiver.toCTypeName() + ' *';
279 break;
280 default:
281 throw new Error('invalid use of funcall expression where a C type name is expected');
282 }
283 };
284 funcall.prototype.toCLines = function(vars, needsreturn) {
285 var lines = [];
286 var name = this.name[this.name.length-1] == ':' ? this.name.substr(0, this.name.length-1) : this.name;
287 var args = this.args.slice(0, this.args.length);
288 if (this.receiver) {
289 args.splice(0, 0, [this.receiver]);
290 }
291 switch(name)
292 {
293 case 'if':
294 lines.push('if (' + this.args[0].toCLLExpr(vars) + ') {');
295 var blines = this.args[1].toCLines(vars, needsreturn);
296 for (var i in blines) {
297 lines.push('\t' + blines[i]);
298 }
299 if (needsreturn) {
300 lines.push('} else {');
301 lines.push('\t return module_false;');
302 lines.push('}');
303 } else {
304 lines.push('}');
305 }
306 break;
307 case 'if:else':
308 lines.push('if (' + this.args[0].toCLLExpr(vars) + ') {');
309 var blines = this.args[1].toCLines(vars, needsreturn);
310 for (var i in blines) {
311 lines.push('\t' + blines[i]);
312 }
313 lines.push('} else {');
314 blines = this.args[2].toCLines(vars, needsreturn);
315 for (var i in blines) {
316 lines.push('\t' + blines[i]);
317 }
318 lines.push('}');
319 break;
320 case 'while:do':
321 if (needsreturn) {
322 throw new Error("while:do can't be last statement in llMessage code block");
323 }
324 lines.push('while (' + this.args[0].toCLLExpr(vars) + ') {');
325 var blines = this.args[1].toCLines(vars);
326 for (var i in blines) {
327 lines.push('\t' + blines[i]);
328 }
329 lines.push('}');
330 break;
331 default:
332 lines.push( (needsreturn ? 'return (object *)' : '') + this.toCLLExpr(vars) + ';');
333 }
334 return lines;
335 };
336
337 funcall.prototype.toCLLExpr = function(vars) {
338 var name = this.name[this.name.length-1] == ':' ? this.name.substr(0, this.name.length-1) : this.name;
339 var args = this.args.slice(0, this.args.length);
340 if (this.receiver) {
341 if(this.args.length == 0) {
342 return this.receiver.toCLLExpr(vars) + '->' + this.name;
343 } else if (this.args.length == 1 && name[name.length-1] == '!') {
344 return this.receiver.toCLLExpr(vars) + '->' + this.name.substr(0, name.length-1) + ' = ' + args[0].toCLLExpr(vars);
345 } else {
346 args.splice(0, 0, this.receiver);
347 }
348 }
349 switch(name)
350 {
351 case 'if':
352 case 'if:else':
353 case 'while:do':
354 throw new Error('if, if:else and while:do not allow in expression context in llMessage block');
355 case 'addr_of':
356 return '&(' + args[0].toCLLExpr(vars) + ')';
357 case 'sizeof':
358 return 'sizeof(' + args[0].toCTypeName() + ')';
359 case 'get':
360 return args[0].toCLLExpr(vars) + '[' + args[1].toCLLExpr(vars) + ']';
361 case 'set':
362 return args[0].toCLLExpr(vars) + '[' + args[1].toCLLExpr(vars) + '] = ' + args[2].toCLLExpr(vars);
363 case 'not':
364 return '!(' + args[0].toCLLExpr(vars) + ')';
365 default:
366 for (var i in args) {
367 args[i] = args[i].toCLLExpr(vars);
368 }
369 return name + '(' + args.join(', ') + ')';
370 }
371 };
211 372
212 function cObject(name) { 373 function cObject(name) {
213 this.name = name; 374 this.name = name;
214 this.slots = {}; 375 this.slots = {};
215 this.properties = []; 376 this.properties = [];
353 return this.toCInstance(); 514 return this.toCInstance();
354 } 515 }
355 516
356 var nextobject = 0; 517 var nextobject = 0;
357 518
358 object.prototype.toC = function() { 519
520 object.prototype.toCObject = function() {
359 var messages = this.messages; 521 var messages = this.messages;
360 var values = []; 522 var values = [];
361 var imports = [] 523 var imports = [];
362 var me = new cObject('object_' + nextobject++); 524 if (!this.name) {
525 this.name = 'object_' + nextobject++;
526 }
527 var me = new cObject(this.name);
363 this.symbols.typename = me.name; 528 this.symbols.typename = me.name;
364 if (this.symbols.needsenv) { 529 if (this.symbols.needsenv) {
365 me.addProperty('env', this.symbols.envVar(), 'struct ' + this.symbols.getEnvType() + ' * '); 530 me.addProperty('env', this.symbols.envVar(), 'struct ' + this.symbols.getEnvType() + ' * ');
366 me.hasenv = true; 531 me.hasenv = true;
367 } 532 }
379 throw new Error('Names in import:from statement must be symbols'); 544 throw new Error('Names in import:from statement must be symbols');
380 } 545 }
381 importsyms.push(new strlit(el.name)); 546 importsyms.push(new strlit(el.name));
382 }); 547 });
383 imports.push({symbols: new listlit(importsyms), src: messages[i].args[1]}); 548 imports.push({symbols: new listlit(importsyms), src: messages[i].args[1]});
549 } else if(messages[i].name == 'llProperty:withType:' && messages[i].args.length == 2) {
550 me.addProperty(messages[i].args[0].name, null, messages[i].args[1].toCTypeName())
551 } else if(messages[i].name == 'llMessage:withVars:andCode:' && messages[i].args.length == 3) {
552 var msgname = messages[i].args[0].name
553 var rawvars = messages[i].args[1].expressions;
554 var vars = {};
555 for(var v in rawvars) {
556 vars[rawvars[v].symbol.name] = rawvars[v].expression.toCTypeName();
557 }
558 me.addMessage(msgname, {
559 vars: vars,
560 lines: messages[i].args[2].toCLines(vars, true)
561 });
384 } else { 562 } else {
385 throw new Error('Only import and import:from calls allowed in object context'); 563
564 throw new Error('Only import and import:from calls allowed in object context. ' + messages[i].name + 'with ' + messages[i].args.length + ' arguments found instead.');
386 } 565 }
387 } else { 566 } else {
388 messages[i].toCObject(me); 567 messages[i].toCObject(me);
389 } 568 }
390 } 569 }
391 570
392 return me.toC(); 571 return me;
393 } 572 };
573
574 object.prototype.toC = function() {
575 return this.toCObject().toC();
576 };
394 577
395 var toplevelcode; 578 var toplevelcode;
396 var forwarddec; 579 var forwarddec;
397 580
398 function addBinaryOp(cobject, opname, cop, objtype) 581 function addBinaryOp(cobject, opname, cop, objtype)
457 return int32; 640 return int32;
458 } 641 }
459 642
460 function makeArray() 643 function makeArray()
461 { 644 {
462 var array = new cObject('array'); 645 var arrayfile = toplevel.names['array'];
463 array.addProperty('size', null, 'uint32_t'); 646 var ast = parseFile(arrayfile.path + '/' + arrayfile.file);
464 array.addProperty('storage', null, 'uint32_t'); 647 ast.name = 'array';
465 array.addProperty('data', null, 'object **'); 648 ast.populateSymbols(toplevel);
466 array.addMessage('get', { 649 return ast.toCObject();
467 vars: {index: 'obj_int32 *'},
468 lines: [
469 'index = va_arg(args, obj_int32 *);',
470 'if (index->num >= 0 && index->num < self->size) {',
471 ' return self->data[index->num];',
472 '}',
473 'return ' + toplevel.moduleVar('false') + ';'
474 ]
475 });
476 array.addMessage('set', {
477 vars: {index: 'obj_int32 *'},
478 lines: [
479 'index = va_arg(args, obj_int32 *);',
480 'if (index->num >= 0 && index->num < self->size) {',
481 ' self->data[index->num] = va_arg(args, object *);',
482 '}',
483 'return &(self->header);'
484 ]
485 });
486 array.addMessage('foreach', {
487 vars: {index: 'obj_int32 *', i: 'int32_t', clos: 'lambda *'},
488 lines: [
489 'clos = va_arg(args, lambda *);',
490 'for (i = 0; i < self->size; i++) {',
491 ' index = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);',
492 ' index->num = i;',
493 ' ccall(clos, 2, index, self->data[i]);',
494 '}',
495 'return &(self->header);'
496 ]
497 });
498 array.addMessage('append', {
499 vars: {tmp: 'object **'},
500 lines: [
501 'if (self->storage == self->size) {',
502 ' self->storage *= 2;',
503 ' tmp = GC_REALLOC(self->data, self->storage * sizeof(object *));',
504 ' if (!tmp) {',
505 ' fputs("Failed to increase array size\\n", stderr);',
506 ' exit(1);',
507 ' }',
508 ' self->data = tmp;',
509 '}',
510 'self->data[self->size++] = va_arg(args, object *);',
511 'return &(self->header);'
512 ]
513 });
514 array.addMessage('length', {
515 vars: {intret: 'obj_int32 *'},
516 lines: [
517 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);',
518 'intret->num = self->size;',
519 'return &(intret->header);'
520 ]
521 });
522 return array;
523 } 650 }
524 651
525 function makeString() 652 function makeString()
526 { 653 {
527 var string = new cObject('string'); 654 var string = new cObject('string');
768 function processUsedToplevel(toplevel) 895 function processUsedToplevel(toplevel)
769 { 896 {
770 var alwaysused = ['true', 'false']; 897 var alwaysused = ['true', 'false'];
771 var ret = ''; 898 var ret = '';
772 var modulenum = 0; 899 var modulenum = 0;
900 var visited = {};
901 for (var i in alwaysused) {
902 toplevel.used[alwaysused[i]] = true;
903 }
773 var newused = Object.keys(toplevel.used); 904 var newused = Object.keys(toplevel.used);
774 var allused = newused; 905 var allused = newused;
775 var visited = {};
776 for (var i in alwaysused) {
777 forwarddec += 'object * ' + toplevel.moduleVar(alwaysused[i]) + ';\n';
778 toplevel.names[alwaysused[i]].populateSymbols(toplevel);
779 visited[alwaysused[i]] = true;
780 }
781 while (newused.length) { 906 while (newused.length) {
782 for (var i in newused) { 907 for (var i in newused) {
783 debugprint('//---module', newused[i], '--- populate symbols'); 908 debugprint('//---module', newused[i], '--- populate symbols');
784 forwarddec += 'object * ' + toplevel.moduleVar(newused[i]) + ';\n'; 909 forwarddec += 'object * ' + toplevel.moduleVar(newused[i]) + ';\n';
785 toplevel.names[newused[i]].populateSymbols(toplevel); 910 toplevel.names[newused[i]].populateSymbols(toplevel);
792 newused.push(symbol); 917 newused.push(symbol);
793 allused.push(symbol); 918 allused.push(symbol);
794 } 919 }
795 } 920 }
796 } 921 }
797 for (var i in alwaysused) {
798 allused.push(alwaysused[i]);
799 }
800 922
801 for (var i = allused.length-1; i >= 0; i--) { 923 for (var i = allused.length-1; i >= 0; i--) {
802 var symbol = allused[i]; 924 var symbol = allused[i];
803 debugprint('//---module', symbol, '--- compile'); 925 debugprint('//---module', symbol, '(' + i +')--- compile');
804 ret += '\t' + toplevel.moduleVar(symbol) + ' = ' + toplevel.names[symbol].toC() + ';\n'; 926 ret += '\t' + toplevel.moduleVar(symbol) + ' = ' + toplevel.names[symbol].toC() + ';\n';
805 } 927 }
806 return ret; 928 return ret;
807 } 929 }
808 930
809 function makeCProg(obj) 931 function makeCProg(obj)
810 { 932 {
933 forwarddec = toplevelcode = '';
811 var builtins = builtinTypes(); 934 var builtins = builtinTypes();
812 forwarddec = toplevelcode = '';
813 for (var i in builtins) { 935 for (var i in builtins) {
814 forwarddec += builtins[i].toEarlyCDef(); 936 forwarddec += builtins[i].toEarlyCDef();
815 toplevelcode += builtins[i].toCDef(); 937 toplevelcode += builtins[i].toCDef();
816 } 938 }
817 addBuiltinModules(toplevel); 939 addBuiltinModules(toplevel);
930 this.selftype = typename; 1052 this.selftype = typename;
931 return this.toC(); 1053 return this.toC();
932 }; 1054 };
933 lambda.prototype.toCModule = function() { 1055 lambda.prototype.toCModule = function() {
934 return makeCProg(this); 1056 return makeCProg(this);
1057 };
1058 lambda.prototype.toCLines = function(vars, needsreturn) {
1059 var lines = [];
1060 for (var i in this.args) {
1061 var name = this.args[i].name;
1062 if (name[0] == ':') {
1063 name = name.substr(1);
1064 }
1065 if(name != 'self') {
1066 lines.push(name + ' = va_arg(args, ' + vars[name] + ');');
1067 }
1068 }
1069 for (var i in this.expressions) {
1070 var exprlines = this.expressions[i].toCLines(vars, needsreturn && i == this.expressions.length - 1);
1071 for (var j in exprlines) {
1072 lines.push('\t' + exprlines[j]);
1073 }
1074 }
1075 return lines;
1076 }
1077 lambda.prototype.toCLLExpr = function(vars) {
1078 if (this.expressions.length != 1) {
1079 throw new Error('lambda in expression context must have a single statement in llMessage block');
1080 }
1081 return this.expressions[0].toCLLExpr(vars);
935 } 1082 }
936 1083
937 assignment.prototype.toC = function() { 1084 assignment.prototype.toC = function() {
938 debugprint('//assignment', this.symbol.name); 1085 debugprint('//assignment', this.symbol.name);
939 var existing = this.symbols.find(this.symbol.name); 1086 var existing = this.symbols.find(this.symbol.name);
980 if (this.expression instanceof object && this.expression.symbols.needsparent) { 1127 if (this.expression instanceof object && this.expression.symbols.needsparent) {
981 cobj.addInit('self->' + escapeCName(this.symbol.name) + '->parent = (object *)self;'); 1128 cobj.addInit('self->' + escapeCName(this.symbol.name) + '->parent = (object *)self;');
982 } 1129 }
983 } 1130 }
984 }; 1131 };
1132 assignment.prototype.toCLines = function(vars, needsreturn) {
1133 return [(needsreturn ? 'return ' : '') + this.symbol.toCLLExpr(vars) + ' = ' + this.expression.toCLLExpr(vars) + ';']
1134 };