1 // Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL]
7 /** Parses a stream of lexed tokens into a tree of CompiledFunction's */
8 class Parser extends Lexer implements ByteCodes {
11 // Constructors //////////////////////////////////////////////////////
13 public Parser(Reader r, String sourceName, int line) throws IOException { super(r, sourceName, line); }
16 public static void main(String[] s) throws Exception {
17 CompiledFunction block = new CompiledFunction("stdin", 0, new InputStreamReader(System.in), null);
18 if (block == null) return;
19 System.out.println(block);
23 // Statics ////////////////////////////////////////////////////////////
25 static byte[] precedence = new byte[MAX_TOKEN + 1];
26 static boolean[] isRightAssociative = new boolean[MAX_TOKEN + 1];
28 isRightAssociative[ASSIGN] = true;
30 precedence[ASSIGN] = 1;
32 precedence[COMMA] = 3;
33 precedence[OR] = precedence[AND] = 4;
34 precedence[GT] = precedence[GE] = 5;
35 precedence[BITOR] = 6;
36 precedence[BITXOR] = 7;
37 precedence[BITAND] = 8;
38 precedence[EQ] = precedence[NE] = 9;
39 precedence[LT] = precedence[LE] = 10;
40 precedence[SHEQ] = precedence[SHNE] = 11;
41 precedence[LSH] = precedence[RSH] = precedence[URSH] = 12;
42 precedence[ADD] = precedence[SUB] = 13;
43 precedence[MUL] = precedence[DIV] = precedence[MOD] = 14;
44 precedence[BITNOT] = 15;
45 precedence[INC] = precedence[DEC] = 16;
52 // Parsing Logic /////////////////////////////////////////////////////////
54 /** the label for the current statement */
55 private String label = null;
57 /** gets a token and throws an exception if it is not <tt>code</tt> */
58 public void consume(int code) throws IOException {
59 if (getToken() != code)
60 throw new ParserException("expected " + codeToString[code] + ", got " + (op == -1 ? "EOF" : codeToString[op]));
63 /** append the largest expression beginning with prefix containing no operators of precedence below <tt>minPrecedence</tt> */
64 public void startExpr(CompiledFunction block) throws IOException { startExpr(-1, block); }
66 public CompiledFunction startExpr(int minPrecedence) throws IOException {
67 CompiledFunction ret = new CompiledFunction(line, sourceName);
68 startExpr(minPrecedence, ret);
69 return ret.size() == 0 ? null : ret;
73 public void startExpr(int minPrecedence, CompiledFunction appendTo) throws IOException {
76 CompiledFunction b = appendTo;
79 case NUMBER: continueExpr(b.add(line, CompiledFunction.LITERAL, number), minPrecedence); return;
80 case STRING: continueExpr(b.add(line, CompiledFunction.LITERAL, string), minPrecedence); return;
81 case THIS: continueExpr(b.add(line, TOPSCOPE, null), minPrecedence); return;
82 case NULL: continueExpr(b.add(line, CompiledFunction.LITERAL, null), minPrecedence); return;
83 case TRUE: case FALSE: continueExpr(b.add(line, CompiledFunction.LITERAL, new Boolean(tok == TRUE)), minPrecedence); return;
85 b.add(line, ARRAY, new Integer(0));
91 if (peekToken() == RB) { consume(RB); continueExpr(b, minPrecedence); return; }
92 b.add(line, LITERAL, new Integer(i++));
93 if (size == b.size()) b.add(line, LITERAL, null);
96 if (peekToken() == RB) { consume(RB); continueExpr(b, minPrecedence); return; }
102 b.add(line, CompiledFunction.LITERAL, new Double(number.doubleValue() * -1));
103 continueExpr(b, minPrecedence);
109 continueExpr(b, minPrecedence);
112 case INC: case DEC: {
114 startExpr(precedence[tok], b);
115 b.set(b.size() - 1, tok, new Boolean(true));
116 continueExpr(b, minPrecedence);
119 case BANG: case BITNOT: case TYPEOF: {
120 startExpr(precedence[tok], b);
122 continueExpr(b, minPrecedence);
126 b.add(line, OBJECT, null);
127 if (peekToken() != RC) while(true) {
128 if (peekToken() != NAME && peekToken() != STRING) throw new Error("expected NAME or STRING");
130 b.add(line, LITERAL, string);
135 if (peekToken() == RC) break;
137 if (peekToken() == RC) break;
140 continueExpr(b, minPrecedence);
144 String name = string;
145 if (peekToken() == ASSIGN) {
147 b.add(line, TOPSCOPE);
148 b.add(line, CompiledFunction.LITERAL, name);
149 startExpr(minPrecedence, b);
150 b.add(line, CompiledFunction.PUT);
151 b.add(line, CompiledFunction.SWAP);
152 b.add(line, CompiledFunction.POP);
154 b.add(line, TOPSCOPE);
155 b.add(line, CompiledFunction.LITERAL, name);
156 b.add(line, CompiledFunction.GET);
158 continueExpr(b, minPrecedence);
164 CompiledFunction b2 = new CompiledFunction(sourceName, line, null);
165 b2.add(line, TOPSCOPE);
167 b2.add(line, LITERAL, "arguments");
168 b2.add(line, LITERAL, "arguments");
169 b2.add(line, DECLARE);
174 if (peekToken() == RP) consume(RP);
176 if (peekToken() == COMMA) {
183 b2.add(line, LITERAL, string);
184 b2.add(line, DECLARE);
186 // retrieve it from the arguments array
187 b2.add(line, LITERAL, new Integer(numArgs));
188 b2.add(line, GET_PRESERVE);
192 // put it to the current scope
193 b2.add(line, TOPSCOPE);
195 b2.add(line, LITERAL, string);
203 if (peekToken() == RP) { consume(RP); break; }
208 // pop off the arguments array
210 parseStatement(true, b2);
211 b2.add(line, LITERAL, null);
212 b2.add(line, RETURN);
213 continueExpr(b.add(line, NEWFUNCTION, b2), minPrecedence);
216 default: pushBackToken(); return;
220 public void continueExpr(CompiledFunction prefix, int minPrecedence) throws IOException {
221 int tok = getToken();
222 if (tok == -1) return;
223 if (minPrecedence > 0 && precedence[tok] != 0)
224 if (precedence[tok] < minPrecedence || (precedence[tok] == minPrecedence && !isRightAssociative[tok]))
225 { pushBackToken(); return; }
226 if (prefix == null) throw new Error("got null prefix");
227 CompiledFunction b = prefix;
231 case ASSIGN_BITOR: case ASSIGN_BITXOR: case ASSIGN_BITAND: case ASSIGN_LSH: case ASSIGN_RSH: case ASSIGN_URSH:
232 case ASSIGN_ADD: case ASSIGN_SUB: case ASSIGN_MUL: case ASSIGN_DIV: case ASSIGN_MOD: {
233 prefix.set(prefix.size() - 1, b.GET_PRESERVE, new Boolean(true));
234 startExpr(precedence[tok - 1], b);
235 prefix.add(line, tok - 1);
236 prefix.add(line, PUT);
237 prefix.add(line, SWAP);
238 prefix.add(line, POP);
239 continueExpr(b, minPrecedence);
243 case INC: case DEC: {
245 b.set(b.size() - 1, tok, new Boolean(false));
246 continueExpr(b, minPrecedence);
253 while(peekToken() != RP) {
256 if (peekToken() == RP) break;
260 b.add(line, CALL, new Integer(i));
261 continueExpr(b, minPrecedence);
265 case BITOR: case BITXOR: case BITAND: case SHEQ: case SHNE: case LSH:
266 case RSH: case URSH: case ADD: case MUL: case DIV: case MOD:
267 case GT: case GE: case EQ: case NE: case LT: case LE: case SUB: {
268 startExpr(precedence[tok], b);
270 continueExpr(b, minPrecedence);
275 b.add(line, tok == AND ? b.JF : b.JT, new Integer(0));
277 startExpr(precedence[tok], b);
278 b.set(size - 1, new Integer(b.size() - size + 2));
279 b.add(line, JMP, new Integer(2));
280 b.add(line, LITERAL, tok == AND ? new Boolean(false) : new Boolean(true));
281 continueExpr(b, minPrecedence);
287 String target = string;
288 if (peekToken() == ASSIGN) {
290 b.add(line, LITERAL, target);
296 b.add(line, LITERAL, target);
299 continueExpr(b, minPrecedence);
306 if (peekToken() == ASSIGN) {
315 continueExpr(b, minPrecedence);
320 b.add(line, JF, new Integer(0));
323 b.set(size - 1, new Integer(b.size() - size + 2));
324 b.add(line, JMP, new Integer(0));
328 b.set(size - 1, new Integer(b.size() - size + 1));
329 continueExpr(b, minPrecedence);
333 default: { pushBackToken(); return; }
338 * a block is either a single statement or a list of statements surrounded by curly braces;
339 * all expressions are also statements
341 public CompiledFunction parseStatement() throws IOException {
342 CompiledFunction ret = new CompiledFunction(sourceName, line, null);
343 ret.add(line, ret.LITERAL, null);
344 parseStatement(false, ret);
345 if (ret.size() == 1) return null;
349 public void parseStatement(boolean requireBraces, CompiledFunction b) throws IOException {
350 int tok = peekToken();
351 if (tok == -1) return;
352 int line = this.line;
353 boolean braced = tok == LC;
354 if (requireBraces && !braced) throw new ParserException("expected {, got " + codeToString[tok]);
355 if (braced) consume(LC);
357 switch(tok = peekToken()) {
359 case THROW: case RETURN: case ASSERT: {
361 if (tok == RETURN && peekToken() == SEMI) b.add(line, LITERAL, null);
368 case BREAK: case CONTINUE: {
370 if (peekToken() == NAME) consume(NAME);
371 b.add(line, tok, string);
384 b.add(line, TOPSCOPE); // push the current scope
387 String name = string;
388 b.add(line, LITERAL, name); // push the name to be declared
389 b.add(line, DECLARE); // declare it
390 if (peekToken() == ASSIGN) { // if there is an '=' after the variable name
391 b.add(line, LITERAL, name); // put the var name back on the stack
397 if (peekToken() != COMMA) break;
401 if (peekToken() == SEMI) consume(SEMI);
411 b.add(line, JF, new Integer(0));
413 parseStatement(false, b);
415 if (peekToken() == ELSE) {
417 b.set(size - 1, new Integer(2 + b.size() - size));
418 b.add(line, JMP, new Integer(0));
420 parseStatement(false, b);
422 b.set(size - 1, new Integer(1 + b.size() - size));
429 if (label != null) b.add(line, LABEL, label);
434 b.add(line, JT, new Integer(2));
437 parseStatement(false, b);
438 b.add(line, CONTINUE); // if we fall out of the end, definately continue
439 b.set(size - 1, new Integer(b.size() - size + 1)); // end of the loop
446 if (label != null) b.add(line, LABEL, label);
448 int size0 = b.size();
453 if (peekToken() == CASE) {
459 b.add(line, JF, new Integer(0));
461 while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) {
462 int size2 = b.size();
463 parseStatement(false, b);
464 if (size2 == b.size()) break;
466 b.set(size - 1, new Integer(1 + b.size() - size));
467 } else if (peekToken() == DEFAULT) {
470 while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) {
471 int size2 = b.size();
472 parseStatement(false, b);
473 if (size2 == b.size()) break;
475 } else if (peekToken() == RC) {
480 throw new ParserException("expected CASE, DEFAULT, or RC; got " + codeToString[peekToken()]);
483 b.set(size0 - 1, new Integer(b.size() - size0 + 1)); // end of the loop
489 if (label != null) b.add(line, LABEL, label);
492 parseStatement(false, b);
496 b.add(line, JT, new Integer(2));
498 b.add(line, CONTINUE);
501 b.set(size - 1, new Integer(b.size() - size + 1)); // end of the loop
506 // We deliberately allow you to omit braces in catch{}/finally{} if they are single statements...
510 parseStatement(true, b);
511 b.add(line, POP); // pop the TryMarker
512 b.add(line, JMP); // jump forward to the end of the catch block
513 int size2 = b.size();
514 b.set(size - 1, new Integer(b.size() - size + 1));// the TRY argument points at the start of the CATCH block
516 if (peekToken() == CATCH) {
521 // FIXME, we need an extra scope here
522 b.add(line, TOPSCOPE); // the exception is on top of the stack; put it to the variable
524 b.add(line, LITERAL);
529 parseStatement(false, b);
532 b.set(size2 - 1, new Integer(b.size() - size2 + 1)); // jump here if no exception was thrown
534 // FIXME: not implemented correctly
535 if (peekToken() == FINALLY) {
537 parseStatement(false, b);
547 boolean hadVar = false;
548 if (tok == VAR) { hadVar = true; tok = getToken(); }
549 String varName = string;
550 boolean forIn = peekToken() == IN;
551 pushBackToken(tok, varName);
554 // FIXME: break needs to work in here
558 b.add(line, PUSHKEYS);
559 b.add(line, LITERAL, "length");
562 CompiledFunction b2 = new CompiledFunction(sourceName, line, null);
564 b.add(line, NEWSCOPE);
566 b.add(line, LITERAL, new Integer(1));
569 b.add(line, LITERAL, new Integer(0));
571 b.add(line, JT, new Integer(7));
572 b.add(line, GET_PRESERVE);
573 b.add(line, LITERAL, varName);
574 b.add(line, LITERAL, varName);
575 b.add(line, DECLARE);
577 parseStatement(false, b);
579 b.add(line, OLDSCOPE);
584 if (hadVar) pushBackToken(VAR, null);
585 b.add(line, NEWSCOPE);
587 parseStatement(false, b);
588 CompiledFunction e2 = new CompiledFunction(sourceName, line, null);
591 if (e2 == null) e2 = new CompiledFunction(sourceName, line, null).add(line, b.LITERAL, Boolean.TRUE);
593 if (label != null) b.add(line, LABEL, label);
595 int size2 = b.size();
597 b.add(line, JT, new Integer(0));
600 if (b.size() > size) b.add(line, POP);
601 b.set(size - 1, new Integer(b.size() - size + 1));
605 b.add(line, JT, new Integer(2));
607 parseStatement(false, b);
608 b.add(line, CONTINUE);
609 b.set(size2 - 1, new Integer(b.size() - size2 + 1)); // end of the loop
611 b.add(line, OLDSCOPE);
618 String oldlabel = label;
620 if (peekToken() == COLON) {
622 parseStatement(false, b);
626 pushBackToken(NAME, label);
628 // fall through to default case
633 if (tok == RC && braced) { consume(RC); return; }
638 if (size == b.size()) return;
640 if (peekToken() == SEMI) consume(SEMI);
649 private class ParserException extends RuntimeException {
650 public ParserException(String s) { super(sourceName + ":" + line + " " + s); }