2003/04/24 10:25:09
[org.ibex.core.git] / src / org / xwt / js / Parser.java
1 package org.xwt.js;
2 import org.xwt.util.*;
3 import java.io.*;
4
5 // FIXME: for..in
6 // FIXME: delete keyword
7 public class Parser extends Lexer {
8
9     public Parser(Reader r) throws IOException { super(r); }
10     private Parser skipToken() throws IOException { getToken(); return this; }
11     
12     /** sorta like gcc trees */
13     public static class Expr {
14         int code = -1;
15
16         Expr left = null;
17         Expr right = null;
18         Expr extra = null;
19
20         Expr next = null;   // if this expr is part of a list
21
22         String string = null;
23
24         public Expr(String s) { this.string = s; }  // an identifier or label
25         public Expr(int code) { this(code, null, null, null); }
26         public Expr(int code, Expr left) { this(code, left, null, null); }
27         public Expr(int code, Expr left, Expr right) { this(code, left, right, null); }
28         public Expr(int code, Expr left, Expr right, Expr extra) { this.left = left; this.right = right; this.extra = extra; this.code = code; }
29     }
30     
31     /** parses a single statement */
32     public Expr parseStatement() throws IOException {
33         int tok;
34         Expr ret;
35         switch(tok = peekToken()) {
36
37         case LC:
38             ret = parseBlock(true);
39
40         case THROW: case RETURN: case ASSERT:
41             ret = new Expr(ASSERT, skipToken().parseExpr());
42
43         case GOTO: case BREAK: case CONTINUE:
44             skipToken();
45             if (getToken() == NAME)
46                 ret = new Expr(tok, new Expr(string));
47             else if (tok == GOTO)
48                 throw new Error("goto must be followed by a label");
49             else
50                 ret = new Expr(tok);
51                         
52         default:
53             ret = parseExpr();
54         }
55
56         if (getToken() != SEMI) throw new Error("expected ;");
57         return ret;
58     }
59
60     /** a block is either a single statement or a list of statements surrounded by curly braces; all expressions are also statements */
61     public Expr parseBlock(boolean requireBraces) throws IOException {
62         int tok = peekToken();
63         if (requireBraces && tok != LC) throw new Error("expected {");
64         if (tok != LC) return parseStatement();
65         skipToken();
66         Expr head = null;
67         Expr tail = null;
68         while(peekToken() != RC)
69             if (head == null) head = tail = parseStatement(); else tail = tail.next = parseStatement();
70         skipToken();
71         return new Expr(LC, head);
72     }
73
74     /** Subexpressions come in two flavors: starters and continuers.
75      *  Starters can appear at the start of an expression or after a
76      *  continuer, and continuers, which can appear after a starter.
77      */
78     public Expr parseExpr() throws IOException {
79         Expr e = parseStarter();
80         while(true) {
81             Expr e2 = parseContinuer(e);
82             if (e2 == null) return e;
83             e = e2;
84         }
85     }
86
87     public Expr parseStarter() throws IOException {
88         Expr e1 = null;     
89         Expr e2 = null;     
90         Expr e3 = null;     
91         Expr head = null;
92         Expr tail = null;
93         int tok = getToken();
94         switch(tok) {
95             
96         case SWITCH: {
97             if (getToken() != LP) throw new Error("expected left paren");
98             Expr switchExpr = parseExpr();
99             if (getToken() != RP) throw new Error("expected left paren");
100             if (getToken() != LC) throw new Error("expected left brace");
101             Expr firstExpr = null;
102             Expr lastExpr = null;
103             while(true) {
104                 if (getToken() != CASE) throw new Error("expected CASE");
105                 Expr caseExpr = parseExpr();
106                 if (getToken() != COLON) throw new Error("expected COLON");
107                 Expr e = new Expr(CASE, caseExpr, parseBlock(false));
108                 if (lastExpr == null) firstExpr = e;
109                 else lastExpr.next = e;
110                 lastExpr = e;
111                 if (getToken() == RC) return new Expr(SWITCH, switchExpr, firstExpr);
112             }
113         }
114             
115         case FUNCTION: {
116             if (getToken() != LP) throw new Error("function keyword must be followed by a left paren");
117             Expr formalArgs = null, cur = null;
118             tok = getToken();
119             while(tok != RP) {
120                 if (tok != NAME) throw new Error("expected a variable name");
121                 if (cur == null) { formalArgs = cur = new Expr(string); }
122                 else { cur.next = new Expr(string); cur = cur.next; }
123                 tok = getToken();
124                 if (tok == RP) break;
125                 if (tok != COMMA) throw new Error("function argument list must consist of alternating NAMEs and COMMAs");
126                 tok = getToken();
127             }
128             return new Expr(tok, formalArgs, parseBlock(true));
129         }
130             
131         case VAR:
132             while(true) {
133                 if (getToken() != NAME) throw new Error("variable declarations must start with a variable name");
134                 Expr name = new Expr(string);
135                 Expr initVal = null;
136                 tok = peekToken();
137                 if (tok == ASSIGN) {
138                     skipToken();
139                     initVal = parseExpr();
140                     tok = peekToken();
141                 }
142                 Expr e = new Expr(VAR, name, initVal);
143                 if (head == null) head = tail = e; else tail = tail.next = e;
144                 if (tok != COMMA) break;
145                 skipToken();
146             }
147             return new Expr(VAR, head);
148             
149         case LC:
150             tok = getToken();
151             while(true) {
152                 if (tok == RP) return new Expr(LC, head);
153                 if (tok != NAME) throw new Error("expecting name");
154                 Expr name = parseExpr();
155                 if (tok != COLON) throw new Error("expecting colon");           
156                 e1 = new Expr(COLON, name, parseExpr());
157                 if (head == null) head = tail = e1; else tail = tail.next = e1;
158                 tok = getToken();
159                 if (tok != COMMA && tok != RP) throw new Error("expected right curly or comma");
160             }
161             
162         case LB:
163             tok = getToken();
164             while(true) {
165                 if (tok == RB) return new Expr(LB, head);
166                 if (head == null) head = tail = parseExpr(); else tail = tail.next = parseExpr();
167                 tok = getToken();
168                 if (tok != COMMA && tok != RP) throw new Error("expected right bracket or comma");
169             }
170             
171         case NAME:
172             return new Expr(string);
173     
174         case INC: case DEC: case TYPEOF:
175             return new Expr(tok, parseExpr());
176             
177         case TRUE: case FALSE: case NOP:
178             return new Expr(tok);
179             
180         case TRY: {
181             // FIXME: we deliberately allow you to omit braces in catch{}/finally{} if they are single statements...
182             Expr tryBlock = parseBlock(true);
183             while ((tok = peekToken()) == CATCH)
184                 if (head == null) head = tail = parseBlock(false); else tail = tail.next = parseBlock(false);
185             if (head == null) throw new Error("try without catch");
186             return new Expr(TRY, tryBlock, head, tok == FINALLY ? skipToken().parseBlock(false) : null);
187         }
188             
189         case IF: case WHILE: {
190             if (getToken() != LP) throw new Error("expected left paren");
191             Expr parenExpr = parseExpr();
192             if (getToken() != RP) throw new Error("expected right paren");
193             Expr firstBlock = parseBlock(false);
194             if (tok == IF && peekToken() == ELSE) return new Expr(tok, parenExpr, firstBlock, skipToken().parseBlock(false));
195             return new Expr(tok, parenExpr, firstBlock);
196         }
197
198         case FOR:
199             // FIXME: for..in
200             if (getToken() != LP) throw new Error("expected left paren");
201             e1 = parseStatement();
202             e2 = parseStatement();
203             e3 = parseStatement();  // FIXME: this guy has to be okay with ending via a )
204             if (getToken() != RP) throw new Error("expected right paren");
205             throw new Error("not yet implemented");
206             //return new Expr(FOR, e1, e2, e3, parseBlock(false));
207             
208         case DO: {
209             Expr firstBlock = parseBlock(false);
210             if (getToken() != WHILE) throw new Error("expecting WHILE");
211             if (getToken() != LP) throw new Error("expected left paren");
212             Expr whileExpr = parseExpr();
213             if (getToken() != RP) throw new Error("expected right paren");
214             if (getToken() != SEMI) throw new Error("semicolon");
215             return new Expr(DO, firstBlock, whileExpr);
216         }
217             
218         case VOID: case RESERVED:
219             throw new Error("reserved word that you shouldn't be using");
220
221         case WITH:
222             throw new Error("WITH not yet implemented"); // FIXME
223
224         default: throw new Error("I wasn't expecting a " + tok);
225         }
226     }
227         
228     // called after each parseExpr(); returns null if we can't make the expression any bigger
229     public Expr parseContinuer(Expr prefix) throws IOException {
230         Expr head = null;
231         Expr tail = null;
232         Expr e1, e2, e3;
233         Expr ret = null;
234         int tok;
235
236         // FIXME: postfix and infix operators -- need to handle precedence
237         switch (tok = getToken()) {
238
239         case BITOR: case BITXOR: case BITAND: case EQ: case NE: case LT: case LE:
240         case GT: case GE: case LSH: case RSH: case URSH: case ADD: case SUB: case MUL:
241         case DIV: case MOD: case BITNOT: case SHEQ: case SHNE: case INSTANCEOF:
242         case OR: case AND: case COMMA: case INC: case DEC:
243             throw new Error("haven't figured out how to handle postfix/infix operators yet");
244             //return new Expr(tok, prefix, (tok == INC || tok == DEC) ? null : parseExpr());
245
246         case ASSIGN:
247             throw new Error("haven't figured out how to handle postfix/infix operators yet");
248
249         case LP:
250             while(peekToken() != RP) {
251                 if (head == null) head = tail = parseExpr(); else tail = tail.next = parseExpr();
252                 tok = getToken();
253                 if (tok == RP) break;
254                 if (tok != COMMA) throw new Error("expected comma or right paren");
255             }
256             return new Expr(LP, prefix, head);
257
258         case LB:
259             e1 = parseExpr();
260             if (getToken() != RB) throw new Error("expected a right brace");
261             return new Expr(LB, prefix, e1);
262             
263         case HOOK:
264             e2 = parseExpr();
265             if (getToken() != COLON) throw new Error("expected colon to close ?: expression");
266             e3 = parseExpr();
267             return new Expr(HOOK, prefix, e2, e3);
268             
269         default:
270             pushBackToken();
271             return null;
272         }
273     }
274     
275 }
276