Mercurial > repos > tabletprog
comparison cbackend.js @ 126:a2d2d8e09291
Merge
author | Mike Pavone <pavone@retrodev.com> |
---|---|
date | Mon, 05 Aug 2013 23:37:17 -0700 |
parents | 9820ecd4eed4 |
children | 34e0befbdd77 bf8f75b69048 |
comparison
equal
deleted
inserted
replaced
125:6f8d868e8da0 | 126:a2d2d8e09291 |
---|---|
5 var methodIds = {}; | 5 var methodIds = {}; |
6 function getMethodId(methodName) | 6 function getMethodId(methodName) |
7 { | 7 { |
8 if (!(methodName in methodIds)) { | 8 if (!(methodName in methodIds)) { |
9 methodIds[methodName] = nextmethodId++; | 9 methodIds[methodName] = nextmethodId++; |
10 | 10 |
11 } | 11 } |
12 return methodIds[methodName]; | 12 return methodIds[methodName]; |
13 } | 13 } |
14 | 14 |
15 function importSym(obj, src, key) | 15 function getOpMethodName(opname) |
16 { | 16 { |
17 if(!(key in src)) { | 17 var optoMeth = {'+': 'ADD_', '-': 'SUB_', '*': 'MUL_', '/': 'DIV_', '%': 'MOD_', '=': 'EQ_', '!=': 'NEQ_', '<': 'LT_', '>': 'GT_', '>=': 'GEQ_', '<=': 'LEQ_', '.': 'CAT_', '&&':'if', '||':'ifnot'}; |
18 throw new Error(key +' not found in source object for import'); | 18 if (opname in optoMeth) { |
19 } | 19 return optoMeth[opname]; |
20 if(key in obj) { | |
21 throw new Error(key +' already exists in target object for import') | |
22 } | |
23 obj[key] = src[key]; | |
24 } | |
25 | |
26 function doImport(obj, src, symlist) | |
27 { | |
28 if (symlist === undefined) { | |
29 each(src, function(key,val) { | |
30 if (key != 'parent') { | |
31 importSym(obj, src, key); | |
32 } | |
33 }); | |
34 } else { | 20 } else { |
35 for (var i = 0; i < symlist.length; ++i) { | 21 return opname; |
36 importSym(obj, src, symlist[i]); | 22 } |
37 } | |
38 } | |
39 return obj; | |
40 } | 23 } |
41 | 24 |
42 op.prototype.toC = function(isReceiver) { | 25 op.prototype.toC = function(isReceiver) { |
43 var optoMeth = {'+': 'ADD_', '-': 'SUB_', '*': 'MUL_', '/': 'DIV_', '%': 'MOD_', '=': 'EQ_', '!=': 'NEQ_', '<': 'LT_', '>': 'GT_', '>=': 'GEQ_', '<=': 'LEQ_', '.': 'CAT_'}; | 26 var method = getOpMethodName(this.op); |
44 var method = optoMeth[this.op]; | 27 return 'mcall(' + getMethodId(method) + '/* operator ' + method + ' */, 2, (object *)' + this.left.toC() + ', ' + this.right.toC() + ')\n'; |
45 return 'mcall(' + getMethodId(method) + '/* ' + method + ' */, 2, (object *)' + this.left.toC() + ', ' + this.right.toC() + ')\n'; | 28 }; |
46 }; | 29 op.prototype.toCLLExpr = function(vars) { |
30 var opmap = {'=': '==', 'xor': '^'}; | |
31 return this.left.toCLLExpr(vars) + (this.op in opmap ? opmap[this.op] : this.op) + this.right.toCLLExpr(vars); | |
32 }; | |
33 op.prototype.toCLines = function(vars, needsreturn) { | |
34 return [ (needsreturn ? 'return (object *)' : '' ) + this.toCLLExpr(vars) + ';']; | |
35 }; | |
36 | |
47 | 37 |
48 function escapeCName(name) | 38 function escapeCName(name) |
49 { | 39 { |
50 if (name == 'self') { | 40 if (name == 'self') { |
51 return name; | 41 return name; |
58 function getSymbolPrefix(info, symbols) | 48 function getSymbolPrefix(info, symbols) |
59 { | 49 { |
60 var pre = ''; | 50 var pre = ''; |
61 switch(info.type) { | 51 switch(info.type) { |
62 case 'self': | 52 case 'self': |
63 | 53 |
64 pre = (new symbol('self', symbols)).toC() + '->'; | 54 pre = (new symbol('self', symbols)).toC() + '->'; |
65 break; | 55 break; |
66 case 'parent': | 56 case 'parent': |
67 pre = (new symbol('self', symbols)).toC() + '->header.'; | 57 pre = (new symbol('self', symbols)).toC() + '->header.'; |
68 for (var i = 0; i < info.depth; ++i) { | 58 for (var i = 0; i < info.depth; ++i) { |
106 } | 96 } |
107 return 'mcall(' + getMethodId(name) + '/* ' + name + ' */, 1, ' + obj + ')'; | 97 return 'mcall(' + getMethodId(name) + '/* ' + name + ' */, 1, ' + obj + ')'; |
108 } | 98 } |
109 return getSymbolPrefix(info, this.symbols) + escapeCName(name); | 99 return getSymbolPrefix(info, this.symbols) + escapeCName(name); |
110 } | 100 } |
101 symbol.prototype.toCTypeName = function() { | |
102 return this.cleanName(); | |
103 }; | |
104 symbol.prototype.toCLLExpr = function(vars) { | |
105 var name = this.cleanName(); | |
106 if (name in vars) { | |
107 return name; | |
108 } | |
109 if (name == 'self') { | |
110 return 'self'; | |
111 } | |
112 var info = this.symbols.find(name, false, true); | |
113 var symbols = this.symbols; | |
114 while (info && info.type == 'local') { | |
115 symbols = symbols.parent; | |
116 info = symbols.find(name, false, true); | |
117 } | |
118 if (!info) { | |
119 return name; | |
120 } | |
121 if (info.type == 'toplevel') { | |
122 return toplevel.moduleVar(name); | |
123 } else if (info.type == 'self') { | |
124 if (info.isll || !(info.def instanceof lambda)) { | |
125 return 'self->' + name; | |
126 } else { | |
127 return 'mcall(' + getMethodId(name) + '/* ' + name + ' */, 1, self)'; | |
128 } | |
129 } | |
130 throw new Error('Unsupported reference type ' + info.type + ' for variable ' + name); | |
131 }; | |
132 symbol.prototype.toCLines = function(vars, needsreturn) { | |
133 return [ (needsreturn ? 'return (object *)' : '' ) + this.toCLLExpr(vars) + ';' ]; | |
134 }; | |
111 | 135 |
112 var declaredInts = {}; | 136 var declaredInts = {}; |
113 | 137 |
114 intlit.prototype.toC = function() { | 138 intlit.prototype.toC = function() { |
115 var str = this.val < 0 ? 'neg_' + (0-this.val).toString() : this.val.toString(); | 139 var str = this.val < 0 ? 'neg_' + (0-this.val).toString() : this.val.toString(); |
117 toplevelcode += 'obj_int32 int32_' + str + ' = {{&obj_int32_meta, NULL}, ' + this.val.toString() + '};\n'; | 141 toplevelcode += 'obj_int32 int32_' + str + ' = {{&obj_int32_meta, NULL}, ' + this.val.toString() + '};\n'; |
118 declaredInts[this.val] = true; | 142 declaredInts[this.val] = true; |
119 } | 143 } |
120 return '((object *)&int32_' + str + ')'; | 144 return '((object *)&int32_' + str + ')'; |
121 } | 145 } |
146 intlit.prototype.toCLLExpr = function(vars) { | |
147 return this.val.toString(); | |
148 }; | |
149 intlit.prototype.toCLines = function(vars, needsreturn) { | |
150 return [ (needsreturn ? 'return (object *)' : '' ) + this.toCLLExpr(vars) + ';' ]; | |
151 }; | |
122 | 152 |
123 floatlit.prototype.toC = function() { | 153 floatlit.prototype.toC = function() { |
124 return 'make_float(' + this.val.toString() + ')'; | 154 return 'make_float(' + this.val.toString() + ')'; |
125 } | 155 } |
156 floatlit.prototype.toCLLExpr = function(vars) { | |
157 return this.val.toString(); | |
158 }; | |
159 floatlit.prototype.toCLines = function(vars, needsreturn) { | |
160 return [ (needsreturn ? 'return (object *)' : '' ) + this.toCLLExpr(vars) + ';' ]; | |
161 }; | |
126 | 162 |
127 var declaredStrings = {}; | 163 var declaredStrings = {}; |
128 var nextStringId = 0; | 164 var nextStringId = 0; |
129 | 165 |
130 strlit.prototype.toC = function() { | 166 strlit.prototype.toC = function() { |
132 //TODO: get the proper byte length | 168 //TODO: get the proper byte length |
133 toplevelcode += 'string str_' + nextStringId + ' = {{&string_meta, NULL}, ' + this.val.length + ', ' + this.val.length + ', "' + this.val.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n').replace('\r', '\\r') + '"};\n'; | 169 toplevelcode += 'string str_' + nextStringId + ' = {{&string_meta, NULL}, ' + this.val.length + ', ' + this.val.length + ', "' + this.val.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n').replace('\r', '\\r') + '"};\n'; |
134 declaredStrings[this.val] = nextStringId++; | 170 declaredStrings[this.val] = nextStringId++; |
135 } | 171 } |
136 return '((object *)&str_' + declaredStrings[this.val] + ')'; | 172 return '((object *)&str_' + declaredStrings[this.val] + ')'; |
137 } | 173 }; |
174 strlit.prototype.toCLLExpr = function(vars) { | |
175 return '"' + this.val.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n').replace('\r', '\\r') + '"'; | |
176 }; | |
177 strlit.prototype.toCLines = function(vars, needsreturn) { | |
178 return [ (needsreturn ? 'return (object *)' : '' ) + this.toCLLExpr(vars) +';' ]; | |
179 }; | |
138 | 180 |
139 listlit.prototype.toC = function() { | 181 listlit.prototype.toC = function() { |
140 var ret = 'make_list(' + this.val.length; | 182 var ret = 'make_list(' + this.val.length; |
141 for (var i = 0; i < this.val.length; i++) { | 183 for (var i = 0; i < this.val.length; i++) { |
142 ret += ', ' + this.val[i].toC(); | 184 ret += ', ' + this.val[i].toC(); |
160 } else if(this.args[0] instanceof symbol) { | 202 } else if(this.args[0] instanceof symbol) { |
161 return this.args[0].name; | 203 return this.args[0].name; |
162 } else { | 204 } else { |
163 throw new Error("Unexpected AST type for foreign:"); | 205 throw new Error("Unexpected AST type for foreign:"); |
164 } | 206 } |
207 } else if(name == 'llProperty:withType' || name == 'llProperty:withVars:andCode') { | |
208 return null; | |
165 } | 209 } |
166 var args = this.args.slice(0, this.args.length); | 210 var args = this.args.slice(0, this.args.length); |
167 if (this.receiver) { | 211 if (this.receiver) { |
168 args.splice(0, 0, this.receiver); | 212 args.splice(0, 0, this.receiver); |
169 } | 213 } |
170 var method = false; | 214 var method = false; |
171 var funinfo = this.symbols.find(name); | 215 var funinfo = this.symbols.find(name); |
172 var start = 0; | 216 var start = 0; |
173 if (!funinfo || funinfo.def instanceof setter) { | 217 if (!funinfo || funinfo.def instanceof setter || funinfo.type == 'toplevel') { |
174 method = true; | 218 method = true; |
175 } else { | 219 } else { |
176 switch(funinfo.type) | 220 switch(funinfo.type) |
177 { | 221 { |
178 case 'self': | 222 case 'self': |
200 for (var i = start; i < args.length; i++) { | 244 for (var i = start; i < args.length; i++) { |
201 args[i] = ', ' + (!i && method ? '(object *)(' : '') + args[i].toC() + (!i && method ? ')' : ''); | 245 args[i] = ', ' + (!i && method ? '(object *)(' : '') + args[i].toC() + (!i && method ? ')' : ''); |
202 } | 246 } |
203 var callpart; | 247 var callpart; |
204 if (method) { | 248 if (method) { |
205 callpart = 'mcall(' + getMethodId(name) + '/* ' + name + ' */'; | 249 if (funinfo && funinfo.type == 'self' && funinfo.def.name) { |
250 callpart = funinfo.def.name + '(' + (funinfo.def.symbols.parent.needsenv ? (new symbol('self', this.symbols)).toC() + '->env' : 'NULL' ); | |
251 } else { | |
252 callpart = 'mcall(' + getMethodId(name) + '/* ' + name + ' */'; | |
253 } | |
206 } else { | 254 } else { |
207 callpart = 'ccall(' + (new symbol(name, this.symbols)).toC(); | 255 callpart = 'ccall(' + (new symbol(name, this.symbols)).toC(); |
208 } | 256 } |
209 return callpart + ', ' + args.length + args.join('') + ')'; | 257 return callpart + ', ' + args.length + args.join('') + ')'; |
210 } | 258 }; |
259 funcall.prototype.toCTypeName = function() { | |
260 switch(this.name) | |
261 { | |
262 case 'ptr:': | |
263 case 'ptr': | |
264 var receiver = this.receiver ? this.receiver : this.args[0]; | |
265 return receiver.toCTypeName() + ' *'; | |
266 break; | |
267 default: | |
268 throw new Error('invalid use of funcall expression where a C type name is expected'); | |
269 } | |
270 }; | |
271 funcall.prototype.toCLines = function(vars, needsreturn) { | |
272 var lines = []; | |
273 var name = this.name[this.name.length-1] == ':' ? this.name.substr(0, this.name.length-1) : this.name; | |
274 var args = this.args.slice(0, this.args.length); | |
275 if (this.receiver) { | |
276 args.splice(0, 0, [this.receiver]); | |
277 } | |
278 switch(name) | |
279 { | |
280 case 'if': | |
281 lines.push('if (' + this.args[0].toCLLExpr(vars) + ') {'); | |
282 var blines = this.args[1].toCLines(vars, needsreturn); | |
283 for (var i in blines) { | |
284 lines.push('\t' + blines[i]); | |
285 } | |
286 if (needsreturn) { | |
287 lines.push('} else {'); | |
288 lines.push('\t return module_false;'); | |
289 lines.push('}'); | |
290 } else { | |
291 lines.push('}'); | |
292 } | |
293 break; | |
294 case 'if:else': | |
295 lines.push('if (' + this.args[0].toCLLExpr(vars) + ') {'); | |
296 var blines = this.args[1].toCLines(vars, needsreturn); | |
297 for (var i in blines) { | |
298 lines.push('\t' + blines[i]); | |
299 } | |
300 lines.push('} else {'); | |
301 blines = this.args[2].toCLines(vars, needsreturn); | |
302 for (var i in blines) { | |
303 lines.push('\t' + blines[i]); | |
304 } | |
305 lines.push('}'); | |
306 break; | |
307 case 'while:do': | |
308 if (needsreturn) { | |
309 throw new Error("while:do can't be last statement in llMessage code block"); | |
310 } | |
311 lines.push('while (' + this.args[0].toCLLExpr(vars) + ') {'); | |
312 var blines = this.args[1].toCLines(vars); | |
313 for (var i in blines) { | |
314 lines.push('\t' + blines[i]); | |
315 } | |
316 lines.push('}'); | |
317 break; | |
318 default: | |
319 lines.push( (needsreturn ? 'return (object *)' : '') + this.toCLLExpr(vars) + ';'); | |
320 } | |
321 return lines; | |
322 }; | |
323 | |
324 funcall.prototype.toCLLExpr = function(vars) { | |
325 var name = this.name[this.name.length-1] == ':' ? this.name.substr(0, this.name.length-1) : this.name; | |
326 var args = this.args.slice(0, this.args.length); | |
327 if (this.receiver) { | |
328 if(this.args.length == 0) { | |
329 return this.receiver.toCLLExpr(vars) + '->' + this.name; | |
330 } else if (this.args.length == 1 && name[name.length-1] == '!') { | |
331 return this.receiver.toCLLExpr(vars) + '->' + this.name.substr(0, name.length-1) + ' = ' + args[0].toCLLExpr(vars); | |
332 } else { | |
333 args.splice(0, 0, this.receiver); | |
334 } | |
335 } | |
336 switch(name) | |
337 { | |
338 case 'if': | |
339 return '((' + args[0].toCLLExpr(vars) + ') ? (' + args[1].toCLLExpr(vars) + ') : 0)'; | |
340 case 'if:else': | |
341 return '((' + args[0].toCLLExpr(vars) + ') ? (' + args[1].toCLLExpr(vars) + ') : (' + args[2].toCLLExpr(vars) + '))'; | |
342 case 'while:do': | |
343 throw new Error('while:do not allowed in expression context in llMessage block'); | |
344 case 'addr_of': | |
345 return '&(' + args[0].toCLLExpr(vars) + ')'; | |
346 case 'sizeof': | |
347 return 'sizeof(' + args[0].toCTypeName() + ')'; | |
348 case 'get': | |
349 return args[0].toCLLExpr(vars) + '[' + args[1].toCLLExpr(vars) + ']'; | |
350 case 'set': | |
351 return args[0].toCLLExpr(vars) + '[' + args[1].toCLLExpr(vars) + '] = ' + args[2].toCLLExpr(vars); | |
352 case 'not': | |
353 return '!(' + args[0].toCLLExpr(vars) + ')'; | |
354 case 'mcall': | |
355 if (args[0] instanceof symbol) { | |
356 args[0] = new intlit(getMethodId(args[0].name)); | |
357 } | |
358 default: | |
359 for (var i in args) { | |
360 args[i] = args[i].toCLLExpr(vars); | |
361 } | |
362 return name + '(' + args.join(', ') + ')'; | |
363 } | |
364 }; | |
211 | 365 |
212 function cObject(name) { | 366 function cObject(name) { |
213 this.name = name; | 367 this.name = name; |
214 this.slots = {}; | 368 this.slots = {}; |
215 this.properties = []; | 369 this.properties = []; |
273 var init = this.init.slice(0, this.init.length); | 427 var init = this.init.slice(0, this.init.length); |
274 init.push('return (object *)self;'); | 428 init.push('return (object *)self;'); |
275 this.addMessage('_init', { | 429 this.addMessage('_init', { |
276 vars: {}, | 430 vars: {}, |
277 lines: init | 431 lines: init |
278 }); | 432 }); |
279 this.initmsgadded = true; | 433 this.initmsgadded = true; |
280 } | 434 } |
281 } | 435 } |
282 | 436 |
283 cObject.prototype.populateSymbols = function() {}; | 437 cObject.prototype.populateSymbols = function() {}; |
314 for (var varname in this.slotvars[i]) { | 468 for (var varname in this.slotvars[i]) { |
315 slotdefs += '\t' + this.slotvars[i][varname] + ' ' + varname + ';\n'; | 469 slotdefs += '\t' + this.slotvars[i][varname] + ' ' + varname + ';\n'; |
316 } | 470 } |
317 if (this.slots[i].length == 1) { | 471 if (this.slots[i].length == 1) { |
318 slotdefs += '\tif (method_id == ' + this.slots[i][0][0] + ') { /* ' + this.slots[i][0][2] + '*/\n' + | 472 slotdefs += '\tif (method_id == ' + this.slots[i][0][0] + ') { /* ' + this.slots[i][0][2] + '*/\n' + |
319 '\t\t' + this.slots[i][0][1] + '\n' + | 473 '\t\t' + this.slots[i][0][1] + '\n' + |
320 '\t}\n' + | 474 '\t}\n' + |
321 '\treturn no_impl(method_id, num_params, (object *)self, args);\n}\n'; | 475 '\treturn no_impl(method_id, num_params, (object *)self, args);\n}\n'; |
322 } else { | 476 } else { |
323 slotdefs += '\tswitch(method_id) {\n'; | 477 slotdefs += '\tswitch(method_id) {\n'; |
324 for (j in this.slots[i]) { | 478 for (j in this.slots[i]) { |
325 slotdefs += '\t\tcase ' + this.slots[i][j][0] + ': /* ' + this.slots[i][j][2] + '*/\n' + | 479 slotdefs += '\t\tcase ' + this.slots[i][j][0] + ': /* ' + this.slots[i][j][2] + '*/\n' + |
326 '\t\t\t' + this.slots[i][j][1] + '\n'; | 480 '\t\t\t' + this.slots[i][j][1] + '\n'; |
327 } | 481 } |
328 slotdefs += '\t\tdefault:\n' + | 482 slotdefs += '\t\tdefault:\n' + |
329 '\t\t\treturn no_impl(method_id, num_params, (object *)self, args);\n\t}\n}\n'; | 483 '\t\t\treturn no_impl(method_id, num_params, (object *)self, args);\n\t}\n}\n'; |
330 | 484 |
331 } | 485 } |
332 metadef += this.name + '_slot_' + i; | 486 metadef += this.name + '_slot_' + i; |
333 } else { | 487 } else { |
334 metadef += 'no_impl'; | 488 metadef += 'no_impl'; |
335 } | 489 } |
353 return this.toCInstance(); | 507 return this.toCInstance(); |
354 } | 508 } |
355 | 509 |
356 var nextobject = 0; | 510 var nextobject = 0; |
357 | 511 |
358 object.prototype.toC = function() { | 512 |
513 object.prototype.toCObject = function() { | |
359 var messages = this.messages; | 514 var messages = this.messages; |
360 var values = []; | 515 var values = []; |
361 var imports = [] | 516 var imports = []; |
362 var me = new cObject('object_' + nextobject++); | 517 if (!this.name) { |
518 this.name = 'object_' + nextobject++; | |
519 } | |
520 var me = new cObject(this.name); | |
363 this.symbols.typename = me.name; | 521 this.symbols.typename = me.name; |
364 if (this.symbols.needsenv) { | 522 if (this.symbols.needsenv) { |
365 me.addProperty('env', this.symbols.envVar(), 'struct ' + this.symbols.getEnvType() + ' * '); | 523 me.addProperty('env', this.symbols.envVar(), 'struct ' + this.symbols.getEnvType() + ' * '); |
366 me.hasenv = true; | 524 me.hasenv = true; |
367 } | 525 } |
379 throw new Error('Names in import:from statement must be symbols'); | 537 throw new Error('Names in import:from statement must be symbols'); |
380 } | 538 } |
381 importsyms.push(new strlit(el.name)); | 539 importsyms.push(new strlit(el.name)); |
382 }); | 540 }); |
383 imports.push({symbols: new listlit(importsyms), src: messages[i].args[1]}); | 541 imports.push({symbols: new listlit(importsyms), src: messages[i].args[1]}); |
542 } else if(messages[i].name == 'llProperty:withType:' && messages[i].args.length == 2) { | |
543 me.addProperty(messages[i].args[0].name, null, messages[i].args[1].toCTypeName()) | |
544 } else if(messages[i].name == 'llMessage:withVars:andCode:' && messages[i].args.length == 3) { | |
545 var msgname = messages[i].args[0].name | |
546 var rawvars = messages[i].args[1].expressions; | |
547 var vars = {}; | |
548 for(var v in rawvars) { | |
549 vars[rawvars[v].symbol.name] = rawvars[v].expression.toCTypeName(); | |
550 } | |
551 me.addMessage(msgname, { | |
552 vars: vars, | |
553 lines: messages[i].args[2].toCLines(vars, true) | |
554 }); | |
384 } else { | 555 } else { |
385 throw new Error('Only import and import:from calls allowed in object context'); | 556 |
557 throw new Error('Only import and import:from calls allowed in object context. ' + messages[i].name + 'with ' + messages[i].args.length + ' arguments found instead.'); | |
386 } | 558 } |
387 } else { | 559 } else { |
388 messages[i].toCObject(me); | 560 messages[i].toCObject(me); |
389 } | 561 } |
390 } | 562 } |
391 | 563 |
392 return me.toC(); | 564 return me; |
393 } | 565 }; |
566 | |
567 object.prototype.toC = function() { | |
568 return this.toCObject().toC(); | |
569 }; | |
394 | 570 |
395 var toplevelcode; | 571 var toplevelcode; |
396 var forwarddec; | 572 var forwarddec; |
397 | 573 |
398 function addBinaryOp(cobject, opname, cop, objtype) | 574 function addBinaryOp(cobject, opname, cop, objtype) |
442 vars: {str: 'string *'}, | 618 vars: {str: 'string *'}, |
443 lines: [ | 619 lines: [ |
444 'str = (string *)make_object(&string_meta, NULL, 0);', | 620 'str = (string *)make_object(&string_meta, NULL, 0);', |
445 'str->data = GC_MALLOC(12);', | 621 'str->data = GC_MALLOC(12);', |
446 'sprintf(str->data, "%d", self->num);', | 622 'sprintf(str->data, "%d", self->num);', |
447 'str->length = str->bytes = strlen(str->data);', | 623 'str->len = str->bytes = strlen(str->data);', |
448 'return &(str->header);' | 624 'return &(str->header);' |
625 ] | |
626 }); | |
627 int32.addMessage('isInteger?', { | |
628 vars: {}, | |
629 lines: [ | |
630 'return ' + toplevel.moduleVar('true') + ';' | |
449 ] | 631 ] |
450 }); | 632 }); |
451 int32.addMessage('hash', { | 633 int32.addMessage('hash', { |
452 vars: {}, | 634 vars: {}, |
453 lines: [ | 635 lines: [ |
457 return int32; | 639 return int32; |
458 } | 640 } |
459 | 641 |
460 function makeArray() | 642 function makeArray() |
461 { | 643 { |
462 var array = new cObject('array'); | 644 var arrayfile = toplevel.names['array']; |
463 array.addProperty('size', null, 'uint32_t'); | 645 var ast = parseFile(arrayfile.path + '/' + arrayfile.file); |
464 array.addProperty('storage', null, 'uint32_t'); | 646 ast.name = 'array'; |
465 array.addProperty('data', null, 'object **'); | 647 ast.populateSymbols(toplevel); |
466 array.addMessage('get', { | 648 return ast.toCObject(); |
467 vars: {index: 'obj_int32 *'}, | |
468 lines: [ | |
469 'index = va_arg(args, obj_int32 *);', | |
470 'if (index->num >= 0 && index->num < self->size) {', | |
471 ' return self->data[index->num];', | |
472 '}', | |
473 'return ' + toplevel.moduleVar('false') + ';' | |
474 ] | |
475 }); | |
476 array.addMessage('set', { | |
477 vars: {index: 'obj_int32 *'}, | |
478 lines: [ | |
479 'index = va_arg(args, obj_int32 *);', | |
480 'if (index->num >= 0 && index->num < self->size) {', | |
481 ' self->data[index->num] = va_arg(args, object *);', | |
482 '}', | |
483 'return &(self->header);' | |
484 ] | |
485 }); | |
486 array.addMessage('foreach', { | |
487 vars: {index: 'obj_int32 *', i: 'int32_t', clos: 'lambda *'}, | |
488 lines: [ | |
489 'clos = va_arg(args, lambda *);', | |
490 'for (i = 0; i < self->size; i++) {', | |
491 ' index = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', | |
492 ' index->num = i;', | |
493 ' ccall(clos, 2, index, self->data[i]);', | |
494 '}', | |
495 'return &(self->header);' | |
496 ] | |
497 }); | |
498 array.addMessage('append', { | |
499 vars: {tmp: 'object **'}, | |
500 lines: [ | |
501 'if (self->storage == self->size) {', | |
502 ' self->storage *= 2;', | |
503 ' tmp = GC_REALLOC(self->data, self->storage * sizeof(object *));', | |
504 ' if (!tmp) {', | |
505 ' fputs("Failed to increase array size\\n", stderr);', | |
506 ' exit(1);', | |
507 ' }', | |
508 ' self->data = tmp;', | |
509 '}', | |
510 'self->data[self->size++] = va_arg(args, object *);', | |
511 'return &(self->header);' | |
512 ] | |
513 }); | |
514 array.addMessage('length', { | |
515 vars: {intret: 'obj_int32 *'}, | |
516 lines: [ | |
517 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', | |
518 'intret->num = self->size;', | |
519 'return &(intret->header);' | |
520 ] | |
521 }); | |
522 return array; | |
523 } | 649 } |
524 | 650 |
525 function makeString() | 651 function makeString() |
526 { | 652 { |
527 var string = new cObject('string'); | 653 var arrayfile = toplevel.names['string']; |
528 string.addProperty('length', null, 'uint32_t'); | 654 var ast = parseFile(arrayfile.path + '/' + arrayfile.file); |
529 string.addProperty('bytes', null, 'uint32_t'); | 655 ast.name = 'string'; |
530 string.addProperty('data', null, 'char *'); | 656 ast.populateSymbols(toplevel); |
531 string.addMessage('length', { | 657 return ast.toCObject(); |
532 vars: {intret: 'obj_int32 *'}, | |
533 lines: [ | |
534 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', | |
535 'intret->num = self->length;', | |
536 'return &(intret->header);' | |
537 ] | |
538 }); | |
539 string.addMessage('byte_length', { | |
540 vars: {intret: 'obj_int32 *'}, | |
541 lines: [ | |
542 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', | |
543 'intret->num = self->bytes;', | |
544 'return &(intret->header);' | |
545 ] | |
546 }); | |
547 string.addMessage('EQ_', { | |
548 vars: {argb: 'string *'}, | |
549 lines: [ | |
550 'argb = va_arg(args, string *);', | |
551 'if (self->length == argb->length && self->bytes == argb->bytes && !memcmp(self->data, argb->data, self->bytes)) {', | |
552 ' return ' + toplevel.moduleVar('true') + ';', | |
553 '}', | |
554 'return ' + toplevel.moduleVar('false') + ';', | |
555 ] | |
556 }); | |
557 string.addMessage('NEQ_', { | |
558 vars: {argb: 'string *'}, | |
559 lines: [ | |
560 'argb = va_arg(args, string *);', | |
561 'if (self->length != argb->length || self->bytes != argb->bytes || memcmp(self->data, argb->data, self->bytes)) {', | |
562 ' return ' + toplevel.moduleVar('true') + ';', | |
563 '}', | |
564 'return ' + toplevel.moduleVar('false') + ';', | |
565 ] | |
566 }); | |
567 string.addMessage('print', { | |
568 vars: {}, | |
569 lines: [ | |
570 'fwrite(self->data, 1, self->bytes, stdout);', | |
571 'return &(self->header);' | |
572 ] | |
573 }); | |
574 string.addMessage('string', { | |
575 vars: {}, | |
576 lines: [ 'return &(self->header);' ] | |
577 }); | |
578 string.addMessage('CAT_', { | |
579 vars: {argbo: 'object *', argb: 'string *', out: 'string *'}, | |
580 lines: [ | |
581 'argbo = va_arg(args, object *);', | |
582 'argb = (string *)mcall(' + getMethodId('string') + ', 1, argbo);', | |
583 'out = (string *)make_object(&string_meta, NULL, 0);', | |
584 'out->bytes = self->bytes + argb->bytes;', | |
585 'out->length = self->length + argb->length;', | |
586 'out->data = GC_MALLOC_ATOMIC(out->bytes+1);', | |
587 'memcpy(out->data, self->data, self->bytes);', | |
588 'memcpy(out->data + self->bytes, argb->data, argb->bytes + 1);', | |
589 'return &(out->header);' | |
590 ] | |
591 }); | |
592 string.addMessage('byte', { | |
593 vars: {index: 'obj_int32 *', intret: 'obj_int32 *'}, | |
594 lines: [ | |
595 'index = va_arg(args, obj_int32 *);', | |
596 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', | |
597 'intret->num = index->num < self->bytes ? self->data[index->num] : 0;', | |
598 'return &(intret->header);' | |
599 ] | |
600 }); | |
601 string.addMessage('int32', { | |
602 vars: {intret: 'obj_int32 *'}, | |
603 lines: [ | |
604 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', | |
605 'intret->num = atoi(self->data);', | |
606 'return &(intret->header);' | |
607 ] | |
608 }); | |
609 string.addMessage('hash', { | |
610 vars: {intret: 'obj_int32 *', i: 'uint32_t'}, | |
611 lines: [ | |
612 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', | |
613 'intret->num = 0;', | |
614 'if (self->bytes) {', | |
615 ' intret->num = self->data[0] << 7;', | |
616 ' for (i = 0; i < self->bytes; i++) {', | |
617 ' intret->num = (1000003 * intret->num) ^ self->data[i];', | |
618 ' }', | |
619 ' intret->num = intret->num ^ self->bytes;', | |
620 '}', | |
621 'return &(intret->header);' | |
622 ] | |
623 }); | |
624 return string; | |
625 } | 658 } |
626 | 659 |
627 function makelambda() | 660 function makelambda() |
628 { | 661 { |
629 var clos = new cObject('lambda'); | 662 var clos = new cObject('lambda'); |
751 ] | 784 ] |
752 }); | 785 }); |
753 toplevel.names['os'] = os; | 786 toplevel.names['os'] = os; |
754 } | 787 } |
755 | 788 |
756 modulefile.prototype.populateSymbols = function (toplevel) { | |
757 if (!this.ast) { | |
758 this.ast = parseFile(this.path + '/' + this.file); | |
759 this.ast.populateSymbols(toplevel); | |
760 } | |
761 }; | |
762 | |
763 modulefile.prototype.toC = function(){ | 789 modulefile.prototype.toC = function(){ |
764 this.populateSymbols(toplevel); | |
765 return this.ast.toCModuleInstance(); | 790 return this.ast.toCModuleInstance(); |
766 }; | 791 }; |
767 | 792 |
768 function processUsedToplevel(toplevel) | 793 function processUsedToplevel(toplevel) |
769 { | 794 { |
770 var alwaysused = ['true', 'false']; | 795 var alwaysused = ['true', 'false']; |
771 var ret = ''; | 796 var ret = ''; |
772 var modulenum = 0; | 797 var modulenum = 0; |
798 var visited = {}; | |
799 for (var i in alwaysused) { | |
800 toplevel.used[alwaysused[i]] = true; | |
801 } | |
773 var newused = Object.keys(toplevel.used); | 802 var newused = Object.keys(toplevel.used); |
774 var allused = newused; | 803 var allused = newused; |
775 var visited = {}; | |
776 for (var i in alwaysused) { | |
777 forwarddec += 'object * ' + toplevel.moduleVar(alwaysused[i]) + ';\n'; | |
778 toplevel.names[alwaysused[i]].populateSymbols(toplevel); | |
779 visited[alwaysused[i]] = true; | |
780 } | |
781 while (newused.length) { | 804 while (newused.length) { |
782 for (var i in newused) { | 805 for (var i in newused) { |
783 debugprint('//---module', newused[i], '--- populate symbols'); | 806 debugprint('//---module', newused[i], '--- populate symbols'); |
784 forwarddec += 'object * ' + toplevel.moduleVar(newused[i]) + ';\n'; | 807 forwarddec += 'object * ' + toplevel.moduleVar(newused[i]) + ';\n'; |
785 toplevel.names[newused[i]].populateSymbols(toplevel); | 808 toplevel.names[newused[i]].populateSymbols(toplevel); |
792 newused.push(symbol); | 815 newused.push(symbol); |
793 allused.push(symbol); | 816 allused.push(symbol); |
794 } | 817 } |
795 } | 818 } |
796 } | 819 } |
797 for (var i in alwaysused) { | 820 |
798 allused.push(alwaysused[i]); | |
799 } | |
800 | |
801 for (var i = allused.length-1; i >= 0; i--) { | 821 for (var i = allused.length-1; i >= 0; i--) { |
802 var symbol = allused[i]; | 822 var symbol = allused[i]; |
803 debugprint('//---module', symbol, '--- compile'); | 823 debugprint('//---module', symbol, '(' + i +')--- compile'); |
804 ret += '\t' + toplevel.moduleVar(symbol) + ' = ' + toplevel.names[symbol].toC() + ';\n'; | 824 ret += '\t' + toplevel.moduleVar(symbol) + ' = ' + toplevel.names[symbol].toC() + ';\n'; |
805 } | 825 } |
806 return ret; | 826 return ret; |
807 } | 827 } |
808 | 828 |
809 function makeCProg(obj) | 829 function makeCProg(obj) |
810 { | 830 { |
831 forwarddec = toplevelcode = ''; | |
811 var builtins = builtinTypes(); | 832 var builtins = builtinTypes(); |
812 forwarddec = toplevelcode = ''; | |
813 for (var i in builtins) { | 833 for (var i in builtins) { |
814 forwarddec += builtins[i].toEarlyCDef(); | 834 forwarddec += builtins[i].toEarlyCDef(); |
815 toplevelcode += builtins[i].toCDef(); | 835 toplevelcode += builtins[i].toCDef(); |
816 } | 836 } |
817 addBuiltinModules(toplevel); | 837 addBuiltinModules(toplevel); |
831 | 851 |
832 object.prototype.toCModuleInstance = function() { | 852 object.prototype.toCModuleInstance = function() { |
833 return this.toC(); | 853 return this.toC(); |
834 } | 854 } |
835 | 855 |
836 var lambdanum = 0; | |
837 | |
838 lambda.prototype.toC = function() { | 856 lambda.prototype.toC = function() { |
839 var args = this.args ? this.args.slice(0, this.args.length) : []; | 857 var args = this.args ? this.args.slice(0, this.args.length) : []; |
840 var exprs = this.expressions; | 858 var exprs = this.expressions; |
841 var mynum = lambdanum++; | 859 debugprint('//', this.name); |
842 debugprint('//lambda', mynum); | |
843 if (Object.keys(this.symbols.closedover).length) { | 860 if (Object.keys(this.symbols.closedover).length) { |
844 this.symbols.envtype = 'lambda_' + mynum + '_env'; | 861 this.symbols.envtype = this.name + '_env'; |
862 forwarddec += 'typedef struct ' + this.symbols.envtype + ' ' + this.symbols.envtype + ';\n' | |
845 } | 863 } |
846 if (this.selftype) { | 864 if (this.selftype) { |
847 this.symbols.defineVar('self', this.selftype); | 865 this.symbols.defineVar('self', this.selftype); |
848 if (args[0] && args[0].cleanName() == 'self') { | 866 if (args[0] && args[0].cleanName() == 'self') { |
849 args.splice(0, 1); | 867 args.splice(0, 1); |
852 } else { | 870 } else { |
853 var offset = 0; | 871 var offset = 0; |
854 } | 872 } |
855 for (var i = 0; i < args.length; ++i) { | 873 for (var i = 0; i < args.length; ++i) { |
856 var argname = args[i].toC(); | 874 var argname = args[i].toC(); |
857 | 875 |
858 args[i] = (argname.indexOf('->') < 0 ? '\tobject * ' : '\t') + argname + ' = va_arg(args, object *);\n'; | 876 args[i] = (argname.indexOf('->') < 0 ? '\tobject * ' : '\t') + argname + ' = va_arg(args, object *);\n'; |
859 } | 877 } |
860 var compiled = [] | 878 var compiled = [] |
861 for (var i in exprs) { | 879 for (var i in exprs) { |
862 var js = exprs[i].toC(); | 880 var js = exprs[i].toC(); |
866 } | 884 } |
867 exprs = compiled; | 885 exprs = compiled; |
868 if (exprs.length) { | 886 if (exprs.length) { |
869 exprs[exprs.length-1] = 'return (object *)(' + exprs[exprs.length-1] + ');'; | 887 exprs[exprs.length-1] = 'return (object *)(' + exprs[exprs.length-1] + ');'; |
870 } | 888 } |
871 | 889 |
872 if (Object.keys(this.symbols.closedover).length) { | 890 if (Object.keys(this.symbols.closedover).length) { |
873 forwarddec += 'typedef struct lambda_' + mynum + '_env {\n'; | 891 forwarddec += 'struct ' + this.name + '_env {\n'; |
874 if (this.symbols.needsParentEnv) { | 892 if (this.symbols.needsParentEnv) { |
875 forwarddec += '\tstruct ' + this.symbols.parentEnvType() + ' * parent;\n'; | 893 forwarddec += '\tstruct ' + this.symbols.parentEnvType() + ' * parent;\n'; |
876 } | 894 } |
877 for (var varname in this.symbols.closedover) { | 895 for (var varname in this.symbols.closedover) { |
878 if (varname == 'self' && this.selftype) { | 896 if (varname == 'self' && this.selftype) { |
879 forwarddec += '\tstruct ' + this.selftype + ' * self;\n'; | 897 forwarddec += '\tstruct ' + this.selftype + ' * self;\n'; |
880 } else { | 898 } else { |
881 forwarddec += '\tobject * ' + escapeCName(varname) + ';\n'; | 899 forwarddec += '\tobject * ' + escapeCName(varname) + ';\n'; |
882 } | 900 } |
883 } | 901 } |
884 forwarddec += '} lambda_' + mynum + '_env;\n' | 902 forwarddec += '};\n' |
885 | 903 |
886 var myenvinit = '\tlambda_' + mynum + '_env * myenv = GC_MALLOC(sizeof(lambda_' + mynum + '_env));\n'; | 904 var myenvinit = '\t' + this.name + '_env * myenv = GC_MALLOC(sizeof(' + this.name + '_env));\n'; |
887 if (this.symbols.needsParentEnv) { | 905 if (this.symbols.needsParentEnv) { |
888 myenvinit += '\tmyenv->parent = env;\n'; | 906 myenvinit += '\tmyenv->parent = env;\n'; |
889 } | 907 } |
890 this.symbols.envtype = 'lambda_' + mynum + '_env'; | 908 this.symbols.envtype = this.name + '_env'; |
891 } else { | 909 } else { |
892 var myenvinit = ''; | 910 var myenvinit = ''; |
893 } | 911 } |
894 | 912 forwarddec += 'object *' + this.name + ' (' + this.symbols.parentEnvType() + ' * env, uint32_t num_args, ...);\n'; |
895 toplevelcode += 'object * lambda_' + mynum + ' (' + this.symbols.parentEnvType() + ' * env, uint32_t num_args, ...) {\n\tva_list args;\n' + myenvinit + '\tva_start(args, num_args);\n'; | 913 |
914 toplevelcode += 'object * ' + this.name + ' ( ' + this.symbols.parentEnvType() + ' * env, uint32_t num_args, ...) {\n\tva_list args;\n' + myenvinit + '\tva_start(args, num_args);\n'; | |
896 if (this.selftype) { | 915 if (this.selftype) { |
897 var selfvar = (new symbol('self', this.symbols)).toC(); | 916 var selfvar = (new symbol('self', this.symbols)).toC(); |
898 if (selfvar == 'self') { | 917 if (selfvar == 'self') { |
899 toplevelcode += '\t' + this.selftype + ' * self = va_arg(args, ' + this.selftype + ' *);\n'; | 918 toplevelcode += '\t' + this.selftype + ' * self = va_arg(args, ' + this.selftype + ' *);\n'; |
900 } else { | 919 } else { |
901 toplevelcode += '\t' + selfvar + ' = va_arg(args, ' + this.selftype + ' *);\n'; | 920 toplevelcode += '\t' + selfvar + ' = va_arg(args, ' + this.selftype + ' *);\n'; |
902 } | 921 } |
903 | 922 |
904 } | 923 } |
905 toplevelcode += args.join('') + '\tva_end(args);\n' + exprs.join(';\n\t') + '\n}\n'; | 924 toplevelcode += args.join('') + '\tva_end(args);\n' + exprs.join(';\n\t') + '\n}\n'; |
906 this.name = 'lambda_' + mynum; | 925 |
907 | |
908 if (this.selftype) { | 926 if (this.selftype) { |
909 return this.name; | 927 return this.name; |
910 } else { | 928 } else { |
911 if (this.symbols.parentEnvType() != 'void') { | 929 if (this.symbols.parentEnvType() != 'void') { |
912 if (this.symbols.parent.passthruenv) { | 930 if (this.symbols.parent.passthruenv) { |
913 var envvar = 'env'; | 931 var envvar = 'env'; |
914 } else { | 932 } else { |
915 var envvar = 'myenv'; | 933 var envvar = 'myenv'; |
916 } | 934 } |
917 debugprint('//lambda_' + mynum, 'has envvar:', envvar, 'num vars closed over:', Object.keys(this.symbols.closedover).length); | 935 debugprint('//' + this.name, 'has envvar:', envvar, 'num vars closed over:', Object.keys(this.symbols.closedover).length); |
918 return 'make_lambda(' + envvar + ', (closure_func)' + this.name + ')'; | 936 return 'make_lambda(' + envvar + ', (closure_func)' + this.name + ')'; |
919 } else { | 937 } else { |
920 toplevelcode += 'lambda lambda_obj_' + mynum + ' = {{&lambda_meta, NULL}, NULL, lambda_' + mynum + '};\n'; | 938 toplevelcode += 'lambda ' + this.name + '_obj = {{&lambda_meta, NULL}, NULL, lambda_' + mynum + '};\n'; |
921 return '((object *)&lambda_obj_' + mynum + ')'; | 939 return '((object *)&' + this.name + '_obj)'; |
922 } | 940 } |
923 } | 941 } |
924 }; | 942 }; |
925 lambda.prototype.toCModuleInstance = function() { | 943 lambda.prototype.toCModuleInstance = function() { |
926 this.toC(); | 944 this.toC(); |
930 this.selftype = typename; | 948 this.selftype = typename; |
931 return this.toC(); | 949 return this.toC(); |
932 }; | 950 }; |
933 lambda.prototype.toCModule = function() { | 951 lambda.prototype.toCModule = function() { |
934 return makeCProg(this); | 952 return makeCProg(this); |
953 }; | |
954 lambda.prototype.toCLines = function(vars, needsreturn) { | |
955 var lines = []; | |
956 for (var i in this.args) { | |
957 var name = this.args[i].name; | |
958 if (name[0] == ':') { | |
959 name = name.substr(1); | |
960 } | |
961 if(name != 'self') { | |
962 lines.push(name + ' = va_arg(args, ' + vars[name] + ');'); | |
963 } | |
964 } | |
965 for (var i in this.expressions) { | |
966 var exprlines = this.expressions[i].toCLines(vars, needsreturn && i == this.expressions.length - 1); | |
967 for (var j in exprlines) { | |
968 lines.push('\t' + exprlines[j]); | |
969 } | |
970 } | |
971 return lines; | |
972 } | |
973 lambda.prototype.toCLLExpr = function(vars) { | |
974 if (this.expressions.length != 1) { | |
975 throw new Error('lambda in expression context must have a single statement in llMessage block'); | |
976 } | |
977 return this.expressions[0].toCLLExpr(vars); | |
935 } | 978 } |
936 | 979 |
937 assignment.prototype.toC = function() { | 980 assignment.prototype.toC = function() { |
938 debugprint('//assignment', this.symbol.name); | 981 debugprint('//assignment', this.symbol.name); |
939 var existing = this.symbols.find(this.symbol.name); | 982 var existing = this.symbols.find(this.symbol.name); |
969 messagevars[escaped] = 'object *'; | 1012 messagevars[escaped] = 'object *'; |
970 params.push(escaped); | 1013 params.push(escaped); |
971 paramget += escaped + ' = va_arg(args, object *); '; | 1014 paramget += escaped + ' = va_arg(args, object *); '; |
972 } | 1015 } |
973 } | 1016 } |
974 cobj.addMessage(this.symbol.name, { | 1017 cobj.addMessage(getOpMethodName(this.symbol.name), { |
975 vars: messagevars, | 1018 vars: messagevars, |
976 lines: [paramget + 'return ' + val + '(' + (cobj.hasenv ? 'self->env' : 'NULL') + ', ' + params.length + (params.length ? ', ' : '') + params.join(', ') + ');'] | 1019 lines: [paramget + 'return ' + val + '(' + (cobj.hasenv ? 'self->env' : 'NULL') + ', ' + params.length + (params.length ? ', ' : '') + params.join(', ') + ');'] |
977 }); | 1020 }); |
978 } else { | 1021 } else { |
979 cobj.addProperty(this.symbol.name, val); | 1022 cobj.addProperty(this.symbol.name, val); |
980 if (this.expression instanceof object && this.expression.symbols.needsparent) { | 1023 if (this.expression instanceof object && this.expression.symbols.needsparent) { |
981 cobj.addInit('self->' + escapeCName(this.symbol.name) + '->parent = (object *)self;'); | 1024 cobj.addInit('self->' + escapeCName(this.symbol.name) + '->parent = (object *)self;'); |
982 } | 1025 } |
983 } | 1026 } |
984 }; | 1027 }; |
1028 assignment.prototype.toCLines = function(vars, needsreturn) { | |
1029 return [(needsreturn ? 'return ' : '') + this.symbol.toCLLExpr(vars) + ' = ' + this.expression.toCLLExpr(vars) + ';'] | |
1030 }; |