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 };