1 // Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
4 import org.ibex.util.*;
8 * Parses a stream of lexed tokens into a tree of JSFunction's.
10 * There are three kinds of things we parse: blocks, statements, and
13 * - Expressions are a special type of statement that evaluates to a
14 * value (for example, "break" is not an expression, * but "3+2"
15 * is). Some tokens sequences start expressions (for * example,
16 * literal numbers) and others continue an expression which * has
17 * already been begun (for example, '+'). Finally, some *
18 * expressions are valid targets for an assignment operation; after
19 * * each of these expressions, continueExprAfterAssignable() is
20 * called * to check for an assignment operation.
22 * - A statement ends with a semicolon and does not return a value.
24 * - A block is a single statement or a sequence of statements
25 * surrounded by curly braces.
27 * Each parsing method saves the parserLine before doing its actual
28 * work and restores it afterwards. This ensures that parsing a
29 * subexpression does not modify the line number until a token
30 * *after* the subexpression has been consumed by the parent
33 * Technically it would be a better design for this class to build an
34 * intermediate parse tree and use that to emit bytecode. Here's the
37 * Advantages of building a parse tree:
38 * - easier to apply optimizations
39 * - would let us handle more sophisticated languages than JavaScript
41 * Advantages of leaving out the parse tree
42 * - faster compilation
43 * - less load on the garbage collector
44 * - much simpler code, easier to understand
47 * Fortunately JS is such a simple language that we can get away with
48 * the half-assed approach and still produce a working, complete
51 * The bytecode language emitted doesn't really cause any appreciable
52 * semantic loss, and is itself a parseable language very similar to
53 * Forth or a postfix variant of LISP. This means that the bytecode
54 * can be transformed into a parse tree, which can be manipulated.
55 * So if we ever want to add an optimizer, it could easily be done by
56 * producing a parse tree from the bytecode, optimizing that tree,
57 * and then re-emitting the bytecode. The parse tree node class
58 * would also be much simpler since the bytecode language has so few
61 * Actually, the above paragraph is slightly inaccurate -- there are
62 * places where we push a value and then perform an arbitrary number
63 * of operations using it before popping it; this doesn't parse well.
64 * But these cases are clearly marked and easy to change if we do
65 * need to move to a parse tree format.
67 class Parser extends Lexer implements ByteCodes {
70 // Constructors //////////////////////////////////////////////////////
72 public Parser(Reader r, String sourceName, int line) throws IOException { super(r, sourceName, line); }
75 public static void main(String[] s) throws IOException {
76 JS block = JS.fromReader("stdin", 0, new InputStreamReader(System.in));
77 if (block == null) return;
78 System.out.println(block);
82 // Statics ////////////////////////////////////////////////////////////
84 static byte[] precedence = new byte[MAX_TOKEN + 1];
85 static boolean[] isRightAssociative = new boolean[MAX_TOKEN + 1];
86 // Use this as the precedence when we want anything up to the comma
87 private final static int NO_COMMA = 2;
89 isRightAssociative[ASSIGN] =
90 isRightAssociative[ASSIGN_BITOR] =
91 isRightAssociative[ASSIGN_BITXOR] =
92 isRightAssociative[ASSIGN_BITAND] =
93 isRightAssociative[ASSIGN_LSH] =
94 isRightAssociative[ASSIGN_RSH] =
95 isRightAssociative[ASSIGN_URSH] =
96 isRightAssociative[ASSIGN_ADD] =
97 isRightAssociative[ASSIGN_SUB] =
98 isRightAssociative[ASSIGN_MUL] =
99 isRightAssociative[ASSIGN_DIV] =
100 isRightAssociative[ASSIGN_MOD] =
101 isRightAssociative[ADD_TRAP] =
102 isRightAssociative[DEL_TRAP] =
105 precedence[COMMA] = 1;
106 // 2 is intentionally left unassigned. we use minPrecedence==2 for comma separated lists
108 precedence[ASSIGN_BITOR] =
109 precedence[ASSIGN_BITXOR] =
110 precedence[ASSIGN_BITAND] =
111 precedence[ASSIGN_LSH] =
112 precedence[ASSIGN_RSH] =
113 precedence[ASSIGN_URSH] =
114 precedence[ASSIGN_ADD] =
115 precedence[ASSIGN_SUB] =
116 precedence[ASSIGN_MUL] =
117 precedence[ASSIGN_DIV] =
118 precedence[ADD_TRAP] =
119 precedence[DEL_TRAP] =
120 precedence[ASSIGN_MOD] = 3;
121 precedence[HOOK] = 4;
124 precedence[BITOR] = 7;
125 precedence[BITXOR] = 8;
126 precedence[BITAND] = 9;
127 precedence[EQ] = precedence[NE] = precedence[SHEQ] = precedence[SHNE] = 10;
128 precedence[LT] = precedence[LE] = precedence[GT] = precedence[GE] = 11;
129 precedence[LSH] = precedence[RSH] = precedence[URSH] = 12;
130 precedence[ADD] = precedence[SUB] = 12;
131 precedence[MUL] = precedence[DIV] = precedence[MOD] = 13;
132 precedence[BITNOT] = precedence[BANG] = precedence[TYPEOF] = 14;
133 precedence[DOT] = precedence[LB] = precedence[LP] = precedence[INC] = precedence[DEC] = 15;
137 // Parsing Logic /////////////////////////////////////////////////////////
139 /** gets a token and throws an exception if it is not <tt>code</tt> */
140 private void consume(int code) throws IOException {
141 if (getToken() != code) {
142 if(code == NAME) switch(op) {
143 case RETURN: case TYPEOF: case BREAK: case CONTINUE: case TRY: case THROW:
144 case ASSERT: case NULL: case TRUE: case FALSE: case IN: case IF: case ELSE:
145 case SWITCH: case CASE: case DEFAULT: case WHILE: case VAR: case WITH:
146 case CATCH: case FINALLY:
147 throw pe("Bad variable name; '" + codeToString[op].toLowerCase() + "' is a javascript keyword");
149 throw pe("expected " + codeToString[code] + ", got " + (op == -1 ? "EOF" : codeToString[op]));
154 * Parse the largest possible expression containing no operators
155 * of precedence below <tt>minPrecedence</tt> and append the
156 * bytecodes for that expression to <tt>appendTo</tt>; the
157 * appended bytecodes MUST grow the stack by exactly one element.
159 private void startExpr(JSFunction appendTo, int minPrecedence) throws IOException {
160 int saveParserLine = parserLine;
161 _startExpr(appendTo, minPrecedence);
162 parserLine = saveParserLine;
164 private void _startExpr(JSFunction appendTo, int minPrecedence) throws IOException {
165 int tok = getToken();
166 JSFunction b = appendTo;
169 case -1: throw pe("expected expression");
171 // all of these simply push values onto the stack
172 case NUMBER: b.add(parserLine, LITERAL, number); break;
173 case STRING: b.add(parserLine, LITERAL, string); break;
174 case NULL: b.add(parserLine, LITERAL, null); break;
175 case TRUE: case FALSE: b.add(parserLine, LITERAL, JS.B(tok == TRUE)); break;
180 b.add(parserLine, TOPSCOPE);
181 b.add(parserLine, LITERAL, "");
182 b.add(parserLine, GET);
183 b.add(parserLine, LITERAL, string);
184 b.add(parserLine, GET);
185 continueExpr(b, minPrecedence);
190 b.add(parserLine, ARRAY, JS.ZERO); // push an array onto the stack
193 if (peekToken() != RB)
194 while(true) { // iterate over the initialization values
196 b.add(parserLine, LITERAL, JS.N(i++)); // push the index in the array to place it into
197 if (peekToken() == COMMA || peekToken() == RB)
198 b.add(parserLine, LITERAL, null); // for stuff like [1,,2,]
200 startExpr(b, NO_COMMA); // push the value onto the stack
201 b.add(parserLine, PUT); // put it into the array
202 b.add(parserLine, POP); // discard the value remaining on the stack
203 if (peekToken() == RB) break;
206 b.set(size0 - 1, JS.N(i)); // back at the ARRAY instruction, write the size of the array
210 case SUB: { // negative literal (like "3 * -1")
212 b.add(parserLine, LITERAL, JS.N(number.doubleValue() * -1));
215 case LP: { // grouping (not calling)
220 case INC: case DEC: { // prefix (not postfix)
221 startExpr(b, precedence[tok]);
222 int prev = b.size - 1;
223 if (b.get(prev) == GET && b.getArg(prev) != null)
224 b.set(prev, LITERAL, b.getArg(prev));
225 else if(b.get(prev) == GET)
228 throw pe("prefixed increment/decrement can only be performed on a valid assignment target");
229 b.add(parserLine, GET_PRESERVE, Boolean.TRUE);
230 b.add(parserLine, LITERAL, JS.N(1));
231 b.add(parserLine, tok == INC ? ADD : SUB, JS.N(2));
232 b.add(parserLine, PUT, null);
233 b.add(parserLine, SWAP, null);
234 b.add(parserLine, POP, null);
237 case BANG: case BITNOT: case TYPEOF: {
238 startExpr(b, precedence[tok]);
239 b.add(parserLine, tok);
242 case LC: { // object constructor
243 b.add(parserLine, OBJECT, null); // put an object on the stack
244 if (peekToken() != RC)
246 if (peekToken() != NAME && peekToken() != STRING)
247 throw pe("expected NAME or STRING");
249 b.add(parserLine, LITERAL, string); // grab the key
251 startExpr(b, NO_COMMA); // grab the value
252 b.add(parserLine, PUT); // put the value into the object
253 b.add(parserLine, POP); // discard the remaining value
254 if (peekToken() == RC) break;
256 if (peekToken() == RC) break; // we permit {,,} -- I'm not sure if ECMA does
262 b.add(parserLine, TOPSCOPE);
263 b.add(parserLine, LITERAL, string);
264 continueExprAfterAssignable(b,minPrecedence);
270 JSFunction b2 = new JSFunction(sourceName, parserLine, null);
271 b.add(parserLine, NEWFUNCTION, b2);
273 // function prelude; arguments array is already on the stack
274 b2.add(parserLine, TOPSCOPE);
275 b2.add(parserLine, SWAP);
276 b2.add(parserLine, DECLARE, "arguments"); // declare arguments (equivalent to 'var arguments;')
277 b2.add(parserLine, SWAP); // set this.arguments and leave the value on the stack
278 b2.add(parserLine, PUT);
280 while(peekToken() != RP) { // run through the list of argument names
282 if (peekToken() == NAME) {
283 consume(NAME); // a named argument
284 String varName = string;
286 b2.add(parserLine, DUP); // dup the args array
287 b2.add(parserLine, GET, JS.N(numArgs - 1)); // retrieve it from the arguments array
288 b2.add(parserLine, TOPSCOPE);
289 b2.add(parserLine, SWAP);
290 b2.add(parserLine, DECLARE, varName); // declare the name
291 b2.add(parserLine, SWAP);
292 b2.add(parserLine, PUT);
293 b2.add(parserLine, POP); // pop the value
294 b2.add(parserLine, POP); // pop the scope
296 if (peekToken() == RP) break;
301 b2.numFormalArgs = numArgs;
302 b2.add(parserLine, POP); // pop off the arguments array
303 b2.add(parserLine, POP); // pop off TOPSCOPE
305 if(peekToken() != LC)
306 throw pe("JSFunctions must have a block surrounded by curly brackets");
308 parseBlock(b2, null); // the function body
310 b2.add(parserLine, LITERAL, null); // in case we "fall out the bottom", return NULL
311 b2.add(parserLine, RETURN);
315 default: throw pe("expected expression, found " + codeToString[tok] + ", which cannot start an expression");
318 // attempt to continue the expression
319 continueExpr(b, minPrecedence);
322 private Grammar parseGrammar(Grammar g) throws IOException {
323 int tok = getToken();
326 case BITOR: return new Grammar.Alternative(g, parseGrammar(null));
327 case ADD: return parseGrammar(new Grammar.Repetition(g, 1, Integer.MAX_VALUE));
328 case MUL: return parseGrammar(new Grammar.Repetition(g, 0, Integer.MAX_VALUE));
329 case HOOK: return parseGrammar(new Grammar.Repetition(g, 0, 1));
333 //case NUMBER: g0 = new Grammar.Literal(number); break;
334 case NAME: g0 = new Grammar.Reference(string); break;
336 g0 = new Grammar.Literal(string);
337 if (peekToken() == DOT) {
342 if (old.length() != 1 || string.length() != 1) throw pe("literal ranges must be single-char strings");
343 g0 = new Grammar.Range(old.charAt(0), string.charAt(0));
346 case LP: g0 = parseGrammar(null); consume(RP); break;
347 default: pushBackToken(); return g;
349 if (g == null) return parseGrammar(g0);
350 return parseGrammar(new Grammar.Juxtaposition(g, g0));
354 * Assuming that a complete assignable (lvalue) has just been
355 * parsed and the object and key are on the stack,
356 * <tt>continueExprAfterAssignable</tt> will attempt to parse an
357 * expression that modifies the assignable. This method always
358 * decreases the stack depth by exactly one element.
360 private void continueExprAfterAssignable(JSFunction b,int minPrecedence) throws IOException {
361 int saveParserLine = parserLine;
362 _continueExprAfterAssignable(b,minPrecedence);
363 parserLine = saveParserLine;
365 private void _continueExprAfterAssignable(JSFunction b,int minPrecedence) throws IOException {
366 if (b == null) throw new Error("got null b; this should never happen");
367 int tok = getToken();
368 if (minPrecedence != -1 && (precedence[tok] < minPrecedence || (precedence[tok] == minPrecedence && !isRightAssociative[tok])))
369 // force the default case
374 b.add(parserLine, GET_PRESERVE);
375 Grammar g = parseGrammar(null);
376 if (peekToken() == LC) {
377 g.action = new JSFunction(sourceName, parserLine, null);
378 parseBlock((JSFunction)g.action);
379 ((JSFunction)g.action).add(parserLine, LITERAL, null); // in case we "fall out the bottom", return NULL
380 ((JSFunction)g.action).add(parserLine, RETURN);
382 b.add(parserLine, MAKE_GRAMMAR, g);
383 b.add(parserLine, PUT);
387 case ASSIGN_BITOR: case ASSIGN_BITXOR: case ASSIGN_BITAND: case ASSIGN_LSH: case ASSIGN_RSH: case ASSIGN_URSH:
388 case ASSIGN_MUL: case ASSIGN_DIV: case ASSIGN_MOD: case ASSIGN_ADD: case ASSIGN_SUB: case ADD_TRAP: case DEL_TRAP: {
389 if (tok != ADD_TRAP && tok != DEL_TRAP) b.add(parserLine, GET_PRESERVE);
391 startExpr(b, precedence[tok]);
395 if (tok != ADD_TRAP && tok != DEL_TRAP) {
396 // tok-1 is always s/^ASSIGN_// (0 is BITOR, 1 is ASSIGN_BITOR, etc)
397 b.add(parserLine, tok - 1, tok-1==ADD ? JS.N(2) : null);
398 b.add(parserLine, PUT);
399 b.add(parserLine, SWAP);
400 b.add(parserLine, POP);
402 b.add(parserLine, tok);
406 case INC: case DEC: { // postfix
407 b.add(parserLine, GET_PRESERVE, Boolean.TRUE);
408 b.add(parserLine, LITERAL, JS.N(1));
409 b.add(parserLine, tok == INC ? ADD : SUB, JS.N(2));
410 b.add(parserLine, PUT, null);
411 b.add(parserLine, SWAP, null);
412 b.add(parserLine, POP, null);
413 b.add(parserLine, LITERAL, JS.N(1));
414 b.add(parserLine, tok == INC ? SUB : ADD, null); // undo what we just did, since this is postfix
418 startExpr(b, precedence[tok]);
419 b.add(parserLine, PUT);
420 b.add(parserLine, SWAP);
421 b.add(parserLine, POP);
426 // Method calls are implemented by doing a GET_PRESERVE
427 // first. If the object supports method calls, it will
429 int n = parseArgs(b, 2);
430 b.add(parserLine, GET_PRESERVE);
431 b.add(parserLine, CALLMETHOD, JS.N(n));
436 if(b.get(b.size-1) == LITERAL && b.getArg(b.size-1) != null)
437 b.set(b.size-1,GET,b.getArg(b.size-1));
439 b.add(parserLine, GET);
447 * Assuming that a complete expression has just been parsed,
448 * <tt>continueExpr</tt> will attempt to extend this expression by
449 * parsing additional tokens and appending additional bytecodes.
451 * No operators with precedence less than <tt>minPrecedence</tt>
454 * If any bytecodes are appended, they will not alter the stack
457 private void continueExpr(JSFunction b, int minPrecedence) throws IOException {
458 int saveParserLine = parserLine;
459 _continueExpr(b, minPrecedence);
460 parserLine = saveParserLine;
462 private void _continueExpr(JSFunction b, int minPrecedence) throws IOException {
463 if (b == null) throw new Error("got null b; this should never happen");
464 int tok = getToken();
465 if (tok == -1) return;
466 if (minPrecedence != -1 && (precedence[tok] < minPrecedence || (precedence[tok] == minPrecedence && !isRightAssociative[tok]))) {
472 case LP: { // invocation (not grouping)
473 int n = parseArgs(b, 1);
474 b.add(parserLine, CALL, JS.N(n));
477 case BITOR: case BITXOR: case BITAND: case SHEQ: case SHNE: case LSH:
478 case RSH: case URSH: case MUL: case DIV: case MOD:
479 case GT: case GE: case EQ: case NE: case LT: case LE: case SUB: {
480 startExpr(b, precedence[tok]);
481 b.add(parserLine, tok);
488 startExpr(b,precedence[tok]);
490 nextTok = getToken();
491 } while(nextTok == tok);
493 b.add(parserLine, tok, JS.N(count));
497 b.add(parserLine, tok == AND ? JSFunction.JF : JSFunction.JT, JS.ZERO); // test to see if we can short-circuit
499 startExpr(b, precedence[tok]); // otherwise check the second value
500 b.add(parserLine, JMP, JS.N(2)); // leave the second value on the stack and jump to the end
501 b.add(parserLine, LITERAL, tok == AND ?
502 JS.B(false) : JS.B(true)); // target of the short-circuit jump is here
503 b.set(size - 1, JS.N(b.size - size)); // write the target of the short-circuit jump
507 // support foo..bar syntax for foo[""].bar
508 if (peekToken() == DOT) {
513 b.add(parserLine, LITERAL, string);
514 continueExprAfterAssignable(b,minPrecedence);
517 case LB: { // subscripting (not array constructor)
520 continueExprAfterAssignable(b,minPrecedence);
524 b.add(parserLine, JF, JS.ZERO); // jump to the if-false expression
526 startExpr(b, minPrecedence); // write the if-true expression
527 b.add(parserLine, JMP, JS.ZERO); // if true, jump *over* the if-false expression
528 b.set(size - 1, JS.N(b.size - size + 1)); // now we know where the target of the jump is
531 startExpr(b, minPrecedence); // write the if-false expression
532 b.set(size - 1, JS.N(b.size - size + 1)); // this is the end; jump to here
536 // pop the result of the previous expression, it is ignored
537 b.add(parserLine,POP);
547 continueExpr(b, minPrecedence); // try to continue the expression
550 // parse a set of comma separated function arguments, assume LP has already been consumed
551 // if swap is true, (because the function is already on the stack) we will SWAP after each argument to keep it on top
552 private int parseArgs(JSFunction b, int pushdown) throws IOException {
554 while(peekToken() != RP) {
556 if (peekToken() != COMMA) {
557 startExpr(b, NO_COMMA);
558 b.add(parserLine, SWAP, JS.N(pushdown));
559 if (peekToken() == RP) break;
567 /** Parse a block of statements which must be surrounded by LC..RC. */
568 void parseBlock(JSFunction b) throws IOException { parseBlock(b, null); }
569 void parseBlock(JSFunction b, String label) throws IOException {
570 int saveParserLine = parserLine;
571 _parseBlock(b, label);
572 parserLine = saveParserLine;
574 void _parseBlock(JSFunction b, String label) throws IOException {
575 if (peekToken() == -1) return;
576 else if (peekToken() != LC) parseStatement(b, null);
579 while(peekToken() != RC && peekToken() != -1) parseStatement(b, null);
584 /** Parse a single statement, consuming the RC or SEMI which terminates it. */
585 void parseStatement(JSFunction b, String label) throws IOException {
586 int saveParserLine = parserLine;
587 _parseStatement(b, label);
588 parserLine = saveParserLine;
590 void _parseStatement(JSFunction b, String label) throws IOException {
591 int tok = peekToken();
592 if (tok == -1) return;
593 switch(tok = getToken()) {
595 case THROW: case ASSERT: case RETURN: {
596 if (tok == RETURN && peekToken() == SEMI)
597 b.add(parserLine, LITERAL, null);
600 b.add(parserLine, tok);
604 case BREAK: case CONTINUE: {
605 if (peekToken() == NAME) consume(NAME);
606 b.add(parserLine, tok, string);
611 b.add(parserLine, TOPSCOPE); // push the current scope
614 b.add(parserLine, DECLARE, string); // declare it
615 if (peekToken() == ASSIGN) { // if there is an '=' after the variable name
617 startExpr(b, NO_COMMA);
618 b.add(parserLine, PUT); // assign it
619 b.add(parserLine, POP); // clean the stack
621 b.add(parserLine, POP); // pop the string pushed by declare
623 if (peekToken() != COMMA) break;
626 b.add(parserLine, POP); // pop off the topscope
627 if ((mostRecentlyReadToken != RC || peekToken() == SEMI) && peekToken() != -1 && mostRecentlyReadToken != SEMI) consume(SEMI);
635 b.add(parserLine, JF, JS.ZERO); // if false, jump to the else-block
637 parseStatement(b, null);
639 if (peekToken() == ELSE) {
641 b.add(parserLine, JMP, JS.ZERO); // if we took the true-block, jump over the else-block
642 b.set(size - 1, JS.N(b.size - size + 1));
644 parseStatement(b, null);
646 b.set(size - 1, JS.N(b.size - size + 1)); // regardless of which branch we took, b[size] needs to point here
651 if (label != null) b.add(parserLine, LABEL, label);
652 b.add(parserLine, LOOP);
654 b.add(parserLine, POP); // discard the first-iteration indicator
656 b.add(parserLine, JT, JS.N(2)); // if the while() clause is true, jump over the BREAK
657 b.add(parserLine, BREAK);
659 parseStatement(b, null);
660 b.add(parserLine, CONTINUE); // if we fall out of the end, definately continue
661 b.set(size - 1, JS.N(b.size - size + 1)); // end of the loop
666 if (label != null) b.add(parserLine, LABEL, label);
667 b.add(parserLine, LOOP);
673 if (peekToken() == CASE) { // we compile CASE statements like a bunch of if..else's
675 b.add(parserLine, DUP); // duplicate the switch() value; we'll consume one copy
678 b.add(parserLine, EQ); // check if we should do this case-block
679 b.add(parserLine, JF, JS.ZERO); // if not, jump to the next one
681 while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) parseStatement(b, null);
682 b.set(size - 1, JS.N(1 + b.size - size));
683 } else if (peekToken() == DEFAULT) {
686 while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) parseStatement(b, null);
687 } else if (peekToken() == RC) {
689 b.add(parserLine, BREAK); // break out of the loop if we 'fall through'
692 throw pe("expected CASE, DEFAULT, or RC; got " + codeToString[peekToken()]);
694 b.set(size0 - 1, JS.N(b.size - size0 + 1)); // end of the loop
699 if (label != null) b.add(parserLine, LABEL, label);
700 b.add(parserLine, LOOP);
702 parseStatement(b, null);
706 b.add(parserLine, JT, JS.N(2)); // check the while() clause; jump over the BREAK if true
707 b.add(parserLine, BREAK);
708 b.add(parserLine, CONTINUE);
711 b.set(size - 1, JS.N(b.size - size + 1)); // end of the loop; write this location to the LOOP instruction
716 b.add(parserLine, TRY); // try bytecode causes a TryMarker to be pushed
717 int tryInsn = b.size - 1;
718 // parse the expression to be TRYed
719 parseStatement(b, null);
720 // pop the try marker. this is pushed when the TRY bytecode is executed
721 b.add(parserLine, POP);
722 // jump forward to the end of the catch block, start of the finally block
723 b.add(parserLine, JMP);
724 int successJMPInsn = b.size - 1;
726 if (peekToken() != CATCH && peekToken() != FINALLY)
727 throw pe("try without catch or finally");
729 int catchJMPDistance = -1;
730 if (peekToken() == CATCH) {
731 Vec catchEnds = new Vec();
732 boolean catchAll = false;
734 catchJMPDistance = b.size - tryInsn;
736 while(peekToken() == CATCH && !catchAll) {
741 exceptionVar = string;
742 int[] writebacks = new int[] { -1, -1, -1 };
743 if (peekToken() != RP) {
744 // extended Ibex catch block: catch(e faultCode "foo.bar.baz")
746 String propName = string;
747 b.add(parserLine, DUP);
748 b.add(parserLine, LITERAL, string);
749 b.add(parserLine, GET);
750 b.add(parserLine, DUP);
751 b.add(parserLine, LITERAL, null);
752 b.add(parserLine, EQ);
753 b.add(parserLine, JT);
754 writebacks[0] = b.size - 1;
755 if (peekToken() == STRING) {
757 b.add(parserLine, DUP);
758 b.add(parserLine, LITERAL, string);
759 b.add(parserLine, LT);
760 b.add(parserLine, JT);
761 writebacks[1] = b.size - 1;
762 b.add(parserLine, DUP);
763 b.add(parserLine, LITERAL, string + "/"); // (slash is ASCII after dot)
764 b.add(parserLine, GE);
765 b.add(parserLine, JT);
766 writebacks[2] = b.size - 1;
769 b.add(parserLine, DUP);
770 b.add(parserLine, LITERAL, number);
771 b.add(parserLine, EQ);
772 b.add(parserLine, JF);
773 writebacks[1] = b.size - 1;
775 b.add(parserLine, POP); // pop the element thats on the stack from the compare
780 // the exception is on top of the stack; put it to the chosen name
781 b.add(parserLine, NEWSCOPE);
782 b.add(parserLine, TOPSCOPE);
783 b.add(parserLine, SWAP);
784 b.add(parserLine, LITERAL,exceptionVar);
785 b.add(parserLine, DECLARE);
786 b.add(parserLine, SWAP);
787 b.add(parserLine, PUT);
788 b.add(parserLine, POP);
789 b.add(parserLine, POP);
791 b.add(parserLine, OLDSCOPE);
793 b.add(parserLine, JMP);
794 catchEnds.addElement(new Integer(b.size-1));
796 for(int i=0; i<3; i++) if (writebacks[i] != -1) b.set(writebacks[i], JS.N(b.size-writebacks[i]));
797 b.add(parserLine, POP); // pop the element thats on the stack from the compare
801 b.add(parserLine, THROW);
803 for(int i=0;i<catchEnds.size();i++) {
804 int n = ((Integer)catchEnds.elementAt(i)).intValue();
805 b.set(n, JS.N(b.size-n));
808 // pop the try and catch markers
809 b.add(parserLine,POP);
810 b.add(parserLine,POP);
813 // jump here if no exception was thrown
814 b.set(successJMPInsn, JS.N(b.size - successJMPInsn));
816 int finallyJMPDistance = -1;
817 if (peekToken() == FINALLY) {
818 b.add(parserLine, LITERAL, null); // null FinallyData
819 finallyJMPDistance = b.size - tryInsn;
821 parseStatement(b, null);
822 b.add(parserLine,FINALLY_DONE);
825 // setup the TRY arguments
826 b.set(tryInsn, new int[] { catchJMPDistance, finallyJMPDistance });
835 boolean hadVar = false; // if it's a for..in, we ignore the VAR
836 if (tok == VAR) { hadVar = true; tok = getToken(); }
837 String varName = string;
838 boolean forIn = peekToken() == IN; // determine if this is a for..in loop or not
839 pushBackToken(tok, varName);
842 b.add(parserLine, NEWSCOPE); // for-loops always create new scopes
843 b.add(parserLine, LITERAL, varName); // declare the new variable
844 b.add(parserLine, DECLARE);
846 b.add(parserLine, LOOP); // we actually only add this to ensure that BREAK works
847 b.add(parserLine, POP); // discard the first-iteration indicator
852 b.add(parserLine, PUSHKEYS); // push the keys as an array; check the length
853 b.add(parserLine, LITERAL, "length");
854 b.add(parserLine, GET);
857 b.add(parserLine, LITERAL, JS.N(1)); // decrement the length
858 b.add(parserLine, SUB);
859 b.add(parserLine, DUP);
860 b.add(parserLine, LITERAL, JS.ZERO); // see if we've exhausted all the elements
861 b.add(parserLine, LT);
862 b.add(parserLine, JF, JS.N(2));
863 b.add(parserLine, BREAK); // if we have, then BREAK
864 b.add(parserLine, GET_PRESERVE); // get the key out of the keys array
865 b.add(parserLine, LITERAL, varName);
866 b.add(parserLine, PUT); // write it to this[varName]
867 parseStatement(b, null); // do some stuff
868 b.add(parserLine, CONTINUE); // continue if we fall out the bottom
870 b.set(size - 1, JS.N(b.size - size + 1)); // BREAK to here
871 b.add(parserLine, OLDSCOPE); // restore the scope
874 if (hadVar) pushBackToken(VAR, null); // yeah, this actually matters
875 b.add(parserLine, NEWSCOPE); // grab a fresh scope
877 parseStatement(b, null); // initializer
878 JSFunction e2 = // we need to put the incrementor before the test
879 new JSFunction(sourceName, parserLine, null); // so we save the test here
880 if (peekToken() != SEMI)
883 e2.add(parserLine, JSFunction.LITERAL, Boolean.TRUE); // handle the for(foo;;foo) case
885 if (label != null) b.add(parserLine, LABEL, label);
886 b.add(parserLine, LOOP);
889 b.add(parserLine, JT, JS.ZERO); // if we're on the first iteration, jump over the incrementor
891 if (peekToken() != RP) { // do the increment thing
893 b.add(parserLine, POP);
895 b.set(size - 1, JS.N(b.size - size + 1));
898 b.paste(e2); // ok, *now* test if we're done yet
899 b.add(parserLine, JT, JS.N(2)); // break out if we don't meet the test
900 b.add(parserLine, BREAK);
901 parseStatement(b, null);
902 b.add(parserLine, CONTINUE); // if we fall out the bottom, CONTINUE
903 b.set(size2 - 1, JS.N(b.size - size2 + 1)); // end of the loop
905 b.add(parserLine, OLDSCOPE); // get our scope back
910 case NAME: { // either a label or an identifier; this is the one place we're not LL(1)
911 String possiblyTheLabel = string;
912 if (peekToken() == COLON) { // label
914 parseStatement(b, possiblyTheLabel);
916 } else { // expression
917 pushBackToken(NAME, possiblyTheLabel);
919 b.add(parserLine, POP);
920 if ((mostRecentlyReadToken != RC || peekToken() == SEMI) && peekToken() != -1 && mostRecentlyReadToken != SEMI) consume(SEMI);
925 case SEMI: return; // yep, the null statement is valid
927 case LC: { // blocks are statements too
929 b.add(parserLine, NEWSCOPE);
930 parseBlock(b, label);
931 b.add(parserLine, OLDSCOPE);
935 default: { // hope that it's an expression
938 b.add(parserLine, POP);
939 if ((mostRecentlyReadToken != RC || peekToken() == SEMI) && peekToken() != -1 && mostRecentlyReadToken != SEMI) consume(SEMI);
946 // ParserException //////////////////////////////////////////////////////////////////////
947 private IOException pe(String s) { return new IOException(sourceName + ":" + parserLine + " " + s); }