1 // Copyright 2003 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 = new ForthBlock(0, "stdin");
23 p.parseStatement(false, block);
24 if (block == null) return;
25 System.out.println(block);
26 if (p.peekToken() == -1) return;
31 // Statics ////////////////////////////////////////////////////////////
33 static byte[] precedence = new byte[MAX_TOKEN + 1];
34 static boolean[] isRightAssociative = new boolean[MAX_TOKEN + 1];
36 isRightAssociative[ASSIGN] = true;
38 precedence[ASSIGN] = 1;
40 precedence[COMMA] = 3;
41 precedence[OR] = precedence[AND] = 4;
42 precedence[GT] = precedence[GE] = 5;
43 precedence[BITOR] = 6;
44 precedence[BITXOR] = 7;
45 precedence[BITAND] = 8;
46 precedence[EQ] = precedence[NE] = 9;
47 precedence[LT] = precedence[LE] = 10;
48 precedence[SHEQ] = precedence[SHNE] = 11;
49 precedence[LSH] = precedence[RSH] = precedence[URSH] = 12;
50 precedence[ADD] = precedence[SUB] = 13;
51 precedence[MUL] = precedence[DIV] = precedence[MOD] = 14;
52 precedence[BITNOT] = precedence[INSTANCEOF] = 15;
53 precedence[INC] = precedence[DEC] = 16;
60 // Parsing Logic /////////////////////////////////////////////////////////
62 /** gets a token and throws an exception if it is not <tt>code</tt> */
63 public void consume(int code) throws IOException {
64 if (getToken() != code)
65 throw new ParserException("expected " + codeToString[code] + ", got " + (op == -1 ? "EOL" : codeToString[op]));
68 /** append the largest expression beginning with prefix containing no operators of precedence below <tt>minPrecedence</tt> */
69 public ForthBlock startExpr() throws IOException { return startExpr(-1); }
70 public ForthBlock startExpr(int minPrecedence) throws IOException { return startExpr(minPrecedence, new ForthBlock(line, sourceName)); }
71 public ForthBlock startExpr(int minPrecedence, ForthBlock appendTo) throws IOException {
75 if (tok == -1) return null;
76 if (minPrecedence > 0 && precedence[tok] != 0)
77 if (precedence[tok] < minPrecedence || (precedence[tok] == minPrecedence && !isRightAssociative[tok]))
78 { pushBackToken(); return null; }
80 ForthBlock b = appendTo;
83 case NUMBER: return continueExpr(b.add(ForthBlock.LITERAL, number), minPrecedence);
84 case STRING: return continueExpr(b.add(ForthBlock.LITERAL, string), minPrecedence);
85 case THIS: return continueExpr(b.add(THIS, null), minPrecedence);
86 case NULL: return continueExpr(b.add(ForthBlock.LITERAL, null), minPrecedence);
87 case TRUE: case FALSE: return continueExpr(b.add(ForthBlock.LITERAL, new Boolean(tok == TRUE)), minPrecedence);
89 b.add(b.ARRAY, new Integer(0));
92 ForthBlock e = startExpr();
93 if (e == null && peekToken() == RB) { consume(RB); return continueExpr(b, minPrecedence); }
94 b.add(b.LITERAL, new Integer(i++));
95 if (e == null) b.add(b.LITERAL, null);
96 else b.add(b.EXPR, e);
99 if (peekToken() == RB) { consume(RB); return continueExpr(b, minPrecedence); }
105 return continueExpr(b.add(ForthBlock.LITERAL, new Double(number.doubleValue() * -1)), minPrecedence);
108 b.add(EXPR, startExpr());
110 return continueExpr(b, minPrecedence);
112 case INC: case DEC: {
114 ForthBlock subexp = startExpr(precedence[tok]);
115 subexp.set(subexp.size() - 1, tok, new Boolean(true));
116 b.add(b.EXPR, subexp);
117 return continueExpr(b, minPrecedence);
119 case BANG: case BITNOT: case INSTANCEOF: case TYPEOF: {
120 b.add(b.EXPR, startExpr(precedence[tok]));
122 return continueExpr(b, minPrecedence);
125 b.add(b.OBJECT, null);
126 if (peekToken() == RC) { consume(RC); return continueExpr(b, minPrecedence); }
128 if (peekToken() != NAME && peekToken() != STRING) throw new Error("expected NAME or STRING");
130 b.add(b.LITERAL, string);
132 b.add(b.EXPR, startExpr());
135 if (peekToken() == RC) { consume(RC); return continueExpr(b, minPrecedence); }
137 if (peekToken() == RC) { consume(RC); return continueExpr(b, minPrecedence); }
141 String name = string;
142 if (peekToken() == ASSIGN) {
145 b.add(ForthBlock.LITERAL, name);
146 b.add(ForthBlock.EXPR, startExpr(minPrecedence));
147 b.add(ForthBlock.PUT);
148 b.add(ForthBlock.SWAP);
149 b.add(ForthBlock.POP);
152 b.add(ForthBlock.LITERAL, name);
153 b.add(ForthBlock.GET);
155 return continueExpr(b, minPrecedence);
157 case Tokens.FUNCTION: {
160 ForthBlock b2 = new ForthBlock(curLine, sourceName);
163 b2.add(b.LITERAL, "arguments");
164 b2.add(b.LITERAL, "arguments");
170 if (peekToken() == RP) consume(RP);
172 if (peekToken() == COMMA) {
173 // FIXME: pop an item off the stack here?
179 b2.add(b.LITERAL, string);
182 // retrieve it from the arguments array
183 b2.add(b.LITERAL, new Integer(numArgs));
184 b2.add(b.GET_PRESERVE);
188 // put it to the current scope
191 b2.add(b.LITERAL, string);
199 if (peekToken() == RP) { consume(RP); break; }
204 // pop off the arguments array
206 parseStatement(true, b2);
207 b2.add(b.LITERAL, null);
209 return continueExpr(b.add(OpCodes.FUNCTION, b2), minPrecedence);
211 default: pushBackToken(); return null;
216 /** return the largest expression beginning with prefix containing no operators of precedence below <tt>minPrecedence</tt> */
217 public ForthBlock continueExpr(ForthBlock prefix, int minPrecedence) throws IOException {
218 return continueExpr(prefix, minPrecedence, new ForthBlock(line, sourceName));
220 public ForthBlock continueExpr(ForthBlock prefix, int minPrecedence, ForthBlock appendTo) throws IOException {
221 int tok = getToken();
223 if (tok == -1) return prefix;
224 if (minPrecedence > 0 && precedence[tok] != 0)
225 if (precedence[tok] < minPrecedence || (precedence[tok] == minPrecedence && !isRightAssociative[tok]))
226 { pushBackToken(); return prefix; }
228 if (prefix == null) throw new Error("got null prefix");
230 ForthBlock b = appendTo;
234 case ASSIGN_BITOR: case ASSIGN_BITXOR: case ASSIGN_BITAND: case ASSIGN_LSH: case ASSIGN_RSH: case ASSIGN_URSH:
235 case ASSIGN_ADD: case ASSIGN_SUB: case ASSIGN_MUL: case ASSIGN_DIV: case ASSIGN_MOD: {
236 b.add(b.EXPR, prefix);
237 prefix.set(prefix.size() - 1, b.GET_PRESERVE, new Boolean(true));
238 prefix.add(prefix.EXPR, startExpr(precedence[tok - 1]));
243 return continueExpr(b, minPrecedence);
246 case INC: case DEC: {
248 prefix.set(prefix.size() - 1, tok, new Boolean(false));
249 b.add(b.EXPR, prefix);
250 return continueExpr(b, minPrecedence);
256 b.add(b.EXPR, prefix);
257 while(peekToken() != RP) {
258 b.add(b.EXPR, startExpr());
260 if (peekToken() == RP) break;
264 b.add(b.CALL, new Integer(i));
265 return continueExpr(b, minPrecedence);
268 case BITOR: case BITXOR: case BITAND: case SHEQ: case SHNE: case LSH:
269 case RSH: case URSH: case ADD: case MUL: case DIV: case MOD:
270 case GT: case GE: case EQ: case NE: case LT: case LE: case SUB: {
271 b.add(b.EXPR, prefix);
272 b.add(b.EXPR, startExpr(precedence[tok]));
274 return continueExpr(b, minPrecedence);
278 b.add(b.LITERAL, tok == AND ? new Boolean(false) : new Boolean(true));
279 b.add(b.EXPR, prefix);
280 b.add(tok == AND ? b.JF : b.JT, new Integer(3));
282 b.add(b.EXPR, startExpr(precedence[tok]));
283 return continueExpr(b, minPrecedence);
288 String target = string;
289 b.add(b.EXPR, prefix);
290 if (peekToken() == ASSIGN) {
292 ForthBlock val = startExpr();
293 b.add(b.LITERAL, target);
299 b.add(b.LITERAL, target);
302 return continueExpr(b, minPrecedence);
306 b.add(b.EXPR, prefix);
307 b.add(b.EXPR, startExpr());
309 if (peekToken() == ASSIGN) {
311 b.add(b.EXPR, startExpr());
318 return continueExpr(b, minPrecedence);
322 b.add(b.EXPR, prefix);
323 b.add(b.JF, new Integer(3));
324 b.add(b.EXPR, startExpr());
325 b.add(b.JMP, new Integer(2));
327 b.add(b.EXPR, startExpr());
328 return continueExpr(b, minPrecedence);
332 default: pushBackToken(); return prefix;
336 /** a block is either a single statement or a list of statements surrounded by curly braces; all expressions are also statements */
337 public ForthBlock parseStatement() throws IOException {
338 ForthBlock ret = new ForthBlock(line, sourceName);
339 ret.add(ret.LITERAL, null);
340 parseStatement(false, ret);
341 if (ret.size() == 1) return null;
345 public void parseStatement(boolean requireBraces) throws IOException {
346 parseStatement(requireBraces, new ForthBlock(line, sourceName));
348 public void parseStatement(boolean requireBraces, ForthBlock b) throws IOException {
349 int tok = peekToken();
350 if (tok == -1) return;
351 boolean braced = tok == LC;
352 if (requireBraces && !braced) throw new ParserException("expected {, got " + codeToString[tok]);
353 if (braced) consume(LC);
356 switch(tok = peekToken()) {
358 case THROW: case RETURN: case ASSERT: {
360 if (tok == RETURN && peekToken() == SEMI) b.add(b.LITERAL, null);
361 else b.add(b.EXPR, startExpr());
367 case BREAK: case CONTINUE: {
369 if (peekToken() == NAME) consume(NAME);
382 b.add(THIS); // push the current scope
385 String name = string;
386 b.add(b.LITERAL, name); // push the name to be declared
387 b.add(b.DECLARE); // declare it
388 if (peekToken() == ASSIGN) { // if there is an '=' after the variable name
389 b.add(b.LITERAL, name); // put the var name back on the stack
391 b.add(b.EXPR, startExpr());
395 if (peekToken() != COMMA) break;
399 if (peekToken() == SEMI) consume(SEMI);
406 b.add(b.EXPR, startExpr());
408 b.add(b.JF, new Integer(4));
409 b.add(b.EXPR, parseStatement());
411 b.add(b.JMP, new Integer(3));
412 if (peekToken() == ELSE) {
414 b.add(b.EXPR, parseStatement());
417 b.add(b.JMP, new Integer(1)); // nop
418 b.add(b.JMP, new Integer(1)); // nop
426 ForthBlock loop = new ForthBlock(curLine, sourceName);
427 b.add(loop.LOOP, loop);
430 loop.add(loop.EXPR, startExpr());
431 loop.add(loop.JT, new Integer(2));
432 loop.add(Lexer.BREAK);
434 parseStatement(false, loop);
436 // if we fall out of the end, definately continue
444 ForthBlock loop = new ForthBlock(curLine, sourceName);
445 b.add(loop.LOOP, loop);
446 loop.add(loop.EXPR, startExpr());
450 ForthBlock caseForthBlock;
454 loop.add(loop.EXPR, startExpr());
456 loop.add(loop.JF, new Integer(2));
457 } else if (tok != DEFAULT) throw new ParserException("expected CASE or DEFAULT");
459 ForthBlock b2 = new ForthBlock(curLine, sourceName);
460 ForthBlock e1 = null;
461 while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) {
462 if ((e1 = parseStatement()) == null) break;
466 loop.add(loop.EXPR, b2);
467 if (peekToken() == RC) {
478 ForthBlock loop = new ForthBlock(curLine, sourceName);
479 b.add(loop.LOOP, loop);
481 parseStatement(false, loop);
484 loop.add(loop.EXPR, startExpr());
485 loop.add(loop.JT, new Integer(2));
486 loop.add(Lexer.BREAK);
487 loop.add(Lexer.CONTINUE);
494 // FIXME: don't just ignore this!
495 // We deliberately allow you to omit braces in catch{}/finally{} if they are single statements...
497 parseStatement(true, b);
499 if (peekToken() == CATCH) {
504 parseStatement(); // just discard the catchblock
507 if (peekToken() == FINALLY) {
509 parseStatement(false, b);
519 if (tok == VAR) tok = getToken();
520 String varName = string;
521 boolean forIn = peekToken() == IN;
522 pushBackToken(tok, varName);
527 b.add(b.EXPR, startExpr());
529 b.add(b.LITERAL, "length");
532 ForthBlock b2 = new ForthBlock(curLine, sourceName);
534 b2.add(b.LITERAL, new Integer(1));
537 b2.add(b.LITERAL, new Integer(0));
539 b2.add(b.JT, new Integer(7));
540 b2.add(b.GET_PRESERVE);
541 b2.add(b.LITERAL, varName);
542 b2.add(b.LITERAL, varName);
545 b2.add(b.EXPR, parseStatement());
546 //b2.add(b.LITERAL, null);
550 ForthBlock b2 = new ForthBlock(curLine, sourceName);
554 ForthBlock e1 = startExpr();
555 if (e1 == null) e1 = new ForthBlock(curLine, sourceName, b.LITERAL, null);
560 ForthBlock e2 = startExpr();
562 ForthBlock e3 = startExpr();
565 if (e2 == null) e2 = new ForthBlock(curLine, sourceName, b.LITERAL, null);
566 if (e3 == null) e3 = new ForthBlock(curLine, sourceName, b.LITERAL, null);
568 ForthBlock b3 = new ForthBlock(curLine, sourceName);
570 b2.add(b.LITERAL, null);
571 b3.add(b.JT, new Integer(3));
575 b3.add(b.JT, new Integer(2));
577 parseStatement(false, b3);
585 String name = string;
586 if (peekToken() == COLON) {
588 b.add(ForthBlock.LABEL, string);
591 pushBackToken(NAME, name);
592 // fall through to default case
597 if (tok == RC && braced) { consume(RC); return; }
600 ForthBlock ret = startExpr();
601 if (ret == null) return;
604 if (peekToken() == SEMI) consume(SEMI);
613 class ParserException extends RuntimeException {
614 public ParserException(String s) { super(sourceName + ":" + line + " " + s); }