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, false);
355 // if the object supports GETCALL, 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, true);
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, boolean swap) throws IOException {
481 while(peekToken() != RP) {
483 if (peekToken() != COMMA) {
484 startExpr(b, NO_COMMA);
485 if (swap) b.add(parserLine, SWAP);
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); }