1 // Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
7 /** parses a stream of lexed tokens into ForthBlock's */
8 public class Parser extends Lexer implements OpCodes {
10 // Constructors //////////////////////////////////////////////////////
12 public Parser(Reader r, String sourceName, int line) throws IOException {
14 this.sourceName = sourceName;
19 public static void main(String[] s) throws Exception {
20 Parser p = new Parser(new InputStreamReader(System.in), "stdin", 0);
22 ForthBlock block = p.parseStatement(false);
23 if (block == null) return;
24 System.out.println(block);
25 if (p.peekToken() == -1) return;
30 // Statics ////////////////////////////////////////////////////////////
32 static byte[] precedence = new byte[MAX_TOKEN + 1];
33 static boolean[] isRightAssociative = new boolean[MAX_TOKEN + 1];
35 precedence[ASSIGN] = 1;
36 isRightAssociative[ASSIGN] = true;
38 precedence[COMMA] = 3;
39 precedence[OR] = precedence[AND] = 4;
40 precedence[GT] = precedence[GE] = 5;
41 precedence[BITOR] = 6;
42 precedence[BITXOR] = 7;
43 precedence[BITAND] = 8;
44 precedence[EQ] = precedence[NE] = 9;
45 precedence[LT] = precedence[LE] = 10;
46 precedence[SHEQ] = precedence[SHNE] = 11;
47 precedence[LSH] = precedence[RSH] = precedence[URSH] = 12;
48 precedence[ADD] = precedence[SUB] = 13;
49 precedence[MUL] = precedence[DIV] = precedence[MOD] = 14;
50 precedence[BITNOT] = precedence[INSTANCEOF] = 15;
51 precedence[INC] = precedence[DEC] = 16;
58 // Parsing Logic /////////////////////////////////////////////////////////
60 public void consume(int code) throws IOException {
61 if (getToken() != code)
62 throw new ParserException("expected " + codeToString[code] + ", got " + (op == -1 ? "EOL" : codeToString[op]));
65 /** parses the largest possible expression */
66 public ForthBlock parseExpr() throws IOException { return parseExpr(null, -1); }
67 public ForthBlock parseExpr(ForthBlock prefix, int minPrecedence) throws IOException {
68 ForthBlock e1 = null, e2 = null, e3 = null, head = null, tail = null, ret = null;
70 int tok = peekToken();
71 if (minPrecedence > 0 && tok < precedence.length && precedence[tok] != 0 &&
72 (isRightAssociative[tok] ? (precedence[tok] < minPrecedence) : (precedence[tok] <= minPrecedence)))
81 if (prefix != null) { pushBackToken(); return prefix; }
82 ForthBlock b = new ForthBlock(curLine, sourceName);
87 b.add(b.LITERAL, name);
89 if (peekToken() == ASSIGN) {
90 b.add(b.LITERAL, name);
92 b.add(b.EXPR, parseExpr());
96 if (peekToken() != COMMA) break;
99 return parseExpr(b, minPrecedence);
102 case IN: pushBackToken(); return prefix;
105 if (prefix != null) { pushBackToken(); return prefix; }
106 ForthBlock b = new ForthBlock(curLine, sourceName);
108 b.add(b.EXPR, parseExpr());
110 b.add(b.JF, new Integer(3));
111 b.add(b.EXPR, parseStatement(false));
112 b.add(b.JMP, new Integer(2));
113 if (peekToken() != ELSE) return parseExpr(b.add(b.LITERAL, null), minPrecedence);
115 b.add(b.EXPR, parseStatement(false));
116 return parseExpr(b, minPrecedence);
119 // FIXME: ugly hack!!
120 case ASSIGN_BITOR: if (tok == ASSIGN_BITOR) tok = BITOR;
121 case ASSIGN_BITXOR: if (tok == ASSIGN_BITXOR) tok = BITXOR;
122 case ASSIGN_BITAND: if (tok == ASSIGN_BITAND) tok = BITAND;
123 case ASSIGN_LSH: if (tok == ASSIGN_LSH) tok = LSH;
124 case ASSIGN_RSH: if (tok == ASSIGN_RSH) tok = RSH;
125 case ASSIGN_URSH: if (tok == ASSIGN_URSH) tok = URSH;
126 case ASSIGN_ADD: if (tok == ASSIGN_ADD) tok = ADD;
127 case ASSIGN_SUB: if (tok == ASSIGN_SUB) tok = SUB;
128 case ASSIGN_MUL: if (tok == ASSIGN_MUL) tok = MUL;
129 case ASSIGN_DIV: if (tok == ASSIGN_DIV) tok = DIV;
130 case ASSIGN_MOD: if (tok == ASSIGN_MOD) tok = MOD;
132 ForthBlock b = (ForthBlock)prefix;
133 b.set(b.size() - 1, b.GET_PRESERVE, new Boolean(true));
134 b.add(b.EXPR, parseExpr(null, precedence[tok]));
139 return parseExpr(b, minPrecedence);
143 if (prefix == null) {
145 ForthBlock b = (ForthBlock)parseExpr(null, precedence[tok]);
146 b.set(b.size() - 1, tok, new Boolean(true));
147 return parseExpr(b, minPrecedence);
150 ForthBlock b = (ForthBlock)prefix;
151 b.set(b.size() - 1, tok, new Boolean(false));
152 return parseExpr(b, minPrecedence);
156 if (prefix == null) { // grouping
157 ForthBlock b = new ForthBlock(curLine, sourceName, ForthBlock.EXPR, parseExpr());
159 return parseExpr(b, minPrecedence);
161 } else { // invocation
162 ForthBlock b = new ForthBlock(curLine, sourceName);
164 b.add(b.EXPR, prefix);
165 while(peekToken() != RP) {
166 b.add(b.EXPR, parseExpr());
168 if (peekToken() == RP) break;
172 b.add(b.CALL, new Integer(i));
173 return parseExpr(b, minPrecedence);
176 case BANG: case BITNOT: case INSTANCEOF: case TYPEOF: {
177 if (prefix != null) { pushBackToken(); return prefix; }
178 ForthBlock b = new ForthBlock(curLine, sourceName);
179 b.add(b.EXPR, parseExpr(null, precedence[tok]));
181 return parseExpr(b, minPrecedence);
185 if (prefix == null && peekToken() == NUMBER) {
187 return parseExpr(new ForthBlock(curLine, sourceName, ForthBlock.LITERAL, new Double(number.doubleValue() * -1)), minPrecedence);
188 } // else fall through
189 case BITOR: case BITXOR: case BITAND: case SHEQ: case SHNE: case LSH:
190 case RSH: case URSH: case ADD: case MUL: case DIV: case MOD:
191 case GT: case GE: case EQ: case NE: case LT: case LE: {
192 if (prefix == null) throw new ParserException("the " + codeToString[tok] + " token cannot start an expression");
193 ForthBlock b = new ForthBlock(curLine, sourceName);
194 b.add(b.EXPR, prefix);
195 b.add(b.EXPR, parseExpr(null, precedence[tok]));
197 return parseExpr(b, minPrecedence);
200 // includes short-circuit logic
202 if (prefix == null) throw new ParserException("the " + codeToString[tok] + " token cannot start an expression");
203 ForthBlock b = new ForthBlock(curLine, sourceName);
204 b.add(b.LITERAL, tok == AND ? new Boolean(false) : new Boolean(true));
205 b.add(b.EXPR, prefix);
206 b.add(tok == AND ? b.JF : b.JT, new Integer(3));
208 b.add(b.EXPR, parseExpr(null, precedence[tok]));
209 return parseExpr(b, minPrecedence);
212 case WITH: throw new ParserException("XWT does not allow the WITH keyword");
213 case VOID: case RESERVED: throw new ParserException("reserved word that you shouldn't be using");
216 if (prefix != null) { pushBackToken(); return prefix; }
217 return parseExpr(new ForthBlock(curLine, sourceName, ForthBlock.LITERAL, number), minPrecedence);
220 if (prefix != null) { pushBackToken(); return prefix; }
221 return parseExpr(new ForthBlock(curLine, sourceName, ForthBlock.LITERAL, string), minPrecedence);
223 case NULL: case TRUE: case FALSE: case NOP:
224 if (prefix != null) { pushBackToken(); return prefix; }
225 return parseExpr(new ForthBlock(curLine, sourceName, ForthBlock.LITERAL, (tok == NULL || tok == NOP) ? null : new Boolean(tok == TRUE)), minPrecedence);
227 case COMMA: pushBackToken(); return prefix;
230 if (prefix != null) { pushBackToken(); return prefix; }
231 return parseExpr(new ForthBlock(curLine, sourceName, OpCodes.THIS, null), minPrecedence);
234 if (prefix != null) { pushBackToken(); return prefix; }
235 String name = string;
236 ForthBlock b = new ForthBlock(curLine, sourceName);
237 if (peekToken() == ASSIGN) {
240 b.add(ForthBlock.LITERAL, name);
241 b.add(ForthBlock.EXPR, parseExpr(null, minPrecedence));
242 b.add(ForthBlock.PUT);
243 b.add(ForthBlock.SWAP);
244 b.add(ForthBlock.POP);
245 return parseExpr(b, minPrecedence);
248 b.add(ForthBlock.LITERAL, name);
249 b.add(ForthBlock.GET);
250 return parseExpr(parseExpr(b, minPrecedence), minPrecedence);
256 String target = string;
257 ForthBlock b = new ForthBlock(curLine, sourceName);
258 b.add(b.EXPR, prefix);
259 if (peekToken() == ASSIGN) {
261 ForthBlock val = parseExpr();
262 b.add(b.LITERAL, target);
268 b.add(b.LITERAL, target);
271 return parseExpr(b, minPrecedence);
275 ForthBlock b = new ForthBlock(curLine, sourceName);
276 if (prefix == null) {
277 b.add(b.ARRAY, new Integer(0));
280 ForthBlock e = parseExpr();
281 if (e == null && peekToken() == RB) { consume(RB); return parseExpr(b, minPrecedence); }
282 b.add(b.LITERAL, new Integer(i++));
283 if (e == null) b.add(b.LITERAL, null);
284 else b.add(b.EXPR, e);
287 if (peekToken() == RB) { consume(RB); return parseExpr(b, minPrecedence); }
291 b.add(b.EXPR, prefix);
292 b.add(b.EXPR, parseExpr());
294 if (peekToken() == ASSIGN) {
296 b.add(b.EXPR, parseExpr());
303 return parseExpr(b, minPrecedence);
308 if (prefix != null) { pushBackToken(); return prefix; }
309 ForthBlock b = new ForthBlock(curLine, sourceName);
310 b.add(b.OBJECT, null);
311 if (peekToken() == RC) { consume(RC); return parseExpr(b, minPrecedence); }
313 if (peekToken() != NAME && peekToken() != STRING) throw new Error("expected NAME or STRING");
315 b.add(b.LITERAL, string);
317 b.add(b.EXPR, parseExpr());
320 if (peekToken() == RC) { consume(RC); return parseExpr(b, minPrecedence); }
322 if (peekToken() == RC) { consume(RC); return parseExpr(b, minPrecedence); }
327 ForthBlock b = new ForthBlock(curLine, sourceName);
328 b.add(b.EXPR, prefix);
329 b.add(b.JF, new Integer(3));
330 b.add(b.EXPR, parseExpr());
331 b.add(b.JMP, new Integer(2));
333 b.add(b.EXPR, parseExpr());
334 return parseExpr(b, minPrecedence);
337 case Tokens.FUNCTION: {
338 if (prefix != null) { pushBackToken(); return prefix; }
340 ForthBlock b = new ForthBlock(curLine, sourceName);
344 b.add(b.LITERAL, "arguments");
345 b.add(b.LITERAL, "arguments");
351 if (peekToken() == RP) consume(RP);
353 if (peekToken() == COMMA) {
359 b.add(b.LITERAL, string);
362 // retrieve it from the arguments array
363 b.add(b.LITERAL, new Integer(numArgs));
364 b.add(b.GET_PRESERVE);
368 // put it to the current scope
371 b.add(b.LITERAL, string);
379 if (peekToken() == RP) { consume(RP); break; }
384 // pop off the arguments array
386 parseStatement(true, b);
387 return parseExpr(new ForthBlock(curLine, sourceName, OpCodes.FUNCTION, b), minPrecedence);
391 if (prefix != null) { pushBackToken(); return prefix; }
393 ForthBlock r = new ForthBlock(curLine, sourceName);
394 ForthBlock loop = new ForthBlock(curLine, sourceName);
395 r.add(loop.LOOP, loop);
396 r.add(r.LITERAL, null);
398 loop.add(loop.EXPR, parseExpr());
399 loop.add(loop.JT, new Integer(2));
400 loop.add(Lexer.BREAK);
402 parseStatement(false, loop);
404 // if we fall out of the end, definately continue
406 return parseExpr(r, minPrecedence);
410 if (prefix != null) { pushBackToken(); return prefix; }
412 ForthBlock r = new ForthBlock(curLine, sourceName);
413 ForthBlock loop = new ForthBlock(curLine, sourceName);
414 r.add(loop.LOOP, loop);
415 r.add(r.LITERAL, null);
416 loop.add(loop.EXPR, parseExpr());
420 ForthBlock caseForthBlock;
424 loop.add(loop.EXPR, parseExpr());
426 loop.add(loop.JF, new Integer(2));
427 } else if (tok != DEFAULT) throw new ParserException("expected CASE or DEFAULT");
429 ForthBlock b = new ForthBlock(curLine, sourceName);
430 while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) {
431 if ((e1 = parseStatement(false)) == null) break;
434 loop.add(loop.EXPR, b);
435 if (peekToken() == RC) {
438 return parseExpr(r, minPrecedence);
444 if (prefix != null) { pushBackToken(); return prefix; }
445 ForthBlock r = new ForthBlock(curLine, sourceName);
446 ForthBlock loop = new ForthBlock(curLine, sourceName);
447 r.add(loop.LOOP, loop);
448 r.add(r.LITERAL, null);
450 parseStatement(false, loop);
453 loop.add(loop.EXPR, parseExpr());
454 loop.add(loop.JT, new Integer(2));
455 loop.add(Lexer.BREAK);
456 loop.add(Lexer.CONTINUE);
459 return parseExpr(r, minPrecedence);
463 // FIXME: don't just ignore this!
464 // We deliberately allow you to omit braces in catch{}/finally{} if they are single statements...
465 if (prefix != null) { pushBackToken(); return prefix; }
466 ForthBlock tryBlock = parseStatement(true);
471 if (getToken() != LP) throw new ParserException("expected (");
472 if (getToken() != NAME) throw new ParserException("expected name");
473 if (getToken() != RP) throw new ParserException("expected )");
476 if (tok == FINALLY) getToken();
478 return parseExpr(tryBlock, minPrecedence);
482 if (prefix != null) { pushBackToken(); return prefix; }
483 if (getToken() != LP) throw new ParserException("expected left paren");
486 if (tok == VAR) tok = getToken();
487 String varName = string;
488 boolean forIn = peekToken() == IN;
489 pushBackToken(tok, varName);
491 ForthBlock b = new ForthBlock(curLine, sourceName);
495 b.add(b.EXPR, parseExpr());
497 b.add(b.LITERAL, "length");
500 ForthBlock b2 = new ForthBlock(curLine, sourceName);
502 b2.add(b.LITERAL, new Integer(1));
505 b2.add(b.LITERAL, new Integer(0));
507 b2.add(b.JT, new Integer(7));
508 b2.add(b.GET_PRESERVE);
509 b2.add(b.LITERAL, varName);
510 b2.add(b.LITERAL, varName);
513 b2.add(b.EXPR, parseStatement(false));
514 b2.add(b.LITERAL, null);
515 return parseExpr(b, minPrecedence);
518 ForthBlock b2 = new ForthBlock(curLine, sourceName);
522 if (e1 == null) e1 = new ForthBlock(curLine, sourceName, b.LITERAL, null);
532 if (e2 == null) e2 = new ForthBlock(curLine, sourceName, b.LITERAL, null);
533 if (e3 == null) e3 = new ForthBlock(curLine, sourceName, b.LITERAL, null);
535 ForthBlock b3 = new ForthBlock(curLine, sourceName);
537 b2.add(b.LITERAL, null);
538 b3.add(b.JT, new Integer(3));
542 b3.add(b.JT, new Integer(2));
544 parseStatement(false, b3);
545 return parseExpr(b, minPrecedence);
555 /** a block is either a single statement or a list of statements surrounded by curly braces; all expressions are also statements */
556 public ForthBlock parseStatement(boolean requireBraces) throws IOException { return parseStatement(requireBraces, null); }
557 public ForthBlock parseStatement(boolean requireBraces, ForthBlock b) throws IOException {
558 ForthBlock smt = null;
559 int tok = peekToken();
560 if (tok == -1) return null;
561 boolean braced = tok == LC;
562 if (requireBraces && !braced) throw new ParserException("expected {, got " + codeToString[tok]);
563 if (braced) consume(LC);
565 ForthBlock ret = new ForthBlock(curLine, sourceName);
566 ForthBlock block = b == null ? new ForthBlock(curLine, sourceName) : b;
567 block.add(ret.LITERAL, Boolean.TRUE);
568 ret.add(block.SCOPE, block);
570 switch(tok = peekToken()) {
572 case LC: smt = parseStatement(true); break;
573 case GOTO: throw new ParserException("goto not supported");
575 case THROW: case RETURN: case ASSERT: {
577 ForthBlock r = new ForthBlock(curLine, sourceName);
578 if (tok == RETURN && peekToken() == SEMI) r.add(b.LITERAL, null);
579 else r.add(b.EXPR, parseExpr());
586 case BREAK: case CONTINUE: {
588 if (peekToken() == NAME) consume(NAME);
589 smt = new ForthBlock(curLine, sourceName, tok, string);
595 if (braced) consume(RC);
596 return block.size() == 0 ? null : ret;
600 if (!braced) return block.size() == 0 ? null : ret;
604 String name = string;
606 if (peekToken() == COLON) {
608 smt = new ForthBlock(curLine, sourceName, ForthBlock.LABEL, string);
611 pushBackToken(NAME, name);
612 // fall through to default case
619 if (smt == null) return block.size() == 0 ? null : ret;
620 if (peekToken() == SEMI) getToken();
624 if (!braced) return smt;
625 block.add(block.EXPR, smt);
626 block.add(block.POP);
630 class ParserException extends RuntimeException {
631 public ParserException(String s) { super(sourceName + ":" + line + " " + s); }