6 // FIXME: delete keyword
7 public class Parser extends Lexer {
9 public Parser(Reader r) throws IOException { super(r); }
10 private Parser skipToken() throws IOException { getToken(); return this; }
12 /** sorta like gcc trees */
13 public static class Expr {
20 Expr next = null; // if this expr is part of a list
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; }
31 /** parses a single statement */
32 public Expr parseStatement() throws IOException {
35 switch(tok = peekToken()) {
38 ret = parseBlock(true);
40 case THROW: case RETURN: case ASSERT:
41 ret = new Expr(ASSERT, skipToken().parseExpr());
43 case GOTO: case BREAK: case CONTINUE:
45 if (getToken() == NAME)
46 ret = new Expr(tok, new Expr(string));
48 throw new Error("goto must be followed by a label");
56 if (getToken() != SEMI) throw new Error("expected ;");
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();
68 while(peekToken() != RC)
69 if (head == null) head = tail = parseStatement(); else tail = tail.next = parseStatement();
71 return new Expr(LC, head);
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.
78 public Expr parseExpr() throws IOException {
79 Expr e = parseStarter();
81 Expr e2 = parseContinuer(e);
82 if (e2 == null) return e;
87 public Expr parseStarter() throws IOException {
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;
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;
111 if (getToken() == RC) return new Expr(SWITCH, switchExpr, firstExpr);
116 if (getToken() != LP) throw new Error("function keyword must be followed by a left paren");
117 Expr formalArgs = null, cur = null;
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; }
124 if (tok == RP) break;
125 if (tok != COMMA) throw new Error("function argument list must consist of alternating NAMEs and COMMAs");
128 return new Expr(tok, formalArgs, parseBlock(true));
133 if (getToken() != NAME) throw new Error("variable declarations must start with a variable name");
134 Expr name = new Expr(string);
139 initVal = parseExpr();
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;
147 return new Expr(VAR, head);
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;
159 if (tok != COMMA && tok != RP) throw new Error("expected right curly or comma");
165 if (tok == RB) return new Expr(LB, head);
166 if (head == null) head = tail = parseExpr(); else tail = tail.next = parseExpr();
168 if (tok != COMMA && tok != RP) throw new Error("expected right bracket or comma");
172 return new Expr(string);
174 case INC: case DEC: case TYPEOF:
175 return new Expr(tok, parseExpr());
177 case TRUE: case FALSE: case NOP:
178 return new Expr(tok);
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);
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);
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));
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);
218 case VOID: case RESERVED:
219 throw new Error("reserved word that you shouldn't be using");
222 throw new Error("WITH not yet implemented"); // FIXME
224 default: throw new Error("I wasn't expecting a " + tok);
228 // called after each parseExpr(); returns null if we can't make the expression any bigger
229 public Expr parseContinuer(Expr prefix) throws IOException {
236 // FIXME: postfix and infix operators -- need to handle precedence
237 switch (tok = getToken()) {
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());
247 throw new Error("haven't figured out how to handle postfix/infix operators yet");
250 while(peekToken() != RP) {
251 if (head == null) head = tail = parseExpr(); else tail = tail.next = parseExpr();
253 if (tok == RP) break;
254 if (tok != COMMA) throw new Error("expected comma or right paren");
256 return new Expr(LP, prefix, head);
260 if (getToken() != RB) throw new Error("expected a right brace");
261 return new Expr(LB, prefix, e1);
265 if (getToken() != COLON) throw new Error("expected colon to close ?: expression");
267 return new Expr(HOOK, prefix, e2, e3);