comparison parser.js @ 8:04ae32e91598

Move compiler and test page related code out of parser.js
author Mike Pavone <pavone@retrodev.com>
date Wed, 21 Mar 2012 20:33:39 -0700
parents 8af72f11714e
children 132c7756860e
comparison
equal deleted inserted replaced
7:8af72f11714e 8:04ae32e91598
1 var mainModule;
2 function toobj(val)
3 {
4 switch(typeof val)
5 {
6 case 'boolean':
7 if(val) {
8 return mainModule.strue;
9 } else {
10 return mainModule.sfalse;
11 }
12 case 'number':
13 return mainModule.snumber(val);
14 }
15 throw new Error("can't make val into object");
16 }
17
18 function indent(str)
19 {
20 return str.split('\n').join('\n\t');
21 }
22
23 function osymbols(parent)
24 {
25 this.parent = parent;
26 this.names = {};
27 this.lastname = null;
28 }
29 osymbols.prototype.find = function(name) {
30 if (name in this.names) {
31 if (this.names[name] instanceof funcall && this.names[name].name == 'foreign:') {
32 return {
33 type: 'foreign',
34 def: this.names[name]
35 };
36 }
37 return {
38 type: 'self',
39 def: this.names[name],
40 };
41 } else if(this.parent) {
42 var ret = this.parent.find(name);
43 if (ret) {
44 if(ret.type == 'self') {
45 ret.type = 'parent';
46 ret.depth = 1;
47 } else if(ret.type == 'parent') {
48 ret.depth++;
49 }
50 }
51 return ret;
52 }
53 return null;
54 };
55 osymbols.prototype.defineMsg = function(name, def) {
56 this.lastname = name;
57 this.names[name] = def;
58 }
59 osymbols.prototype.parentObject = function() {
60 if (!this.parent) {
61 return 'null';
62 }
63 return 'this';
64 }
65
66 function lsymbols(parent)
67 {
68 this.parent = parent;
69 this.names = {};
70 this.lastname = null;
71 this.needsSelfVar = false;
72 }
73 lsymbols.prototype.find = function(name) {
74 if (name in this.names) {
75 if (this.names[name] instanceof funcall && this.names[name].name == 'foreign:') {
76 return {
77 type: 'foreign',
78 def: this.names[name]
79 };
80 }
81 return {
82 type: 'local',
83 def: this.names[name]
84 };
85 } else if(this.parent) {
86 var ret = this.parent.find(name);
87 if (ret && ret.type == 'local') {
88 ret.type = 'upvar';
89 }
90 return ret;
91 }
92 return null;
93 };
94 lsymbols.prototype.defineVar = function(name, def) {
95 this.lastname = name;
96 this.names[name] = def;
97 };
98 lsymbols.prototype.selfVar = function() {
99 if (this.parent && this.parent instanceof lsymbols) {
100 this.parent.needsSelf();
101 return 'self';
102 } else {
103 return 'this';
104 }
105 };
106 lsymbols.prototype.needsSelf = function() {
107 if (this.parent && this.parent instanceof lsymbols) {
108 this.parent.needsSelf();
109 } else {
110 this.needsSelfVar = true;
111 }
112 };
113 1
114 function op(left, op, right) 2 function op(left, op, right)
115 { 3 {
116 this.left = left; 4 this.left = left;
117 this.op = op; 5 this.op = op;
118 this.right = right; 6 this.right = right;
119 } 7 }
120 op.prototype.toJS = function(symbols, isReceiver) {
121 var ret = '(' + this.left.toJS(symbols) +' '+ (this.op == '=' ? '==' : this.op) +' '+ this.right.toJS(symbols) + ')';
122 if (isReceiver) {
123 ret = 'toobj' + ret;
124 }
125 return ret;
126 };
127 8
128 function symbol(name) 9 function symbol(name)
129 { 10 {
130 this.name = name; 11 this.name = name;
131 }
132 symbol.prototype.toJS = function(symbols) {
133 var name = this.cleanName();
134 if (name == 'self') {
135 return symbols.selfVar();
136 }
137 name = name.replace("_", "UN_").replace(":", "CN_").replace("!", "EX_").replace('?', 'QS_').replace('@', 'AT_');
138 var reserved = {'true': true, 'false': true, 'this': true, 'if': true, 'else': true, 'NaN': true};
139 if (name in reserved) {
140 name = 's' + name;
141 }
142 return name;
143 } 12 }
144 symbol.prototype.cleanName = function() { 13 symbol.prototype.cleanName = function() {
145 return this.name[0] == ':' ? this.name.substr(1) : this.name; 14 return this.name[0] == ':' ? this.name.substr(1) : this.name;
146 } 15 }
147 16
148 function intlit(val) 17 function intlit(val)
149 { 18 {
150 this.val = val; 19 this.val = val;
151 } 20 }
152 intlit.prototype.toJS = function(symbols) {
153 return this.val.toString();
154 }
155 21
156 function floatlit(val) 22 function floatlit(val)
157 { 23 {
158 this.val = val; 24 this.val = val;
159 } 25 }
160 floatlit.prototype.toJS = function(symbols) {
161 return this.val.toString();
162 }
163 26
164 function strlit(val) 27 function strlit(val)
165 { 28 {
166 this.val = val; 29 this.val = val;
167 }
168 strlit.prototype.toJS = function(symbols) {
169 console.log('string:', this.val);
170 return '"' + this.val.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n').replace('\r', '\\r') + '"';
171 } 30 }
172 31
173 function funcall(name, args) 32 function funcall(name, args)
174 { 33 {
175 this.name = name; 34 this.name = name;
176 this.args = args; 35 this.args = args;
177 this.receiver = null; 36 this.receiver = null;
178 } 37 }
179 funcall.prototype.toJS = function(symbols) {
180 var name = this.name[this.name.length-1] == ':' ? this.name.substr(0, this.name.length-1) : this.name;
181 var args = this.args.slice(0, this.args.length);
182 if (this.receiver) {
183 args.splice(0, 0, this.receiver);
184 }
185 var funinfo = symbols.find(name);
186 if (!funinfo) {
187 var receiver = args[0];
188 args.splice(0, 1);
189 for (var i in args) {
190 args[i] = args[i].toJS(symbols);
191 }
192 return receiver.toJS(symbols, true) + '.' + (new symbol(name)).toJS(symbols) + '(' + args.join(', ') + ')';
193 }
194 switch(funinfo.type)
195 {
196 case 'self':
197 if (args.length < funinfo.def.args.length || funinfo.def.args[0].name != 'self') {
198 var receiver = new symbol('self');
199 } else {
200 var receiver = args[0];
201 args.splice(0, 1);
202 }
203 for (var i in args) {
204 args[i] = args[i].toJS(symbols);
205 }
206 return receiver.toJS(symbols, true) + '.' + (new symbol(name)).toJS(symbols) + '(' + args.join(', ') + ')';
207 case 'parent':
208 var ret = 'this';
209 for (var i = 0; i < funinfo.depth; ++i) {
210 ret += '.parent';
211 }
212 for (var i in args) {
213 args[i] = args[i].toJS(symbols);
214 }
215 ret += (new symbol(name)).toJS(symbols) + '(' + args.join(', ') + ')';
216 return ret;
217 case 'local':
218 case 'upvar':
219 case 'foreign':
220 for (var i in args) {
221 args[i] = args[i].toJS(symbols);
222 }
223 return (new symbol(name)).toJS(symbols) + '(' + args.join(', ') + ')';
224 }
225 }
226 38
227 function object(messages) 39 function object(messages)
228 { 40 {
229 this.messages = messages; 41 this.messages = messages;
230 }
231 object.prototype.toJS = function(symbols) {
232 var messages = this.messages;
233 symbols = new osymbols(symbols);
234 var compiled = []
235 for (var i in messages) {
236 var js = messages[i].toJSObject(symbols);
237 if (js) {
238 compiled.push(indent(js));
239 }
240 }
241 return '{\n\tparent: ' + symbols.parentObject() + ',\n\t' + compiled.join(',\n\t') + '\n}';
242 }
243
244 object.prototype.toJSModule = function() {
245 return '(function () {\n\tvar module = ' + indent(this.toJS(null)) + ';\n\treturn module;\n})'
246 } 42 }
247 43
248 function lambda(args, expressions) 44 function lambda(args, expressions)
249 { 45 {
250 this.args = args; 46 this.args = args;
251 this.expressions = expressions; 47 this.expressions = expressions;
252 } 48 }
253 lambda.prototype.toJS = function(symbols) {
254 var args = this.args ? this.args.slice(0, this.args.length) : [];
255 if (args.length && args[0].cleanName() == 'self') {
256 args.splice(0, 1);
257 }
258 var exprs = this.expressions;
259 symbols = new lsymbols(symbols);
260 for (var i in args) {
261 symbols.defineVar(args[i].cleanName(), null);
262 args[i] = args[i].toJS(symbols);
263 }
264 var compiled = []
265 for (var i in exprs) {
266 var js = exprs[i].toJS(symbols);
267 if (js) {
268 compiled.push(indent(js));
269 }
270 }
271 exprs = compiled;
272 if (exprs.length) {
273 exprs[exprs.length-1] = 'return ' + exprs[exprs.length-1] + ';';
274 }
275 return 'function (' + args.join(', ') + ') {\n\t' + (symbols.needsSelfVar ? 'var self = this;\n\t' : '') + exprs.join(';\n\t') + '\n}'
276 };
277 lambda.prototype.toJSModule = lambda.prototype.toJS
278 49
279 function assignment(sym, expr) 50 function assignment(sym, expr)
280 { 51 {
281 this.symbol = sym; 52 this.symbol = sym;
282 this.expression = expr; 53 this.expression = expr;
283 } 54 }
284 assignment.prototype.toJS = function(symbols) {
285 var existing = symbols.find(this.symbol.name);
286 var prefix = '';
287 if (!existing) {
288 symbols.defineVar(this.symbol.name, this.expression);
289 prefix = 'var ';
290 } else {
291 switch (existing.type)
292 {
293 case 'self':
294 prefix = 'this.';
295 break;
296 case 'parent':
297 prefix = 'this.';
298 for (var i = 0; i < existing.depth; ++i) {
299 prefix += 'parent.';
300 }
301 break;
302 }
303 }
304 if (this.expression instanceof funcall && this.expression.name == 'foreign:') {
305 return null;
306 }
307 return prefix + this.symbol.toJS(symbols) + ' = ' + this.expression.toJS(symbols);
308 };
309 assignment.prototype.toJSObject = function(symbols) {
310 symbols.defineMsg(this.symbol.name, this.expression);
311 if (this.expression instanceof funcall && this.expression.name == 'foreign:') {
312 return null;
313 }
314 return this.symbol.toJS(symbols) + ': ' + this.expression.toJS(symbols);
315 };
316 55
317 var grammar = 56 var grammar =
318 'start = ws module:(object / lambda) ws { return module; };' + 57 'start = ws module:(object / lambda) ws { return module; };' +
319 'ws = ([ \\t\\n\\r] / "//" [^\\n]* "\\n")*;' + 58 'ws = ([ \\t\\n\\r] / "//" [^\\n]* "\\n")*;' +
320 'hws = ([ \\t] / "/*" ([^*] / "*" ! "/")* "*/" )*;' + 59 'hws = ([ \\t] / "/*" ([^*] / "*" ! "/")* "*/" )*;' +
337 'methcall = receiver:opexpr hws info:methcallrest { info.receiver = receiver; return info; };' + 76 'methcall = receiver:opexpr hws info:methcallrest { info.receiver = receiver; return info; };' +
338 'methcallrest = funcall / unarymeth;' + 77 'methcallrest = funcall / unarymeth;' +
339 'unarymeth = name:symbol { return new funcall(name.name, []); };'; 78 'unarymeth = name:symbol { return new funcall(name.name, []); };';
340 var parser = PEG.buildParser(grammar); 79 var parser = PEG.buildParser(grammar);
341 80
342 //var parser = PEG.buildParser('start = expr; expr = int; int = digits:[0-9]+ { return parseInt(digits.join(""), 10); }');
343 onReady(function() {
344 q('#parse').onclick = function() {
345 var text = q('textarea').value;
346 try {
347 var parsed = parser.parse(text);
348 q('pre').innerHTML = text + "\n\n" + JSON.stringify(parsed);
349 console.log(parsed);
350 } catch(e) {
351 q('pre').innerHTML = e.message + '\nLine: ' + e.line + '\nCol: ' + e.column;
352 }
353 }
354 q('#tojs').onclick = function() {
355 var text = q('textarea').value;
356 //try {
357 var parsed = parser.parse(text);
358 var js = parsed.toJSModule();
359 q('pre').innerHTML = js;
360 console.log(parsed);
361 /*} catch(e) {
362 q('pre').innerHTML = e.message + '\nLine: ' + e.line + '\nCol: ' + e.column;
363 }*/
364 }
365 q('#run').onclick = function() {
366 var text = q('textarea').value;
367 //try {
368 var parsed = parser.parse(text);
369 var js = parsed.toJSModule();
370 mainModule = eval(js)();
371 q('pre').innerHTML = mainModule.main();
372 /*} catch(e) {
373 q('pre').innerHTML = e.message + '\nLine: ' + e.line + '\nCol: ' + e.column;
374 }*/
375 }
376 });
377 81