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, null);
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, null);
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);
355 // if the object supports CALLMETHOD, we use this, and jump over the following two instructions
356 b.add(parserLine,CALLMETHOD,new Integer(n));
357 b.add(parserLine,GET);
358 b.add(parserLine,CALL,new Integer(n));
363 if(b.get(b.size-1) == LITERAL && b.getArg(b.size-1) != null)
364 b.set(b.size-1,GET,b.getArg(b.size-1));
366 b.add(parserLine, GET);
374 * Assuming that a complete expression has just been parsed,
375 * <tt>continueExpr</tt> will attempt to extend this expression by
376 * parsing additional tokens and appending additional bytecodes.
378 * No operators with precedence less than <tt>minPrecedence</tt>
381 * If any bytecodes are appended, they will not alter the stack
384 private void continueExpr(JSFunction b, int minPrecedence) throws IOException {
385 int saveParserLine = parserLine;
386 _continueExpr(b, minPrecedence);
387 parserLine = saveParserLine;
389 private void _continueExpr(JSFunction b, int minPrecedence) throws IOException {
390 if (b == null) throw new Error("got null b; this should never happen");
391 int tok = getToken();
392 if (tok == -1) return;
393 if (minPrecedence != -1 && (precedence[tok] < minPrecedence || (precedence[tok] == minPrecedence && !isRightAssociative[tok]))) {
399 case LP: { // invocation (not grouping)
400 int n = parseArgs(b);
401 b.add(parserLine, CALL, new Integer(n));
404 case BITOR: case BITXOR: case BITAND: case SHEQ: case SHNE: case LSH:
405 case RSH: case URSH: case MUL: case DIV: case MOD:
406 case GT: case GE: case EQ: case NE: case LT: case LE: case SUB: {
407 startExpr(b, precedence[tok]);
408 b.add(parserLine, tok);
415 startExpr(b,precedence[tok]);
417 nextTok = getToken();
418 } while(nextTok == tok);
420 b.add(parserLine, tok, new Integer(count));
424 b.add(parserLine, tok == AND ? b.JF : b.JT, new Integer(0)); // test to see if we can short-circuit
426 startExpr(b, precedence[tok]); // otherwise check the second value
427 b.add(parserLine, JMP, new Integer(2)); // leave the second value on the stack and jump to the end
428 b.add(parserLine, LITERAL, tok == AND ?
429 new Boolean(false) : new Boolean(true)); // target of the short-circuit jump is here
430 b.set(size - 1, new Integer(b.size - size)); // write the target of the short-circuit jump
434 // support foo..bar syntax for foo[""].bar
435 if (peekToken() == DOT) {
440 b.add(parserLine, LITERAL, string);
441 continueExprAfterAssignable(b,minPrecedence);
444 case LB: { // subscripting (not array constructor)
447 continueExprAfterAssignable(b,minPrecedence);
451 b.add(parserLine, JF, new Integer(0)); // jump to the if-false expression
453 startExpr(b, minPrecedence); // write the if-true expression
454 b.add(parserLine, JMP, new Integer(0)); // if true, jump *over* the if-false expression
455 b.set(size - 1, new Integer(b.size - size + 1)); // now we know where the target of the jump is
458 startExpr(b, minPrecedence); // write the if-false expression
459 b.set(size - 1, new Integer(b.size - size + 1)); // this is the end; jump to here
463 // pop the result of the previous expression, it is ignored
464 b.add(parserLine,POP);
474 continueExpr(b, minPrecedence); // try to continue the expression
477 // parse a set of comma separated function arguments, assume LP has already been consumed
478 // if swap is true, (because the function is already on the stack) we will SWAP after each argument to keep it on top
479 private int parseArgs(JSFunction b) throws IOException {
481 while(peekToken() != RP) {
483 if (peekToken() != COMMA) {
484 startExpr(b, NO_COMMA);
485 b.add(parserLine, SWAP, new Integer(2));
486 if (peekToken() == RP) break;
494 /** Parse a block of statements which must be surrounded by LC..RC. */
495 void parseBlock(JSFunction b) throws IOException { parseBlock(b, null); }
496 void parseBlock(JSFunction b, String label) throws IOException {
497 int saveParserLine = parserLine;
498 _parseBlock(b, label);
499 parserLine = saveParserLine;
501 void _parseBlock(JSFunction b, String label) throws IOException {
502 if (peekToken() == -1) return;
503 else if (peekToken() != LC) parseStatement(b, null);
506 while(peekToken() != RC && peekToken() != -1) parseStatement(b, null);
511 /** Parse a single statement, consuming the RC or SEMI which terminates it. */
512 void parseStatement(JSFunction b, String label) throws IOException {
513 int saveParserLine = parserLine;
514 _parseStatement(b, label);
515 parserLine = saveParserLine;
517 void _parseStatement(JSFunction b, String label) throws IOException {
518 int tok = peekToken();
519 if (tok == -1) return;
520 switch(tok = getToken()) {
522 case THROW: case ASSERT: case RETURN: {
523 if (tok == RETURN && peekToken() == SEMI)
524 b.add(parserLine, LITERAL, null);
527 b.add(parserLine, tok);
531 case BREAK: case CONTINUE: {
532 if (peekToken() == NAME) consume(NAME);
533 b.add(parserLine, tok, string);
538 b.add(parserLine, TOPSCOPE); // push the current scope
541 b.add(parserLine, DECLARE, string); // declare it
542 if (peekToken() == ASSIGN) { // if there is an '=' after the variable name
544 startExpr(b, NO_COMMA);
545 b.add(parserLine, PUT); // assign it
546 b.add(parserLine, POP); // clean the stack
548 b.add(parserLine, POP); // pop the string pushed by declare
550 if (peekToken() != COMMA) break;
553 b.add(parserLine, POP); // pop off the topscope
554 if ((mostRecentlyReadToken != RC || peekToken() == SEMI) && peekToken() != -1 && mostRecentlyReadToken != SEMI) consume(SEMI);
562 b.add(parserLine, JF, new Integer(0)); // if false, jump to the else-block
564 parseStatement(b, null);
566 if (peekToken() == ELSE) {
568 b.add(parserLine, JMP, new Integer(0)); // if we took the true-block, jump over the else-block
569 b.set(size - 1, new Integer(b.size - size + 1));
571 parseStatement(b, null);
573 b.set(size - 1, new Integer(b.size - size + 1)); // regardless of which branch we took, b[size] needs to point here
578 if (label != null) b.add(parserLine, LABEL, label);
579 b.add(parserLine, LOOP);
581 b.add(parserLine, POP); // discard the first-iteration indicator
583 b.add(parserLine, JT, new Integer(2)); // if the while() clause is true, jump over the BREAK
584 b.add(parserLine, BREAK);
586 parseStatement(b, null);
587 b.add(parserLine, CONTINUE); // if we fall out of the end, definately continue
588 b.set(size - 1, new Integer(b.size - size + 1)); // end of the loop
593 if (label != null) b.add(parserLine, LABEL, label);
594 b.add(parserLine, LOOP);
600 if (peekToken() == CASE) { // we compile CASE statements like a bunch of if..else's
602 b.add(parserLine, DUP); // duplicate the switch() value; we'll consume one copy
605 b.add(parserLine, EQ); // check if we should do this case-block
606 b.add(parserLine, JF, new Integer(0)); // if not, jump to the next one
608 while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) parseStatement(b, null);
609 b.set(size - 1, new Integer(1 + b.size - size));
610 } else if (peekToken() == DEFAULT) {
613 while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) parseStatement(b, null);
614 } else if (peekToken() == RC) {
616 b.add(parserLine, BREAK); // break out of the loop if we 'fall through'
619 throw pe("expected CASE, DEFAULT, or RC; got " + codeToString[peekToken()]);
621 b.set(size0 - 1, new Integer(b.size - size0 + 1)); // end of the loop
626 if (label != null) b.add(parserLine, LABEL, label);
627 b.add(parserLine, LOOP);
629 parseStatement(b, null);
633 b.add(parserLine, JT, new Integer(2)); // check the while() clause; jump over the BREAK if true
634 b.add(parserLine, BREAK);
635 b.add(parserLine, CONTINUE);
638 b.set(size - 1, new Integer(b.size - size + 1)); // end of the loop; write this location to the LOOP instruction
643 b.add(parserLine, TRY); // try bytecode causes a TryMarker to be pushed
644 int tryInsn = b.size - 1;
645 // parse the expression to be TRYed
646 parseStatement(b, null);
647 // pop the try marker. this is pushed when the TRY bytecode is executed
648 b.add(parserLine, POP);
649 // jump forward to the end of the catch block, start of the finally block
650 b.add(parserLine, JMP);
651 int successJMPInsn = b.size - 1;
653 if (peekToken() != CATCH && peekToken() != FINALLY)
654 throw pe("try without catch or finally");
656 int catchJMPDistance = -1;
657 if (peekToken() == CATCH) {
658 catchJMPDistance = b.size - tryInsn;
663 exceptionVar = string;
665 b.add(parserLine, TOPSCOPE); // the exception is on top of the stack; put it to the chosen name
666 b.add(parserLine, SWAP);
667 b.add(parserLine, LITERAL,exceptionVar);
668 b.add(parserLine, SWAP);
669 b.add(parserLine, PUT);
670 b.add(parserLine, POP);
671 b.add(parserLine, POP);
672 parseStatement(b, null);
673 // pop the try and catch markers
674 b.add(parserLine,POP);
675 b.add(parserLine,POP);
678 // jump here if no exception was thrown
679 b.set(successJMPInsn, new Integer(b.size - successJMPInsn));
681 int finallyJMPDistance = -1;
682 if (peekToken() == FINALLY) {
683 b.add(parserLine, LITERAL, null); // null FinallyData
684 finallyJMPDistance = b.size - tryInsn;
686 parseStatement(b, null);
687 b.add(parserLine,FINALLY_DONE);
690 // setup the TRY arguments
691 b.set(tryInsn, new int[] { catchJMPDistance, finallyJMPDistance });
700 boolean hadVar = false; // if it's a for..in, we ignore the VAR
701 if (tok == VAR) { hadVar = true; tok = getToken(); }
702 String varName = string;
703 boolean forIn = peekToken() == IN; // determine if this is a for..in loop or not
704 pushBackToken(tok, varName);
707 b.add(parserLine, NEWSCOPE); // for-loops always create new scopes
708 b.add(parserLine, LITERAL, varName); // declare the new variable
709 b.add(parserLine, DECLARE);
711 b.add(parserLine, LOOP); // we actually only add this to ensure that BREAK works
712 b.add(parserLine, POP); // discard the first-iteration indicator
717 b.add(parserLine, PUSHKEYS); // push the keys as an array; check the length
718 b.add(parserLine, LITERAL, "length");
719 b.add(parserLine, GET);
722 b.add(parserLine, LITERAL, new Integer(1)); // decrement the length
723 b.add(parserLine, SUB);
724 b.add(parserLine, DUP);
725 b.add(parserLine, LITERAL, new Integer(0)); // see if we've exhausted all the elements
726 b.add(parserLine, LT);
727 b.add(parserLine, JF, new Integer(2));
728 b.add(parserLine, BREAK); // if we have, then BREAK
729 b.add(parserLine, GET_PRESERVE); // get the key out of the keys array
730 b.add(parserLine, LITERAL, varName);
731 b.add(parserLine, PUT); // write it to this[varName]
732 parseStatement(b, null); // do some stuff
733 b.add(parserLine, CONTINUE); // continue if we fall out the bottom
735 b.set(size - 1, new Integer(b.size - size + 1)); // BREAK to here
736 b.add(parserLine, OLDSCOPE); // restore the scope
739 if (hadVar) pushBackToken(VAR, null); // yeah, this actually matters
740 b.add(parserLine, NEWSCOPE); // grab a fresh scope
742 parseStatement(b, null); // initializer
743 JSFunction e2 = // we need to put the incrementor before the test
744 new JSFunction(sourceName, parserLine, null, null); // so we save the test here
745 if (peekToken() != SEMI)
748 e2.add(parserLine, b.LITERAL, Boolean.TRUE); // handle the for(foo;;foo) case
750 if (label != null) b.add(parserLine, LABEL, label);
751 b.add(parserLine, LOOP);
754 b.add(parserLine, JT, new Integer(0)); // if we're on the first iteration, jump over the incrementor
756 if (peekToken() != RP) { // do the increment thing
758 b.add(parserLine, POP);
760 b.set(size - 1, new Integer(b.size - size + 1));
763 b.paste(e2); // ok, *now* test if we're done yet
764 b.add(parserLine, JT, new Integer(2)); // break out if we don't meet the test
765 b.add(parserLine, BREAK);
766 parseStatement(b, null);
767 b.add(parserLine, CONTINUE); // if we fall out the bottom, CONTINUE
768 b.set(size2 - 1, new Integer(b.size - size2 + 1)); // end of the loop
770 b.add(parserLine, OLDSCOPE); // get our scope back
775 case NAME: { // either a label or an identifier; this is the one place we're not LL(1)
776 String possiblyTheLabel = string;
777 if (peekToken() == COLON) { // label
779 parseStatement(b, possiblyTheLabel);
781 } else { // expression
782 pushBackToken(NAME, possiblyTheLabel);
784 b.add(parserLine, POP);
785 if ((mostRecentlyReadToken != RC || peekToken() == SEMI) && peekToken() != -1 && mostRecentlyReadToken != SEMI) consume(SEMI);
790 case SEMI: return; // yep, the null statement is valid
792 case LC: { // blocks are statements too
794 b.add(parserLine, NEWSCOPE);
795 parseBlock(b, label);
796 b.add(parserLine, OLDSCOPE);
800 default: { // hope that it's an expression
803 b.add(parserLine, POP);
804 if ((mostRecentlyReadToken != RC || peekToken() == SEMI) && peekToken() != -1 && mostRecentlyReadToken != SEMI) consume(SEMI);
811 // ParserException //////////////////////////////////////////////////////////////////////
812 private IOException pe(String s) { return new IOException(sourceName + ":" + parserLine + " " + s); }