2003/04/30 04:39:41
[org.ibex.core.git] / src / org / xwt / js / Parser.java
index 861432c..cd2498b 100644 (file)
@@ -3,9 +3,10 @@ import org.xwt.util.*;
 import java.io.*;
 
 // FIXME: for..in
-// FIXME: delete keyword
 public class Parser extends Lexer {
 
+    public static void main(String[] s) throws Exception { System.out.println(new Parser(new InputStreamReader(System.in)).parseExpr()); }
+
     public Parser(Reader r) throws IOException { super(r); }
     private Parser skipToken() throws IOException { getToken(); return this; }
     
@@ -20,8 +21,26 @@ public class Parser extends Lexer {
        Expr next = null;   // if this expr is part of a list
 
        String string = null;
+       Number number = null;
+
+       public String toString() { return toString(0); }
+
+       public String toString(int indent) {
+           String ret = "";
+           for(int i=0; i<indent; i++) ret += " ";
+           ret += codeToString[code];
+           if (code == STRING) ret += " \"" + string + "\"";
+           else if (code == NUMBER) ret += " " + number;
+           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);
+           // FIXME: next
+           return ret;
+       }
 
-       public Expr(String s) { this.string = s; }  // an identifier or label
+       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, Expr left) { this(code, left, null, null); }
        public Expr(int code, Expr left, Expr right) { this(code, left, right, null); }
@@ -71,29 +90,103 @@ public class Parser extends Lexer {
        return new Expr(LC, head);
     }
 
