comparison ilbackend.js @ 205:6fe9343b1400

Some minor work on creating an IL backend based on the C backend
author Mike Pavone <pavone@retrodev.com>
date Sun, 13 Oct 2013 20:23:08 -0700
parents
children d1dc2d70bdfd
comparison
equal deleted inserted replaced
204:a8dffa4d4b54 205:6fe9343b1400
1 var mainModule;
2 var modules = {};
3
4 var methodIds = {};
5 var methodNames = [];
6 var assignNames;
7
8 function register(num)
9 {
10 this.num = num;
11 }
12
13 register.prototype = {
14 valueOf: function() {
15 return 'r' + this.num;
16 },
17 get isRegister() { return true; },
18 get isArgument() { return false; }
19 };
20
21 function argument(num)
22 {
23 this.num = num;
24 }
25
26 argument.prototype = {
27 valueOf: function() {
28 return 'a' + this.num;
29 },
30 get isRegister() { return true; },
31 get isArgument() { return true; }
32 };
33
34 function retreg(size)
35 {
36 this.size = size;
37 this.reg = NULL;
38 }
39
40 retreg.prototype = {
41 valueOf: function() {
42 if (!this.reg) {
43 return 'retr';
44 }
45 return this.reg.valueOf();
46 },
47 clobber: function(code) {
48 this.reg = code.getReg();
49 code.addInst('mov', 'retr', this.reg, this.size);
50 },
51 get isRegister() { return true; },
52 get isArgument() { return false; }
53 }
54
55 function indexed(base, offset, index, size)
56 {
57 this.base = base;
58 this.offset = offset;
59 this.index = index;
60 this.size = size;
61 }
62
63 indexed.prototype = {
64 valueOf: function() {
65 return this.offset + '[' + this.base + ' ' + this.index + '*' + this.size + ']';
66 },
67 get isRegister() { return false; },
68 get isArgument() { return false; }
69 };
70
71 function offset(base, offset)
72 {
73 this.base = base;
74 this.offset = offset;
75 }
76
77 offset.prototype = {
78 valueOf: function() {
79 var out = '';
80 if (this.offset) {
81 out += this.offset;
82 }
83 return out + '[' + this.base + ']';
84 },
85 get isRegister() { return false; },
86 get isArgument() { return false; }
87 };
88
89 function funCode(name)
90 {
91 this.name = name;
92 this.instructions = [];
93 this.nextReg = 0;
94 this.toclobber = [];
95 this.envreg = null;
96 }
97
98 funCode.prototype = {
99 addInst: function() {
100 var inst = '';
101 for (var i = 0; i < arguments.length; i++) {
102 if (arguments[0] == 'call') {
103 for (var i = 0; i < this.toclobber.length; i++) {
104 this.toclobber[i].clobber();
105 }
106 this.toclobber = [];
107 }
108 if (inst) {
109 inst += ' ';
110 }
111 inst += arguments[i];
112 }
113 this.instructions.push(inst);
114 },
115 getReg: function() {
116 return new register(this.nextReg++);
117 },
118 getRetReg: function(size) {
119 var reg = new retreg(size);
120 this.toclobber.push(reg);
121 return reg;
122 },
123 getEnvReg: function() {
124 if (!this.envreg) {
125 this.envreg = this.getReg();
126 }
127 return this.envreg;
128 },
129 valueOf: function() {
130 return this.name + ':\n\t' + this.instructions.join('\n\t') + '\n';
131 }
132 };
133 function getMethodId(methodName)
134 {
135 if (!(methodName in methodIds)) {
136 methodIds[methodName] = methodNames.length;
137 methodNames.push(methodName);
138 }
139 return methodIds[methodName];
140 }
141
142 function getOpMethodName(opname)
143 {
144 var optoMeth = {'+': 'ADD_', '-': 'SUB_', '*': 'MUL_', '/': 'DIV_', '%': 'MOD_',
145 '=': 'EQ_', '!=': 'NEQ_', '<': 'LT_', '>': 'GT_', '>=': 'GEQ_', '<=': 'LEQ_',
146 '.': 'CAT_', '&&':'if', '||':'ifnot', '|': 'CONS_'};
147 if (opname in optoMeth) {
148 return optoMeth[opname];
149 } else {
150 return opname;
151 }
152 }
153
154 var slot_arr_offset = 8;
155 function getMethodSlot(code, base, methodid)
156 {
157 var reg;
158 if (!base.isRegister()) {
159 reg = code.getReg();
160 code.addInst('mov', base, reg, 'q');
161 base = reg;
162 }
163 reg = code.getReg();
164 code.addInst('mov', new offset(base, 0), reg, 'q');
165 return new offset(reg, slot_arr_offset + (methodid & 0xF)*8);
166 }
167
168 op.prototype.toIL = function(code, isReceiver) {
169 var method = getOpMethodName(this.op);
170 var left = this.left.toIL(code);
171 var right = this.right.toIL(code);
172 var methId = getMethodId(method);
173 if (this.op == '|') {
174 code.addInst('call', getMethodSlot(code, right, methId), methId, right, left);
175 } else {
176 code.addInst('call', getMethodSlot(code, left, methId), methId, left, right);
177 }
178 return code.getRetReg();
179 };
180 op.prototype.toILLLExpr = function(vars) {
181 var opmap = {'=': '==', 'xor': '^', 'or': '|', 'and': '&'};
182 return this.left.toILLLExpr(vars) + (this.op in opmap ? opmap[this.op] : this.op) + this.right.toILLLExpr(vars);
183 };
184 op.prototype.toILLines = function(vars, needsreturn) {
185 return [ (needsreturn ? 'return (object *)' : '' ) + this.toILLLExpr(vars) + ';'];
186 };
187
188
189 function escapeCName(name)
190 {
191 if (name == 'self') {
192 return name;
193 }
194 name = name.replace(/_/g, "UN_").replace(/:/g, "CN_").replace(/!/g, "EX_").replace(/\?/g, 'QS_').replace(/@/g, 'AT_');
195 name = 'tp_' + name;
196 return name;
197 }
198
199 function getSymbolPrefix(info, symbols)
200 {
201 var pre = '';
202 switch(info.type) {
203 case 'self':
204
205 pre = (new symbol('self', symbols)).toIL() + '->';
206 break;
207 case 'parent':
208 pre = (new symbol('self', symbols)).toIL() + '->header.';
209 for (var i = 0; i < info.depth; ++i) {
210 pre += (i ? '->' : '') + 'parent';
211 }
212 pre = '((' + info.selftype + ' *)' + pre + ')->';
213 break;
214 case 'upvar':
215 pre = 'env->';
216 for (var i = info.startdepth; i < info.depth; ++i) {
217 pre += 'parent->';
218 }
219 break;
220 case 'recupvar':
221 if (info.subtype == 'object') {
222 pre = 'self->env->';
223 } else {
224 //TODO: fill this case in if necessary
225 }
226 pre += getSymbolPrefix(info.parent);
227 case 'closedover':
228 pre = 'myenv->';
229 }
230 return pre;
231 }
232
233 symbol.prototype.toIL = function(code) {
234 var name = this.cleanName();
235 var info = this.symbols.find(name);
236 if (!info) {
237 throw new Error('symbol ' + name + ' not found in ' + assignNames.join('<-'));
238 }
239 switch (info.type)
240 {
241 case 'toplevel':
242 return toplevel.moduleVar(name);
243 case 'self':
244 if (info.def instanceof lambda) {
245 var self = (new symbol('self', this.symbols)).toIL(code);
246 var methId = getMethodId(name);
247 code.addInst('call', getMethodSlot(code, self, methId), methId, self);
248 return code.getRetReg();
249 } else {
250 throw new Error('implement me');
251 }
252 case 'parent':
253 if (info.def instanceof lambda) {
254 throw new Error('implement me');
255 var obj = (new symbol('self', this.symbols)).toIL() + '->header.';
256 for (var i = 0; i < info.depth; ++i) {
257 obj += (i ? '->' : '') + 'parent';
258 }
259 return 'mcall(' + getMethodId(name) + '/* ' + name + ' */, 1, ' + obj + ')';
260 } else {
261 throw new Error('implement me');
262 }
263 case 'closedover':
264 var env = code.getEnvReg();
265 return new offset(env, info.offset)
266 default:
267 throw new Error('implement ' + info.type);
268 }
269 };
270 symbol.prototype.toILTypeName = function() {
271 return this.cleanName();
272 };
273 symbol.prototype.toILLLExpr = function(vars) {
274 var name = this.cleanName();
275 if (name in vars) {
276 return name;
277 }
278 if (name == 'self') {
279 return 'self';
280 }
281 var info = this.symbols.find(name, false, true);
282 var symbols = this.symbols;
283 while (info && info.type == 'local') {
284 symbols = symbols.parent;
285 info = symbols.find(name, false, true);
286 }
287 if (!info) {
288 return name;
289 }
290 if (info.type == 'toplevel') {
291 return toplevel.moduleVar(name);
292 } else if (info.type == 'self') {
293 if (info.isll || !(info.def instanceof lambda)) {
294 return 'self->' + name;
295 } else {
296 return 'mcall(' + getMethodId(name) + '/* ' + name + ' */, 1, self)';
297 }
298 }
299 throw new Error('Unsupported reference type ' + info.type + ' for variable ' + name);
300 };
301 symbol.prototype.toILLines = function(vars, needsreturn) {
302 return [ (needsreturn ? 'return (object *)' : '' ) + this.toILLLExpr(vars) + ';' ];
303 };
304
305 var declaredInts = {};
306
307 intlit.prototype.toIL = function() {
308 var intType = (this.unsigned ? 'u' : '') + 'int' + this.bits;
309 var str = intType + '_' + (this.val < 0 ? 'neg_' + (0-this.val).toString() : this.val.toString());
310 if (!(str in declaredInts)) {
311 toplevelcode += 'obj_' + intType + ' ' + str + ' = {{&obj_' + intType + '_meta, NULL}, ' + this.val.toString() + '};\n';
312 declaredInts[str] = true;
313 }
314 return '((object *)&' + str + ')';
315 }
316 intlit.prototype.toILLLExpr = function(vars) {
317 return this.val.toString();
318 };
319 intlit.prototype.toILLines = function(vars, needsreturn) {
320 return [ (needsreturn ? 'return (object *)' : '' ) + this.toILLLExpr(vars) + ';' ];
321 };
322
323 floatlit.prototype.toIL = function() {
324 return 'make_float(' + this.val.toString() + ')';
325 }
326 floatlit.prototype.toILLLExpr = function(vars) {
327 return this.val.toString();
328 };
329 floatlit.prototype.toILLines = function(vars, needsreturn) {
330 return [ (needsreturn ? 'return (object *)' : '' ) + this.toILLLExpr(vars) + ';' ];
331 };
332
333 var declaredStrings = {};
334 var nextStringId = 0;
335
336 strlit.prototype.toIL = function() {
337 if (!(this.val in declaredStrings)) {
338 //TODO: get the proper byte length
339 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';
340 declaredStrings[this.val] = nextStringId++;
341 }
342 return '((object *)&str_' + declaredStrings[this.val] + ')';
343 };
344 strlit.prototype.toILLLExpr = function(vars) {
345 return '"' + this.val.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n').replace(/\r/g, '\\r') + '"';
346 };
347 strlit.prototype.toILLines = function(vars, needsreturn) {
348 return [ (needsreturn ? 'return (object *)' : '' ) + this.toILLLExpr(vars) +';' ];
349 };
350
351 listlit.prototype.toIL = function() {
352 var ret = 'make_list(' + this.val.length;
353 for (var i = this.val.length-1; i >= 0; i--) {
354 ret += ', ' + this.val[i].toIL();
355 }
356 return ret + ')';
357 }
358
359 arraylit.prototype.toIL = function() {
360 var ret = 'make_array(' + this.val.length;
361 for (var i = 0; i < this.val.length; i++) {
362 ret += ', ' + this.val[i].toIL();
363 }
364 return ret + ')';
365 }
366
367 funcall.prototype.toIL = function() {
368 var name = this.name[this.name.length-1] == ':' ? this.name.substr(0, this.name.length-1) : this.name;
369 if (name == 'foreign') {
370 if ((this.args[0] instanceof lambda) || (this.args[0] instanceof object)) {
371 return null;
372 } else if(this.args[0] instanceof symbol) {
373 return this.args[0].name;
374 } else {
375 throw new Error("Unexpected AST type for foreign:");
376 }
377 } else if(name == 'llProperty:withType' || name == 'llProperty:withVars:andCode') {
378 return null;
379 }
380 var args = this.args.slice(0, this.args.length);
381 if (this.receiver) {
382 args.splice(0, 0, this.receiver);
383 }
384 var method = false;
385 var funinfo = this.symbols.find(name);
386 var start = 0;
387 if (!funinfo || funinfo.def instanceof setter || funinfo.type == 'toplevel') {
388 method = true;
389 } else {
390 switch(funinfo.type)
391 {
392 case 'self':
393 case 'parent':
394 var defargs = funinfo.def.args.length;
395 if (!defargs || funinfo.def.args[0].name != 'self') {
396 defargs ++
397 }
398 if (args.length < defargs) {
399 if (funinfo.type == 'self') {
400 args.splice(0, 0, new symbol('self', this.symbols));
401 } else {
402 var obj = (new symbol('self', this.symbols)).toIL() + '->header.';
403 for (var i = 0; i < funinfo.depth; ++i) {
404 obj += (i ? '->' : '') + 'parent';
405 }
406 args.splice(0, 0, ', ' + obj);
407 start = 1;
408 }
409 } else if(!(args[0] instanceof symbol) || args[0].name != 'self') {
410 funinfo = null;
411 }
412 method = true;
413 break;
414 }
415 }
416 for (var i = start; i < args.length; i++) {
417 args[i] = ', ' + (!i && method ? '(object *)(' : '') + args[i].toIL() + (!i && method ? ')' : '');
418 }
419 var callpart;
420 if (method) {
421 if (funinfo && funinfo.type == 'self' && funinfo.def.name) {
422 callpart = funinfo.def.name + '(' + (funinfo.def.symbols.parent.needsenv ? (new symbol('self', this.symbols)).toIL() + '->env' : 'NULL' );
423 } else {
424 callpart = 'mcall(' + getMethodId(name) + '/* ' + name + ' */';
425 }
426 } else {
427 var closVar = (new symbol(name, this.symbols)).toIL();
428 callpart = '((lambda *)' + closVar + ')->func(((lambda *)' + closVar + ')->env';
429 }
430 return callpart + ', ' + args.length + args.join('') + ')';
431 };
432 funcall.prototype.toILTypeName = function() {
433 switch(this.name)
434 {
435 case 'ptr:':
436 case 'ptr':
437 var receiver = this.receiver ? this.receiver : this.args[0];
438 return receiver.toILTypeName() + ' *';
439 break;
440 case 'struct:':
441 case 'struct':
442 var receiver = this.receiver ? this.receiver : this.args[0];
443 return 'struct ' + receiver.toILTypeName();
444 break;
445 case 'funptr:':
446 case 'funptr':
447 var rettype;
448 var firstArg;
449 if (this.receiver) {
450 rettype = this.receiver;
451 firstArg = 0;
452 } else {
453 rettype = this.args[0];
454 firstArg = 1;
455 }
456 var argtypes = '';
457 for (var i = firstArg; i < this.args.length; i++) {
458 if (argtypes) {
459 argtypes += ', '
460 }
461 argtypes += this.args[i].toILTypeName();
462 }
463 return [rettype.toILTypeName() + '(*', ')(' + argtypes + ')'];
464 break;
465 default:
466 throw new Error('invalid use of funcall expression where a C type name is expected');
467 }
468 };
469 funcall.prototype.toILLines = function(vars, needsreturn) {
470 var lines = [];
471 var name = this.name[this.name.length-1] == ':' ? this.name.substr(0, this.name.length-1) : this.name;
472 var args = this.args.slice(0, this.args.length);
473 if (this.receiver) {
474 args.splice(0, 0, [this.receiver]);
475 }
476 switch(name)
477 {
478 case 'if':
479 lines.push('if (' + this.args[0].toILLLExpr(vars) + ') {');
480 var blines = this.args[1].toILLines(vars, needsreturn);
481 for (var i in blines) {
482 lines.push('\t' + blines[i]);
483 }
484 if (needsreturn) {
485 lines.push('} else {');
486 lines.push('\t return module_false;');
487 lines.push('}');
488 } else {
489 lines.push('}');
490 }
491 break;
492 case 'if:else':
493 lines.push('if (' + this.args[0].toILLLExpr(vars) + ') {');
494 var blines = this.args[1].toILLines(vars, needsreturn);
495 for (var i in blines) {
496 lines.push('\t' + blines[i]);
497 }
498 lines.push('} else {');
499 blines = this.args[2].toILLines(vars, needsreturn);
500 for (var i in blines) {
501 lines.push('\t' + blines[i]);
502 }
503 lines.push('}');
504 break;
505 case 'while:do':
506 if (needsreturn) {
507 throw new Error("while:do can't be last statement in llMessage code block");
508 }
509 lines.push('while (' + this.args[0].toILLLExpr(vars) + ') {');
510 var blines = this.args[1].toILLines(vars);
511 for (var i in blines) {
512 lines.push('\t' + blines[i]);
513 }
514 lines.push('}');
515 break;
516 default:
517 lines.push( (needsreturn ? 'return (object *)' : '') + this.toILLLExpr(vars) + ';');
518 }
519 return lines;
520 };
521
522 funcall.prototype.toILLLExpr = function(vars) {
523 var name = this.name[this.name.length-1] == ':' ? this.name.substr(0, this.name.length-1) : this.name;
524 var args = this.args.slice(0, this.args.length);
525 if (this.receiver) {
526 if(this.args.length == 0) {
527 if (this.receiver instanceof symbol && this.receiver.name in vars && vars[this.receiver.name].substr(-1) != "*") {
528 return this.receiver.toILLLExpr(vars) + '.' + this.name;
529 } else {
530 return this.receiver.toILLLExpr(vars) + '->' + this.name;
531 }
532 } else if (this.args.length == 1 && name[name.length-1] == '!') {
533 if (this.receiver instanceof symbol && this.receiver.name in vars && vars[this.receiver.name].substr(-1) != "*") {
534 return this.receiver.toILLLExpr(vars) + '.' + this.name.substr(0, name.length-1) + ' = ' + args[0].toILLLExpr(vars);
535 } else {
536 return this.receiver.toILLLExpr(vars) + '->' + this.name.substr(0, name.length-1) + ' = ' + args[0].toILLLExpr(vars);
537 }
538 } else {
539 args.splice(0, 0, this.receiver);
540 }
541 }
542 switch(name)
543 {
544 case 'if':
545 return '((' + args[0].toILLLExpr(vars) + ') ? (' + args[1].toILLLExpr(vars) + ') : 0)';
546 case 'if:else':
547 return '((' + args[0].toILLLExpr(vars) + ') ? (' + args[1].toILLLExpr(vars) + ') : (' + args[2].toILLLExpr(vars) + '))';
548 case 'while:do':
549 throw new Error('while:do not allowed in expression context in llMessage block');
550 case 'addr_of':
551 return '&(' + args[0].toILLLExpr(vars) + ')';
552 case 'sizeof':
553 return 'sizeof(' + args[0].toILTypeName() + ')';
554 case 'get':
555 return args[0].toILLLExpr(vars) + '[' + args[1].toILLLExpr(vars) + ']';
556 case 'set':
557 return args[0].toILLLExpr(vars) + '[' + args[1].toILLLExpr(vars) + '] = ' + args[2].toILLLExpr(vars);
558 case 'not':
559 return '!(' + args[0].toILLLExpr(vars) + ')';
560 case 'castTo':
561 return '((' + args[1].toILTypeName() + ')(' + args[0].toILLLExpr(vars) + '))';
562 case 'mcall':
563 if (args[0] instanceof symbol) {
564 args[0] = new intlit(getMethodId(args[0].name));
565 }
566 default:
567 for (var i in args) {
568 args[i] = args[i].toILLLExpr(vars);
569 }
570 return name + '(' + args.join(', ') + ')';
571 }
572 };
573
574 function cObject(name) {
575 this.name = name;
576 this.slots = {};
577 this.properties = [];
578 this.values = [];
579 this.slotvars = {};
580 this.includes = {};
581 this.parent = 'NULL';
582 this.init = [];
583 this.initmsgadded = false;
584 }
585
586 cObject.prototype.addInclude = function(includefile) {
587 this.includes[includefile] = true;
588 }
589
590 cObject.prototype.addMessage = function(msgname, implementation) {
591 var methodid = getMethodId(msgname);
592 var trunc = methodid & 0xF;
593 if (!(trunc in this.slots)) {
594 this.slots[trunc] = [];
595 }
596 this.slots[trunc].push([methodid, '\t\t' + implementation.lines.join('\n\t\t') + '\n', msgname]);
597 if (!(trunc in this.slotvars)) {
598 this.slotvars[trunc] = {};
599 }
600 for (var varname in implementation.vars) {
601 this.slotvars[trunc][varname] = implementation.vars[varname];
602 }
603 }
604
605 cObject.prototype.addProperty = function(propname, value, type) {
606 if (type != undefined) {
607 this.properties.push([propname, type]);
608 if (value !== null) {
609 this.values.push(value);
610 }
611 } else {
612 var escaped = escapeCName(propname);
613 this.addMessage(propname, {
614 vars: {},
615 lines: [
616 'return self->' + escaped + ';'
617 ]});
618 this.addMessage(propname + '!', {
619 vars: {},
620 lines: [
621 'self->' + escaped + ' = va_arg(args, object *);',
622 'return (object *)self;'
623 ]});
624 this.properties.push(escaped);
625 this.values.push(value);
626 }
627 }
628
629 cObject.prototype.addInit = function(statement) {
630 this.init.push(statement);
631 };
632
633 cObject.prototype.addImport = function(symbols, source) {
634 this.imported.push(source);
635 var importNum = imported.length - 1;
636 if (symbols) {
637 each(symbols, function(i, sym) {
638 this.addMessage(sym.name, {
639 vars: {},
640 lines: [
641 'return self->import_' + importNum + '->meta->meth_lookup[method_id & 0xF](method_id, num_args, self->import_' + importNum + ', args);'
642 ]
643 });
644 });
645 } else {
646 //TODO: handle proxying for unimplemented methods
647 }
648 };
649
650 cObject.prototype.checkInitMsg = function() {
651 if (!this.initmsgadded && this.init.length) {
652 var init = this.init.slice(0, this.init.length);
653 init.push('return (object *)self;');
654 this.addMessage('_init', {
655 vars: {},
656 lines: init
657 });
658 this.initmsgadded = true;
659 }
660 }
661
662 cObject.prototype.populateSymbols = function() {};
663
664 cObject.prototype.toEarlyCDef = function() {
665 this.checkInitMsg();
666 var includes = '';
667 for (var file in this.includes) {
668 includes += '#include ' + file + '\n';
669 }
670 var objdef = 'typedef struct ' + this.name + ' {\n\tobject header;\n';
671 for (var i in this.properties) {
672 if (this.properties[i] instanceof Array) {
673 if (this.properties[i][1] instanceof Array) {
674 objdef += '\t' + this.properties[i][1][0] + this.properties[i][0] + this.properties[i][1][1] + ';\n';
675 } else {
676 objdef += '\t' + this.properties[i][1] + ' ' + this.properties[i][0] + ';\n';
677 }
678 } else {
679 objdef += '\tobject * ' + this.properties[i] + ';\n'
680 }
681 }
682 objdef += '} ' + this.name + ';\nobj_meta ' + this.name + '_meta;\n';
683 return includes + objdef;
684 }
685
686 cObject.prototype.toILDef = function() {
687 this.checkInitMsg();
688 var slotdefs = '';
689 var metadef = 'obj_meta ' + this.name + '_meta = {sizeof(' + this.name +'), {';
690 for (var i = 0; i < 16; i++) {
691 if (i) {
692 metadef += ', ';
693 }
694 if (i in this.slots) {
695 slotdefs += 'object * ' + this.name + '_slot_' + i + '(uint32_t method_id, uint32_t num_params, object * oself, va_list args) {\n\t' +
696 this.name + ' *self = (' + this.name + ' *)oself;\n';
697 for (var varname in this.slotvars[i]) {
698 if (this.slotvars[i][varname] instanceof Array) {
699 slotdefs += '/*foo*/\t' + this.slotvars[i][varname][0] + ' ' + varname + this.slotvars[i][varname][1] + ';\n';
700 } else {
701 slotdefs += '/*bar*/\t' + this.slotvars[i][varname] + ' ' + varname + ';\n';
702 }
703 }
704 if (this.slots[i].length == 1) {
705 slotdefs += '\tif (method_id == ' + this.slots[i][0][0] + ') { /* ' + this.slots[i][0][2] + '*/\n' +
706 '\t\t' + this.slots[i][0][1] + '\n' +
707 '\t}\n' +
708 '\treturn no_impl(method_id, num_params, (object *)self, args);\n}\n';
709 } else {
710 slotdefs += '\tswitch(method_id) {\n';
711 for (j in this.slots[i]) {
712 slotdefs += '\t\tcase ' + this.slots[i][j][0] + ': /* ' + this.slots[i][j][2] + '*/\n' +
713 '\t\t\t' + this.slots[i][j][1] + '\n';
714 }
715 slotdefs += '\t\tdefault:\n' +
716 '\t\t\treturn no_impl(method_id, num_params, (object *)self, args);\n\t}\n}\n';
717
718 }
719 metadef += this.name + '_slot_' + i;
720 } else {
721 metadef += 'no_impl';
722 }
723 }
724 metadef += '}};\n';
725 return slotdefs + metadef;
726 }
727
728 cObject.prototype.toILInstance = function() {
729 this.checkInitMsg();
730 var ret = 'make_object(&' + this.name + '_meta, ' + this.parent + ', ' + this.values.length + (this.values.length ? ', ' : '') + this.values.join(', ') + ')';
731 if (this.initmsgadded) {
732 ret = 'mcall(' + getMethodId('_init') + ', 1, ' + ret + ')'
733 }
734 return ret;
735 }
736
737 cObject.prototype.toIL = function() {
738 forwarddec += this.toEarlyCDef();
739 toplevelcode += this.toILDef();
740 return this.toILInstance();
741 }
742
743 var nextobject = 0;
744
745
746 object.prototype.toILObject = function() {
747 var messages = this.messages;
748 if (!this.name) {
749 this.name = 'object_' + nextobject++;
750 }
751 var me = new cObject(this.name);
752 this.symbols.typename = me.name;
753 if (this.symbols.needsenv) {
754 me.addProperty('env', this.symbols.envVar(), 'struct ' + this.symbols.getEnvType() + ' * ');
755 me.hasenv = true;
756 }
757 if (this.symbols.needsparent && !(this.symbols.parent instanceof osymbols)) {
758 me.parent = '(object *)(' + (new symbol('self', this.symbols.parent)).toIL() + ')';
759 }
760 for (var i in messages) {
761 if (messages[i] instanceof funcall) {
762 if (messages[i].name == 'import:' && messages[i].args.length == 1) {
763 me.addImport(false, messages[i].args[0]);
764 } else if(messages[i].name == 'import:from:' && messages[i].args.length == 2) {
765 var importsyms = [];
766 each(messages[i].args[0].val, function(i, el) {
767 if (!(el instanceof symbol)) {
768 throw new Error('Names in import:from statement must be symbols');
769 }
770 importsyms.push(el);
771 });
772 me.addImport(importsyms, messages[i].args[1]);
773 } else if(messages[i].name == 'llProperty:withType:' && messages[i].args.length == 2) {
774 me.addProperty(messages[i].args[0].name, null, messages[i].args[1].toILTypeName());
775 } else if(messages[i].name == 'llMessage:withVars:andCode:' && messages[i].args.length == 3) {
776 var msgname = messages[i].args[0].name
777 var rawvars = messages[i].args[1].expressions;
778 var vars = {};
779 for(var v in rawvars) {
780 vars[rawvars[v].symbol.name] = rawvars[v].expression.toILTypeName();
781 }
782 me.addMessage(msgname, {
783 vars: vars,
784 lines: messages[i].args[2].toILLines(vars, true)
785 });
786 } else if(messages[i].name == 'includeSystemHeader:' && messages[i].args.length == 1) {
787 me.addInclude("<" + messages[i].args[0].val + ">");
788 } else {
789
790 throw new Error('Only import and import:from calls allowed in object context. ' + messages[i].name + 'with ' + messages[i].args.length + ' arguments found instead.');
791 }
792 } else {
793 messages[i].toILObject(me);
794 }
795 }
796
797 return me;
798 };
799
800 object.prototype.toIL = function() {
801 return this.toILObject().toIL();
802 };
803
804 var toplevelcode;
805 var forwarddec;
806
807 function addBinaryOp(cobject, opname, cop, objtype)
808 {
809 cobject.addMessage(opname, {
810 vars: {ret: objtype + ' *', argb: objtype +' *'},
811 lines: [
812 'argb = va_arg(args, ' + objtype + ' *);',
813 'ret = (' + objtype + ' *)make_object(&' + objtype + '_meta, NULL, 0);',
814 'ret->num = self->num ' + cop + ' argb->num;',
815 'return &(ret->header);'
816 ]
817 });
818 }
819
820 function addCompOp(cobject, opname, cop, objtype)
821 {
822 cobject.addMessage(opname, {
823 vars: {argb: objtype + ' *'},
824 lines: [
825 'argb = va_arg(args, ' + objtype + ' *);',
826 'if (self->num ' + cop + ' argb->num) {',
827 ' return ' + toplevel.moduleVar('true') + ';',
828 '}',
829 'return ' + toplevel.moduleVar('false') + ';',
830 ]
831 });
832 }
833
834 function makeInt(bits, unsigned)
835 {
836 var typename = 'obj_' + (unsigned ? 'u' : '') + 'int' + bits;
837 var intObj = new cObject(typename);
838 intObj.addProperty('num', null, (unsigned ? 'u' : '') + 'int' + bits +'_t');
839 addBinaryOp(intObj, 'ADD_', '+', typename);
840 addBinaryOp(intObj, 'SUB_', '-', typename);
841 addBinaryOp(intObj, 'MUL_', '*', typename);
842 addBinaryOp(intObj, 'DIV_', '/', typename);
843 addBinaryOp(intObj, 'MOD_', '%', typename);
844 addBinaryOp(intObj, 'or', '|', typename);
845 addBinaryOp(intObj, 'xor', '^', typename);
846 addBinaryOp(intObj, 'and', '&', typename);
847 addBinaryOp(intObj, 'lshift:by', '<<', typename);
848 addBinaryOp(intObj, 'rshift:by', '>>', typename);
849 addCompOp(intObj, 'LT_', '<', typename);
850 addCompOp(intObj, 'GT_', '>', typename);
851 addCompOp(intObj, 'EQ_', '==', typename);
852 addCompOp(intObj, 'NEQ_', '!=', typename);
853 addCompOp(intObj, 'GEQ_', '>=', typename);
854 addCompOp(intObj, 'LEQ_', '<=', typename);
855 intObj.addInclude('<string.h>');
856 //-9223372036854775808
857 //01234567890123456789
858 intObj.addMessage('string', {
859 vars: {str: 'string *'},
860 lines: [
861 'str = (string *)make_object(&string_meta, NULL, 0);',
862 'str->data = GC_MALLOC(' + (bits == 64 ? 21 : 12) + ');',
863 'sprintf(str->data, "%' + (bits == 64 ? 'l' : '') + (unsigned ? 'u' : 'd') + '", self->num);',
864 'str->len = str->bytes = strlen(str->data);',
865 'return &(str->header);'
866 ]
867 });
868 //7FFFFFFFFFFFFFFF
869 //01234567890123456789
870 intObj.addMessage('hex', {
871 vars: {str: 'string *'},
872 lines: [
873 'str = (string *)make_object(&string_meta, NULL, 0);',
874 'str->data = GC_MALLOC(' + (bits == 64 ? 17 : 9) + ');',
875 'sprintf(str->data, "%' + (bits == 64 ? 'l' : '') +'X", self->num);',
876 'str->len = str->bytes = strlen(str->data);',
877 'return &(str->header);'
878 ]
879 });
880 intObj.addMessage('isInteger?', {
881 vars: {},
882 lines: [
883 'return ' + toplevel.moduleVar('true') + ';'
884 ]
885 });
886 intObj.addMessage('hash', {
887 vars: {},
888 lines: [
889 'return &(self->header);'
890 ]
891 });
892 intObj.addMessage('signed?', {
893 vars: {},
894 lines: [
895 'return ' + toplevel.moduleVar(unsigned ? 'false' : 'true') + ';'
896 ]
897 });
898 var sizes = [8, 16, 32, 64];
899 var destunsigned = [false, true];
900 for (var i = 0; i < sizes.length; i++) {
901 size = sizes[i];
902 for (var j = 0; j < destunsigned.length; j++) {
903 uns = destunsigned[j];
904 if (uns == unsigned && size == bits) {
905 intObj.addMessage((uns ? 'u' : '') + 'int' + size, {
906 vars: {},
907 lines: [
908 'return &(self->header);'
909 ]
910 });
911 } else {
912 var retType = 'obj_' + (uns ? 'u' : '') + 'int' + size;
913 var retName = 'ret' + (uns ? 'u' : '') + size;
914 var vars = {};
915 vars[retName] = retType + ' *';
916 intObj.addMessage((uns ? 'u' : '') + 'int' + size, {
917
918 vars: vars,
919 lines: [
920 retName + ' = ('+retType+' *)make_object(&' + retType +'_meta, NULL, 0);',
921 retName + '->num = self->num;',
922 'return &(' + retName + '->header);'
923 ]
924 });
925 }
926 }
927 }
928
929 return intObj;
930 }
931
932 function makeArray()
933 {
934 var arrayfile = toplevel.names['array'];
935 var ast = parseFile(arrayfile.path + '/' + arrayfile.file);
936 ast.name = 'array';
937 ast.populateSymbols(toplevel);
938 return ast.toILObject();
939 }
940
941 function makeString()
942 {
943 var arrayfile = toplevel.names['string'];
944 var ast = parseFile(arrayfile.path + '/' + arrayfile.file);
945 ast.name = 'string';
946 ast.populateSymbols(toplevel);
947 return ast.toILObject();
948 }
949
950 function makelambda()
951 {
952 var clos = new cObject('lambda');
953 clos.addProperty('env', null, 'void *');
954 clos.addProperty('func', null, 'closure_func');
955 clos.addMessage('while:do', {
956 vars: {action: 'lambda *', ret: 'object *'},
957 lines: [
958 'action = va_arg(args, lambda *);',
959 'ret = ' + toplevel.moduleVar('true') + ';',
960 'while(' + toplevel.moduleVar('true') + ' == ccall(self, 0)) {',
961 ' ccall(action, 0);',
962 '}',
963 'return ret;'
964 ]
965 });
966 return clos;
967 }
968
969 function builtinTypes()
970 {
971 return [makeInt(64, false), makeInt(32, false), makeInt(16, false), makeInt(8, false),
972 makeInt(64, true) , makeInt(32, true), makeInt(16, true), makeInt(8, true),
973 makeArray(), makeString(), makelambda()];
974 }
975
976 function addBuiltinModules(toplevel)
977 {
978 var os = new cObject('mod_obj_os');
979 os.addInclude('<sys/stat.h>');
980 os.addInclude('<fcntl.h>');
981 os.addInclude('<stdlib.h>');
982 os.addInclude('<time.h>');
983 os.addInclude('<unistd.h>');
984 os.addMessage('write', {
985 vars: {str: 'string *', intret: 'obj_int32 *', filedes: 'obj_int32 *'},
986 lines: [
987 'filedes = va_arg(args, obj_int32 *);',
988 'str = va_arg(args, string *);',
989 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);',
990 'intret->num = write(filedes->num, str->data, str->bytes);',
991 'return &(intret->header);'
992 ]
993 });
994 os.addMessage('read', {
995 vars: {str: 'string *', size: 'obj_int32 *', filedes: 'obj_int32 *'},
996 lines: [
997 'filedes = va_arg(args, obj_int32 *);',
998 'size = va_arg(args, obj_int32 *);',
999 'str = (string *)make_object(&string_meta, NULL, 0);',
1000 'str->data = GC_MALLOC_ATOMIC(size->num + 1);',
1001 'str->len = str->bytes = read(filedes->num, str->data, size->num);',
1002 'if (str->bytes < 0) { str->bytes = str->len = 0; }',
1003 'str->data[str->bytes] = 0;',
1004 'return &(str->header);'
1005 ]
1006 });
1007 os.addMessage('open', {
1008 vars: {str: 'string *', flags: 'obj_int32 *', filedes: 'obj_int32 *', perm: 'obj_int32 *'},
1009 lines: [
1010 'str = va_arg(args, string *);',
1011 'flags = va_arg(args, obj_int32 *);',
1012 'filedes = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);',
1013 'if (num_params == 3) {',
1014 ' filedes->num = open(str->data, flags->num);',
1015 '} else if (num_params == 4) {',
1016 ' perm = va_arg(args, obj_int32 *);',
1017 ' filedes->num = open(str->data, flags->num, perm->num);',
1018 '} else {',
1019 ' filedes->num = -1;',
1020 '}',
1021 'return &(filedes->header);'
1022 ]
1023 });
1024 os.addMessage('close', {
1025 vars: {filedes: 'obj_int32 *', intret: 'obj_int32 *'},
1026 lines: [
1027 'filedes = va_arg(args, obj_int32 *);',
1028 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);',
1029 'intret->num = close(filedes->num);',
1030 'return &(intret->header);'
1031 ]
1032 });
1033 os.addMessage('O_RDONLY', {
1034 vars: {intret: 'obj_int32 *'},
1035 lines: [
1036 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);',
1037 'intret->num = O_RDONLY;',
1038 'return &(intret->header);'
1039 ]
1040 });
1041 os.addMessage('O_WRONLY', {
1042 vars: {intret: 'obj_int32 *'},
1043 lines: [
1044 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);',
1045 'intret->num = O_WRONLY;',
1046 'return &(intret->header);'
1047 ]
1048 });
1049 os.addMessage('O_RDWR', {
1050 vars: {intret: 'obj_int32 *'},
1051 lines: [
1052 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);',
1053 'intret->num = O_RDWR;',
1054 'return &(intret->header);'
1055 ]
1056 });
1057 os.addMessage('O_CREAT', {
1058 vars: {intret: 'obj_int32 *'},
1059 lines: [
1060 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);',
1061 'intret->num = O_CREAT;',
1062 'return &(intret->header);'
1063 ]
1064 });
1065 os.addMessage('O_APPEND', {
1066 vars: {intret: 'obj_int32 *'},
1067 lines: [
1068 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);',
1069 'intret->num = O_APPEND;',
1070 'return &(intret->header);'
1071 ]
1072 });
1073 os.addMessage('O_TRUNC', {
1074 vars: {intret: 'obj_int32 *'},
1075 lines: [
1076 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);',
1077 'intret->num = O_TRUNC;',
1078 'return &(intret->header);'
1079 ]
1080 });
1081 os.addMessage('rand', {
1082 vars: {intret: 'obj_int32 *'},
1083 lines: [
1084 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);',
1085 'intret->num = rand();',
1086 'return &(intret->header);'
1087 ]
1088 });
1089 os.addMessage('rand64', {
1090 vars: {intret64: 'obj_int64 *'},
1091 lines: [
1092 'intret64 = (obj_int64 *)make_object(&obj_int64_meta, NULL, 0);',
1093 'intret64->num = (((int64_t)rand()) << 32 ) | rand();',
1094 'return &(intret64->header);'
1095 ]
1096 });
1097 os.addMessage('srand', {
1098 vars: {oseed: 'object *', seed: 'obj_int32 *'},
1099 lines: [
1100 'oseed = va_arg(args, object *);',
1101 'seed = mcall(' + getMethodId("int32") + ', 1, oseed);',
1102 'srand(seed->num);',
1103 'return &(seed->header);'
1104 ]
1105 });
1106 os.addMessage('time', {
1107 vars: {intret64: 'obj_int64 *'},
1108 lines: [
1109 'intret64 = (obj_int64 *)make_object(&obj_int64_meta, NULL, 0);',
1110 'intret64->num = time(NULL);',
1111 'return &(intret64->header);'
1112 ]
1113 });
1114 os.addMessage('sleep', {
1115 vars: {osecs: 'object *', secs: 'obj_int32 *', intret: 'obj_int32 *'},
1116 lines: [
1117 'osecs = va_arg(args, object *);',
1118 'secs = mcall(' + getMethodId("int32") + ', 1, osecs);',
1119 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);',
1120 'intret->num = sleep(secs->num);',
1121 'return &(intret->header);'
1122 ]
1123 });
1124 toplevel.names['os'] = os;
1125 }
1126
1127 modulefile.prototype.toIL = function(){
1128 return this.ast.toILModuleInstance();
1129 };
1130
1131 function processUsedToplevel(toplevel)
1132 {
1133 var alwaysused = ['true', 'false', 'list'];
1134 var ret = '';
1135 var modulenum = 0;
1136 var visited = {};
1137 for (var i in alwaysused) {
1138 toplevel.used[alwaysused[i]] = true;
1139 }
1140 var newused = Object.keys(toplevel.used);
1141 var allused = newused;
1142 while (newused.length) {
1143 for (var i in newused) {
1144 debugprint('//---module', newused[i], '--- populate symbols');
1145 forwarddec += 'object * ' + toplevel.moduleVar(newused[i]) + ';\n';
1146 toplevel.names[newused[i]].populateSymbols(toplevel);
1147 visited[newused[i]] = true;
1148 }
1149 newused = [];
1150 for (var symbol in toplevel.used) {
1151 if (!(symbol in visited)) {
1152 debugprint('//found new usage of module', symbol);
1153 newused.push(symbol);
1154 allused.push(symbol);
1155 }
1156 }
1157 }
1158
1159 for (var i = allused.length-1; i >= 0; i--) {
1160 var symbol = allused[i];
1161 debugprint('//---module', symbol, '(' + i +')--- compile');
1162 assignNames.push(symbol);
1163 ret += '\t' + toplevel.moduleVar(symbol) + ' = ' + toplevel.names[symbol].toIL() + ';\n';
1164 assignNames.pop();
1165 }
1166 return ret;
1167 }
1168
1169 function makeCProg(obj)
1170 {
1171 forwarddec = toplevelcode = '';
1172 assignNames = [];
1173 var builtins = builtinTypes();
1174 for (var i in builtins) {
1175 forwarddec += builtins[i].toEarlyCDef();
1176 toplevelcode += builtins[i].toILDef();
1177 }
1178 addBuiltinModules(toplevel);
1179 debugprint('//------POPULATING SYMBOLS-----');
1180 obj.populateSymbols(toplevel);
1181 var moduleinit = processUsedToplevel(toplevel);
1182 debugprint('//------COMPILING AST-----');
1183 var rest = 'object * mainModule() {\n' + moduleinit + '\tmain_module = ' + obj.toILModuleInstance() + ';\n\treturn main_module;\n}\n';
1184 var mnames = 'char * methodNames[] = {\n';
1185 for (var i = 0; i < methodNames.length; i++) {
1186 mnames += '\t"' + methodNames[i].replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n") + '",\n';
1187 }
1188 mnames += '};\n';
1189 return '#include "runtime/proghead.inc"\n' +
1190 '#define METHOD_ID_MAIN ' + getMethodId('main') + '\n' +
1191 '#define METHOD_ID_EMPTY ' + getMethodId('empty') + '\n' +
1192 '#define METHOD_ID_CONS ' + getMethodId(getOpMethodName('|')) + '\n' +
1193 mnames + forwarddec + toplevelcode + rest + '#include "runtime/progfoot.inc"\n';
1194 }
1195
1196 object.prototype.toILModule = function() {
1197 return makeCProg(this);
1198 }
1199
1200 object.prototype.toILModuleInstance = function() {
1201 return this.toIL();
1202 }
1203
1204 lambda.prototype.toIL = function(parentcode) {
1205 var code = new funCode(this.name);
1206 var args = this.args ? this.args.slice(0, this.args.length) : [];
1207 var exprs = this.expressions;
1208 var assignPath = assignNames.join('<-');
1209 debugprint('//', this.name, assignPath);
1210 var addedTypeDef = false;
1211 if (Object.keys(this.symbols.closedover).length) {
1212 this.symbols.envtype = this.name + '_env';
1213 forwarddec += 'typedef struct ' + this.symbols.envtype + ' ' + this.symbols.envtype + ';\n'
1214 var addedTypeDef = true;
1215 }
1216 if (this.selftype) {
1217 this.symbols.defineVar('self', this.selftype);
1218 if (args[0] && args[0].cleanName() == 'self') {
1219 args.splice(0, 1);
1220 }
1221 var offset = 1;
1222 } else {
1223 var offset = 0;
1224 }
1225 for (var i = 0; i < args.length; ++i) {
1226 var argname = args[i].toIL(code);
1227
1228 args[i] = (argname.indexOf('->') < 0 ? '\tobject * ' : '\t') + argname + ' = va_arg(args, object *);\n';
1229 }
1230 var compiled = []
1231 for (var i in exprs) {
1232 var js = exprs[i].toIL(code);
1233 if (js) {
1234 compiled.push(indent(js));
1235 }
1236 }
1237 if (compiled.length) {
1238 if (exprs[exprs.length - 1] instanceof assignment) {
1239 compiled.push('return ' + exprs[exprs.length - 1].symbol.toIL() + ';');
1240 } else {
1241 compiled[compiled.length-1] = 'return (object *)(' + compiled[compiled.length-1] + ');';
1242 }
1243 }
1244 exprs = compiled;
1245
1246 if (Object.keys(this.symbols.closedover).length) {
1247 if (!addedTypeDef) {
1248 for (var key in this.symbols.closedover) {
1249 print(key, ": ", this.symbols.closedover[key]);
1250 }
1251 throw new Error('this.symbols.closedover is not empty, but it was when compilation of ' + this.name + ' "' + assignPath + '" started');
1252 }
1253 forwarddec += 'struct ' + this.name + '_env {\n';
1254 if (this.symbols.needsParentEnv) {
1255 forwarddec += '\tstruct ' + this.symbols.parentEnvType() + ' * parent;\n';
1256 }
1257 for (var varname in this.symbols.closedover) {
1258 if (varname == 'self' && this.selftype) {
1259 forwarddec += '\tstruct ' + this.selftype + ' * self;\n';
1260 } else {
1261 forwarddec += '\tobject * ' + escapeCName(varname) + ';\n';
1262 }
1263 }
1264 forwarddec += '};\n'
1265
1266 var myenvinit = '\t' + this.name + '_env * myenv = GC_MALLOC(sizeof(' + this.name + '_env));\n';
1267 if (this.symbols.needsParentEnv) {
1268 myenvinit += '\tmyenv->parent = env;\n';
1269 }
1270 this.symbols.envtype = this.name + '_env';
1271 } else {
1272 var myenvinit = '';
1273 }
1274 forwarddec += 'object *' + this.name + ' (' + this.symbols.parentEnvType() + ' * env, uint32_t num_args, ...);\n';
1275
1276 toplevelcode += '//' + assignPath + "\n";
1277 toplevelcode += 'object * ' + this.name + ' ( ' + this.symbols.parentEnvType() + ' * env, uint32_t num_args, ...) {\n\tva_list args;\n' + myenvinit + '\tva_start(args, num_args);\n';
1278 if (this.selftype) {
1279 var selfvar = (new symbol('self', this.symbols)).toIL();
1280 if (selfvar == 'self') {
1281 toplevelcode += '\t' + this.selftype + ' * self = va_arg(args, ' + this.selftype + ' *);\n';
1282 } else {
1283 toplevelcode += '\t' + selfvar + ' = va_arg(args, ' + this.selftype + ' *);\n';
1284 }
1285
1286 }
1287 toplevelcode += args.join('') + '\tva_end(args);\n' + exprs.join(';\n\t') + '\n}\n';
1288
1289 if (this.selftype) {
1290 return this.name;
1291 } else {
1292 if (this.symbols.parentEnvType() != 'void') {
1293 if (this.symbols.parent.passthruenv) {
1294 var envvar = 'env';
1295 } else {
1296 var envvar = 'myenv';
1297 }
1298 debugprint('//' + this.name, 'has envvar:', envvar, 'num vars closed over:', Object.keys(this.symbols.closedover).length);
1299 return 'make_lambda(' + envvar + ', (closure_func)' + this.name + ')';
1300 } else {
1301 toplevelcode += 'lambda ' + this.name + '_obj = {{&lambda_meta, NULL}, NULL, ' + this.name + '};\n';
1302 return '((object *)&' + this.name + '_obj)';
1303 }
1304 }
1305 };
1306 lambda.prototype.toILModuleInstance = function() {
1307 this.toIL();
1308 return this.name + '(NULL, 0)';
1309 };
1310 lambda.prototype.toILObject = function(typename) {
1311 this.selftype = typename;
1312 return this.toIL();
1313 };
1314 lambda.prototype.toILModule = function() {
1315 return makeCProg(this);
1316 };
1317 lambda.prototype.toILLines = function(vars, needsreturn) {
1318 var lines = [];
1319 for (var i in this.args) {
1320 var name = this.args[i].name;
1321 if (name[0] == ':') {
1322 name = name.substr(1);
1323 }
1324 if(name != 'self') {
1325 lines.push(name + ' = va_arg(args, ' + vars[name] + ');');
1326 }
1327 }
1328 for (var i in this.expressions) {
1329 var exprlines = this.expressions[i].toILLines(vars, needsreturn && i == this.expressions.length - 1);
1330 for (var j in exprlines) {
1331 lines.push('\t' + exprlines[j]);
1332 }
1333 }
1334 return lines;
1335 }
1336 lambda.prototype.toILLLExpr = function(vars) {
1337 if (this.expressions.length != 1) {
1338 throw new Error('lambda in expression context must have a single statement in llMessage block');
1339 }
1340 return this.expressions[0].toILLLExpr(vars);
1341 }
1342
1343 assignment.prototype.toIL = function() {
1344 debugprint('//assignment', this.symbol.name);
1345 var existing = this.symbols.find(this.symbol.name);
1346 var prefix = '';
1347 assignNames.push(this.symbol.name);
1348 var val = this.expression.toIL();
1349 assignNames.pop(this.symbol.name);
1350 if (val === null) {
1351 return null;
1352 }
1353 if (existing.type == 'local' && !existing.isdeclared) {
1354 prefix = 'object *';
1355 this.symbols.declareVar(this.symbol.name);
1356 debugprint('//declared var', this.symbol.name);
1357 }
1358 return prefix + this.symbol.toIL() + ' = ' + val;
1359 };
1360 assignment.prototype.toILObject = function(cobj) {
1361 debugprint('//message definition', this.symbol.name);
1362 assignNames.push('#' + this.symbol.name);
1363 if (this.expression.toILObject) {
1364 var val = this.expression.toILObject(cobj.name);
1365 } else {
1366 var val = this.expression.toIL();
1367 }
1368 assignNames.pop();
1369 if (val === null) {
1370 return;
1371 }
1372 if (this.expression instanceof lambda) {
1373 var params = ['((object *)self)'];
1374 var paramget = '';
1375 var messagevars = {};
1376 for (var i in this.expression.args) {
1377 var escaped = escapeCName(this.expression.args[i].cleanName());
1378 if (escaped != 'self') {
1379 messagevars[escaped] = 'object *';
1380 params.push(escaped);
1381 paramget += escaped + ' = va_arg(args, object *); ';
1382 }
1383 }
1384 cobj.addMessage(getOpMethodName(this.symbol.name), {
1385 vars: messagevars,
1386 lines: [paramget + 'return ' + val + '(' + (cobj.hasenv ? 'self->env' : 'NULL') + ', ' + params.length + (params.length ? ', ' : '') + params.join(', ') + ');']
1387 });
1388 } else {
1389 cobj.addProperty(this.symbol.name, val);
1390 if (this.expression instanceof object && this.expression.symbols.needsparent) {
1391 cobj.addInit('self->' + escapeCName(this.symbol.name) + '->parent = (object *)self;');
1392 }
1393 }
1394 };
1395 assignment.prototype.toILLines = function(vars, needsreturn) {
1396 return [(needsreturn ? 'return ' : '') + this.symbol.toILLLExpr(vars) + ' = ' + this.expression.toILLLExpr(vars) + ';']
1397 };