Mercurial > repos > tabletprog
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 }; |