X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=src%2Forg%2Fxwt%2Fjs%2FParser.java;h=d4a27531baa733b57a0c3aca38f94c8d3c834656;hb=6d2ebd2a12bccf7f4fdbd2beb6828fb4cca26d6d;hp=f70a13d7d8f99d5d37fc8cef2a887a503000aa32;hpb=11fb6d3f994008abe6c16b4402c3904a543e240e;p=org.ibex.core.git diff --git a/src/org/xwt/js/Parser.java b/src/org/xwt/js/Parser.java index f70a13d..d4a2753 100644 --- a/src/org/xwt/js/Parser.java +++ b/src/org/xwt/js/Parser.java @@ -1,25 +1,23 @@ -// Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL] +// Copyright 2003 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 ByteCodeBlock's */ +class Parser extends Lexer implements ByteCodes { -/** parses a stream of lexed tokens into a tree of Expr's */ -public class Parser extends Lexer { // Constructors ////////////////////////////////////////////////////// - public Parser(Reader r, String sourceName, int line) throws IOException { - super(r); - this.sourceName = sourceName; - this.line = line; - } + public Parser(Reader r, String sourceName, int line) throws IOException { super(r, sourceName, line); } + /** for debugging */ public static void main(String[] s) throws Exception { Parser p = new Parser(new InputStreamReader(System.in), "stdin", 0); while(true) { - Expr block = p.parseBlock(false); + ByteCodeBlock block = new ByteCodeBlock(0, "stdin"); + p.parseStatement(false, block); if (block == null) return; System.out.println(block); if (p.peekToken() == -1) return; @@ -32,12 +30,13 @@ public class Parser extends Lexer { static byte[] precedence = new byte[MAX_TOKEN + 1]; static boolean[] isRightAssociative = new boolean[MAX_TOKEN + 1]; static { - precedence[ASSIGN] = 1; isRightAssociative[ASSIGN] = true; + + precedence[ASSIGN] = 1; precedence[HOOK] = 2; precedence[COMMA] = 3; - precedence[GT] = precedence[GE] = 4; - precedence[OR] = precedence[AND] = 5; + precedence[OR] = precedence[AND] = 4; + precedence[GT] = precedence[GE] = 5; precedence[BITOR] = 6; precedence[BITXOR] = 7; precedence[BITAND] = 8; @@ -47,7 +46,7 @@ public class Parser extends Lexer { precedence[LSH] = precedence[RSH] = precedence[URSH] = 12; precedence[ADD] = precedence[SUB] = 13; precedence[MUL] = precedence[DIV] = precedence[MOD] = 14; - precedence[BITNOT] = precedence[INSTANCEOF] = 15; + precedence[BITNOT] = 15; precedence[INC] = precedence[DEC] = 16; precedence[LP] = 17; precedence[LB] = 18; @@ -57,742 +56,580 @@ public class Parser extends Lexer { // Parsing Logic ///////////////////////////////////////////////////////// - /** a block is either a single statement or a list of statements surrounded by curly braces; all expressions are also statements */ - public Expr parseBlock(boolean requireBraces) throws IOException { - Expr ret = null; - int tok = peekToken(); - boolean braced = tok == LC; - if (requireBraces && !braced) throw new ParserException("expected {"); - if (braced) getToken(); - Expr head = null; - Expr tail = null; + private ByteCodeBlock newbb(int line) { return new ByteCodeBlock(line, sourceName); } + + /** gets a token and throws an exception if it is not code */ + public void consume(int code) throws IOException { + if (getToken() != code) + throw new ParserException("expected " + codeToString[code] + ", got " + (op == -1 ? "EOF" : codeToString[op])); + } + + /** append the largest expression beginning with prefix containing no operators of precedence below minPrecedence */ + public void startExpr(ByteCodeBlock block) throws IOException { startExpr(-1, block); } + /* + public ByteCodeBlock startExpr(int minPrecedence) throws IOException { + ByteCodeBlock ret = new ByteCodeBlock(line, sourceName); + startExpr(minPrecedence, ret); + return ret.size() == 0 ? null : ret; + } + */ + + public void startExpr(int minPrecedence, ByteCodeBlock appendTo) throws IOException { + int tok = getToken(); int curLine = line; - OUTER: while(true) { - Expr smt; + if (tok == -1) return; - switch(tok = peekToken()) { - case -1: break OUTER; - case LC: smt = parseBlock(true); break; - case THROW: case RETURN: case ASSERT: - getToken(); - smt = new Expr(curLine, tok, parseMaximalExpr()); - if (getToken() != SEMI) throw new ParserException("expected ;"); - break; + ByteCodeBlock b = appendTo; - /* - FIXME - case NAME: { - getToken(); - String str = string; - if (getToken() != COLON) throw new ParserException("expected COLON after label"); - Expr labeledBlock = parseBlock(false); - if (labeledBlock.code != WHILE && labeledBlock.code != FOR && labeledBlock.code != SWITCH) - throw new ParserException("you can only label a WHILE, FOR, or SWITCH block"); - labeledBlock.string = str; - return labeledBlock; + switch (tok) { + case NUMBER: continueExpr(b.add(ByteCodeBlock.LITERAL, number), minPrecedence); return; + case STRING: continueExpr(b.add(ByteCodeBlock.LITERAL, string), minPrecedence); return; + case THIS: continueExpr(b.add(TOPSCOPE, null), minPrecedence); return; + case NULL: continueExpr(b.add(ByteCodeBlock.LITERAL, null), minPrecedence); return; + case TRUE: case FALSE: continueExpr(b.add(ByteCodeBlock.LITERAL, new Boolean(tok == TRUE)), minPrecedence); return; + case LB: { + b.add(ARRAY, new Integer(0)); + int i = 0; + while(true) { + int size = b.size(); + startExpr(b); + if (size == b.size()) + if (peekToken() == RB) { consume(RB); continueExpr(b, minPrecedence); return; } + b.add(LITERAL, new Integer(i++)); + if (size == b.size()) b.add(LITERAL, null); + b.add(PUT); + b.add(POP); + if (peekToken() == RB) { consume(RB); continueExpr(b, minPrecedence); return; } + consume(COMMA); } - */ - - case GOTO: case BREAK: case CONTINUE: { + } + case SUB: { + consume(NUMBER); + b.add(ByteCodeBlock.LITERAL, new Double(number.doubleValue() * -1)); + continueExpr(b, minPrecedence); + return; + } + case LP: { + startExpr(b); + consume(RP); + continueExpr(b, minPrecedence); + return; + } + case INC: case DEC: { + // prefix + startExpr(precedence[tok], b); + b.set(b.size() - 1, tok, new Boolean(true)); + continueExpr(b, minPrecedence); + return; + } + case BANG: case BITNOT: case TYPEOF: { + startExpr(precedence[tok], b); + b.add(tok); + continueExpr(b, minPrecedence); + return; + } + case LC: { + b.add(OBJECT, null); + if (peekToken() != RC) while(true) { + if (peekToken() != NAME && peekToken() != STRING) throw new Error("expected NAME or STRING"); getToken(); - int t = peekToken(); - if (t == NAME) { - getToken(); - smt = new Expr(curLine, tok, new Expr(curLine, string)); - } else if (t == GOTO) { - throw new ParserException("goto must be followed by a label"); - } else if (t == SEMI) { - getToken(); - smt = new Expr(curLine, tok); - } else { - throw new ParserException(codeToString[tok] + " followed by a " + codeToString[t] + "; expected NAME or SEMI"); - } - break; + b.add(LITERAL, string); + consume(COLON); + startExpr(b); + b.add(PUT); + b.add(POP); + if (peekToken() == RC) break; + consume(COMMA); + if (peekToken() == RC) break; } + consume(RC); + continueExpr(b, minPrecedence); + return; + } + case NAME: { + String name = string; + if (peekToken() == ASSIGN) { + consume(ASSIGN); + b.add(TOPSCOPE); + b.add(ByteCodeBlock.LITERAL, name); + startExpr(minPrecedence, b); + b.add(ByteCodeBlock.PUT); + b.add(ByteCodeBlock.SWAP); + b.add(ByteCodeBlock.POP); + } else { + b.add(TOPSCOPE); + b.add(ByteCodeBlock.LITERAL, name); + b.add(ByteCodeBlock.GET); + } + continueExpr(b, minPrecedence); + return; + } + case FUNCTION: { + consume(LP); + int numArgs = 0; + ByteCodeBlock b2 = newbb(curLine); + b2.add(TOPSCOPE); + b2.add(SWAP); + b2.add(LITERAL, "arguments"); + b2.add(LITERAL, "arguments"); + b2.add(DECLARE); + b2.add(SWAP); + b2.add(PUT); + b2.add(SWAP); + b2.add(POP); + if (peekToken() == RP) consume(RP); + else while(true) { + if (peekToken() == COMMA) { + consume(COMMA); - case RC: - if (braced) getToken(); - break OUTER; - - case SEMI: - getToken(); - if (!braced) break OUTER; - continue; - - default: - smt = parseMaximalExpr(); - if (smt == null) { - if (head == null) throw new ParserException("empty statement list; next token is " + codeToString[peekToken()]); - break OUTER; + } else { + consume(NAME); + + // declare the name + b2.add(LITERAL, string); + b2.add(DECLARE); + + // retrieve it from the arguments array + b2.add(LITERAL, new Integer(numArgs)); + b2.add(GET_PRESERVE); + b2.add(SWAP); + b2.add(POP); + + // put it to the current scope + b2.add(TOPSCOPE); + b2.add(SWAP); + b2.add(LITERAL, string); + b2.add(SWAP); + b2.add(PUT); + + // clean the stack + b2.add(POP); + b2.add(POP); + + if (peekToken() == RP) { consume(RP); break; } + consume(COMMA); } - break; + numArgs++; } - if (head == null) head = tail = smt; else tail = (tail.next = smt); + // pop off the arguments array + b2.add(POP); + parseStatement(true, b2); + b2.add(LITERAL, null); + b2.add(RETURN); + continueExpr(b.add(NEWFUNCTION, b2), minPrecedence); + return; } - return new Expr(curLine, LC, head); - } - - /** throws an error if the next token is not code */ - public void expect(int code) throws IOException { - int got = peekToken(); - if (got != code) - throw new ParserException("expected " + codeToString[code] + ", got " + (got == -1 ? "EOL" : codeToString[got])); - } - - /** parses the largest possible expression */ - public Expr parseMaximalExpr() throws IOException { return parseMaximalExpr(null, -1); } - public Expr parseMaximalExpr(Expr prefix, int minPrecedence) throws IOException { - while(true) { - if (peekToken() == -1) break; - Expr save = prefix; - prefix = parseSingleExpr(prefix, minPrecedence); - if (save == prefix) break; - if (prefix == null) throw new ParserException("parseSingleExpr() returned null"); + default: pushBackToken(); return; } - return prefix; } - public Expr parseSingleExpr(Expr prefix, int minPrecedence) throws IOException { - Expr e1 = null, e2 = null, e3 = null, head = null, tail = null, ret = null; - - int tok = peekToken(); - if (minPrecedence > 0 && - tok < precedence.length && - precedence[tok] != 0 && - (isRightAssociative[tok] ? - (precedence[tok] < minPrecedence) : - (precedence[tok] <= minPrecedence))) { - return prefix; - } - getToken(); + public void continueExpr(ByteCodeBlock prefix, int minPrecedence) throws IOException { + int tok = getToken(); int curLine = line; + if (tok == -1) return; + if (minPrecedence > 0 && precedence[tok] != 0) + if (precedence[tok] < minPrecedence || (precedence[tok] == minPrecedence && !isRightAssociative[tok])) + { pushBackToken(); return; } - // these case arms match the precedence of operators; each arm is a precedence level. - switch (tok) { + if (prefix == null) throw new Error("got null prefix"); + ByteCodeBlock b = prefix; - case WITH: throw new ParserException("XWT does not allow the WITH keyword"); - case VOID: case RESERVED: throw new ParserException("reserved word that you shouldn't be using"); - case NAME: if (prefix != null) { pushBackToken(); return prefix; } else return parseMaximalExpr(new Expr(curLine, NAME, string), minPrecedence); - case STRING: if (prefix != null) { pushBackToken(); return prefix; } else return new Expr(curLine, string); - case NUMBER: if (prefix != null) { pushBackToken(); return prefix; } else return new Expr(curLine, number); - case NULL: case TRUE: case FALSE: case NOP: if (prefix != null) { pushBackToken(); return prefix; } else return new Expr(curLine, tok); + switch (tok) { - case ASSIGN_BITOR: case ASSIGN_BITXOR: case ASSIGN_BITAND: case ASSIGN_LSH: - case ASSIGN_RSH: case ASSIGN_URSH: case ASSIGN_ADD: case ASSIGN_SUB: case ASSIGN_MUL: case ASSIGN_DIV: case ASSIGN_MOD: - return new Expr(curLine, ASSIGN, prefix, new Expr(curLine, tok - 1, prefix, parseMaximalExpr(null, precedence[ASSIGN]))); + case ASSIGN_BITOR: case ASSIGN_BITXOR: case ASSIGN_BITAND: case ASSIGN_LSH: case ASSIGN_RSH: case ASSIGN_URSH: + case ASSIGN_ADD: case ASSIGN_SUB: case ASSIGN_MUL: case ASSIGN_DIV: case ASSIGN_MOD: { + prefix.set(prefix.size() - 1, b.GET_PRESERVE, new Boolean(true)); + startExpr(precedence[tok - 1], b); + prefix.add(tok - 1); + prefix.add(PUT); + prefix.add(SWAP); + prefix.add(POP); + continueExpr(b, minPrecedence); + return; + } - case COMMA: pushBackToken();return prefix; + case INC: case DEC: { + // postfix + b.set(b.size() - 1, tok, new Boolean(false)); + continueExpr(b, minPrecedence); + return; + } - case THIS: - if (prefix != null) { pushBackToken(); return prefix; } - return new Expr(curLine, THIS); + case LP: { + // invocation + int i = 0; + while(peekToken() != RP) { + i++; + startExpr(b); + if (peekToken() == RP) break; + consume(COMMA); + } + consume(RP); + b.add(CALL, new Integer(i)); + continueExpr(b, minPrecedence); + return; + } - case SUB: - if (prefix == null && peekToken() == NUMBER) { - getToken(); - return new Expr(curLine, new Double(number.doubleValue() * -1)); - } // else fall through case BITOR: case BITXOR: case BITAND: case SHEQ: case SHNE: case LSH: case RSH: case URSH: case ADD: case MUL: case DIV: case MOD: - case ASSIGN: case GT: case GE: case OR: case AND: - case EQ: case NE: case LT: case LE: - if (prefix == null) throw new ParserException("the " + codeToString[tok] + " token cannot start an expression"); - return new Expr(curLine, tok, prefix, parseMaximalExpr(null, precedence[tok])); - - case DOT: { - //e1 = parseMaximalExpr(null, precedence[DOT]); - //if (e1.code == NAME) e1.code = STRING; - //else throw new ParserException("argument to DOT must be a NAME; got a " + codeToString[e1.code]); - //return new Expr(curLine, DOT, prefix, e1); - tok = getToken(); - if (tok != NAME) throw new ParserException("DOT must be followed by a NAME"); - return new Expr(curLine, DOT, prefix, new Expr(curLine, STRING, string)); + case GT: case GE: case EQ: case NE: case LT: case LE: case SUB: { + startExpr(precedence[tok], b); + b.add(tok); + continueExpr(b, minPrecedence); + return; } - case BANG: case BITNOT: case INSTANCEOF: case TYPEOF: - if (prefix != null) { pushBackToken(); return prefix; } - return new Expr(curLine, tok, parseMaximalExpr(null, precedence[tok])); + case OR: case AND: { + b.add(tok == AND ? b.JF : b.JT, new Integer(0)); + int size = b.size(); + startExpr(precedence[tok], b); + b.set(size - 1, new Integer(b.size() - size + 2)); + b.add(JMP, new Integer(2)); + b.add(LITERAL, tok == AND ? new Boolean(false) : new Boolean(true)); + continueExpr(b, minPrecedence); + return; + } - case INC: case DEC: - if (prefix == null) { - // prefix - e1 = parseMaximalExpr(null, precedence[tok]); - return new Expr(curLine, ASSIGN, e1, new Expr(curLine, tok == INC ? ADD : SUB, e1, new Expr(curLine, new Integer(1)))); + case DOT: { + consume(NAME); + String target = string; + if (peekToken() == ASSIGN) { + consume(ASSIGN); + b.add(LITERAL, target); + startExpr(b); + b.add(PUT); + b.add(SWAP); + b.add(POP); } else { - // postfix - return new Expr(curLine, tok, prefix); - } - - case LP: - if (prefix == null) { // grouping - Expr r = parseMaximalExpr(); - expect(RP); getToken(); - return r; - - } else { // invocation - while(peekToken() != RP) { - Expr e = parseMaximalExpr(); - if (head == null) head = tail = e; else tail = tail.next = e; - tok = getToken(); - if (tok == RP) { pushBackToken(); break; } - if (tok != COMMA) throw new ParserException("expected comma or right paren, got " + codeToString[tok]); - } - getToken(); - return new Expr(curLine, LP, prefix, head); + b.add(LITERAL, target); + b.add(GET); } + continueExpr(b, minPrecedence); + return; + } - case LB: - if (prefix != null) { - // subscripting - e1 = parseMaximalExpr(); - if (getToken() != RB) throw new ParserException("expected a right brace"); - return new Expr(curLine, DOT, prefix, e1); + case LB: { + startExpr(b); + consume(RB); + if (peekToken() == ASSIGN) { + consume(ASSIGN); + startExpr(b); + b.add(PUT); + b.add(SWAP); + b.add(POP); } else { - // array ctor - while(true) { - if (peekToken() == RB) { getToken(); return new Expr(curLine, LB, prefix, head); } - Expr eee = parseMaximalExpr(); - if (eee != null) { - if (head == null) head = tail = eee; - else tail.next = tail = eee; - } - tok = getToken(); - if (tok == RB) return new Expr(curLine, LB, prefix, head); - if (tok != COMMA) throw new ParserException("expected right bracket or comma"); - } - } - - case LC: - if (prefix != null) { pushBackToken(); return prefix; } - tok = getToken(); - if (tok == RC) return new Expr(curLine, RC, head); - while(true) { - if (tok != NAME && tok != STRING) throw new ParserException("expecting name, got " + codeToString[tok]); - Expr name = new Expr(curLine, NAME, string); - if (getToken() != COLON) throw new ParserException("expecting colon"); - e1 = new Expr(curLine, COLON, name, parseMaximalExpr()); - if (head == null) head = tail = e1; else tail = tail.next = e1; - tok = getToken(); - if (tok != COMMA && tok != RC) throw new ParserException("expected right curly or comma, got " + codeToString[tok]); - if (tok == RC) return new Expr(curLine, RC, head); - tok = getToken(); - if (tok == RC) return new Expr(curLine, RC, head); - } - - case HOOK: - e2 = parseMaximalExpr(); - if (getToken() != COLON) throw new ParserException("expected colon to close ?: expression"); - e3 = parseMaximalExpr(); - return new Expr(curLine, HOOK, prefix, new Expr(curLine, ELSE, e2, e3)); - - case SWITCH: { - if (prefix != null) { pushBackToken(); return prefix; } - if (getToken() != LP) throw new ParserException("expected left paren"); - Expr switchExpr = parseMaximalExpr(); - if (getToken() != RP) throw new ParserException("expected left paren"); - if (getToken() != LC) throw new ParserException("expected left brace"); - Expr firstExpr = null; - Expr lastExpr = null; - while(true) { - tok = getToken(); - Expr caseExpr; - if (tok == DEFAULT) { - caseExpr = null; - } else if (tok == CASE) { - // FIXME: we don't support non-brace-enclosed CASE blocks - caseExpr = parseMaximalExpr(); - } else { - throw new ParserException("expected CASE"); - } - expect(COLON); getToken(); - head = tail = null; - while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) { - e1 = parseBlock(false); - if (e1 == null) break; - if (head == null) head = tail = e1; else tail = tail.next = e1; - } - e1 = new Expr(curLine, tok, caseExpr, new Expr(curLine, LC, head)); - if (lastExpr == null) firstExpr = e1; else lastExpr.next = e1; - lastExpr = e1; - if (peekToken() == RC) {getToken(); return new Expr(curLine, SWITCH, switchExpr, firstExpr); } + b.add(GET); } + continueExpr(b, minPrecedence); + return; } - - case FUNCTION: { - if (prefix != null) { pushBackToken(); return prefix; } - if (getToken() != LP) throw new ParserException("function keyword must be followed by a left paren"); - Expr formalArgs = null, cur = null; - tok = getToken(); - while(tok != RP) { - if (tok != NAME) throw new ParserException("expected a variable name"); - if (cur == null) { formalArgs = cur = new Expr(curLine, string); } - else { cur.next = new Expr(curLine, NAME, string); cur = cur.next; } - tok = getToken(); - if (tok == RP) break; - if (tok != COMMA) throw new ParserException("function argument list must consist of alternating NAMEs and COMMAs"); - tok = getToken(); - } - return new Expr(curLine, FUNCTION, formalArgs, parseBlock(true)); + + case HOOK: { + b.add(JF, new Integer(0)); + int size = b.size(); + startExpr(b); + b.set(size - 1, new Integer(b.size() - size + 2)); + b.add(JMP, new Integer(0)); + consume(COLON); + size = b.size(); + startExpr(b); + b.set(size - 1, new Integer(b.size() - size + 1)); + continueExpr(b, minPrecedence); + return; } - case VAR: - if (prefix != null) { pushBackToken(); return prefix; } - while(true) { - if (getToken() != NAME) throw new ParserException("variable declarations must start with a variable name"); - Expr name = new Expr(curLine, NAME, string); - Expr initVal = null; - tok = peekToken(); - Expr e = null; - if (tok == ASSIGN) { - getToken(); - initVal = parseMaximalExpr(); - tok = peekToken(); - e = new Expr(curLine, ASSIGN, name, initVal); - } else { - e = new Expr(curLine, NAME, name); - } - if (head == null) head = tail = e; else tail = tail.next = e; - if (tok != COMMA) break; - getToken(); - } - return new Expr(curLine, VAR, head); + default: { pushBackToken(); return; } + } + } + + /** a block is either a single statement or a list of statements surrounded by curly braces; all expressions are also statements */ - case TRY: { - // We deliberately allow you to omit braces in catch{}/finally{} if they are single statements... - if (prefix != null) { pushBackToken(); return prefix; } - Expr tryBlock = parseBlock(true); - - tok = peekToken(); - if (tok == CATCH) { - getToken(); - if (getToken() != LP) throw new ParserException("expected ("); - if (getToken() != NAME) throw new ParserException("expected name"); - Expr name = new Expr(curLine, NAME, string); - if (getToken() != RP) throw new ParserException("expected )"); - head = tail = new Expr(curLine, CATCH, name, parseBlock(false)); - tok = peekToken(); - } - if (tok == FINALLY) { - getToken(); - e1 = new Expr(curLine, FINALLY, parseBlock(false)); - if (head == null) head = tail = e1; else tail = tail.next = e1; - } + public ByteCodeBlock parseStatement() throws IOException { + ByteCodeBlock ret = new ByteCodeBlock(line, sourceName); + ret.add(ret.LITERAL, null); + parseStatement(false, ret); + if (ret.size() == 1) return null; + return ret; + } - if (head == null) throw new ParserException("try without catch or finally"); - return new Expr(curLine, TRY, tryBlock, head); - } + public void parseStatement(boolean requireBraces, ByteCodeBlock b) throws IOException { + int tok = peekToken(); + if (tok == -1) return; + boolean braced = tok == LC; + if (requireBraces && !braced) throw new ParserException("expected {, got " + codeToString[tok]); + if (braced) consume(LC); + int curLine = line; + while(true) { + switch(tok = peekToken()) { - case IF: case WHILE: { - if (prefix != null) { pushBackToken(); return prefix; } - if (getToken() != LP) throw new ParserException("expected left paren"); - Expr parenExpr = parseMaximalExpr(); - int t; - if ((t = getToken()) != RP) throw new ParserException("expected right paren, but got " + codeToString[t]); - Expr firstBlock = parseBlock(false); - if (tok == IF && peekToken() == ELSE) { + case THROW: case RETURN: case ASSERT: { getToken(); - return new Expr(curLine, tok, parenExpr, new Expr(curLine, ELSE, firstBlock, parseBlock(false))); - } else if (tok == IF) { - return new Expr(curLine, tok, parenExpr, new Expr(curLine, ELSE, firstBlock, new Expr(curLine, LC))); - } else if (tok == WHILE) { - return new Expr(curLine, tok, parenExpr, firstBlock); + if (tok == RETURN && peekToken() == SEMI) b.add(LITERAL, null); + else startExpr(b); + consume(SEMI); + b.add(tok); + break; } - } - case IN: pushBackToken(); return prefix; - - case FOR: - if (prefix != null) { pushBackToken(); return prefix; } - if (getToken() != LP) throw new ParserException("expected left paren"); - e1 = parseMaximalExpr(null, -1); - if (peekToken() == IN) { + case BREAK: case CONTINUE: { getToken(); - e2 = parseMaximalExpr(null, -1); - if (getToken() != RP) throw new ParserException("expected right paren"); - return new Expr(curLine, FOR, new Expr(curLine, IN, e1, e2), parseBlock(false)); + if (peekToken() == NAME) consume(NAME); + b.add(tok, string); + consume(SEMI); + break; + } - } else { - if (getToken() != SEMI) throw new ParserException("expected ;"); - e2 = parseMaximalExpr(); - if (getToken() != SEMI) throw new ParserException("expected ;"); - e3 = parseMaximalExpr(); - if (getToken() != RP) throw new ParserException("expected right paren"); - return new Expr(curLine, LC, e1, new Expr(curLine, WHILE, e2, new Expr(curLine, LC, parseBlock(false), e3))); + case SEMI: + consume(SEMI); + if (!braced) return; + break; + + case VAR: { + consume(VAR); + b.add(TOPSCOPE); // push the current scope + while(true) { + consume(NAME); + String name = string; + b.add(LITERAL, name); // push the name to be declared + b.add(DECLARE); // declare it + if (peekToken() == ASSIGN) { // if there is an '=' after the variable name + b.add(LITERAL, name); // put the var name back on the stack + consume(ASSIGN); + startExpr(b); + b.add(PUT); + b.add(POP); + } + if (peekToken() != COMMA) break; + consume(COMMA); + } + b.add(POP); + if (peekToken() == SEMI) consume(SEMI); + break; } - - case DO: { - if (prefix != null) { pushBackToken(); return prefix; } - Expr firstBlock = parseBlock(false); - if (getToken() != WHILE) throw new ParserException("expecting WHILE"); - if (getToken() != LP) throw new ParserException("expected left paren"); - Expr whileExpr = parseMaximalExpr(); - if (getToken() != RP) throw new ParserException("expected right paren"); - if (getToken() != SEMI) throw new ParserException("semicolon"); - return new Expr(curLine, DO, firstBlock, whileExpr); - } - - default: - pushBackToken(); - return prefix; - } - } - - // Expr ////////////////////////////////////////////////////////////////////// - - /** sorta like gcc trees */ - class Expr { - int code = -1; - - final Expr left; - final Expr right; - Expr next = null; // if this expr is part of a list - int line = -1; - String sourceName = "unknown"; - - String string = null; - Number number = null; - - public String toString() { return toString(0); } - public String toString(int indent) { - String ret = ""; - for(int i=0; i> toLong(right.eval(s))); - case Lexer.URSH: return new Long(toLong(left.eval(s)) >>> toLong(right.eval(s))); - - case Lexer.LT: return toDouble(left.eval(s)) < toDouble(right.eval(s)) ? Boolean.TRUE : Boolean.FALSE; - case Lexer.LE: return toDouble(left.eval(s)) <= toDouble(right.eval(s)) ? Boolean.TRUE : Boolean.FALSE; - case Lexer.GT: return toDouble(left.eval(s)) > toDouble(right.eval(s)) ? Boolean.TRUE : Boolean.FALSE; - case Lexer.GE: return toDouble(left.eval(s)) >= toDouble(right.eval(s)) ? Boolean.TRUE : Boolean.FALSE; - - case Lexer.OR: return new Boolean(toBoolean(left.eval(s)) || toBoolean(right.eval(s))); - case Lexer.AND: return new Boolean(toBoolean(left.eval(s)) && toBoolean(right.eval(s))); - case Lexer.BANG: return new Boolean(!toBoolean(left.eval(s))); - - case Lexer.EQ: - case Lexer.NE: { - // FIXME: should use Javascript coercion-equality rules - Object l = left.eval(s); - Object r = right.eval(s); - boolean ret = (l == null && r == null) || (l != null && l.equals(r)); - return new Boolean(code == Lexer.EQ ? ret : !ret); + case WHILE: { + consume(WHILE); + consume(LP); + b.add(LOOP); + int size = b.size(); + b.add(POP); + startExpr(b); + b.add(JT, new Integer(2)); + b.add(BREAK); + consume(RP); + parseStatement(false, b); + b.add(CONTINUE); // if we fall out of the end, definately continue + b.set(size - 1, new Integer(b.size() - size + 1)); // end of the loop + break; } - case Lexer.INC: - case Lexer.DEC: - case Lexer.ASSIGN: { - Object v = (code == Lexer.ASSIGN) ? right.eval(s) : new Double(toDouble(left.eval(s)) + (code == Lexer.INC ? 1 : -1)); - if (left.code == Lexer.DOT) { - Object o = left.left.eval(s); - if (o instanceof String) { - throw new EvaluatorException("can't set properties on a String"); - } else if (o instanceof Number) { - throw new EvaluatorException("can't set properties on a Number"); - } else if (o instanceof Boolean) { - throw new EvaluatorException("can't set properties on a Boolean"); + case SWITCH: { + consume(SWITCH); + consume(LP); + b.add(LOOP); + int size0 = b.size(); + startExpr(b); + consume(RP); + consume(LC); + while(true) + if (peekToken() == CASE) { + consume(CASE); + b.add(DUP); + startExpr(b); + consume(COLON); + b.add(EQ); + b.add(JF, new Integer(0)); + int size = b.size(); + while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) { + int size2 = b.size(); + parseStatement(false, b); + if (size2 == b.size()) break; + } + b.set(size - 1, new Integer(1 + b.size() - size)); + } else if (peekToken() == DEFAULT) { + consume(DEFAULT); + consume(COLON); + while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) { + int size2 = b.size(); + parseStatement(false, b); + if (size2 == b.size()) break; + } + } else if (peekToken() == RC) { + consume(RC); + b.add(BREAK); + break; } else { - JS target = (JS)left.left.eval(s); - if (target == null) throw new JS.Exn(new EvaluatorException("attempted to put to the null value")); - target.put(left.right.eval(s), v); - return v; + throw new ParserException("expected CASE, DEFAULT, or RC; got " + codeToString[peekToken()]); } - } else { - s.put(left.string, v); - return v; - } + b.add(BREAK); + b.set(size0 - 1, new Integer(b.size() - size0 + 1)); // end of the loop + break; } - - case Lexer.TYPEOF: { - Object o = left.eval(s); - if (o == null) return "null"; - if (o.getClass() == String.class) return "string"; - if (o.getClass() == Boolean.class) return "boolean"; - if (o instanceof Number) return "number"; - if (o instanceof JS.Array) return "array"; - if (o instanceof JS) return "object"; - throw new EvaluatorException("typeof " + o.getClass().getName() + " unknown"); + + case DO: { + consume(DO); + b.add(LOOP); + int size = b.size(); + parseStatement(false, b); + consume(WHILE); + consume(LP); + startExpr(b); + b.add(JT, new Integer(2)); + b.add(BREAK); + b.add(CONTINUE); + consume(RP); + consume(SEMI); + b.set(size - 1, new Integer(b.size() - size + 1)); // end of the loop + break; } - - case Lexer.NUMBER: return number; - case Lexer.STRING: return string; - - case Lexer.NULL: return null; - case Lexer.FALSE: return Boolean.FALSE; - case Lexer.TRUE: return Boolean.TRUE; - case Lexer.ASSERT: if (!toBoolean(left.eval(s))) throw new EvaluatorException("assertion failed"); - case Lexer.THROW: throw new JS.Exn(left.eval(s)); - case Lexer.NAME: return s.get(string); - case Lexer.THIS: return s.isTransparent() ? s : this.eval(s.getParentScope()); - - case Lexer.DOT: { - final Object o = left.eval(s); - Object v = ((right.code == Lexer.NAME) || (right.code == Lexer.STRING)) ? right.string : right.eval(s); - if (o instanceof String) { - if (v.equals("length")) return new Integer(((String)o).length()); - else if (v.equals("substring")) return new JS.Function() { - public Object _call(JS.Array args) { - if (args.length() == 1) return ((String)o).substring(toNumber(args.elementAt(0)).intValue()); - else if (args.length() == 2) return ((String)o).substring(toNumber(args.elementAt(0)).intValue(), - toNumber(args.elementAt(1)).intValue()); - else throw new EvaluatorException("String.substring() can only take one or two arguments"); - } - }; - else if (v.equals("indexOf")) return new JS.Function() { - public Object _call(JS.Array args) { - if (args.length() != 1) return null; - return new Integer(((String)o).indexOf(args.elementAt(0).toString())); - } - }; - throw new EvaluatorException("Not Implemented: properties on String objects"); - } else if (o instanceof Boolean) { - throw new EvaluatorException("Not Implemented: properties on Boolean objects"); - } else if (o instanceof Number) { - throw new EvaluatorException("Not Implemented: properties on Number objects"); - } else if (o instanceof JS) { - return ((JS)o).get(v); + + case TRY: { + // We deliberately allow you to omit braces in catch{}/finally{} if they are single statements... + consume(TRY); + parseStatement(true, b); + + if (peekToken() == CATCH) { + getToken(); + consume(LP); + consume(NAME); + consume(RP); + parseStatement(); // just discard the catchblock } - } - case Lexer.TRY: { - boolean safeToExit = false; - try { - Object ret = left.eval(s); - safeToExit = true; - return ret; - } catch (JS.Exn e) { - Expr c = right; - if (c.code == Lexer.CATCH) { - JS.Scope scope = new JS.Scope(s); - s.put(c.left.string, e); - c.right.eval(scope); - c = c.next; - } - if (c.code == Lexer.FINALLY) { - JS.Scope scope = new JS.Scope(s); - c.left.eval(scope); - } - } finally { - if (!safeToExit) Log.log(this, "WARNING: Java exception penetrated a JavaScript try{} block"); + if (peekToken() == FINALLY) { + consume(FINALLY); + parseStatement(false, b); } - return null; + break; } - case Lexer.LP: - JS.Function f = (JS.Function)left.eval(s); - JS.Array arguments = new JS.Array(); - for(Expr e = right; e != null; e = e.next) arguments.addElement(e.eval(s)); - if (f == null) throw new JS.Exn(new EvaluatorException("attempted to call null")); - return f.call(arguments); - - case Lexer.FUNCTION: - return new JS.Function() { - public String toString() { return right.sourceName + ":" + right.line; } - public String getSourceName() throws JS.Exn { return right.sourceName; } - public Object _call(final JS.Array args) throws JS.Exn { - Function save = JS.getCurrentFunction(); - JS.currentFunction.put(Thread.currentThread(), this); - JS.Scope scope = new JS.Scope(s) { - // FIXME - public String getSourceName() { return sourceName; } - public boolean isTransparent() { return true; } - public Object get(Object key) throws JS.Exn { - if (key.equals("trapee")) return org.xwt.Trap.currentTrapee(); - else if (key.equals("cascade")) return org.xwt.Trap.cascadeFunction; - else if (key.equals("arguments")) return args; - return super.get(key); - } - }; - int i = 0; - for(Expr e = left; e != null; e = e.next) scope.put(e.string, args.get(new Integer(i++))); - try { - return right.eval(scope); - } catch (ReturnException r) { - return r.retval; - } catch (ControlTransferException c) { - throw new EvaluatorException("error, ControlTransferException tried to leave a function: " - + c); - } finally { - if (save == null) JS.currentFunction.remove(Thread.currentThread()); - else JS.currentFunction.put(Thread.currentThread(), save); - } - } - }; - - case Lexer.FOR: - Object[] keys = ((JS)left.right.eval(s)).keys(); - for(int i=0; i size) b.add(POP); + b.set(size - 1, new Integer(b.size() - size + 1)); + consume(RP); + + b.paste(e2); + b.add(JT, new Integer(2)); + b.add(BREAK); + parseStatement(false, b); + b.add(CONTINUE); + b.set(size2 - 1, new Integer(b.size() - size2 + 1)); // end of the loop + + b.add(POPSCOPE); + break; } + } - case Lexer.VAR: - for(Expr e = left; e != null; e = e.next) - if (e.code == Lexer.NAME) { - s.declare(e.string); - } else { - s.declare(e.left.string); - e.eval(s); - } - return null; - - case Lexer.HOOK: return toBoolean(left.eval(s)) ? right.left.eval(s) : right.right.eval(s); - case Lexer.IF: return toBoolean(left.eval(s)) ? right.left.eval(s) : right.right.eval(s); - case Lexer.BREAK: throw new BreakException(string); - case Lexer.CONTINUE: throw new ContinueException(string); - case Lexer.RETURN: throw new ReturnException(left == null ? null : left.eval(s)); - - case Lexer.WHILE: - case Lexer.DO: - try { - boolean first = true; - while((first && code == Lexer.DO) || toBoolean(left.eval(s))) { - first = false; - try { right.eval(s); - } catch (ContinueException c) { - if (c.label == null || c.label.equals(string)) continue; - } catch (BreakException b) { - if (b.label == null || b.label.equals(string)) return null; - throw (BreakException)b.fillInStackTrace(); - } - } - } catch (BreakException e) { - if (e.label != null && !e.label.equals(string)) throw e; + case NAME: { + consume(NAME); + String name = string; + if (peekToken() == COLON) { + consume(COLON); + b.add(ByteCodeBlock.LABEL, string); + break; + } else { + pushBackToken(NAME, name); + // fall through to default case } - return null; - - default: throw new EvaluatorException("don't know how to eval an Expr with code " + Lexer.codeToString[code] + "\n" + this); } - } - - class EvaluatorException extends RuntimeException { - public EvaluatorException(String s) { super(sourceName + ":" + line + " " + s); } + // fall through + case RC: + if (tok == RC && braced) { consume(RC); return; } + // fall through + default: { + int size = b.size(); + startExpr(b); + if (size == b.size()) return; + b.add(POP); + if (peekToken() == SEMI) consume(SEMI); + break; + } + } + + if (!braced) return; } } - - static abstract class ControlTransferException extends Exception { } - static class BreakException extends ControlTransferException { - public String label; - BreakException(String label) { this.label = label; } - } - static class ContinueException extends ControlTransferException { - public String label; - ContinueException(String label) { this.label = label; } - } - static class ReturnException extends ControlTransferException { - public Object retval; - ReturnException(Object retval) { this.retval = retval; } - } - + class ParserException extends RuntimeException { public ParserException(String s) { super(sourceName + ":" + line + " " + s); } } - - public static Number toNumber(Object o) { - if (o == null) return new Long(0); - if (o instanceof Number) return ((Number)o); - if (o instanceof String) return new Double((String)o); - if (o instanceof Boolean) return ((Boolean)o).booleanValue() ? new Long(1) : new Long(0); - if (o instanceof JS) return ((JS)o).coerceToNumber(); - // FIXME - throw new Error("toNumber() got object of type " + o.getClass().getName()); - } - } + +}