2003/05/02 13:30:52
authormegacz <megacz@xwt.org>
Fri, 30 Jan 2004 06:59:43 +0000 (06:59 +0000)
committermegacz <megacz@xwt.org>
Fri, 30 Jan 2004 06:59:43 +0000 (06:59 +0000)
darcs-hash:20040130065943-2ba56-77d6616b28bf4c841546aab69e13385240c35114.gz

src/org/xwt/js/Parser.java

index cd2498b..a7f29b7 100644 (file)
@@ -5,7 +5,15 @@ import java.io.*;
 // FIXME: for..in
 public class Parser extends Lexer {
 
-    public static void main(String[] s) throws Exception { System.out.println(new Parser(new InputStreamReader(System.in)).parseExpr()); }
+    public static void main(String[] s) throws Exception {
+       Parser p = new Parser(new InputStreamReader(System.in));
+       while(true) {
+           Expr block = p.parseBlock(false);
+           if (block == null) return;
+           System.out.println(block);
+           if (p.peekToken() == -1) return;
+       }
+    }
 
     public Parser(Reader r) throws IOException { super(r); }
     private Parser skipToken() throws IOException { getToken(); return this; }
@@ -29,12 +37,13 @@ public class Parser extends Lexer {
            String ret = "";
            for(int i=0; i<indent; i++) ret += " ";
            ret += codeToString[code];
-           if (code == STRING) ret += " \"" + string + "\"";
-           else if (code == NUMBER) ret += " " + number;
+           if (code == NUMBER) ret += " " + number;
+           else if (string != null) ret += " \"" + string + "\"";
            ret += "\n";
            if (left != null) ret += left.toString(indent + 2);
-           if (right != null) ret += left.toString(indent + 2);
-           if (extra != null) ret += left.toString(indent + 2);
+           if (right != null) ret += right.toString(indent + 2);
+           if (extra != null) ret += extra.toString(indent + 2);
+           if (next != null) ret += next.toString(indent);
            // FIXME: next
            return ret;
        }
@@ -42,51 +51,60 @@ public class Parser extends Lexer {
        public Expr(String s) { code = STRING; this.string = s; }  // an identifier or label
        public Expr(Number n) { code = NUMBER; this.number = n; }  // an identifier or label
        public Expr(int code) { this(code, null, null, null); }
+       public Expr(int code, String s) { this.code = code; string = s; }
        public Expr(int code, Expr left) { this(code, left, null, null); }
        public Expr(int code, Expr left, Expr right) { this(code, left, right, null); }
        public Expr(int code, Expr left, Expr right, Expr extra) { this.left = left; this.right = right; this.extra = extra; this.code = code; }
     }
     
-    /** parses a single statement */
-    public Expr parseStatement() throws IOException {
-       int tok;
-       Expr ret;
-       switch(tok = peekToken()) {
-
-       case LC:
-           ret = parseBlock(true);
-
-       case THROW: case RETURN: case ASSERT:
-           ret = new Expr(ASSERT, skipToken().parseExpr());
-
-       case GOTO: case BREAK: case CONTINUE:
-           skipToken();
-           if (getToken() == NAME)
-               ret = new Expr(tok, new Expr(string));
-           else if (tok == GOTO)
-               throw new Error("goto must be followed by a label");
-           else
-               ret = new Expr(tok);
-                       
-       default:
-           ret = parseExpr();
-       }
-
-       if (getToken() != SEMI) throw new Error("expected ;");
-       return ret;
-    }
-
     /** 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 (requireBraces && tok != LC) throw new Error("expected {");
-       if (tok != LC) return parseStatement();
-       skipToken();
+       boolean braced = tok == LC;
+       if (requireBraces && !braced) throw new Error("expected {");
+       if (braced) skipToken();
        Expr head = null;
        Expr tail = null;
-       while(peekToken() != RC)
-           if (head == null) head = tail = parseStatement(); else tail = tail.next = parseStatement();
-       skipToken();
+       OUTER: while(true) {
+           Expr smt;
+           switch(tok = peekToken()) {
+           case -1: break OUTER;
+           case LC: smt = parseBlock(true); break;
+           case THROW: case RETURN: case ASSERT:
+               smt = new Expr(tok, skipToken().parseExpr());
+               if (getToken() != SEMI) throw new Error("expected ;");
+               break;
+           case GOTO: case BREAK: case CONTINUE:
+               skipToken();
+               if (getToken() == NAME)
+                   smt = new Expr(tok, new Expr(string));
+               else if (tok == GOTO)
+                   throw new Error("goto must be followed by a label");
+               else
+                   smt = new Expr(tok);
+               if (getToken() != SEMI) throw new Error("expected ;");
+               break;
+
+           case RC:
+               if (braced) skipToken();
+               break OUTER;
+
+           case SEMI:
+               skipToken();
+               if (!braced) break OUTER;
+               continue;
+
+           default:
+               smt = parseExpr();
+               if (smt == null) {
+                   if (head == null) throw new Error("empty statement list");
+                   break OUTER;
+               }
+               break;
+           }
+           if (head == null) head = tail = smt; else tail = (tail.next = smt);
+       }
        return new Expr(LC, head);
     }
 
@@ -107,11 +125,24 @@ public class Parser extends Lexer {
        precedence[MUL] = precedence[DIV] = precedence[MOD] = 13;
        precedence[BITNOT] = precedence[INSTANCEOF] = 14;
        precedence[INC] = precedence[DEC] = 15;
+       precedence[LP] = 16;
+       precedence[DOT] = 17;
     }
 
     // called after each parseExpr(); returns null if we can't make the expression any bigger
-    public Expr parseExpr() throws IOException { return parseExpr(null, 0); }
+    public Expr parseExpr() throws IOException { return parseExpr_(null, 0); }
     public Expr parseExpr(Expr prefix, int minPrecedence) throws IOException {
+       Expr save = null;
+       do {
+           save = prefix;
+           if (peekToken() == -1) break;
+           prefix = parseExpr_(prefix, minPrecedence);
+           if (prefix == null) throw new Error("parseExpr_() returned null");
+       } while (save != prefix);
+       return prefix;
+    }
+
+    public Expr parseExpr_(Expr prefix, int minPrecedence) throws IOException {
        Expr e1 = null;     
        Expr e2 = null;     
        Expr e3 = null;     
@@ -120,8 +151,10 @@ public class Parser extends Lexer {
        Expr ret = null;
        int tok = getToken();
 
-       if (minPrecedence != 0 && tok < precedence.length && precedence[tok] != 0 && precedence[tok] < minPrecedence)
-           return null;
+       if (minPrecedence != -1 && minPrecedence != 0 && tok < precedence.length && precedence[tok] != 0 && precedence[tok] <= minPrecedence) {
+           pushBackToken();
+           return prefix;
+       }
 
        // these case arms match the precedence of operators; each arm is a precedence level.
        switch (tok) {
@@ -129,24 +162,35 @@ public class Parser extends Lexer {
        case COMMA: case ASSIGN: case GT: case GE: case OR: case AND:
         case BITOR: case BITXOR: case BITAND: case EQ: case NE: case LT:
         case LE: case SHEQ: case SHNE: case LSH: case RSH: case URSH:
-        case ADD: case SUB: case MUL: case DIV: case MOD:
+        case ADD: case SUB: case MUL: case DIV: case MOD: case DOT:
            return new Expr(tok, prefix, parseExpr(null, precedence[tok]));
-           
+
        case BITNOT: case INSTANCEOF:
            return new Expr(tok, parseExpr(null, precedence[tok]));
-           
+
            // FIXME: this isn't 100% right
        case INC: case DEC:
            return new Expr(tok, prefix, (tok == INC || tok == DEC) ? null : parseExpr());
 
        case LP:
-           while(peekToken() != RP) {
-               if (head == null) head = tail = parseExpr(); else tail = tail.next = parseExpr();
-               tok = getToken();
-               if (tok == RP) break;
-               if (tok != COMMA) throw new Error("expected comma or right paren");
+           if (prefix == null) {
+               // grouping
+               Expr r = parseExpr(null, -1);
+               if ((tok = getToken()) != RP) throw new Error("expected ), got " + codeToString[tok]);
+               return r;
+
+           } else {
+               // invocation
+               while(peekToken() != RP) {
+                   Expr e = parseExpr(null, precedence[COMMA]);
+                   if (head == null) head = tail = e; else tail = tail.next = e;
+                   tok = getToken();
+                   if (tok == RP) { pushBackToken(); break; }
+                   if (tok != COMMA) throw new Error("expected comma or right paren, got " + codeToString[tok]);
+               }
+               skipToken();
+               return new Expr(LP, prefix, head);
            }
-           return new Expr(LP, prefix, head);
 
        case LB:
            if (prefix != null) {
@@ -169,7 +213,7 @@ public class Parser extends Lexer {
            if (prefix != null) throw new Error("didn't expect non-null prefix");
            tok = getToken();
            while(true) {
-               if (tok == RP) return new Expr(LC, head);
+               if (tok == RC) return new Expr(LC, head);
                if (tok != NAME) throw new Error("expecting name");
                Expr name = parseExpr();
                if (tok != COLON) throw new Error("expecting colon");           
@@ -213,13 +257,13 @@ public class Parser extends Lexer {
            while(tok != RP) {
                if (tok != NAME) throw new Error("expected a variable name");
                if (cur == null) { formalArgs = cur = new Expr(string); }
-               else { cur.next = new Expr(string); cur = cur.next; }
+               else { cur.next = new Expr(NAME, string); cur = cur.next; }
                tok = getToken();
                if (tok == RP) break;
                if (tok != COMMA) throw new Error("function argument list must consist of alternating NAMEs and COMMAs");
                tok = getToken();
            }
-           return new Expr(tok, formalArgs, parseBlock(true));
+           return new Expr(FUNCTION, formalArgs, parseBlock(true));
        }
            
        case STRING:
@@ -232,25 +276,31 @@ public class Parser extends Lexer {
            if (prefix != null) throw new Error("didn't expect non-null prefix");
            while(true) {
                if (getToken() != NAME) throw new Error("variable declarations must start with a variable name");
-               Expr name = new Expr(NAME, new Expr(string));
+               Expr name = new Expr(NAME, string);
                Expr initVal = null;
                tok = peekToken();
+               Expr e = null;
                if (tok == ASSIGN) {
                    skipToken();
                    initVal = parseExpr();
                    tok = peekToken();
+                   e = new Expr(ASSIGN, name, initVal);
+               } else {
+                   e = new Expr(NAME, name);
                }
-               Expr e = new Expr(VAR, name, initVal);
                if (head == null) head = tail = e; else tail = tail.next = e;
                if (tok != COMMA) break;
                skipToken();
            }
            return new Expr(VAR, head);
-           
+
+       case NULL:
+           return new Expr(NULL);
+
        case NAME:
-           if (prefix != null) throw new Error("didn't expect non-null prefix");
-           return new Expr(string);
-    
+           if (prefix != null) { pushBackToken(); return prefix; }
+           return parseExpr(new Expr(NAME, string), minPrecedence);
+
        case TRUE: case FALSE: case NOP:
            if (prefix != null) throw new Error("didn't expect non-null prefix");
            return new Expr(tok);
@@ -259,19 +309,30 @@ public class Parser extends Lexer {
            // FIXME: we deliberately allow you to omit braces in catch{}/finally{} if they are single statements...
            if (prefix != null) throw new Error("didn't expect non-null prefix");
            Expr tryBlock = parseBlock(true);
-           while ((tok = peekToken()) == CATCH)
-               if (head == null) head = tail = parseBlock(false); else tail = tail.next = parseBlock(false);
+           while ((tok = peekToken()) == CATCH) {
+               skipToken();
+               if (getToken() != LP) throw new Error("expected (");
+               if (getToken() != NAME) throw new Error("expected name");
+               // FIXME: record the name
+               if (getToken() != RP) throw new Error("expected )");
+               if (head == null) head = tail = parseBlock(false);
+               else tail = tail.next = parseBlock(false);
+           }
            if (head == null) throw new Error("try without catch");
            return new Expr(TRY, tryBlock, head, tok == FINALLY ? skipToken().parseBlock(false) : null);
        }
+
+           // FIXME: a.b=c becomes PUT(a,b,c) not ASSIGN(DOT(a,b),c)
            
        case IF: case WHILE: {
            if (prefix != null) throw new Error("didn't expect non-null prefix");
            if (getToken() != LP) throw new Error("expected left paren");
-           Expr parenExpr = parseExpr();
-           if (getToken() != RP) throw new Error("expected right paren");
+           Expr parenExpr = parseExpr(null, -1);
+           int t;
+           if ((t = getToken()) != RP) throw new Error("expected right paren, but got " + codeToString[t]);
            Expr firstBlock = parseBlock(false);
-           if (tok == IF && peekToken() == ELSE) return new Expr(tok, parenExpr, firstBlock, skipToken().parseBlock(false));
+           if (tok == IF && peekToken() == ELSE)
+               return new Expr(tok, parenExpr, firstBlock, skipToken().parseBlock(false));
            return new Expr(tok, parenExpr, firstBlock);
        }
 
@@ -279,9 +340,11 @@ public class Parser extends Lexer {
            // FIXME: for..in
            if (prefix != null) throw new Error("didn't expect non-null prefix");
            if (getToken() != LP) throw new Error("expected left paren");
-           e1 = parseStatement();
-           e2 = parseStatement();
-           e3 = parseStatement();  // FIXME: this guy has to be okay with ending via a )
+           e1 = parseExpr(null, -1);
+           if (getToken() != SEMI) throw new Error("expected ;");
+           e2 = parseExpr(null, -1);
+           if (getToken() != SEMI) throw new Error("expected ;");
+           e3 = parseExpr(null, -1);  // FIXME: this guy has to be okay with ending via a )
            if (getToken() != RP) throw new Error("expected right paren");
            throw new Error("not yet implemented");
            //return new Expr(FOR, e1, e2, e3, parseBlock(false));
@@ -305,7 +368,7 @@ public class Parser extends Lexer {
 
        default:
            pushBackToken();
-           return null;
+           return prefix;
        }
     }