Mercurial > repos > tabletprog
comparison cbackend.js @ 31:668f533e5284
Add initial version of C backend
author | Mike Pavone <pavone@retrodev.com> |
---|---|
date | Sat, 07 Jul 2012 16:48:36 -0700 |
parents | |
children | 64f1d516fbfd |
comparison
equal
deleted
inserted
replaced
30:608eb70fe261 | 31:668f533e5284 |
---|---|
1 var mainModule; | |
2 var modules = {}; | |
3 | |
4 var nextmethodId = 0; | |
5 var methodIds = {}; | |
6 function getMethodId(methodName) | |
7 { | |
8 if (!(methodName in methodIds)) { | |
9 methodIds[methodName] = nextmethodId++; | |
10 | |
11 } | |
12 return methodIds[methodName]; | |
13 } | |
14 | |
15 function importSym(obj, src, key) | |
16 { | |
17 if(!(key in src)) { | |
18 throw new Error(key +' not found in source object for import'); | |
19 } | |
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 { | |
35 for (var i = 0; i < symlist.length; ++i) { | |
36 importSym(obj, src, symlist[i]); | |
37 } | |
38 } | |
39 return obj; | |
40 } | |
41 | |
42 op.prototype.toC = function(isReceiver) { | |
43 var optoMeth = {'+': 'ADD_', '-': 'SUB_', '*': 'MUL_', '/': 'DIV_', '=': 'EQ_', '!=': 'NEQ_', '<': 'LT_', '>': 'GT_', '>=': 'GEQ_', '<=': 'LEQ_'}; | |
44 var method = optoMeth[this.op]; | |
45 var ret = '(params[0] = ' + this.left.toC() + ',\n'; | |
46 ret += 'params[1] = ' + this.right.toC() + ',\n'; | |
47 ret += 'mcall(' + getMethodId(method) + ', 2, params))\n'; | |
48 return ret; | |
49 }; | |
50 | |
51 function escapeCName(name) | |
52 { | |
53 name = name.replace("_", "UN_").replace(":", "CN_").replace("!", "EX_").replace('?', 'QS_').replace('@', 'AT_'); | |
54 name = 'tp_' + name; | |
55 return name; | |
56 } | |
57 | |
58 symbol.prototype.toC = function() { | |
59 var name = this.cleanName(); | |
60 if (name == 'self') { | |
61 return name; | |
62 } | |
63 var info = this.symbols.find(name); | |
64 if (!info) { | |
65 throw new Error('symbol ' + name + ' not found'); | |
66 } | |
67 var pre = ''; | |
68 if (info.type == 'self') { | |
69 pre = this.symbols.selfVar() + '->'; | |
70 } else if(info.type == 'parent') { | |
71 pre = this.symbols.selfVar(); | |
72 for (var i = 0; i < funinfo.depth; ++i) { | |
73 pre += '->parent'; | |
74 } | |
75 } else if (info.type == 'toplevel') { | |
76 pre = 'modules.'; | |
77 modules[name] = false; | |
78 } | |
79 return pre + escapeCName(name); | |
80 } | |
81 | |
82 intlit.prototype.toC = function() { | |
83 var str = this.val.toString(); | |
84 toplevelcode += 'obj_int32 int32_' + str + ' = {{&obj_int32_meta, NULL}, ' + str + '};\n'; | |
85 return '((object *)&int32_' + str + ')'; | |
86 } | |
87 | |
88 floatlit.prototype.toC = function() { | |
89 return 'make_float(' + this.val.toString() + ')'; | |
90 } | |
91 | |
92 strlit.prototype.toC = function() { | |
93 return 'make_str("' + this.val.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n').replace('\r', '\\r') + '")'; | |
94 } | |
95 | |
96 listlit.prototype.toC = function() { | |
97 var ret = 'make_list('; | |
98 each(this.val, function(idx, el) { | |
99 ret += (idx ? ', ' : '') + el.toC(); | |
100 }); | |
101 return ret + ')'; | |
102 } | |
103 | |
104 funcall.prototype.toC = function() { | |
105 var name = this.name[this.name.length-1] == ':' ? this.name.substr(0, this.name.length-1) : this.name; | |
106 if (name == 'foreign') { | |
107 if ((this.args[0] instanceof lambda) || (this.args[0] instanceof object)) { | |
108 return null; | |
109 } else if(this.args[0] instanceof symbol) { | |
110 return this.args[0].name; | |
111 } else { | |
112 throw new Error("Unexpected AST type for foreign:"); | |
113 } | |
114 } | |
115 var args = this.args.slice(0, this.args.length); | |
116 if (this.receiver) { | |
117 args.splice(0, 0, this.receiver); | |
118 } | |
119 var method = false; | |
120 var funinfo = this.symbols.find(name); | |
121 if (!funinfo || funinfo.def instanceof setter) { | |
122 method = true; | |
123 } else { | |
124 switch(funinfo.type) | |
125 { | |
126 case 'self': | |
127 if (args.length < funinfo.def.args.length || funinfo.def.args[0].name != 'self') { | |
128 args.splice(0, 0, new symbol('self', this.symbols)); | |
129 } else { | |
130 args.splice(0, 1); | |
131 } | |
132 method = true; | |
133 break; | |
134 case 'parent': | |
135 ret = 'self'; | |
136 for (var i = 0; i < funinfo.depth; ++i) { | |
137 ret += '->parent'; | |
138 } | |
139 break; | |
140 } | |
141 } | |
142 for (var i in args) { | |
143 args[i] = 'params[' + i + '] = ' + args[i].toC() + ',\n'; | |
144 } | |
145 var callpart; | |
146 if (method) { | |
147 callpart = 'mcall(' + getMethodId(name); | |
148 } else { | |
149 callpart = 'ccall(' + escapeCName(name); | |
150 } | |
151 return '(' + args.join('') + callpart + ', ' + args.length + ', ' + 'params))'; | |
152 } | |
153 | |
154 function cObject(name) { | |
155 this.name = name; | |
156 this.slots = {}; | |
157 this.properties = []; | |
158 this.values = []; | |
159 } | |
160 | |
161 cObject.prototype.addMessage = function(msgname, implementation) { | |
162 var methodid = getMethodId(msgname); | |
163 var trunc = methodid & 0xF; | |
164 if (!(trunc in this.slots)) { | |
165 this.slots[trunc] = []; | |
166 } | |
167 this.slots[trunc].push([methodid, implementation, msgname]); | |
168 } | |
169 | |
170 cObject.prototype.addProperty = function(propname, value, type) { | |
171 if (type != undefined) { | |
172 this.properties.push([propname, type]); | |
173 } else { | |
174 var escaped = escapeCName(propname); | |
175 this.addMessage(propname, 'return self->' + escaped + ';'); | |
176 this.addMessage(propname + '!', 'self->' + escaped + ' = params[1]; return params[0];'); | |
177 this.properties.push(escaped); | |
178 this.values.push(value); | |
179 } | |
180 } | |
181 | |
182 cObject.prototype.toEarlyCDef = function() { | |
183 var objdef = 'typedef struct {\n\tobject header;\n'; | |
184 for (var i in this.properties) { | |
185 if (this.properties[i] instanceof Array) { | |
186 objdef += '\t' + this.properties[i][1] + ' ' + this.properties[i][0] + ';\n'; | |
187 } else { | |
188 objdef += '\tobject * ' + this.properties[i] + ';\n' | |
189 } | |
190 } | |
191 objdef += '} ' + this.name + ';\nobj_meta ' + this.name + '_meta;\n'; | |
192 return objdef; | |
193 } | |
194 | |
195 cObject.prototype.toCDef = function() { | |
196 var slotdefs = ''; | |
197 var metadef = 'obj_meta ' + this.name + '_meta = {sizeof(' + this.name +'), {'; | |
198 for (var i = 0; i < 16; i++) { | |
199 if (i) { | |
200 metadef += ', '; | |
201 } | |
202 if (i in this.slots) { | |
203 slotdefs += 'object * ' + this.name + '_slot_' + i + '(uint32_t method_id, uint32_t num_params, object ** params) {\n\t' + | |
204 this.name + ' *self = (' + this.name + ' *)params[0];'; | |
205 if (this.slots[i].length == 1) { | |
206 slotdefs += '\tif (method_id == ' + this.slots[i][0][0] + ') { /* ' + this.slots[i][0][2] + '*/\n' + | |
207 '\t\t' + this.slots[i][0][1] + '\n' + | |
208 '\t}\n' + | |
209 '\treturn no_impl(method_id, num_params, params);\n}\n'; | |
210 } else { | |
211 slotdefs += '\tswitch(method_id) {\n'; | |
212 for (j in this.slots[i]) { | |
213 slotdefs += '\t\tcase ' + this.slots[i][j][0] + ': /* ' + this.slots[i][j][2] + '*/\n' + | |
214 '\t\t\t' + this.slots[i][j][1] + '\n'; | |
215 } | |
216 slotdefs += '\t\tdefault:\n\treturn no_impl(method_id, num_params, params);\n}\n'; | |
217 } | |
218 metadef += this.name + '_slot_' + i; | |
219 } else { | |
220 metadef += 'no_impl'; | |
221 } | |
222 } | |
223 metadef += '}};\n'; | |
224 return slotdefs + metadef; | |
225 } | |
226 | |
227 cObject.prototype.toCInstance = function() { | |
228 return 'make_object(&' + this.name + '_meta, NULL, ' + this.values.length + (this.values.length ? ', ' : '') + this.values.join(', ') + ')'; | |
229 } | |
230 | |
231 var nextobject = 0; | |
232 | |
233 object.prototype.toC = function() { | |
234 var messages = this.messages; | |
235 var values = []; | |
236 var imports = [] | |
237 var me = new cObject('object_' + nextobject++); | |
238 for (var i in messages) { | |
239 if (messages[i] instanceof funcall) { | |
240 if (messages[i].name == 'import:' && messages[i].args.length == 1) { | |
241 imports.push({symbols: false, src: messages[i].args[0]}); | |
242 } else if(messages[i].name == 'import:from:' && messages[i].args.length == 2) { | |
243 var importsyms = []; | |
244 each(messages[i].args[0].val, function(i, el) { | |
245 if (!(el instanceof symbol)) { | |
246 throw new Error('Names in import:from statement must be symbols'); | |
247 } | |
248 importsyms.push(new strlit(el.name)); | |
249 }); | |
250 imports.push({symbols: new listlit(importsyms), src: messages[i].args[1]}); | |
251 } else { | |
252 throw new Error('Only import and import:from calls allowed in object context'); | |
253 } | |
254 } else { | |
255 messages[i].toCObject(me); | |
256 } | |
257 } | |
258 forwarddec += me.toEarlyCDef(); | |
259 toplevelcode += me.toCDef(); | |
260 return me.toCInstance(); | |
261 } | |
262 | |
263 var toplevelcode; | |
264 var forwarddec; | |
265 | |
266 function makeCProg(obj) | |
267 { | |
268 var int32 = new cObject('obj_int32'); | |
269 int32.addProperty('num', null, 'int32_t'); | |
270 int32.addMessage('ADD_', 'params[0] = make_object(&obj_int32_meta, NULL, 0); ((obj_int32 *)params[0])->num = self->num + ((obj_int32 *)params[1])->num; return params[0];'); | |
271 int32.addMessage('SUB_', 'params[0] = make_object(&obj_int32_meta, NULL, 0); ((obj_int32 *)params[0])->num = self->num - ((obj_int32 *)params[1])->num; return params[0];'); | |
272 forwarddec = toplevelcode = ''; | |
273 forwarddec += int32.toEarlyCDef(); | |
274 toplevelcode += int32.toCDef(); | |
275 obj.populateSymbols(toplevel); | |
276 var rest = 'object * mainModule() {\n\treturn ' + obj.toC() + ';\n}\n'; | |
277 return '#include "runtime/proghead.inc"\n#define METHOD_ID_MAIN ' + getMethodId('main') + '\n' + forwarddec + toplevelcode + rest + '#include "runtime/progfoot.inc"\n'; | |
278 } | |
279 | |
280 object.prototype.toCModule = function() { | |
281 return makeCProg(this); | |
282 } | |
283 | |
284 var lambdanum = 0; | |
285 | |
286 lambda.prototype.toC = function() { | |
287 var args = this.args ? this.args.slice(0, this.args.length) : []; | |
288 var exprs = this.expressions; | |
289 if (this.selftype) { | |
290 if (args[0] && args[0].cleanName() == 'self') { | |
291 args.splice(0, 1); | |
292 } | |
293 var offset = 1; | |
294 } else { | |
295 var offset = 0; | |
296 } | |
297 for (var i = 0; i < args.length; ++i) { | |
298 args[i] = '\tobject * ' + args[i].toC() + ' = params[' + (offset + i) + '];\n'; | |
299 } | |
300 var compiled = [] | |
301 for (var i in exprs) { | |
302 var js = exprs[i].toC(); | |
303 if (js) { | |
304 compiled.push(indent(js)); | |
305 } | |
306 } | |
307 exprs = compiled; | |
308 if (exprs.length) { | |
309 exprs[exprs.length-1] = 'return ' + exprs[exprs.length-1] + ';'; | |
310 } | |
311 var mynum = lambdanum++; | |
312 toplevelcode += 'object * lambda_' + mynum + ' (void * env, uint32_t num_args, object ** params) {\n'; | |
313 if (this.selftype) { | |
314 toplevelcode += '\t' + this.selftype + ' * self = (' + this.selftype + ' *)params[0];\n'; | |
315 } | |
316 toplevelcode += args.join('') + exprs.join(';\n\t') + '\n}\n'; | |
317 | |
318 toplevelcode += 'closure lambda_obj_' + mynum + ' = {{&lambda_meta, NULL}, NULL, lambda_' + mynum + '};\n'; | |
319 return '((object *)&lambda_obj_' + mynum + ')'; | |
320 }; | |
321 lambda.prototype.toCObject = function(typename) { | |
322 this.selftype = typename; | |
323 return this.toC(); | |
324 }; | |
325 lambda.prototype.toCModule = function() { | |
326 return makeCProg(this); | |
327 } | |
328 | |
329 assignment.prototype.toC = function() { | |
330 var existing = this.symbols.find(this.symbol.name); | |
331 var prefix = ''; | |
332 if (!existing) { | |
333 prefix = 'object * '; | |
334 } else { | |
335 switch (existing.type) | |
336 { | |
337 case 'self': | |
338 prefix = 'self->'; | |
339 break; | |
340 case 'parent': | |
341 prefix = 'self->header.'; | |
342 for (var i = 0; i < existing.depth; ++i) { | |
343 prefix += 'parent->'; | |
344 } | |
345 break; | |
346 } | |
347 } | |
348 var val = this.expression.toC(); | |
349 if (val === null) { | |
350 return null; | |
351 } | |
352 return prefix + this.symbol.toC() + ' = ' + val; | |
353 }; | |
354 assignment.prototype.toCObject = function(cobj) { | |
355 if (this.expression.toCObject) { | |
356 var val = this.expression.toCObject(cobj.name); | |
357 } else { | |
358 var val = this.expression.toC(); | |
359 } | |
360 if (val === null) { | |
361 return; | |
362 } | |
363 if (this.expression instanceof lambda) { | |
364 cobj.addMessage(this.symbol.name, 'return ccall(' + val + ', num_params, params);'); | |
365 } else { | |
366 cobj.addProperty(this.symbol.name, val); | |
367 } | |
368 }; |