1 // Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL]
7 // FEATURE intern Integers/Numbers
10 * Parses a stream of lexed tokens into a tree of JSFunction's.
12 * There are three kinds of things we parse: blocks, statements, and
15 * - Expressions are a special type of statement that evaluates to a
16 * value (for example, "break" is not an expression, * but "3+2"
17 * is). Some tokens sequences start expressions (for * example,
18 * literal numbers) and others continue an expression which * has
19 * already been begun (for example, '+'). Finally, some *
20 * expressions are valid targets for an assignment operation; after
21 * * each of these expressions, continueExprAfterAssignable() is
22 * called * to check for an assignment operation.
24 * - A statement ends with a semicolon and does not return a value.
26 * - A block is a single statement or a sequence of statements
27 * surrounded by curly braces.
29 * Each parsing method saves the parserLine before doing its actual
30 * work and restores it afterwards. This ensures that parsing a
31 * subexpression does not modify the line number until a token
32 * *after* the subexpression has been consumed by the parent
35 * Technically it would be a better design for this class to build an
36 * intermediate parse tree and use that to emit bytecode. Here's the
39 * Advantages of building a parse tree:
40 * - easier to apply optimizations
41 * - would let us handle more sophisticated languages than JavaScript
43 * Advantages of leaving out the parse tree
44 * - faster compilation
45 * - less load on the garbage collector
46 * - much simpler code, easier to understand
49 * Fortunately JS is such a simple language that we can get away with
50 * the half-assed approach and still produce a working, complete
53 * The bytecode language emitted doesn't really cause any appreciable
54 * semantic loss, and is itself a parseable language very similar to
55 * Forth or a postfix variant of LISP. This means that the bytecode
56 * can be transformed into a parse tree, which can be manipulated.
57 * So if we ever want to add an optimizer, it could easily be done by
58 * producing a parse tree from the bytecode, optimizing that tree,
59 * and then re-emitting the bytecode. The parse tree node class
60 * would also be much simpler since the bytecode language has so few
63 * Actually, the above paragraph is slightly inaccurate -- there are
64 * places where we push a value and then perform an arbitrary number
65 * of operations using it before popping it; this doesn't parse well.
66 * But these cases are clearly marked and easy to change if we do
67 * need to move to a parse tree format.
69 class Parser extends Lexer implements ByteCodes {
72 // Constructors //////////////////////////////////////////////////////
74 public Parser(Reader r, String sourceName, int line) throws IOException { super(r, sourceName, line); }
77 public static void main(String[] s) throws Exception {
78 JSFunction block = new JSFunction("stdin", 0, new InputStreamReader(System.in), null);
79 if (block == null) return;
80 System.out.println(block);
84 // Statics ////////////////////////////////////////////////////////////
86 static byte[] precedence = new byte[MAX_TOKEN + 1];
87 static boolean[] isRightAssociative = new boolean[MAX_TOKEN + 1];
88 // Use this as the precedence when we want anything up to the comma
89 private final static int NO_COMMA = 2;
91 isRightAssociative[ASSIGN] =
92 isRightAssociative[ASSIGN_BITOR] =
93 isRightAssociative[ASSIGN_BITXOR] =
94 isRightAssociative[ASSIGN_BITAND] =
95 isRightAssociative[ASSIGN_LSH] =
96 isRightAssociative[ASSIGN_RSH] =
97 isRightAssociative[ASSIGN_URSH] =
98 isRightAssociative[ASSIGN_ADD] =
99 isRightAssociative[ASSIGN_SUB] =
100 isRightAssociative[ASSIGN_MUL] =
101 isRightAssociative[ASSIGN_DIV] =
102 isRightAssociative[ASSIGN_MOD] = true;
104 precedence[COMMA] = 1;
105 // 2 is intentionally left unassigned. we use minPrecedence==2 for comma separated lists
107 precedence[ASSIGN_BITOR] =
108 precedence[ASSIGN_BITXOR] =
109 precedence[ASSIGN_BITAND] =
110 precedence[ASSIGN_LSH] =
111 precedence[ASSIGN_RSH] =
112 precedence[ASSIGN_URSH] =
113 precedence[ASSIGN_ADD] =
114 precedence[ASSIGN_SUB] =
115 precedence[ASSIGN_MUL] =
116 precedence[ASSIGN_DIV] =
117 precedence[ASSIGN_MOD] = 3;
118 precedence[HOOK] = 4;
121 precedence[BITOR] = 7;
122 precedence[BITXOR] = 8;
123 precedence[BITAND] = 9;
124 precedence[EQ] = precedence[NE] = precedence[SHEQ] = precedence[SHNE] = 10;
125 precedence[LT] = precedence[LE] = precedence[GT] = precedence[GE] = 11;
126 precedence[LSH] = precedence[RSH] = precedence[URSH] = 12;
127 precedence[ADD] = precedence[SUB] = 12;
128 precedence[MUL] = precedence[DIV] = precedence[MOD] = 13;
129 precedence[BITNOT] = precedence[BANG] = precedence[TYPEOF] = 14;
130 precedence[DOT] = precedence[LB] = precedence[LP] = precedence[INC] = precedence[DEC] = 15;
134 // Parsing Logic /////////////////////////////////////////////////////////
136 /** gets a token and throws an exception if it is not <tt>code</tt> */
137 private void consume(int code) throws IOException {
138 if (getToken() != code) throw pe("expected " + codeToString[code] + ", got " + (op == -1 ? "EOF" : codeToString[op]));
142 * Parse the largest possible expression containing no operators
143 * of precedence below <tt>minPrecedence</tt> and append the
144 * bytecodes for that expression to <tt>appendTo</tt>; the
145 * appended bytecodes MUST grow the stack by exactly one element.
147 private void startExpr(JSFunction appendTo, int minPrecedence) throws IOException {
148 int saveParserLine = parserLine;
149 _startExpr(appendTo, minPrecedence);
150 parserLine = saveParserLine;
152 private void _startExpr(JSFunction appendTo, int minPrecedence) throws IOException {
153 int tok = getToken();
154 JSFunction b = appendTo;
157 case -1: throw pe("expected expression");
159 // all of these simply push values onto the stack
160 case NUMBER: b.add(parserLine, LITERAL, number); break;
161 case STRING: b.add(parserLine, LITERAL, string); break;
162 case NULL: b.add(parserLine, LITERAL, null); break;
163 case TRUE: case FALSE: b.add(parserLine, LITERAL, new Boolean(tok == TRUE)); break;
166 b.add(parserLine, ARRAY, new Integer(0)); // push an array onto the stack
169 if (peekToken() != RB)
170 while(true) { // iterate over the initialization values
172 b.add(parserLine, LITERAL, new Integer(i++)); // push the index in the array to place it into
173 if (peekToken() == COMMA || peekToken() == RB)
174 b.add(parserLine, LITERAL, null); // for stuff like [1,,2,]
176 startExpr(b, NO_COMMA); // push the value onto the stack
177 b.add(parserLine, PUT); // put it into the array
178 b.add(parserLine, POP); // discard the value remaining on the stack
179 if (peekToken() == RB) break;
182 b.set(size0 - 1, new Integer(i)); // back at the ARRAY instruction, write the size of the array
186 case SUB: { // negative literal (like "3 * -1")
188 b.add(parserLine, LITERAL, new Double(number.doubleValue() * -1));
191 case LP: { // grouping (not calling)
196 case INC: case DEC: { // prefix (not postfix)
197 startExpr(b, precedence[tok]);
198 int prev = b.size - 1;
199 if (b.get(prev) == GET && b.getArg(prev) != null)
200 b.set(prev, LITERAL, b.getArg(prev));
201 else if(b.get(prev) == GET)
204 throw pe("prefixed increment/decrement can only be performed on a valid assignment target");
205 b.add(parserLine, GET_PRESERVE, Boolean.TRUE);
206 b.add(parserLine, LITERAL, new Integer(1));
207 b.add(parserLine, tok == INC ? ADD : SUB, new Integer(2));
208 b.add(parserLine, PUT, null);
209 b.add(parserLine, SWAP, null);
210 b.add(parserLine, POP, null);
213 case BANG: case BITNOT: case TYPEOF: {
214 startExpr(b, precedence[tok]);
215 b.add(parserLine, tok);
218 case LC: { // object constructor
219 b.add(parserLine, OBJECT, null); // put an object on the stack
220 if (peekToken() != RC)
222 if (peekToken() != NAME && peekToken() != STRING)
223 throw pe("expected NAME or STRING");
225 b.add(parserLine, LITERAL, string); // grab the key
227 startExpr(b, NO_COMMA); // grab the value
228 b.add(parserLine, PUT); // put the value into the object
229 b.add(parserLine, POP); // discard the remaining value
230 if (peekToken() == RC) break;
232 if (peekToken() == RC) break; // we permit {,,} -- I'm not sure if ECMA does
238 b.add(parserLine, TOPSCOPE);
239 b.add(parserLine, LITERAL, string);
240 continueExprAfterAssignable(b,minPrecedence);
246 JSFunction b2 = new JSFunction(sourceName, parserLine, null, null);
247 b.add(parserLine, NEWFUNCTION, b2);
249 // function prelude; arguments array is already on the stack
250 b2.add(parserLine, TOPSCOPE);
251 b2.add(parserLine, SWAP);
252 b2.add(parserLine, DECLARE, "arguments"); // declare arguments (equivalent to 'var arguments;')
253 b2.add(parserLine, SWAP); // set this.arguments and leave the value on the stack
254 b2.add(parserLine, PUT);
256 while(peekToken() != RP) { // run through the list of argument names
258 if (peekToken() == NAME) {
259 consume(NAME); // a named argument
260 String varName = string;
262 b2.add(parserLine, DUP); // dup the args array
263 b2.add(parserLine, GET, new Integer(numArgs - 1)); // retrieve it from the arguments array
264 b2.add(parserLine, TOPSCOPE);
265 b2.add(parserLine, SWAP);
266 b2.add(parserLine, DECLARE, varName); // declare the name
267 b2.add(parserLine, SWAP);
268 b2.add(parserLine, PUT);
269 b2.add(parserLine, POP); // pop the value
270 b2.add(parserLine, POP); // pop the scope
272 if (peekToken() == RP) break;
277 b2.numFormalArgs = numArgs;
278 b2.add(parserLine, POP); // pop off the arguments array
279 b2.add(parserLine, POP); // pop off TOPSCOPE
281 if(peekToken() != LC)
282 throw pe("JSFunctions must have a block surrounded by curly brackets");
284 parseBlock(b2, null); // the function body
286 b2.add(parserLine, LITERAL, null); // in case we "fall out the bottom", return NULL
287 b2.add(parserLine, RETURN);
291 default: throw pe("expected expression, found " + codeToString[tok] + ", which cannot start an expression");
294 // attempt to continue the expression
295 continueExpr(b, minPrecedence);
300 * Assuming that a complete assignable (lvalue) has just been
301 * parsed and the object and key are on the stack,
302 * <tt>continueExprAfterAssignable</tt> will attempt to parse an
303 * expression that modifies the assignable. This method always
304 * decreases the stack depth by exactly one element.
306 private void continueExprAfterAssignable(JSFunction b,int minPrecedence) throws IOException {
307 int saveParserLine = parserLine;
308 _continueExprAfterAssignable(b,minPrecedence);
309 parserLine = saveParserLine;
311 private void _continueExprAfterAssignable(JSFunction b,int minPrecedence) throws IOException {
312 if (b == null) throw new Error("got null b; this should never happen");
313 int tok = getToken();
314 if (minPrecedence != -1 && (precedence[tok] < minPrecedence || (precedence[tok] == minPrecedence && !isRightAssociative[tok])))
315 // force the default case
318 case ASSIGN_BITOR: case ASSIGN_BITXOR: case ASSIGN_BITAND: case ASSIGN_LSH: case ASSIGN_RSH: case ASSIGN_URSH:
319 case ASSIGN_MUL: case ASSIGN_DIV: case ASSIGN_MOD: case ASSIGN_ADD: case ASSIGN_SUB: {
320 b.add(parserLine, GET_PRESERVE);
321 startExpr(b, precedence[tok]);
323 if (tok == ASSIGN_ADD || tok == ASSIGN_SUB) {
324 b.add(parserLine, tok);
326 // tok-1 is always s/^ASSIGN_// (0 is BITOR, 1 is ASSIGN_BITOR, etc)
327 b.add(parserLine, tok - 1, tok-1==ADD ? new Integer(2) : null);
328 b.add(parserLine, PUT);
329 b.add(parserLine, SWAP);
330 b.add(parserLine, POP);
331 if (tok == ASSIGN_ADD || tok == ASSIGN_SUB) b.set(size, tok, new Integer(b.size - size));
334 case INC: case DEC: { // postfix
335 b.add(parserLine, GET_PRESERVE, Boolean.TRUE);
336 b.add(parserLine, LITERAL, new Integer(1));
337 b.add(parserLine, tok == INC ? ADD : SUB, new Integer(2));
338 b.add(parserLine, PUT, null);
339 b.add(parserLine, SWAP, null);
340 b.add(parserLine, POP, null);
341 b.add(parserLine, LITERAL, new Integer(1));
342 b.add(parserLine, tok == INC ? SUB : ADD, null); // undo what we just did, since this is postfix
346 startExpr(b, precedence[tok]);
347 b.add(parserLine, PUT);
348 b.add(parserLine, SWAP);
349 b.add(parserLine, POP);
353 int n = parseArgs(b, 2);
354 b.add(parserLine, GET_PRESERVE);
355 b.add(parserLine, CALLMETHOD, new Integer(n));
360 if(b.get(b.size-1) == LITERAL && b.getArg(b.size-1) != null)
361 b.set(b.size-1,GET,b.getArg(b.size-1));
363 b.add(parserLine, GET);
371 * Assuming that a complete expression has just been parsed,
372 * <tt>continueExpr</tt> will attempt to extend this expression by
373 * parsing additional tokens and appending additional bytecodes.
375 * No operators with precedence less than <tt>minPrecedence</tt>
378 * If any bytecodes are appended, they will not alter the stack
381 private void continueExpr(JSFunction b, int minPrecedence) throws IOException {
382 int saveParserLine = parserLine;
383 _continueExpr(b, minPrecedence);
384 parserLine = saveParserLine;
386 private void _continueExpr(JSFunction b, int minPrecedence) throws IOException {
387 if (b == null) throw new Error("got null b; this should never happen");
388 int tok = getToken();
389 if (tok == -1) return;
390 if (minPrecedence != -1 && (precedence[tok] < minPrecedence || (precedence[tok] == minPrecedence && !isRightAssociative[tok]))) {
396 case LP: { // invocation (not grouping)
397 int n = parseArgs(b, 1);
398 b.add(parserLine, CALL, new Integer(n));
401 case BITOR: case BITXOR: case BITAND: case SHEQ: case SHNE: case LSH:
402 case RSH: case URSH: case MUL: case DIV: case MOD:
403 case GT: case GE: case EQ: case NE: case LT: case LE: case SUB: {
404 startExpr(b, precedence[tok]);
405 b.add(parserLine, tok);
412 startExpr(b,precedence[tok]);
414 nextTok = getToken();
415 } while(nextTok == tok);
417 b.add(parserLine, tok, new Integer(count));
421 b.add(parserLine, tok == AND ? b.JF : b.JT, new Integer(0)); // test to see if we can short-circuit
423 startExpr(b, precedence[tok]); // otherwise check the second value
424 b.add(parserLine, JMP, new Integer(2)); // leave the second value on the stack and jump to the end
425 b.add(parserLine, LITERAL, tok == AND ?
426 new Boolean(false) : new Boolean(true)); // target of the short-circuit jump is here
427 b.set(size - 1, new Integer(b.size - size)); // write the target of the short-circuit jump
431 // support foo..bar syntax for foo[""].bar
432 if (peekToken() == DOT) {
437 b.add(parserLine, LITERAL, string);
438 continueExprAfterAssignable(b,minPrecedence);
441 case LB: { // subscripting (not array constructor)
444 continueExprAfterAssignable(b,minPrecedence);
448 b.add(parserLine, JF, new Integer(0)); // jump to the if-false expression
450 startExpr(b, minPrecedence); // write the if-true expression
451 b.add(parserLine, JMP, new Integer(0)); // if true, jump *over* the if-false expression
452 b.set(size - 1, new Integer(b.size - size + 1)); // now we know where the target of the jump is
455 startExpr(b, minPrecedence); // write the if-false expression
456 b.set(size - 1, new Integer(b.size - size + 1)); // this is the end; jump to here
460 // pop the result of the previous expression, it is ignored
461 b.add(parserLine,POP);
471 continueExpr(b, minPrecedence); // try to continue the expression
474 // parse a set of comma separated function arguments, assume LP has already been consumed
475 // if swap is true, (because the function is already on the stack) we will SWAP after each argument to keep it on top
476 private int parseArgs(JSFunction b, int pushdown) throws IOException {
478 while(peekToken() != RP) {
480 if (peekToken() != COMMA) {
481 startExpr(b, NO_COMMA);
482 b.add(parserLine, SWAP, new Integer(pushdown));
483 if (peekToken() == RP) break;
491 /** Parse a block of statements which must be surrounded by LC..RC. */
492 void parseBlock(JSFunction b) throws IOException { parseBlock(b, null); }
493 void parseBlock(JSFunction b, String label) throws IOException {
494 int saveParserLine = parserLine;
495 _parseBlock(b, label);
496 parserLine = saveParserLine;
498 void _parseBlock(JSFunction b, String label) throws IOException {
499 if (peekToken() == -1) return;
500 else if (peekToken() != LC) parseStatement(b, null);
503 while(peekToken() != RC && peekToken() != -1) parseStatement(b, null);
508 /** Parse a single statement, consuming the RC or SEMI which terminates it. */
509 void parseStatement(JSFunction b, String label) throws IOException {
510 int saveParserLine = parserLine;
511 _parseStatement(b, label);
512 parserLine = saveParserLine;
514 void _parseStatement(JSFunction b, String label) throws IOException {
515 int tok = peekToken();
516 if (tok == -1) return;
517 switch(tok = getToken()) {
519 case THROW: case ASSERT: case RETURN: {
520 if (tok == RETURN && peekToken() == SEMI)
521 b.add(parserLine, LITERAL, null);
524 b.add(parserLine, tok);
528 case BREAK: case CONTINUE: {
529 if (peekToken() == NAME) consume(NAME);
530 b.add(parserLine, tok, string);
535 b.add(parserLine, TOPSCOPE); // push the current scope
538 b.add(parserLine, DECLARE, string); // declare it
539 if (peekToken() == ASSIGN) { // if there is an '=' after the variable name
541 startExpr(b, NO_COMMA);
542 b.add(parserLine, PUT); // assign it
543 b.add(parserLine, POP); // clean the stack
545 b.add(parserLine, POP); // pop the string pushed by declare
547 if (peekToken() != COMMA) break;
550 b.add(parserLine, POP); // pop off the topscope
551 if ((mostRecentlyReadToken != RC || peekToken() == SEMI) && peekToken() != -1 && mostRecentlyReadToken != SEMI) consume(SEMI);
559 b.add(parserLine, JF, new Integer(0)); // if false, jump to the else-block
561 parseStatement(b, null);
563 if (peekToken() == ELSE) {
565 b.add(parserLine, JMP, new Integer(0)); // if we took the true-block, jump over the else-block
566 b.set(size - 1, new Integer(b.size - size + 1));
568 parseStatement(b, null);
570 b.set(size - 1, new Integer(b.size - size + 1)); // regardless of which branch we took, b[size] needs to point here
575 if (label != null) b.add(parserLine, LABEL, label);
576 b.add(parserLine, LOOP);
578 b.add(parserLine, POP); // discard the first-iteration indicator
580 b.add(parserLine, JT, new Integer(2)); // if the while() clause is true, jump over the BREAK
581 b.add(parserLine, BREAK);
583 parseStatement(b, null);
584 b.add(parserLine, CONTINUE); // if we fall out of the end, definately continue
585 b.set(size - 1, new Integer(b.size - size + 1)); // end of the loop
590 if (label != null) b.add(parserLine, LABEL, label);
591 b.add(parserLine, LOOP);
597 if (peekToken() == CASE) { // we compile CASE statements like a bunch of if..else's
599 b.add(parserLine, DUP); // duplicate the switch() value; we'll consume one copy
602 b.add(parserLine, EQ); // check if we should do this case-block
603 b.add(parserLine, JF, new Integer(0)); // if not, jump to the next one
605 while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) parseStatement(b, null);
606 b.set(size - 1, new Integer(1 + b.size - size));
607 } else if (peekToken() == DEFAULT) {
610 while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) parseStatement(b, null);
611 } else if (peekToken() == RC) {
613 b.add(parserLine, BREAK); // break out of the loop if we 'fall through'
616 throw pe("expected CASE, DEFAULT, or RC; got " + codeToString[peekToken()]);
618 b.set(size0 - 1, new Integer(b.size - size0 + 1)); // end of the loop
623 if (label != null) b.add(parserLine, LABEL, label);
624 b.add(parserLine, LOOP);
626 parseStatement(b, null);
630 b.add(parserLine, JT, new Integer(2)); // check the while() clause; jump over the BREAK if true
631 b.add(parserLine, BREAK);
632 b.add(parserLine, CONTINUE);
635 b.set(size - 1, new Integer(b.size - size + 1)); // end of the loop; write this location to the LOOP instruction
640 b.add(parserLine, TRY); // try bytecode causes a TryMarker to be pushed
641 int tryInsn = b.size - 1;
642 // parse the expression to be TRYed
643 parseStatement(b, null);
644 // pop the try marker. this is pushed when the TRY bytecode is executed
645 b.add(parserLine, POP);
646 // jump forward to the end of the catch block, start of the finally block
647 b.add(parserLine, JMP);
648 int successJMPInsn = b.size - 1;
650 if (peekToken() != CATCH && peekToken() != FINALLY)
651 throw pe("try without catch or finally");
653 int catchJMPDistance = -1;
654 if (peekToken() == CATCH) {
655 catchJMPDistance = b.size - tryInsn;
660 exceptionVar = string;
662 b.add(parserLine, TOPSCOPE); // the exception is on top of the stack; put it to the chosen name
663 b.add(parserLine, SWAP);
664 b.add(parserLine, LITERAL,exceptionVar);
665 b.add(parserLine, SWAP);
666 b.add(parserLine, PUT);
667 b.add(parserLine, POP);
668 b.add(parserLine, POP);
669 parseStatement(b, null);
670 // pop the try and catch markers
671 b.add(parserLine,POP);
672 b.add(parserLine,POP);
675 // jump here if no exception was thrown
676 b.set(successJMPInsn, new Integer(b.size - successJMPInsn));
678 int finallyJMPDistance = -1;
679 if (peekToken() == FINALLY) {
680 b.add(parserLine, LITERAL, null); // null FinallyData
681 finallyJMPDistance = b.size - tryInsn;
683 parseStatement(b, null);
684 b.add(parserLine,FINALLY_DONE);
687 // setup the TRY arguments
688 b.set(tryInsn, new int[] { catchJMPDistance, finallyJMPDistance });
697 boolean hadVar = false; // if it's a for..in, we ignore the VAR
698 if (tok == VAR) { hadVar = true; tok = getToken(); }
699 String varName = string;
700 boolean forIn = peekToken() == IN; // determine if this is a for..in loop or not
701 pushBackToken(tok, varName);
704 b.add(parserLine, NEWSCOPE); // for-loops always create new scopes
705 b.add(parserLine, LITERAL, varName); // declare the new variable
706 b.add(parserLine, DECLARE);
708 b.add(parserLine, LOOP); // we actually only add this to ensure that BREAK works
709 b.add(parserLine, POP); // discard the first-iteration indicator
714 b.add(parserLine, PUSHKEYS); // push the keys as an array; check the length
715 b.add(parserLine, LITERAL, "length");
716 b.add(parserLine, GET);
719 b.add(parserLine, LITERAL, new Integer(1)); // decrement the length
720 b.add(parserLine, SUB);
721 b.add(parserLine, DUP);
722 b.add(parserLine, LITERAL, new Integer(0)); // see if we've exhausted all the elements
723 b.add(parserLine, LT);
724 b.add(parserLine, JF, new Integer(2));
725 b.add(parserLine, BREAK); // if we have, then BREAK
726 b.add(parserLine, GET_PRESERVE); // get the key out of the keys array
727 b.add(parserLine, LITERAL, varName);
728 b.add(parserLine, PUT); // write it to this[varName]
729 parseStatement(b, null); // do some stuff
730 b.add(parserLine, CONTINUE); // continue if we fall out the bottom
732 b.set(size - 1, new Integer(b.size - size + 1)); // BREAK to here
733 b.add(parserLine, OLDSCOPE); // restore the scope
736 if (hadVar) pushBackToken(VAR, null); // yeah, this actually matters
737 b.add(parserLine, NEWSCOPE); // grab a fresh scope
739 parseStatement(b, null); // initializer
740 JSFunction e2 = // we need to put the incrementor before the test
741 new JSFunction(sourceName, parserLine, null, null); // so we save the test here
742 if (peekToken() != SEMI)
745 e2.add(parserLine, b.LITERAL, Boolean.TRUE); // handle the for(foo;;foo) case
747 if (label != null) b.add(parserLine, LABEL, label);
748 b.add(parserLine, LOOP);
751 b.add(parserLine, JT, new Integer(0)); // if we're on the first iteration, jump over the incrementor
753 if (peekToken() != RP) { // do the increment thing
755 b.add(parserLine, POP);
757 b.set(size - 1, new Integer(b.size - size + 1));
760 b.paste(e2); // ok, *now* test if we're done yet
761 b.add(parserLine, JT, new Integer(2)); // break out if we don't meet the test
762 b.add(parserLine, BREAK);
763 parseStatement(b, null);
764 b.add(parserLine, CONTINUE); // if we fall out the bottom, CONTINUE
765 b.set(size2 - 1, new Integer(b.size - size2 + 1)); // end of the loop
767 b.add(parserLine, OLDSCOPE); // get our scope back
772 case NAME: { // either a label or an identifier; this is the one place we're not LL(1)
773 String possiblyTheLabel = string;
774 if (peekToken() == COLON) { // label
776 parseStatement(b, possiblyTheLabel);
778 } else { // expression
779 pushBackToken(NAME, possiblyTheLabel);
781 b.add(parserLine, POP);
782 if ((mostRecentlyReadToken != RC || peekToken() == SEMI) && peekToken() != -1 && mostRecentlyReadToken != SEMI) consume(SEMI);
787 case SEMI: return; // yep, the null statement is valid
789 case LC: { // blocks are statements too
791 b.add(parserLine, NEWSCOPE);
792 parseBlock(b, label);
793 b.add(parserLine, OLDSCOPE);
797 default: { // hope that it's an expression
800 b.add(parserLine, POP);
801 if ((mostRecentlyReadToken != RC || peekToken() == SEMI) && peekToken() != -1 && mostRecentlyReadToken != SEMI) consume(SEMI);
808 // ParserException //////////////////////////////////////////////////////////////////////
809 private IOException pe(String s) { return new IOException(sourceName + ":" + parserLine + " " + s); }