comparison parser.js @ 6:554602d4cbc6

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