-    /** Subexpressions come in two flavors: starters and continuers.
-     *  Starters can appear at the start of an expression or after a
-     *  continuer, and continuers, which can appear after a starter.
-     */
-    public Expr parseExpr() throws IOException {
-       Expr e = parseStarter();
-       while(true) {
-           Expr e2 = parseContinuer(e);
-           if (e2 == null) return e;
-           e = e2;
-       }
+    static byte[] precedence = new byte[MAX_TOKEN + 1];
+    static {
+       precedence[COMMA] = 1;
+       precedence[ASSIGN] = 2;
+       precedence[GT] = precedence[GE] = 3;
+       precedence[OR] = precedence[AND] = 4;
+       precedence[BITOR] = 5;
+       precedence[BITXOR] = 6;
+       precedence[BITAND] = 7;
+       precedence[EQ] = precedence[NE] = 8;
+       precedence[LT] = precedence[LE] = 9;
+       precedence[SHEQ] = precedence[SHNE] = 10;
+       precedence[LSH] = precedence[RSH] = precedence[URSH] = 11;
+       precedence[ADD] = precedence[SUB] = 12;
+       precedence[MUL] = precedence[DIV] = precedence[MOD] = 13;
+       precedence[BITNOT] = precedence[INSTANCEOF] = 14;
+       precedence[INC] = precedence[DEC] = 15;
     }
 
-    public Expr parseStarter() throws IOException {
+    // 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(Expr prefix, int minPrecedence) throws IOException {
        Expr e1 = null;     
        Expr e2 = null;     
        Expr e3 = null;     
        Expr head = null;
        Expr tail = null;
+       Expr ret = null;
        int tok = getToken();
-       switch(tok) {
+
+       if (minPrecedence != 0 && tok < precedence.length && precedence[tok] != 0 && precedence[tok] < minPrecedence)
+           return null;
+
+       // these case arms match the precedence of operators; each arm is a precedence level.
+       switch (tok) {
+
+       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:
+           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");
+           }
+           return new Expr(LP, prefix, head);
+
+       case LB:
+           if (prefix != null) {
+               // subscripting
+               e1 = parseExpr();
+               if (getToken() != RB) throw new Error("expected a right brace");
+               return new Expr(LB, prefix, e1);
+           } else {
+               // array ctor
+               tok = getToken();
+               while(true) {
+                   if (tok == RB) return new Expr(LB, prefix, head);
+                   if (head == null) head = tail = parseExpr(); else tail = tail.next = parseExpr();
+                   tok = getToken();
+                   if (tok != COMMA && tok != RP) throw new Error("expected right bracket or comma");
+               }
+           }
+           
+       case LC:
+           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 != NAME) throw new Error("expecting name");
+               Expr name = parseExpr();
+               if (tok != COLON) throw new Error("expecting colon");           
+               e1 = new Expr(COLON, name, parseExpr());
+               if (head == null) head = tail = e1; else tail = tail.next = e1;
+               tok = getToken();
+               if (tok != COMMA && tok != RP) throw new Error("expected right curly or comma");
+           }
+           
+       case HOOK:
+           e2 = parseExpr();
+           if (getToken() != COLON) throw new Error("expected colon to close ?: expression");
+           e3 = parseExpr();
+           return new Expr(HOOK, prefix, e2, e3);
            
        case SWITCH: {
+           if (prefix != null) throw new Error("didn't expect non-null prefix");
            if (getToken() != LP) throw new Error("expected left paren");
            Expr switchExpr = parseExpr();
            if (getToken() != RP) throw new Error("expected left paren");
@@ -113,6 +206,7 @@ public class Parser extends Lexer {
        }
            
        case FUNCTION: {
+           if (prefix != null) throw new Error("didn't expect non-null prefix");
            if (getToken() != LP) throw new Error("function keyword must be followed by a left paren");
            Expr formalArgs = null, cur = null;
            tok = getToken();
@@ -128,10 +222,17 @@ public class Parser extends Lexer {
            return new Expr(tok, formalArgs, parseBlock(true));
        }
            
+       case STRING:
+           return new Expr(string);
+
+       case NUMBER:
+           return new Expr(number);
+
        case VAR:
+           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(string);
+               Expr name = new Expr(NAME, new Expr(string));
                Expr initVal = null;
                tok = peekToken();
                if (tok == ASSIGN) {
@@ -146,39 +247,17 @@ public class Parser extends Lexer {
            }
            return new Expr(VAR, head);
            
-       case LC:
-           tok = getToken();
-           while(true) {
-               if (tok == RP) return new Expr(LC, head);
-               if (tok != NAME) throw new Error("expecting name");
-               Expr name = parseExpr();
-               if (tok != COLON) throw new Error("expecting colon");           
-               e1 = new Expr(COLON, name, parseExpr());
-               if (head == null) head = tail = e1; else tail = tail.next = e1;
-               tok = getToken();
-               if (tok != COMMA && tok != RP) throw new Error("expected right curly or comma");
-           }
-           
-       case LB:
-           tok = getToken();
-           while(true) {
-               if (tok == RB) return new Expr(LB, head);
-               if (head == null) head = tail = parseExpr(); else tail = tail.next = parseExpr();
-               tok = getToken();
-               if (tok != COMMA && tok != RP) throw new Error("expected right bracket or comma");
-           }
-           
        case NAME:
+           if (prefix != null) throw new Error("didn't expect non-null prefix");
            return new Expr(string);
     
-       case INC: case DEC: case TYPEOF:
-           return new Expr(tok, parseExpr());
-           
        case TRUE: case FALSE: case NOP:
+           if (prefix != null) throw new Error("didn't expect non-null prefix");
            return new Expr(tok);
            
        case TRY: {
            // 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);
@@ -187,6 +266,7 @@ public class Parser extends Lexer {
        }
            
        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");
@@ -197,6 +277,7 @@ public class Parser extends Lexer {
 
        case FOR:
            // 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();
@@ -206,6 +287,7 @@ public class Parser extends Lexer {
            //return new Expr(FOR, e1, e2, e3, parseBlock(false));
            
        case DO: {
+           if (prefix != null) throw new Error("didn't expect non-null prefix");
            Expr firstBlock = parseBlock(false);
            if (getToken() != WHILE) throw new Error("expecting WHILE");
            if (getToken() != LP) throw new Error("expected left paren");
@@ -221,51 +303,6 @@ public class Parser extends Lexer {
        case WITH:
            throw new Error("WITH not yet implemented"); // FIXME
 
-       default: throw new Error("I wasn't expecting a " + tok);
-       }
-    }
-       
-    // called after each parseExpr(); returns null if we can't make the expression any bigger
-    public Expr parseContinuer(Expr prefix) throws IOException {
-       Expr head = null;
-       Expr tail = null;
-       Expr e1, e2, e3;
-       Expr ret = null;
-       int tok;
-
-       // FIXME: postfix and infix operators -- need to handle precedence
-       switch (tok = getToken()) {
-
-       case BITOR: case BITXOR: case BITAND: case EQ: case NE: case LT: case LE:
-       case GT: case GE: case LSH: case RSH: case URSH: case ADD: case SUB: case MUL:
-       case DIV: case MOD: case BITNOT: case SHEQ: case SHNE: case INSTANCEOF:
-       case OR: case AND: case COMMA: case INC: case DEC:
-           throw new Error("haven't figured out how to handle postfix/infix operators yet");
-           //return new Expr(tok, prefix, (tok == INC || tok == DEC) ? null : parseExpr());
-
-       case ASSIGN:
-           throw new Error("haven't figured out how to handle postfix/infix operators yet");
-
-       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");
-           }
-           return new Expr(LP, prefix, head);
-
-       case LB:
-           e1 = parseExpr();
-           if (getToken() != RB) throw new Error("expected a right brace");
-           return new Expr(LB, prefix, e1);
-           
-       case HOOK:
-           e2 = parseExpr();
-           if (getToken() != COLON) throw new Error("expected colon to close ?: expression");
-           e3 = parseExpr();
-           return new Expr(HOOK, prefix, e2, e3);
-           
        default:
            pushBackToken();
            return null;