1 // Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL]
7 /** Parses a stream of lexed tokens into a tree of ByteCodeBlock'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 Parser p = new Parser(new InputStreamReader(System.in), "stdin", 0);
19 ByteCodeBlock block = new ByteCodeBlock(0, "stdin");
20 p.parseStatement(false, block);
21 if (block == null) return;
22 System.out.println(block);
23 if (p.peekToken() == -1) return;
28 // Statics ////////////////////////////////////////////////////////////
30 static byte[] precedence = new byte[MAX_TOKEN + 1];
31 static boolean[] isRightAssociative = new boolean[MAX_TOKEN + 1];
33 isRightAssociative[ASSIGN] = true;
35 precedence[ASSIGN] = 1;
37 precedence[COMMA] = 3;
38 precedence[OR] = precedence[AND] = 4;
39 precedence[GT] = precedence[GE] = 5;
40 precedence[BITOR] = 6;
41 precedence[BITXOR] = 7;
42 precedence[BITAND] = 8;
43 precedence[EQ] = precedence[NE] = 9;
44 precedence[LT] = precedence[LE] = 10;
45 precedence[SHEQ] = precedence[SHNE] = 11;
46 precedence[LSH] = precedence[RSH] = precedence[URSH] = 12;
47 precedence[ADD] = precedence[SUB] = 13;
48 precedence[MUL] = precedence[DIV] = precedence[MOD] = 14;
49 precedence[BITNOT] = 15;
50 precedence[INC] = precedence[DEC] = 16;
57 // Parsing Logic /////////////////////////////////////////////////////////
59 private ByteCodeBlock newbb(int line) { return new ByteCodeBlock(line, sourceName); }
61 /** gets a token and throws an exception if it is not <tt>code</tt> */
62 public void consume(int code) throws IOException {
63 if (getToken() != code)
64 throw new ParserException("expected " + codeToString[code] + ", got " + (op == -1 ? "EOF" : codeToString[op]));
67 /** append the largest expression beginning with prefix containing no operators of precedence below <tt>minPrecedence</tt> */
68 public ByteCodeBlock startExpr() throws IOException { return startExpr(-1); }
69 public void startExpr(ByteCodeBlock block) throws IOException { startExpr(-1, block); }
70 public ByteCodeBlock startExpr(int minPrecedence) throws IOException {
71 ByteCodeBlock ret = new ByteCodeBlock(line, sourceName);
72 startExpr(minPrecedence, ret);
73 return ret.size() == 0 ? null : ret;
76 public void startExpr(int minPrecedence, ByteCodeBlock appendTo) throws IOException {
79 if (tok == -1) return;
81 ByteCodeBlock b = appendTo;
84 case NUMBER: continueExpr(b.add(ByteCodeBlock.LITERAL, number), minPrecedence); return;
85 case STRING: continueExpr(b.add(ByteCodeBlock.LITERAL, string), minPrecedence); return;
86 case THIS: continueExpr(b.add(TOPSCOPE, null), minPrecedence); return;
87 case NULL: continueExpr(b.add(ByteCodeBlock.LITERAL, null), minPrecedence); return;
88 case TRUE: case FALSE: continueExpr(b.add(ByteCodeBlock.LITERAL, new Boolean(tok == TRUE)), minPrecedence); return;
90 b.add(ARRAY, new Integer(0));
96 if (peekToken() == RB) { consume(RB); continueExpr(b, minPrecedence); return; }
97 b.add(LITERAL, new Integer(i++));
98 if (size == b.size()) b.add(LITERAL, null);
101 if (peekToken() == RB) { consume(RB); continueExpr(b, minPrecedence); return; }
107 b.add(ByteCodeBlock.LITERAL, new Double(number.doubleValue() * -1));
108 continueExpr(b, minPrecedence);
114 continueExpr(b, minPrecedence);
117 case INC: case DEC: {
119 startExpr(precedence[tok], b);
120 b.set(b.size() - 1, tok, new Boolean(true));
121 continueExpr(b, minPrecedence);
124 case BANG: case BITNOT: case TYPEOF: {
125 startExpr(precedence[tok], b);
127 continueExpr(b, minPrecedence);
132 if (peekToken() != RC) while(true) {
133 if (peekToken() != NAME && peekToken() != STRING) throw new Error("expected NAME or STRING");
135 b.add(LITERAL, string);
140 if (peekToken() == RC) break;
142 if (peekToken() == RC) break;
145 continueExpr(b, minPrecedence);
149 String name = string;
150 if (peekToken() == ASSIGN) {
153 b.add(ByteCodeBlock.LITERAL, name);
154 startExpr(minPrecedence, b);
155 b.add(ByteCodeBlock.PUT);
156 b.add(ByteCodeBlock.SWAP);
157 b.add(ByteCodeBlock.POP);
160 b.add(ByteCodeBlock.LITERAL, name);
161 b.add(ByteCodeBlock.GET);
163 continueExpr(b, minPrecedence);
169 ByteCodeBlock b2 = newbb(curLine);
172 b2.add(LITERAL, "arguments");
173 b2.add(LITERAL, "arguments");
179 if (peekToken() == RP) consume(RP);
181 if (peekToken() == COMMA) {
188 b2.add(LITERAL, string);
191 // retrieve it from the arguments array
192 b2.add(LITERAL, new Integer(numArgs));
193 b2.add(GET_PRESERVE);
197 // put it to the current scope
200 b2.add(LITERAL, string);
208 if (peekToken() == RP) { consume(RP); break; }
213 // pop off the arguments array
215 parseStatement(true, b2);
216 b2.add(LITERAL, null);
218 continueExpr(b.add(NEWFUNCTION, b2), minPrecedence);
221 default: pushBackToken(); return;
225 public void continueExpr(ByteCodeBlock prefix, int minPrecedence) throws IOException {
226 int tok = getToken();
228 if (tok == -1) return;
229 if (minPrecedence > 0 && precedence[tok] != 0)
230 if (precedence[tok] < minPrecedence || (precedence[tok] == minPrecedence && !isRightAssociative[tok]))
231 { pushBackToken(); return; }
233 if (prefix == null) throw new Error("got null prefix");
234 ByteCodeBlock b = prefix;
238 case ASSIGN_BITOR: case ASSIGN_BITXOR: case ASSIGN_BITAND: case ASSIGN_LSH: case ASSIGN_RSH: case ASSIGN_URSH:
239 case ASSIGN_ADD: case ASSIGN_SUB: case ASSIGN_MUL: case ASSIGN_DIV: case ASSIGN_MOD: {
240 prefix.set(prefix.size() - 1, b.GET_PRESERVE, new Boolean(true));
241 startExpr(precedence[tok - 1], b);
246 continueExpr(b, minPrecedence);
250 case INC: case DEC: {
252 b.set(b.size() - 1, tok, new Boolean(false));
253 continueExpr(b, minPrecedence);
260 while(peekToken() != RP) {
263 if (peekToken() == RP) break;
267 b.add(CALL, new Integer(i));
268 continueExpr(b, minPrecedence);
272 case BITOR: case BITXOR: case BITAND: case SHEQ: case SHNE: case LSH:
273 case RSH: case URSH: case ADD: case MUL: case DIV: case MOD:
274 case GT: case GE: case EQ: case NE: case LT: case LE: case SUB: {
275 startExpr(precedence[tok], b);
277 continueExpr(b, minPrecedence);
282 b.add(tok == AND ? b.JF : b.JT, new Integer(0));
284 startExpr(precedence[tok], b);
285 b.set(size - 1, new Integer(b.size() - size + 2));
286 b.add(JMP, new Integer(2));
287 b.add(LITERAL, tok == AND ? new Boolean(false) : new Boolean(true));
288 continueExpr(b, minPrecedence);
294 String target = string;
295 if (peekToken() == ASSIGN) {
297 b.add(LITERAL, target);
303 b.add(LITERAL, target);
306 continueExpr(b, minPrecedence);
313 if (peekToken() == ASSIGN) {
322 continueExpr(b, minPrecedence);
327 b.add(JF, new Integer(0));
330 b.set(size - 1, new Integer(b.size() - size + 2));
331 b.add(JMP, new Integer(0));
335 b.set(size - 1, new Integer(b.size() - size + 1));
336 continueExpr(b, minPrecedence);
340 default: { pushBackToken(); return; }
344 /** a block is either a single statement or a list of statements surrounded by curly braces; all expressions are also statements */
345 public ByteCodeBlock parseStatement() throws IOException {
346 ByteCodeBlock ret = new ByteCodeBlock(line, sourceName);
347 ret.add(ret.LITERAL, null);
348 parseStatement(false, ret);
349 if (ret.size() == 1) return null;
353 public void parseStatement(boolean requireBraces) throws IOException {
354 parseStatement(requireBraces, new ByteCodeBlock(line, sourceName));
356 public void parseStatement(boolean requireBraces, ByteCodeBlock b) throws IOException {
357 int tok = peekToken();
358 if (tok == -1) return;
359 boolean braced = tok == LC;
360 if (requireBraces && !braced) throw new ParserException("expected {, got " + codeToString[tok]);
361 if (braced) consume(LC);
364 switch(tok = peekToken()) {
366 case THROW: case RETURN: case ASSERT: {
368 if (tok == RETURN && peekToken() == SEMI) b.add(LITERAL, null);
375 case BREAK: case CONTINUE: {
377 if (peekToken() == NAME) consume(NAME);
390 b.add(TOPSCOPE); // push the current scope
393 String name = string;
394 b.add(LITERAL, name); // push the name to be declared
395 b.add(DECLARE); // declare it
396 if (peekToken() == ASSIGN) { // if there is an '=' after the variable name
397 b.add(LITERAL, name); // put the var name back on the stack
403 if (peekToken() != COMMA) break;
407 if (peekToken() == SEMI) consume(SEMI);
417 b.add(JF, new Integer(0));
419 parseStatement(false, b);
421 if (peekToken() == ELSE) {
423 b.set(size - 1, new Integer(2 + b.size() - size));
424 b.add(JMP, new Integer(0));
426 parseStatement(false, b);
428 b.set(size - 1, new Integer(1 + b.size() - size));
435 ByteCodeBlock loop = newbb(curLine);
440 loop.add(JT, new Integer(2));
443 parseStatement(false, loop);
445 // if we fall out of the end, definately continue
453 ByteCodeBlock loop = newbb(curLine);
459 if (peekToken() == CASE) {
465 loop.add(JF, new Integer(0));
466 int size = loop.size();
467 while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) {
468 int size2 = loop.size();
469 parseStatement(false, loop);
470 if (size2 == loop.size()) break;
472 loop.set(size - 1, new Integer(1 + loop.size() - size));
473 } else if (peekToken() == DEFAULT) {
476 while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) {
477 int size2 = loop.size();
478 parseStatement(false, loop);
479 if (size2 == loop.size()) break;
481 } else if (peekToken() == RC) {
486 throw new ParserException("expected CASE, DEFAULT, or RC; got " + codeToString[peekToken()]);
493 ByteCodeBlock loop = newbb(curLine);
496 parseStatement(false, loop);
500 loop.add(JT, new Integer(2));
509 // We deliberately allow you to omit braces in catch{}/finally{} if they are single statements...
511 parseStatement(true, b);
513 if (peekToken() == CATCH) {
518 parseStatement(); // just discard the catchblock
521 if (peekToken() == FINALLY) {
523 parseStatement(false, b);
533 boolean hadVar = false;
534 if (tok == VAR) { hadVar = true; tok = getToken(); }
535 String varName = string;
536 boolean forIn = peekToken() == IN;
537 pushBackToken(tok, varName);
544 b.add(LITERAL, "length");
547 ByteCodeBlock b2 = newbb(curLine);
549 b2.add(LITERAL, new Integer(1));
552 b2.add(LITERAL, new Integer(0));
554 b2.add(JT, new Integer(7));
555 b2.add(GET_PRESERVE);
556 b2.add(LITERAL, varName);
557 b2.add(LITERAL, varName);
560 parseStatement(false, b2);
564 if (hadVar) pushBackToken(VAR, null);
565 ByteCodeBlock b2 = newbb(curLine);
569 int size = b2.size();
570 parseStatement(false, b2);
571 ByteCodeBlock e2 = startExpr();
573 if (e2 == null) e2 = newbb(curLine).add(b.LITERAL, null);
575 ByteCodeBlock b3 = newbb(curLine);
577 b2.add(LITERAL, null);
579 b3.add(JT, new Integer(0));
583 if (b3.size() - size > 0) b3.add(POP);
584 b3.set(size - 1, new Integer(b3.size() - size + 1));
587 b3.add(JT, new Integer(2));
589 parseStatement(false, b3);
597 String name = string;
598 if (peekToken() == COLON) {
600 b.add(ByteCodeBlock.LABEL, string);
603 pushBackToken(NAME, name);
604 // fall through to default case
609 if (tok == RC && braced) { consume(RC); return; }
614 if (size == b.size()) return;
616 if (peekToken() == SEMI) consume(SEMI);
625 class ParserException extends RuntimeException {
626 public ParserException(String s) { super(sourceName + ":" + line + " " + s); }