From 4c0fde245156c0b57d95934b9b1a2ec0fd1aa849 Mon Sep 17 00:00:00 2001 From: megacz Date: Fri, 30 Jan 2004 07:00:37 +0000 Subject: [PATCH] 2003/06/03 00:53:07 darcs-hash:20040130070037-2ba56-a60521fd8558545921a99e6e7bac2f6a6a3791b2.gz --- src/org/xwt/js/Lexer.java | 71 +++++-- src/org/xwt/js/Parser.java | 474 ++++++++++++++++++++++++-------------------- 2 files changed, 309 insertions(+), 236 deletions(-) diff --git a/src/org/xwt/js/Lexer.java b/src/org/xwt/js/Lexer.java index e8cd2d4..d7a5725 100644 --- a/src/org/xwt/js/Lexer.java +++ b/src/org/xwt/js/Lexer.java @@ -29,21 +29,18 @@ class Lexer { System.out.println(codeToString[tok]); } - private SmartReader in; - private boolean pushedBack = false; - public int op; - public int twoBack; + public Number number = null; + public String string = null; + public int line = 0; public int col = 0; + + private SmartReader in; public String sourceName = "unknown"; - public Number number; - public String string; public Lexer(Reader r) throws IOException { in = new SmartReader(r); } - public int peekToken() throws IOException { int ret = getToken(); pushBackToken(); return ret; } - public void pushBackToken() { pushBackToken(op); } - public void pushBackToken(int i) { if (pushedBack) throw new Error("can't push back twice"); pushedBack = true; op = i; } + // Token Constants ////////////////////////////////////////////////////////// @@ -389,16 +386,6 @@ class Lexer { return STRING; } - public int getToken() throws IOException { - if (pushedBack) { pushedBack = false; return op; } - do { - if (op != EOL) twoBack = op; - op = _getToken(); - if (op == EOL) { line++; col = 0; } - } while (op == EOL); - return op; - } - public int _getToken() throws IOException { int c; do { if ((c = in.read()) == '\n') break; } while (isWhiteSpace(c) || c == '\n'); @@ -488,4 +475,50 @@ class Lexer { } } + + // PushBack Stuff //////////////////////////////////////////////////////////// + + int pushBackDepth = 0; + int[] pushBackInts = new int[10]; + Object[] pushBackObjects = new Object[10]; + + public void pushBackToken() { + if (pushBackDepth >= pushBackInts.length - 1) { + int[] newInts = new int[pushBackInts.length * 2]; + System.arraycopy(pushBackInts, 0, newInts, 0, pushBackInts.length); + pushBackInts = newInts; + Object[] newObjects = new Object[pushBackObjects.length * 2]; + System.arraycopy(pushBackObjects, 0, newObjects, 0, pushBackObjects.length); + pushBackObjects = newObjects; + } + pushBackInts[pushBackDepth] = op; + pushBackObjects[pushBackDepth] = number != null ? (Object)number : (Object)string; + pushBackDepth++; + } + + public int peekToken() throws IOException { + int ret = getToken(); + pushBackToken(); + return ret; + } + + public int getToken() throws IOException { + number = null; + string = null; + if (pushBackDepth > 0) { + pushBackDepth--; + op = pushBackInts[pushBackDepth]; + if (pushBackObjects[pushBackDepth] != null) { + number = pushBackObjects[pushBackDepth] instanceof Number ? (Number)pushBackObjects[pushBackDepth] : null; + string = pushBackObjects[pushBackDepth] instanceof String ? (String)pushBackObjects[pushBackDepth] : null; + } + } else { + do { + op = _getToken(); + if (op == EOL) { line++; col = 0; } + } while (op == EOL); + } + return op; + } + } diff --git a/src/org/xwt/js/Parser.java b/src/org/xwt/js/Parser.java index ba343cf..b7456fe 100644 --- a/src/org/xwt/js/Parser.java +++ b/src/org/xwt/js/Parser.java @@ -16,6 +16,7 @@ public class Parser extends Lexer { this.line = line; } + /** for debugging */ public static void main(String[] s) throws Exception { Parser p = new Parser(new InputStreamReader(System.in), "stdin", 0); while(true) { @@ -57,94 +58,13 @@ 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(); - if (tok == -1) return null; - boolean braced = tok == LC; - if (requireBraces && !braced) throw new ParserException("expected {"); - if (braced) getToken(); - Expr head = null; - Expr tail = null; - int curLine = line; - OUTER: while(true) { - Expr smt; - - switch(tok = peekToken()) { - case -1: break OUTER; - case LC: smt = parseBlock(true); break; - case THROW: case RETURN: case ASSERT: - getToken(); - if (peekToken() == SEMI) { - getToken(); - smt = new Expr(curLine, tok); - break; - } - smt = new Expr(curLine, tok, parseMaximalExpr()); - if (getToken() != SEMI) throw new ParserException("expected ;"); - break; - - /* - 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; - } - */ - - case GOTO: case BREAK: case CONTINUE: { - 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; - } - - case RC: - if (braced) getToken(); - break OUTER; - - case SEMI: - getToken(); - if (!braced) break OUTER; - continue; - - default: - smt = parseMaximalExpr(); - if (smt == null) { - if (head == null) return null; - break OUTER; - } - if (peekToken() == SEMI) getToken(); - break; - } - if (!braced) return smt; - if (head == null) head = tail = smt; else tail = (tail.next = smt); - } - return new Expr(curLine, LC, head); + public void consume(int code) throws IOException { + int got = getToken(); + if (got != code) throw new ParserException("expected " + codeToString[code] + ", got " + (got == -1 ? "EOL" : codeToString[got])); } - - /** 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])); + if (got != code) throw new ParserException("expected " + codeToString[code] + ", got " + (got == -1 ? "EOL" : codeToString[got])); } /** parses the largest possible expression */ @@ -238,54 +158,56 @@ public class Parser extends Lexer { return r; } else { // invocation + ExprList list = new ExprList(curLine, LP); 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]); + list.add(parseMaximalExpr()); + if (peekToken() == RP) break; + consume(COMMA); } getToken(); - return new Expr(curLine, LP, prefix, head); + return new Expr(curLine, LP, prefix, list); } 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); - } else { + if (prefix == null) { // array ctor + ExprList list = new ExprList(curLine, LB); 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; + Expr e = parseMaximalExpr(); + if (e != null) { + list.add(e); + } else { + if (peekToken() == COMMA) list.add(new Expr(curLine, NULL)); } - tok = getToken(); - if (tok == RB) return new Expr(curLine, LB, prefix, head); - if (tok != COMMA) throw new ParserException("expected right bracket or comma"); + if (peekToken() == RB) { consume(RB); return list; } + consume(COMMA); } + } else { + // subscripting + e1 = parseMaximalExpr(); + if (getToken() != RB) throw new ParserException("expected a right brace"); + return new Expr(curLine, DOT, prefix, e1); } - case LC: + case LC: { + // object ctor if (prefix != null) { pushBackToken(); return prefix; } tok = getToken(); - if (tok == RC) return new Expr(curLine, RC, head); + ExprList list = new ExprList(curLine, RC); + if (tok == RC) return list; 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; + list.add(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); + if (tok == RC) return list; tok = getToken(); - if (tok == RC) return new Expr(curLine, RC, head); + if (tok == RC) return list; } + } case HOOK: e2 = parseMaximalExpr(); @@ -299,52 +221,48 @@ public class Parser extends Lexer { 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; + ExprList toplevel = new ExprList(curLine, LC); 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; + if (tok == DEFAULT) caseExpr = null; + else if (tok == CASE) caseExpr = parseMaximalExpr(); + else throw new ParserException("expected CASE or DEFAULT"); + consume(COLON); + // FIXME: we shouldn't be creating a scope here + ExprList list = new ExprList(curLine, LC); 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; + if ((e1 = parseBlock(false)) == null) break; + list.add(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); } + toplevel.add(new Expr(curLine, tok, caseExpr, list)); + if (peekToken() == RC) { consume(RC); return new Expr(curLine, SWITCH, switchExpr, toplevel); } } } 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(); + consume(LP); + ExprList list = new ExprList(curLine, LC); + if (peekToken() == RP) consume(RP); + else while(true) { + tok = peekToken(); + if (tok == COMMA) { + consume(COMMA); + list.add(new Expr(curLine, NULL)); + } else { + consume(NAME); + list.add(new Expr(curLine, NAME, string)); + if (peekToken() == RP) { consume(RP); break; } + consume(COMMA); + } } - return new Expr(curLine, FUNCTION, formalArgs, parseBlock(true)); + return new Expr(curLine, FUNCTION, list, parseBlock(true)); } - case VAR: + case VAR: { if (prefix != null) { pushBackToken(); return prefix; } + ExprList list = new ExprList(curLine, VAR); while(true) { if (getToken() != NAME) throw new ParserException("variable declarations must start with a variable name"); String name = string; @@ -363,11 +281,12 @@ public class Parser extends Lexer { } else { e = new Expr(curLine, VAR, new Expr(curLine, name)); } - if (head == null) head = tail = e; else tail = tail.next = e; + list.add(e); if (tok != COMMA) break; getToken(); } - return new Expr(curLine, VAR, head); + return list; + } case TRY: { // We deliberately allow you to omit braces in catch{}/finally{} if they are single statements... @@ -375,23 +294,23 @@ public class Parser extends Lexer { Expr tryBlock = parseBlock(true); tok = peekToken(); + ExprList list = new ExprList(curLine, TRY); 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)); + list.add(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; + list.add(new Expr(curLine, FINALLY, parseBlock(false))); } - if (head == null) throw new ParserException("try without catch or finally"); - return new Expr(curLine, TRY, tryBlock, head); + if (list.size() == 0) throw new ParserException("try without catch or finally"); + return new Expr(curLine, TRY, tryBlock, list); } case IF: case WHILE: { @@ -405,7 +324,14 @@ public class Parser extends Lexer { getToken(); return new Expr(curLine, tok, parenExpr, new Expr(curLine, ELSE, firstBlock, parseBlock(false))); } else { - return new Expr(curLine, tok, parenExpr, firstBlock); + if (tok == IF) return new Expr(curLine, tok, parenExpr, firstBlock); + if (tok == WHILE) { + ExprList list = new ExprList(curLine, WHILE); + list.add(parenExpr); + list.add(firstBlock); + list.add(new Expr(curLine, NULL)); + return list; + } } } @@ -423,27 +349,36 @@ public class Parser extends Lexer { } else { Expr initExpr = e1; + if (initExpr == null) initExpr = new Expr(curLine, NULL); expect(SEMI); getToken(); Expr whileExpr = parseMaximalExpr(); expect(SEMI); getToken(); Expr incExpr = parseMaximalExpr(); expect(RP); getToken(); Expr body = parseBlock(false); - body.next = incExpr; Expr loop = new Expr(curLine, WHILE, whileExpr, body); - if (initExpr == null) initExpr = loop; else initExpr.next = loop; - return new Expr(curLine, LC, initExpr); + ExprList list = new ExprList(curLine, LC); + list.add(initExpr); + ExprList list2 = new ExprList(curLine, WHILE); + list.add(list2); + list2.add(whileExpr); + list2.add(body); + list2.add(incExpr); + return list; } 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); + ExprList list = new ExprList(curLine, DO); + Expr body = parseBlock(false); + consume(WHILE); + consume(LP); + list.add(parseMaximalExpr()); + list.add(body); + list.add(new Expr(curLine, NULL)); + consume(RP); + consume(SEMI); + return list; } default: @@ -454,13 +389,163 @@ public class Parser extends Lexer { // Expr ////////////////////////////////////////////////////////////////////// + class ExprList extends Expr { + Vec v = new Vec(); + public ExprList(int curLine, int code) { super(curLine, code); } + public void add(Expr e) { v.addElement(e); } + public int numExprs() { return v.size(); } + public int size() { return v.size(); } + public Expr elementAt(int i) { return (Expr)v.elementAt(i); } + public Object eval(final JS.Scope s) throws ControlTransferException, JS.Exn { + switch(code) { + case LC: { + // Block + JS.Scope scope = new JS.Scope(s); + for(int i=0; i