X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=src%2Forg%2Fxwt%2Fjs%2FParser.java;fp=src%2Forg%2Fxwt%2Fjs%2FParser.java;h=0000000000000000000000000000000000000000;hb=3591b88b94a6bb378af3d4abe6eb5233ce583104;hp=1914cc7e3226acaa6f8f57fe9ae85373515ed1de;hpb=de378041d5ca2aca1a2b5a31ef15ae90a86c977f;p=org.ibex.core.git diff --git a/src/org/xwt/js/Parser.java b/src/org/xwt/js/Parser.java deleted file mode 100644 index 1914cc7..0000000 --- a/src/org/xwt/js/Parser.java +++ /dev/null @@ -1,950 +0,0 @@ -// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL] -package org.xwt.js; - -import org.xwt.util.*; -import java.io.*; - -/** - * Parses a stream of lexed tokens into a tree of JSFunction's. - * - * There are three kinds of things we parse: blocks, statements, and - * expressions. - * - * - Expressions are a special type of statement that evaluates to a - * value (for example, "break" is not an expression, * but "3+2" - * is). Some tokens sequences start expressions (for * example, - * literal numbers) and others continue an expression which * has - * already been begun (for example, '+'). Finally, some * - * expressions are valid targets for an assignment operation; after - * * each of these expressions, continueExprAfterAssignable() is - * called * to check for an assignment operation. - * - * - A statement ends with a semicolon and does not return a value. - * - * - A block is a single statement or a sequence of statements - * surrounded by curly braces. - * - * Each parsing method saves the parserLine before doing its actual - * work and restores it afterwards. This ensures that parsing a - * subexpression does not modify the line number until a token - * *after* the subexpression has been consumed by the parent - * expression. - * - * Technically it would be a better design for this class to build an - * intermediate parse tree and use that to emit bytecode. Here's the - * tradeoff: - * - * Advantages of building a parse tree: - * - easier to apply optimizations - * - would let us handle more sophisticated languages than JavaScript - * - * Advantages of leaving out the parse tree - * - faster compilation - * - less load on the garbage collector - * - much simpler code, easier to understand - * - less error-prone - * - * Fortunately JS is such a simple language that we can get away with - * the half-assed approach and still produce a working, complete - * compiler. - * - * The bytecode language emitted doesn't really cause any appreciable - * semantic loss, and is itself a parseable language very similar to - * Forth or a postfix variant of LISP. This means that the bytecode - * can be transformed into a parse tree, which can be manipulated. - * So if we ever want to add an optimizer, it could easily be done by - * producing a parse tree from the bytecode, optimizing that tree, - * and then re-emitting the bytecode. The parse tree node class - * would also be much simpler since the bytecode language has so few - * operators. - * - * Actually, the above paragraph is slightly inaccurate -- there are - * places where we push a value and then perform an arbitrary number - * of operations using it before popping it; this doesn't parse well. - * But these cases are clearly marked and easy to change if we do - * need to move to a parse tree format. - */ -class Parser extends Lexer implements ByteCodes { - - - // Constructors ////////////////////////////////////////////////////// - - public Parser(Reader r, String sourceName, int line) throws IOException { super(r, sourceName, line); } - - /** for debugging */ - public static void main(String[] s) throws Exception { - JS block = JS.fromReader("stdin", 0, new InputStreamReader(System.in)); - if (block == null) return; - System.out.println(block); - } - - - // Statics //////////////////////////////////////////////////////////// - - static byte[] precedence = new byte[MAX_TOKEN + 1]; - static boolean[] isRightAssociative = new boolean[MAX_TOKEN + 1]; - // Use this as the precedence when we want anything up to the comma - private final static int NO_COMMA = 2; - static { - isRightAssociative[ASSIGN] = - isRightAssociative[ASSIGN_BITOR] = - isRightAssociative[ASSIGN_BITXOR] = - isRightAssociative[ASSIGN_BITAND] = - isRightAssociative[ASSIGN_LSH] = - isRightAssociative[ASSIGN_RSH] = - isRightAssociative[ASSIGN_URSH] = - isRightAssociative[ASSIGN_ADD] = - isRightAssociative[ASSIGN_SUB] = - isRightAssociative[ASSIGN_MUL] = - isRightAssociative[ASSIGN_DIV] = - isRightAssociative[ASSIGN_MOD] = - isRightAssociative[ADD_TRAP] = - isRightAssociative[DEL_TRAP] = - true; - - precedence[COMMA] = 1; - // 2 is intentionally left unassigned. we use minPrecedence==2 for comma separated lists - precedence[ASSIGN] = - precedence[ASSIGN_BITOR] = - precedence[ASSIGN_BITXOR] = - precedence[ASSIGN_BITAND] = - precedence[ASSIGN_LSH] = - precedence[ASSIGN_RSH] = - precedence[ASSIGN_URSH] = - precedence[ASSIGN_ADD] = - precedence[ASSIGN_SUB] = - precedence[ASSIGN_MUL] = - precedence[ASSIGN_DIV] = - precedence[ADD_TRAP] = - precedence[DEL_TRAP] = - precedence[ASSIGN_MOD] = 3; - precedence[HOOK] = 4; - precedence[OR] = 5; - precedence[AND] = 6; - precedence[BITOR] = 7; - precedence[BITXOR] = 8; - precedence[BITAND] = 9; - precedence[EQ] = precedence[NE] = precedence[SHEQ] = precedence[SHNE] = 10; - precedence[LT] = precedence[LE] = precedence[GT] = precedence[GE] = 11; - precedence[LSH] = precedence[RSH] = precedence[URSH] = 12; - precedence[ADD] = precedence[SUB] = 12; - precedence[MUL] = precedence[DIV] = precedence[MOD] = 13; - precedence[BITNOT] = precedence[BANG] = precedence[TYPEOF] = 14; - precedence[DOT] = precedence[LB] = precedence[LP] = precedence[INC] = precedence[DEC] = 15; - } - - - // Parsing Logic ///////////////////////////////////////////////////////// - - /** gets a token and throws an exception if it is not code */ - private void consume(int code) throws IOException { - if (getToken() != code) { - if(code == NAME) switch(op) { - case RETURN: case TYPEOF: case BREAK: case CONTINUE: case TRY: case THROW: - case ASSERT: case NULL: case TRUE: case FALSE: case IN: case IF: case ELSE: - case SWITCH: case CASE: case DEFAULT: case WHILE: case VAR: case WITH: - case CATCH: case FINALLY: - throw pe("Bad variable name; '" + codeToString[op].toLowerCase() + "' is a javascript keyword"); - } - throw pe("expected " + codeToString[code] + ", got " + (op == -1 ? "EOF" : codeToString[op])); - } - } - - /** - * Parse the largest possible expression containing no operators - * of precedence below minPrecedence and append the - * bytecodes for that expression to appendTo; the - * appended bytecodes MUST grow the stack by exactly one element. - */ - private void startExpr(JSFunction appendTo, int minPrecedence) throws IOException { - int saveParserLine = parserLine; - _startExpr(appendTo, minPrecedence); - parserLine = saveParserLine; - } - private void _startExpr(JSFunction appendTo, int minPrecedence) throws IOException { - int tok = getToken(); - JSFunction b = appendTo; - - switch (tok) { - case -1: throw pe("expected expression"); - - // all of these simply push values onto the stack - case NUMBER: b.add(parserLine, LITERAL, number); break; - case STRING: b.add(parserLine, LITERAL, string); break; - case NULL: b.add(parserLine, LITERAL, null); break; - case TRUE: case FALSE: b.add(parserLine, LITERAL, JS.B(tok == TRUE)); break; - - // (.foo) syntax - case DOT: { - consume(NAME); - b.add(parserLine, TOPSCOPE); - b.add(parserLine, LITERAL, ""); - b.add(parserLine, GET); - b.add(parserLine, LITERAL, string); - b.add(parserLine, GET); - continueExpr(b, minPrecedence); - break; - } - - case LB: { - b.add(parserLine, ARRAY, JS.ZERO); // push an array onto the stack - int size0 = b.size; - int i = 0; - if (peekToken() != RB) - while(true) { // iterate over the initialization values - int size = b.size; - b.add(parserLine, LITERAL, JS.N(i++)); // push the index in the array to place it into - if (peekToken() == COMMA || peekToken() == RB) - b.add(parserLine, LITERAL, null); // for stuff like [1,,2,] - else - startExpr(b, NO_COMMA); // push the value onto the stack - b.add(parserLine, PUT); // put it into the array - b.add(parserLine, POP); // discard the value remaining on the stack - if (peekToken() == RB) break; - consume(COMMA); - } - b.set(size0 - 1, JS.N(i)); // back at the ARRAY instruction, write the size of the array - consume(RB); - break; - } - case SUB: { // negative literal (like "3 * -1") - consume(NUMBER); - b.add(parserLine, LITERAL, JS.N(number.doubleValue() * -1)); - break; - } - case LP: { // grouping (not calling) - startExpr(b, -1); - consume(RP); - break; - } - case INC: case DEC: { // prefix (not postfix) - startExpr(b, precedence[tok]); - int prev = b.size - 1; - if (b.get(prev) == GET && b.getArg(prev) != null) - b.set(prev, LITERAL, b.getArg(prev)); - else if(b.get(prev) == GET) - b.pop(); - else - throw pe("prefixed increment/decrement can only be performed on a valid assignment target"); - b.add(parserLine, GET_PRESERVE, Boolean.TRUE); - b.add(parserLine, LITERAL, JS.N(1)); - b.add(parserLine, tok == INC ? ADD : SUB, JS.N(2)); - b.add(parserLine, PUT, null); - b.add(parserLine, SWAP, null); - b.add(parserLine, POP, null); - break; - } - case BANG: case BITNOT: case TYPEOF: { - startExpr(b, precedence[tok]); - b.add(parserLine, tok); - break; - } - case LC: { // object constructor - b.add(parserLine, OBJECT, null); // put an object on the stack - if (peekToken() != RC) - while(true) { - if (peekToken() != NAME && peekToken() != STRING) - throw pe("expected NAME or STRING"); - getToken(); - b.add(parserLine, LITERAL, string); // grab the key - consume(COLON); - startExpr(b, NO_COMMA); // grab the value - b.add(parserLine, PUT); // put the value into the object - b.add(parserLine, POP); // discard the remaining value - if (peekToken() == RC) break; - consume(COMMA); - if (peekToken() == RC) break; // we permit {,,} -- I'm not sure if ECMA does - } - consume(RC); - break; - } - case NAME: { - b.add(parserLine, TOPSCOPE); - b.add(parserLine, LITERAL, string); - continueExprAfterAssignable(b,minPrecedence); - break; - } - case FUNCTION: { - consume(LP); - int numArgs = 0; - JSFunction b2 = new JSFunction(sourceName, parserLine, null); - b.add(parserLine, NEWFUNCTION, b2); - - // function prelude; arguments array is already on the stack - b2.add(parserLine, TOPSCOPE); - b2.add(parserLine, SWAP); - b2.add(parserLine, DECLARE, "arguments"); // declare arguments (equivalent to 'var arguments;') - b2.add(parserLine, SWAP); // set this.arguments and leave the value on the stack - b2.add(parserLine, PUT); - - while(peekToken() != RP) { // run through the list of argument names - numArgs++; - if (peekToken() == NAME) { - consume(NAME); // a named argument - String varName = string; - - b2.add(parserLine, DUP); // dup the args array - b2.add(parserLine, GET, JS.N(numArgs - 1)); // retrieve it from the arguments array - b2.add(parserLine, TOPSCOPE); - b2.add(parserLine, SWAP); - b2.add(parserLine, DECLARE, varName); // declare the name - b2.add(parserLine, SWAP); - b2.add(parserLine, PUT); - b2.add(parserLine, POP); // pop the value - b2.add(parserLine, POP); // pop the scope - } - if (peekToken() == RP) break; - consume(COMMA); - } - consume(RP); - - b2.numFormalArgs = numArgs; - b2.add(parserLine, POP); // pop off the arguments array - b2.add(parserLine, POP); // pop off TOPSCOPE - - if(peekToken() != LC) - throw pe("JSFunctions must have a block surrounded by curly brackets"); - - parseBlock(b2, null); // the function body - - b2.add(parserLine, LITERAL, null); // in case we "fall out the bottom", return NULL - b2.add(parserLine, RETURN); - - break; - } - default: throw pe("expected expression, found " + codeToString[tok] + ", which cannot start an expression"); - } - - // attempt to continue the expression - continueExpr(b, minPrecedence); - } - - private Grammar parseGrammar(Grammar g) throws IOException { - int tok = getToken(); - if (g != null) - switch(tok) { - case BITOR: return new Grammar.Alternative(g, parseGrammar(null)); - case ADD: return parseGrammar(new Grammar.Repetition(g, 1, Integer.MAX_VALUE)); - case MUL: return parseGrammar(new Grammar.Repetition(g, 0, Integer.MAX_VALUE)); - case HOOK: return parseGrammar(new Grammar.Repetition(g, 0, 1)); - } - Grammar g0 = null; - switch(tok) { - //case NUMBER: g0 = new Grammar.Literal(number); break; - case NAME: g0 = new Grammar.Reference(string); break; - case STRING: - g0 = new Grammar.Literal(string); - if (peekToken() == DOT) { - String old = string; - consume(DOT); - consume(DOT); - consume(STRING); - if (old.length() != 1 || string.length() != 1) throw pe("literal ranges must be single-char strings"); - g0 = new Grammar.Range(old.charAt(0), string.charAt(0)); - } - break; - case LP: g0 = parseGrammar(null); consume(RP); break; - default: pushBackToken(); return g; - } - if (g == null) return parseGrammar(g0); - return parseGrammar(new Grammar.Juxtaposition(g, g0)); - } - - /** - * Assuming that a complete assignable (lvalue) has just been - * parsed and the object and key are on the stack, - * continueExprAfterAssignable will attempt to parse an - * expression that modifies the assignable. This method always - * decreases the stack depth by exactly one element. - */ - private void continueExprAfterAssignable(JSFunction b,int minPrecedence) throws IOException { - int saveParserLine = parserLine; - _continueExprAfterAssignable(b,minPrecedence); - parserLine = saveParserLine; - } - private void _continueExprAfterAssignable(JSFunction b,int minPrecedence) throws IOException { - if (b == null) throw new Error("got null b; this should never happen"); - int tok = getToken(); - if (minPrecedence != -1 && (precedence[tok] < minPrecedence || (precedence[tok] == minPrecedence && !isRightAssociative[tok]))) - // force the default case - tok = -1; - switch(tok) { - - case GRAMMAR: { - b.add(parserLine, GET_PRESERVE); - Grammar g = parseGrammar(null); - if (peekToken() == LC) { - g.action = new JSFunction(sourceName, parserLine, null); - parseBlock((JSFunction)g.action); - ((JSFunction)g.action).add(parserLine, LITERAL, null); // in case we "fall out the bottom", return NULL - ((JSFunction)g.action).add(parserLine, RETURN); - } - b.add(parserLine, MAKE_GRAMMAR, g); - b.add(parserLine, PUT); - break; - } - - case ASSIGN_BITOR: case ASSIGN_BITXOR: case ASSIGN_BITAND: case ASSIGN_LSH: case ASSIGN_RSH: case ASSIGN_URSH: - case ASSIGN_MUL: case ASSIGN_DIV: case ASSIGN_MOD: case ASSIGN_ADD: case ASSIGN_SUB: case ADD_TRAP: case DEL_TRAP: { - if (tok != ADD_TRAP && tok != DEL_TRAP) b.add(parserLine, GET_PRESERVE); - - startExpr(b, precedence[tok]); - - int size = b.size; - - if (tok != ADD_TRAP && tok != DEL_TRAP) { - // tok-1 is always s/^ASSIGN_// (0 is BITOR, 1 is ASSIGN_BITOR, etc) - b.add(parserLine, tok - 1, tok-1==ADD ? JS.N(2) : null); - b.add(parserLine, PUT); - b.add(parserLine, SWAP); - b.add(parserLine, POP); - } else { - b.add(parserLine, tok); - } - break; - } - case INC: case DEC: { // postfix - b.add(parserLine, GET_PRESERVE, Boolean.TRUE); - b.add(parserLine, LITERAL, JS.N(1)); - b.add(parserLine, tok == INC ? ADD : SUB, JS.N(2)); - b.add(parserLine, PUT, null); - b.add(parserLine, SWAP, null); - b.add(parserLine, POP, null); - b.add(parserLine, LITERAL, JS.N(1)); - b.add(parserLine, tok == INC ? SUB : ADD, null); // undo what we just did, since this is postfix - break; - } - case ASSIGN: { - startExpr(b, precedence[tok]); - b.add(parserLine, PUT); - b.add(parserLine, SWAP); - b.add(parserLine, POP); - break; - } - case LP: { - - // Method calls are implemented by doing a GET_PRESERVE - // first. If the object supports method calls, it will - // return JS.METHOD - int n = parseArgs(b, 2); - b.add(parserLine, GET_PRESERVE); - b.add(parserLine, CALLMETHOD, JS.N(n)); - break; - } - default: { - pushBackToken(); - if(b.get(b.size-1) == LITERAL && b.getArg(b.size-1) != null) - b.set(b.size-1,GET,b.getArg(b.size-1)); - else - b.add(parserLine, GET); - return; - } - } - } - - - /** - * Assuming that a complete expression has just been parsed, - * continueExpr will attempt to extend this expression by - * parsing additional tokens and appending additional bytecodes. - * - * No operators with precedence less than minPrecedence - * will be parsed. - * - * If any bytecodes are appended, they will not alter the stack - * depth. - */ - private void continueExpr(JSFunction b, int minPrecedence) throws IOException { - int saveParserLine = parserLine; - _continueExpr(b, minPrecedence); - parserLine = saveParserLine; - } - private void _continueExpr(JSFunction b, int minPrecedence) throws IOException { - if (b == null) throw new Error("got null b; this should never happen"); - int tok = getToken(); - if (tok == -1) return; - if (minPrecedence != -1 && (precedence[tok] < minPrecedence || (precedence[tok] == minPrecedence && !isRightAssociative[tok]))) { - pushBackToken(); - return; - } - - switch (tok) { - case LP: { // invocation (not grouping) - int n = parseArgs(b, 1); - b.add(parserLine, CALL, JS.N(n)); - break; - } - case BITOR: case BITXOR: case BITAND: case SHEQ: case SHNE: case LSH: - case RSH: case URSH: case MUL: case DIV: case MOD: - case GT: case GE: case EQ: case NE: case LT: case LE: case SUB: { - startExpr(b, precedence[tok]); - b.add(parserLine, tok); - break; - } - case ADD: { - int count=1; - int nextTok; - do { - startExpr(b,precedence[tok]); - count++; - nextTok = getToken(); - } while(nextTok == tok); - pushBackToken(); - b.add(parserLine, tok, JS.N(count)); - break; - } - case OR: case AND: { - b.add(parserLine, tok == AND ? b.JF : b.JT, JS.ZERO); // test to see if we can short-circuit - int size = b.size; - startExpr(b, precedence[tok]); // otherwise check the second value - b.add(parserLine, JMP, JS.N(2)); // leave the second value on the stack and jump to the end - b.add(parserLine, LITERAL, tok == AND ? - JS.B(false) : JS.B(true)); // target of the short-circuit jump is here - b.set(size - 1, JS.N(b.size - size)); // write the target of the short-circuit jump - break; - } - case DOT: { - // support foo..bar syntax for foo[""].bar - if (peekToken() == DOT) { - string = ""; - } else { - consume(NAME); - } - b.add(parserLine, LITERAL, string); - continueExprAfterAssignable(b,minPrecedence); - break; - } - case LB: { // subscripting (not array constructor) - startExpr(b, -1); - consume(RB); - continueExprAfterAssignable(b,minPrecedence); - break; - } - case HOOK: { - b.add(parserLine, JF, JS.ZERO); // jump to the if-false expression - int size = b.size; - startExpr(b, minPrecedence); // write the if-true expression - b.add(parserLine, JMP, JS.ZERO); // if true, jump *over* the if-false expression - b.set(size - 1, JS.N(b.size - size + 1)); // now we know where the target of the jump is - consume(COLON); - size = b.size; - startExpr(b, minPrecedence); // write the if-false expression - b.set(size - 1, JS.N(b.size - size + 1)); // this is the end; jump to here - break; - } - case COMMA: { - // pop the result of the previous expression, it is ignored - b.add(parserLine,POP); - startExpr(b,-1); - break; - } - default: { - pushBackToken(); - return; - } - } - - continueExpr(b, minPrecedence); // try to continue the expression - } - - // parse a set of comma separated function arguments, assume LP has already been consumed - // if swap is true, (because the function is already on the stack) we will SWAP after each argument to keep it on top - private int parseArgs(JSFunction b, int pushdown) throws IOException { - int i = 0; - while(peekToken() != RP) { - i++; - if (peekToken() != COMMA) { - startExpr(b, NO_COMMA); - b.add(parserLine, SWAP, JS.N(pushdown)); - if (peekToken() == RP) break; - } - consume(COMMA); - } - consume(RP); - return i; - } - - /** Parse a block of statements which must be surrounded by LC..RC. */ - void parseBlock(JSFunction b) throws IOException { parseBlock(b, null); } - void parseBlock(JSFunction b, String label) throws IOException { - int saveParserLine = parserLine; - _parseBlock(b, label); - parserLine = saveParserLine; - } - void _parseBlock(JSFunction b, String label) throws IOException { - if (peekToken() == -1) return; - else if (peekToken() != LC) parseStatement(b, null); - else { - consume(LC); - while(peekToken() != RC && peekToken() != -1) parseStatement(b, null); - consume(RC); - } - } - - /** Parse a single statement, consuming the RC or SEMI which terminates it. */ - void parseStatement(JSFunction b, String label) throws IOException { - int saveParserLine = parserLine; - _parseStatement(b, label); - parserLine = saveParserLine; - } - void _parseStatement(JSFunction b, String label) throws IOException { - int tok = peekToken(); - if (tok == -1) return; - switch(tok = getToken()) { - - case THROW: case ASSERT: case RETURN: { - if (tok == RETURN && peekToken() == SEMI) - b.add(parserLine, LITERAL, null); - else - startExpr(b, -1); - b.add(parserLine, tok); - consume(SEMI); - break; - } - case BREAK: case CONTINUE: { - if (peekToken() == NAME) consume(NAME); - b.add(parserLine, tok, string); - consume(SEMI); - break; - } - case VAR: { - b.add(parserLine, TOPSCOPE); // push the current scope - while(true) { - consume(NAME); - b.add(parserLine, DECLARE, string); // declare it - if (peekToken() == ASSIGN) { // if there is an '=' after the variable name - consume(ASSIGN); - startExpr(b, NO_COMMA); - b.add(parserLine, PUT); // assign it - b.add(parserLine, POP); // clean the stack - } else { - b.add(parserLine, POP); // pop the string pushed by declare - } - if (peekToken() != COMMA) break; - consume(COMMA); - } - b.add(parserLine, POP); // pop off the topscope - if ((mostRecentlyReadToken != RC || peekToken() == SEMI) && peekToken() != -1 && mostRecentlyReadToken != SEMI) consume(SEMI); - break; - } - case IF: { - consume(LP); - startExpr(b, -1); - consume(RP); - - b.add(parserLine, JF, JS.ZERO); // if false, jump to the else-block - int size = b.size; - parseStatement(b, null); - - if (peekToken() == ELSE) { - consume(ELSE); - b.add(parserLine, JMP, JS.ZERO); // if we took the true-block, jump over the else-block - b.set(size - 1, JS.N(b.size - size + 1)); - size = b.size; - parseStatement(b, null); - } - b.set(size - 1, JS.N(b.size - size + 1)); // regardless of which branch we took, b[size] needs to point here - break; - } - case WHILE: { - consume(LP); - if (label != null) b.add(parserLine, LABEL, label); - b.add(parserLine, LOOP); - int size = b.size; - b.add(parserLine, POP); // discard the first-iteration indicator - startExpr(b, -1); - b.add(parserLine, JT, JS.N(2)); // if the while() clause is true, jump over the BREAK - b.add(parserLine, BREAK); - consume(RP); - parseStatement(b, null); - b.add(parserLine, CONTINUE); // if we fall out of the end, definately continue - b.set(size - 1, JS.N(b.size - size + 1)); // end of the loop - break; - } - case SWITCH: { - consume(LP); - if (label != null) b.add(parserLine, LABEL, label); - b.add(parserLine, LOOP); - int size0 = b.size; - startExpr(b, -1); - consume(RP); - consume(LC); - while(true) - if (peekToken() == CASE) { // we compile CASE statements like a bunch of if..else's - consume(CASE); - b.add(parserLine, DUP); // duplicate the switch() value; we'll consume one copy - startExpr(b, -1); - consume(COLON); - b.add(parserLine, EQ); // check if we should do this case-block - b.add(parserLine, JF, JS.ZERO); // if not, jump to the next one - int size = b.size; - while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) parseStatement(b, null); - b.set(size - 1, JS.N(1 + b.size - size)); - } else if (peekToken() == DEFAULT) { - consume(DEFAULT); - consume(COLON); - while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) parseStatement(b, null); - } else if (peekToken() == RC) { - consume(RC); - b.add(parserLine, BREAK); // break out of the loop if we 'fall through' - break; - } else { - throw pe("expected CASE, DEFAULT, or RC; got " + codeToString[peekToken()]); - } - b.set(size0 - 1, JS.N(b.size - size0 + 1)); // end of the loop - break; - } - - case DO: { - if (label != null) b.add(parserLine, LABEL, label); - b.add(parserLine, LOOP); - int size = b.size; - parseStatement(b, null); - consume(WHILE); - consume(LP); - startExpr(b, -1); - b.add(parserLine, JT, JS.N(2)); // check the while() clause; jump over the BREAK if true - b.add(parserLine, BREAK); - b.add(parserLine, CONTINUE); - consume(RP); - consume(SEMI); - b.set(size - 1, JS.N(b.size - size + 1)); // end of the loop; write this location to the LOOP instruction - break; - } - - case TRY: { - b.add(parserLine, TRY); // try bytecode causes a TryMarker to be pushed - int tryInsn = b.size - 1; - // parse the expression to be TRYed - parseStatement(b, null); - // pop the try marker. this is pushed when the TRY bytecode is executed - b.add(parserLine, POP); - // jump forward to the end of the catch block, start of the finally block - b.add(parserLine, JMP); - int successJMPInsn = b.size - 1; - - if (peekToken() != CATCH && peekToken() != FINALLY) - throw pe("try without catch or finally"); - - int catchJMPDistance = -1; - if (peekToken() == CATCH) { - Vec catchEnds = new Vec(); - boolean catchAll = false; - - catchJMPDistance = b.size - tryInsn; - - while(peekToken() == CATCH && !catchAll) { - String exceptionVar; - getToken(); - consume(LP); - consume(NAME); - exceptionVar = string; - int[] writebacks = new int[] { -1, -1, -1 }; - if (peekToken() != RP) { - // extended XWT catch block: catch(e faultCode "foo.bar.baz") - consume(NAME); - String propName = string; - b.add(parserLine, DUP); - b.add(parserLine, LITERAL, string); - b.add(parserLine, GET); - b.add(parserLine, DUP); - b.add(parserLine, LITERAL, null); - b.add(parserLine, EQ); - b.add(parserLine, JT); - writebacks[0] = b.size - 1; - if (peekToken() == STRING) { - consume(STRING); - b.add(parserLine, DUP); - b.add(parserLine, LITERAL, string); - b.add(parserLine, LT); - b.add(parserLine, JT); - writebacks[1] = b.size - 1; - b.add(parserLine, DUP); - b.add(parserLine, LITERAL, string + "/"); // (slash is ASCII after dot) - b.add(parserLine, GE); - b.add(parserLine, JT); - writebacks[2] = b.size - 1; - } else { - consume(NUMBER); - b.add(parserLine, DUP); - b.add(parserLine, LITERAL, number); - b.add(parserLine, EQ); - b.add(parserLine, JF); - writebacks[1] = b.size - 1; - } - b.add(parserLine, POP); // pop the element thats on the stack from the compare - } else { - catchAll = true; - } - consume(RP); - // the exception is on top of the stack; put it to the chosen name - b.add(parserLine, NEWSCOPE); - b.add(parserLine, TOPSCOPE); - b.add(parserLine, SWAP); - b.add(parserLine, LITERAL,exceptionVar); - b.add(parserLine, DECLARE); - b.add(parserLine, SWAP); - b.add(parserLine, PUT); - b.add(parserLine, POP); - b.add(parserLine, POP); - parseBlock(b, null); - b.add(parserLine, OLDSCOPE); - - b.add(parserLine, JMP); - catchEnds.addElement(new Integer(b.size-1)); - - for(int i=0; i<3; i++) if (writebacks[i] != -1) b.set(writebacks[i], JS.N(b.size-writebacks[i])); - b.add(parserLine, POP); // pop the element thats on the stack from the compare - } - - if(!catchAll) - b.add(parserLine, THROW); - - for(int i=0;i