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 void startExpr(ForthBlock block) throws IOException { startExpr(-1, block); }
71 public ForthBlock startExpr(int minPrecedence) throws IOException {
72 ForthBlock ret = new ForthBlock(line, sourceName);
73 startExpr(minPrecedence, ret);
74 return ret.size() == 0 ? null : ret;
77 public void startExpr(int minPrecedence, ForthBlock appendTo) throws IOException {
80 if (tok == -1) return;
81 if (minPrecedence > 0 && precedence[tok] != 0)
82 if (precedence[tok] < minPrecedence || (precedence[tok] == minPrecedence && !isRightAssociative[tok]))
83 { pushBackToken(); return; }
85 ForthBlock b = appendTo;
88 case NUMBER: continueExpr(b.add(ForthBlock.LITERAL, number), minPrecedence); return;
89 case STRING: continueExpr(b.add(ForthBlock.LITERAL, string), minPrecedence); return;
90 case THIS: continueExpr(b.add(THIS, null), minPrecedence); return;
91 case NULL: continueExpr(b.add(ForthBlock.LITERAL, null), minPrecedence); return;
92 case TRUE: case FALSE: continueExpr(b.add(ForthBlock.LITERAL, new Boolean(tok == TRUE)), minPrecedence); return;
94 b.add(b.ARRAY, new Integer(0));
100 if (peekToken() == RB) { consume(RB); continueExpr(b, minPrecedence); return; }
101 b.add(b.LITERAL, new Integer(i++));
102 if (size == b.size()) b.add(b.LITERAL, null);
105 if (peekToken() == RB) { consume(RB); continueExpr(b, minPrecedence); return; }
111 continueExpr(b.add(ForthBlock.LITERAL, new Double(number.doubleValue() * -1)), minPrecedence);
117 continueExpr(b, minPrecedence);
120 case INC: case DEC: {
122 startExpr(precedence[tok], b);
123 b.set(b.size() - 1, tok, new Boolean(true));
124 continueExpr(b, minPrecedence);
127 case BANG: case BITNOT: case INSTANCEOF: case TYPEOF: {
128 startExpr(precedence[tok], b);
130 continueExpr(b, minPrecedence);
134 b.add(b.OBJECT, null);
135 if (peekToken() == RC) { consume(RC); continueExpr(b, minPrecedence); return; }
137 if (peekToken() != NAME && peekToken() != STRING) throw new Error("expected NAME or STRING");
139 b.add(b.LITERAL, string);
144 if (peekToken() == RC) { consume(RC); continueExpr(b, minPrecedence); return; }
146 if (peekToken() == RC) { consume(RC); continueExpr(b, minPrecedence); return; }
150 String name = string;
151 if (peekToken() == ASSIGN) {
154 b.add(ForthBlock.LITERAL, name);
155 startExpr(minPrecedence, b);
156 b.add(ForthBlock.PUT);
157 b.add(ForthBlock.SWAP);
158 b.add(ForthBlock.POP);
161 b.add(ForthBlock.LITERAL, name);
162 b.add(ForthBlock.GET);
164 continueExpr(b, minPrecedence);
167 case Tokens.FUNCTION: {
170 ForthBlock b2 = new ForthBlock(curLine, sourceName);
173 b2.add(b.LITERAL, "arguments");
174 b2.add(b.LITERAL, "arguments");
180 if (peekToken() == RP) consume(RP);
182 if (peekToken() == COMMA) {
183 // FIXME: pop an item off the stack here?
189 b2.add(b.LITERAL, string);
192 // retrieve it from the arguments array
193 b2.add(b.LITERAL, new Integer(numArgs));
194 b2.add(b.GET_PRESERVE);
198 // put it to the current scope
201 b2.add(b.LITERAL, string);
209 if (peekToken() == RP) { consume(RP); break; }
214 // pop off the arguments array
216 parseStatement(true, b2);
217 b2.add(b.LITERAL, null);
219 continueExpr(b.add(OpCodes.FUNCTION, b2), minPrecedence);
222 default: pushBackToken(); return;
226 public void continueExpr(ForthBlock prefix, int minPrecedence) throws IOException {
227 int tok = getToken();
229 if (tok == -1) return;
230 if (minPrecedence > 0 && precedence[tok] != 0)
231 if (precedence[tok] < minPrecedence || (precedence[tok] == minPrecedence && !isRightAssociative[tok]))
232 { pushBackToken(); return; }
234 if (prefix == null) throw new Error("got null prefix");
235 ForthBlock b = prefix;
239 case ASSIGN_BITOR: case ASSIGN_BITXOR: case ASSIGN_BITAND: case ASSIGN_LSH: case ASSIGN_RSH: case ASSIGN_URSH:
240 case ASSIGN_ADD: case ASSIGN_SUB: case ASSIGN_MUL: case ASSIGN_DIV: case ASSIGN_MOD: {
241 prefix.set(prefix.size() - 1, b.GET_PRESERVE, new Boolean(true));
242 startExpr(precedence[tok - 1], b);
247 continueExpr(b, minPrecedence);
251 case INC: case DEC: {
253 b.set(b.size() - 1, tok, new Boolean(false));
254 continueExpr(b, minPrecedence);
261 while(peekToken() != RP) {
264 if (peekToken() == RP) break;
268 b.add(b.CALL, new Integer(i));
269 continueExpr(b, minPrecedence);
273 case BITOR: case BITXOR: case BITAND: case SHEQ: case SHNE: case LSH:
274 case RSH: case URSH: case ADD: case MUL: case DIV: case MOD:
275 case GT: case GE: case EQ: case NE: case LT: case LE: case SUB: {
276 startExpr(precedence[tok], b);
278 continueExpr(b, minPrecedence);
283 b.add(tok == AND ? b.JF : b.JT, new Integer(0));
285 startExpr(precedence[tok], b);
286 b.arg[size - 1] = new Integer(b.size() - size + 2);
287 b.add(b.JMP, new Integer(2));
288 b.add(b.LITERAL, tok == AND ? new Boolean(false) : new Boolean(true));
289 continueExpr(b, minPrecedence);
295 String target = string;
296 if (peekToken() == ASSIGN) {
298 b.add(b.LITERAL, target);
304 b.add(b.LITERAL, target);
307 continueExpr(b, minPrecedence);
314 if (peekToken() == ASSIGN) {
323 continueExpr(b, minPrecedence);
328 b.add(b.JF, new Integer(0));
331 b.arg[size - 1] = new Integer(b.size() - size + 2);
332 b.add(b.JMP, new Integer(0));
336 b.arg[size - 1] = new Integer(b.size() - size + 1);
337 continueExpr(b, minPrecedence);
341 default: { pushBackToken(); return; }
345 /** a block is either a single statement or a list of statements surrounded by curly braces; all expressions are also statements */
346 public ForthBlock parseStatement() throws IOException {
347 ForthBlock ret = new ForthBlock(line, sourceName);
348 ret.add(ret.LITERAL, null);
349 parseStatement(false, ret);
350 if (ret.size() == 1) return null;
354 public void parseStatement(boolean requireBraces) throws IOException {
355 parseStatement(requireBraces, new ForthBlock(line, sourceName));
357 public void parseStatement(boolean requireBraces, ForthBlock b) throws IOException {
358 int tok = peekToken();
359 if (tok == -1) return;
360 boolean braced = tok == LC;
361 if (requireBraces && !braced) throw new ParserException("expected {, got " + codeToString[tok]);
362 if (braced) consume(LC);
365 switch(tok = peekToken()) {
367 case THROW: case RETURN: case ASSERT: {
369 if (tok == RETURN && peekToken() == SEMI) b.add(b.LITERAL, null);
376 case BREAK: case CONTINUE: {
378 if (peekToken() == NAME) consume(NAME);
391 b.add(THIS); // push the current scope
394 String name = string;
395 b.add(b.LITERAL, name); // push the name to be declared
396 b.add(b.DECLARE); // declare it
397 if (peekToken() == ASSIGN) { // if there is an '=' after the variable name
398 b.add(b.LITERAL, name); // put the var name back on the stack
404 if (peekToken() != COMMA) break;
408 if (peekToken() == SEMI) consume(SEMI);
418 b.add(b.JF, new Integer(0));
420 parseStatement(false, b);
422 if (peekToken() == ELSE) {
424 b.arg[size - 1] = new Integer(2 + b.size() - size);
425 b.add(b.JMP, new Integer(3));
426 b.add(b.EXPR, parseStatement());
429 b.arg[size - 1] = new Integer(1 + b.size() - size);
437 ForthBlock loop = new ForthBlock(curLine, sourceName);
438 b.add(loop.LOOP, loop);
442 loop.add(loop.JT, new Integer(2));
443 loop.add(Lexer.BREAK);
445 parseStatement(false, loop);
447 // if we fall out of the end, definately continue
455 ForthBlock loop = new ForthBlock(curLine, sourceName);
456 b.add(loop.LOOP, loop);
461 if (peekToken() == CASE) {
467 loop.add(loop.JF, new Integer(0));
468 int size = loop.size();
469 while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) {
470 int size2 = loop.size();
471 parseStatement(false, loop);
472 if (size2 == loop.size()) break;
474 loop.arg[size - 1] = new Integer(1 + loop.size() - size);
475 } else if (peekToken() == DEFAULT) {
478 while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) {
479 int size2 = loop.size();
480 parseStatement(false, loop);
481 if (size2 == loop.size()) break;
483 } else if (peekToken() == RC) {
488 throw new ParserException("expected CASE, DEFAULT, or RC; got " + codeToString[peekToken()]);
495 ForthBlock loop = new ForthBlock(curLine, sourceName);
496 b.add(loop.LOOP, loop);
498 parseStatement(false, loop);
502 loop.add(loop.JT, new Integer(2));
503 loop.add(Lexer.BREAK);
504 loop.add(Lexer.CONTINUE);
511 // FIXME: don't just ignore this!
512 // We deliberately allow you to omit braces in catch{}/finally{} if they are single statements...
514 parseStatement(true, b);
516 if (peekToken() == CATCH) {
521 parseStatement(); // just discard the catchblock
524 if (peekToken() == FINALLY) {
526 parseStatement(false, b);
536 if (tok == VAR) tok = getToken();
537 String varName = string;
538 boolean forIn = peekToken() == IN;
539 pushBackToken(tok, varName);
546 b.add(b.LITERAL, "length");
549 ForthBlock b2 = new ForthBlock(curLine, sourceName);
551 b2.add(b.LITERAL, new Integer(1));
554 b2.add(b.LITERAL, new Integer(0));
556 b2.add(b.JT, new Integer(7));
557 b2.add(b.GET_PRESERVE);
558 b2.add(b.LITERAL, varName);
559 b2.add(b.LITERAL, varName);
562 b2.add(b.EXPR, parseStatement());
566 ForthBlock b2 = new ForthBlock(curLine, sourceName);
570 int size = b2.size();
572 if (b2.size() - size > 0) b2.add(b.POP);
574 ForthBlock e2 = startExpr();
577 if (e2 == null) e2 = new ForthBlock(curLine, sourceName, b.LITERAL, null);
579 ForthBlock b3 = new ForthBlock(curLine, sourceName);
581 b2.add(b.LITERAL, null);
583 b3.add(b.JT, new Integer(0));
587 if (b3.size() - size > 0) b3.add(b.POP);
588 b3.arg[size - 1] = new Integer(b3.size() - size + 1);
591 b3.add(b.JT, new Integer(2));
593 parseStatement(false, b3);
601 String name = string;
602 if (peekToken() == COLON) {
604 b.add(ForthBlock.LABEL, string);
607 pushBackToken(NAME, name);
608 // fall through to default case
613 if (tok == RC && braced) { consume(RC); return; }
618 if (size == b.size()) return;
620 if (peekToken() == SEMI) consume(SEMI);
629 class ParserException extends RuntimeException {
630 public ParserException(String s) { super(sourceName + ":" + line + " " + s); }