2003/06/07 08:21:30
[org.ibex.core.git] / src / org / xwt / js / Parser.java
1 // Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL]
2 package org.xwt.js;
3
4 import org.xwt.util.*;
5 import java.io.*;
6
7 /** parses a stream of lexed tokens into ForthBlock's */
8 public class Parser extends Lexer implements OpCodes {
9
10     // Constructors //////////////////////////////////////////////////////
11
12     public Parser(Reader r, String sourceName, int line) throws IOException {
13         super(r);
14         this.sourceName = sourceName;
15         this.line = line;
16     }
17
18     /** for debugging */
19     public static void main(String[] s) throws Exception {
20         Parser p = new Parser(new InputStreamReader(System.in), "stdin", 0);
21         while(true) {
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;
27         }
28     }
29
30
31     // Statics ////////////////////////////////////////////////////////////
32
33     static byte[] precedence = new byte[MAX_TOKEN + 1];
34     static boolean[] isRightAssociative = new boolean[MAX_TOKEN + 1];
35     static boolean[] mustStartExpression = new boolean[MAX_TOKEN + 1];
36     static boolean[] cannotStartExpression = new boolean[MAX_TOKEN + 1];
37     static {
38
39         mustStartExpression[VAR] = true;
40         mustStartExpression[IF] = true;
41         mustStartExpression[BANG] = true;
42         mustStartExpression[BITNOT] = true;
43         mustStartExpression[INSTANCEOF] = true;
44         mustStartExpression[TYPEOF] = true;
45         mustStartExpression[NUMBER] = true;
46         mustStartExpression[STRING] = true;
47         mustStartExpression[NULL] = true;
48         mustStartExpression[TRUE] = true;
49         mustStartExpression[FALSE] = true;
50         mustStartExpression[Tokens.FUNCTION] = true;
51         mustStartExpression[NAME] = true;
52         mustStartExpression[LC] = true;
53         mustStartExpression[THIS] = true;
54         
55         cannotStartExpression[OR] = true;
56         cannotStartExpression[AND] = true;
57         cannotStartExpression[BITOR] = true;
58         cannotStartExpression[BITXOR] = true;
59         cannotStartExpression[BITAND] = true;
60         cannotStartExpression[SHEQ] = true;
61         cannotStartExpression[SHNE] = true;
62         cannotStartExpression[LSH] = true;
63         cannotStartExpression[RSH] = true;
64         cannotStartExpression[URSH] = true;
65         cannotStartExpression[ADD] = true;
66         cannotStartExpression[MUL] = true;
67         cannotStartExpression[DIV] = true;
68         cannotStartExpression[MOD] = true;
69         cannotStartExpression[GT] = true;
70         cannotStartExpression[GE] = true;
71         cannotStartExpression[EQ] = true;
72         cannotStartExpression[NE] = true;
73         cannotStartExpression[LT] = true;
74         cannotStartExpression[LE] = true;
75         cannotStartExpression[DOT] = true;
76         cannotStartExpression[HOOK] = true;
77         cannotStartExpression[ASSIGN_BITOR] = true;
78         cannotStartExpression[ASSIGN_BITXOR] = true;
79         cannotStartExpression[ASSIGN_BITAND] = true;
80         cannotStartExpression[ASSIGN_LSH] = true;
81         cannotStartExpression[ASSIGN_RSH] = true;
82         cannotStartExpression[ASSIGN_URSH] = true;
83         cannotStartExpression[ASSIGN_ADD] = true;
84         cannotStartExpression[ASSIGN_SUB] = true;
85         cannotStartExpression[ASSIGN_MUL] = true;
86         cannotStartExpression[ASSIGN_DIV] = true;
87         cannotStartExpression[ASSIGN_MOD] = true;
88
89         isRightAssociative[ASSIGN] = true;
90
91         precedence[ASSIGN] = 1;
92         precedence[HOOK] = 2;
93         precedence[COMMA] = 3;
94         precedence[OR] = precedence[AND] = 4;
95         precedence[GT] = precedence[GE] = 5;
96         precedence[BITOR] = 6;
97         precedence[BITXOR] = 7;
98         precedence[BITAND] = 8;
99         precedence[EQ] = precedence[NE] = 9;
100         precedence[LT] = precedence[LE] = 10;
101         precedence[SHEQ] = precedence[SHNE] = 11;
102         precedence[LSH] = precedence[RSH] = precedence[URSH] = 12;
103         precedence[ADD] = precedence[SUB] = 13;
104         precedence[MUL] = precedence[DIV] = precedence[MOD] = 14;
105         precedence[BITNOT] = precedence[INSTANCEOF] = 15;
106         precedence[INC] = precedence[DEC] = 16;
107         precedence[LP] = 17;
108         precedence[LB] = 18;
109         precedence[DOT] = 19;
110     }
111
112
113     // Parsing Logic /////////////////////////////////////////////////////////
114
115     /** gets a token and throws an exception if it is not <tt>code</tt> */
116     public void consume(int code) throws IOException {
117         if (getToken() != code)
118             throw new ParserException("expected " + codeToString[code] + ", got " + (op == -1 ? "EOL" : codeToString[op]));
119     }
120
121     /** parses the largest possible expression */
122     public ForthBlock parseExpr() throws IOException { return parseExpr(null, -1); }
123
124     /** return the largest expression beginning with prefix containing no operators of precedence below <tt>minPrecedence</tt> */
125     public ForthBlock parseExpr(ForthBlock prefix, int minPrecedence) throws IOException {
126         return parseExpr(prefix, minPrecedence, new ForthBlock(line, sourceName));
127     }
128
129     /** append the largest expression beginning with prefix containing no operators of precedence below <tt>minPrecedence</tt> */
130     public ForthBlock parseExpr(ForthBlock prefix, int minPrecedence, ForthBlock appendTo) throws IOException {
131
132         int tok = getToken();
133         int curLine = line;
134         if (tok == -1) return prefix;
135         if (minPrecedence > 0 && precedence[tok] != 0)
136             if (precedence[tok] < minPrecedence || (precedence[tok] == minPrecedence && !isRightAssociative[tok]))
137                 { pushBackToken(); return prefix; }
138
139         if (prefix != null && mustStartExpression[tok]) { pushBackToken(); return prefix; }
140         if (prefix == null && cannotStartExpression[tok]) { pushBackToken(); return prefix; }
141
142         ForthBlock b = appendTo;
143
144         switch (tok) {
145
146         case NUMBER: return parseExpr(b.add(ForthBlock.LITERAL, number), minPrecedence);
147         case STRING: return parseExpr(b.add(ForthBlock.LITERAL, string), minPrecedence);
148         case THIS: return parseExpr(b.add(THIS, null), minPrecedence);
149         case NULL: return parseExpr(b.add(ForthBlock.LITERAL, null), minPrecedence);
150         case TRUE: case FALSE: return parseExpr(b.add(ForthBlock.LITERAL, new Boolean(tok == TRUE)), minPrecedence);
151
152         case ASSIGN_BITOR: case ASSIGN_BITXOR: case ASSIGN_BITAND: case ASSIGN_LSH: case ASSIGN_RSH: case ASSIGN_URSH:
153         case ASSIGN_ADD: case ASSIGN_SUB: case ASSIGN_MUL: case ASSIGN_DIV: case ASSIGN_MOD: {
154             b.add(b.EXPR, prefix);
155             prefix.set(prefix.size() - 1, b.GET_PRESERVE, new Boolean(true));
156             prefix.add(prefix.EXPR, parseExpr(null, precedence[tok - 1]));
157             prefix.add(tok - 1);
158             prefix.add(b.PUT);
159             prefix.add(b.SWAP);
160             prefix.add(b.POP);
161             return parseExpr(b, minPrecedence);
162         }
163
164         case INC: case DEC:
165             if (prefix == null) {
166                 // prefix
167                 ForthBlock subexp = parseExpr(null, precedence[tok]);
168                 subexp.set(subexp.size() - 1, tok, new Boolean(true));
169                 b.add(b.EXPR, subexp);
170                 return parseExpr(b, minPrecedence);
171             } else {
172                 // postfix
173                 prefix.set(prefix.size() - 1, tok, new Boolean(false));
174                 b.add(b.EXPR, prefix);
175                 return parseExpr(b, minPrecedence);
176             }
177
178         case LP:
179             if (prefix == null) {  // grouping
180                 b.add(EXPR, parseExpr());
181                 consume(RP);
182                 return parseExpr(b, minPrecedence);
183
184             } else {  // invocation
185                 int i = 0;
186                 b.add(b.EXPR, prefix);
187                 while(peekToken() != RP) {
188                     b.add(b.EXPR, parseExpr());
189                     i++;
190                     if (peekToken() == RP) break;
191                     consume(COMMA);
192                 }
193                 consume(RP);
194                 b.add(b.CALL, new Integer(i));
195                 return parseExpr(b, minPrecedence);
196             }
197
198         case BANG: case BITNOT: case INSTANCEOF: case TYPEOF: {
199             b.add(b.EXPR, parseExpr(null, precedence[tok]));
200             b.add(tok);
201             return parseExpr(b, minPrecedence);
202         }
203
204         case SUB:
205             if (prefix == null) {
206                 consume(NUMBER);
207                 return parseExpr(b.add(ForthBlock.LITERAL, new Double(number.doubleValue() * -1)), minPrecedence);
208             }
209             // fall through
210         case BITOR: case BITXOR: case BITAND: case SHEQ: case SHNE: case LSH:
211         case RSH: case URSH: case ADD: case MUL: case DIV: case MOD:
212         case GT: case GE: case EQ: case NE: case LT: case LE: {
213             b.add(b.EXPR, prefix);
214             b.add(b.EXPR, parseExpr(null, precedence[tok]));
215             b.add(tok);
216             return parseExpr(b, minPrecedence);
217         }
218
219         case OR: case AND: {
220             b.add(b.LITERAL, tok == AND ? new Boolean(false) : new Boolean(true));
221             b.add(b.EXPR, prefix);
222             b.add(tok == AND ? b.JF : b.JT, new Integer(3));
223             b.add(b.POP);
224             b.add(b.EXPR, parseExpr(null, precedence[tok]));
225             return parseExpr(b, minPrecedence);
226         }
227
228         case NAME: {
229             String name = string;
230             if (peekToken() == ASSIGN) {
231                 consume(ASSIGN);
232                 b.add(THIS);
233                 b.add(ForthBlock.LITERAL, name);
234                 b.add(ForthBlock.EXPR, parseExpr(null, minPrecedence));
235                 b.add(ForthBlock.PUT);
236                 b.add(ForthBlock.SWAP);
237                 b.add(ForthBlock.POP);
238             } else {
239                 b.add(THIS);
240                 b.add(ForthBlock.LITERAL, name);
241                 b.add(ForthBlock.GET);
242             }
243             return parseExpr(b, minPrecedence);
244         }
245
246         case DOT: {
247             consume(NAME);
248             String target = string;
249             b.add(b.EXPR, prefix);
250             if (peekToken() == ASSIGN) {
251                 consume(ASSIGN);
252                 ForthBlock val = parseExpr();
253                 b.add(b.LITERAL, target);
254                 b.add(b.EXPR, val);
255                 b.add(b.PUT);
256                 b.add(b.SWAP);
257                 b.add(b.POP);
258             } else {
259                 b.add(b.LITERAL, target);
260                 b.add(b.GET);
261             }
262             return parseExpr(b, minPrecedence);
263         }
264
265         case LB: {
266             if (prefix == null) {
267                 b.add(b.ARRAY, new Integer(0));
268                 int i = 0;
269                 while(true) {
270                     ForthBlock e = parseExpr();
271                     if (e == null && peekToken() == RB) { consume(RB); return parseExpr(b, minPrecedence); }
272                     b.add(b.LITERAL, new Integer(i++));
273                     if (e == null) b.add(b.LITERAL, null);
274                     else b.add(b.EXPR, e);
275                     b.add(b.PUT);
276                     b.add(b.POP);
277                     if (peekToken() == RB) { consume(RB); return parseExpr(b, minPrecedence); }
278                     consume(COMMA);
279                 }
280             } else {
281                 b.add(b.EXPR, prefix);
282                 b.add(b.EXPR, parseExpr());
283                 consume(RB);
284                 if (peekToken() == ASSIGN) {
285                     consume(ASSIGN);
286                     b.add(b.EXPR, parseExpr());
287                     b.add(b.PUT);
288                     b.add(b.SWAP);
289                     b.add(b.POP);
290                 } else {
291                     b.add(b.GET);
292                 }
293                 return parseExpr(b, minPrecedence);
294             }
295         }
296
297         case LC: {
298             b.add(b.OBJECT, null);
299             if (peekToken() == RC) { consume(RC); return parseExpr(b, minPrecedence); }
300             while(true) {
301                 if (peekToken() != NAME && peekToken() != STRING) throw new Error("expected NAME or STRING");
302                 getToken();
303                 b.add(b.LITERAL, string);
304                 consume(COLON);
305                 b.add(b.EXPR, parseExpr());
306                 b.add(b.PUT);
307                 b.add(b.POP);
308                 if (peekToken() == RC) { consume(RC); return parseExpr(b, minPrecedence); }
309                 consume(COMMA);
310                 if (peekToken() == RC) { consume(RC); return parseExpr(b, minPrecedence); }
311             }
312         }
313             
314         case HOOK: {
315             b.add(b.EXPR, prefix);
316             b.add(b.JF, new Integer(3));
317             b.add(b.EXPR, parseExpr());
318             b.add(b.JMP, new Integer(2));
319             consume(COLON);
320             b.add(b.EXPR, parseExpr());
321             return parseExpr(b, minPrecedence);
322         }
323             
324         case Tokens.FUNCTION: {
325             consume(LP);
326             int numArgs = 0;
327             ForthBlock b2 = new ForthBlock(curLine, sourceName);
328             b2.add(THIS);
329             b2.add(b.SWAP);
330             b2.add(b.LITERAL, "arguments");
331             b2.add(b.LITERAL, "arguments");
332             b2.add(b.DECLARE);
333             b2.add(b.SWAP);
334             b2.add(b.PUT);
335             b2.add(b.SWAP);
336             b2.add(b.POP);
337             if (peekToken() == RP) consume(RP);
338             else while(true) {
339                 if (peekToken() == COMMA) {
340                     // FIXME: pop an item off the stack here?
341                     consume(COMMA);
342                 } else {
343                     consume(NAME);
344
345                     // declare the name
346                     b2.add(b.LITERAL, string);
347                     b2.add(b.DECLARE);
348
349                     // retrieve it from the arguments array
350                     b2.add(b.LITERAL, new Integer(numArgs));
351                     b2.add(b.GET_PRESERVE);
352                     b2.add(b.SWAP);
353                     b2.add(b.POP);
354
355                     // put it to the current scope
356                     b2.add(THIS);
357                     b2.add(b.SWAP);
358                     b2.add(b.LITERAL, string);
359                     b2.add(b.SWAP);
360                     b2.add(b.PUT);
361
362                     // clean the stack
363                     b2.add(b.POP);
364                     b2.add(b.POP);
365
366                     if (peekToken() == RP) { consume(RP); break; }
367                     consume(COMMA);
368                 }
369                 numArgs++;
370             }
371             // pop off the arguments array
372             b2.add(b.POP);
373             parseStatement(true, b2);
374             b2.add(b.LITERAL, null);
375             b2.add(RETURN);
376             return parseExpr(b.add(OpCodes.FUNCTION, b2), minPrecedence);
377         }
378             
379         default: pushBackToken(); return prefix;
380         }
381     }
382     
383     /** a block is either a single statement or a list of statements surrounded by curly braces; all expressions are also statements */
384     public ForthBlock parseStatement() throws IOException {
385         ForthBlock ret = new ForthBlock(line, sourceName);
386         ret.add(ret.LITERAL, null);
387         parseStatement(false, ret);
388         if (ret.size() == 1) return null;
389         return ret;
390     }
391
392     public void parseStatement(boolean requireBraces) throws IOException {
393         parseStatement(requireBraces, new ForthBlock(line, sourceName));
394     }
395     public void parseStatement(boolean requireBraces, ForthBlock b) throws IOException {
396         int tok = peekToken();
397         if (tok == -1) return;
398         boolean braced = tok == LC;
399         if (requireBraces && !braced) throw new ParserException("expected {, got " + codeToString[tok]);
400         if (braced) consume(LC);
401         int curLine = line;
402         while(true) {
403             switch(tok = peekToken()) {
404
405             case THROW: case RETURN: case ASSERT: {
406                 getToken();
407                 if (tok == RETURN && peekToken() == SEMI) b.add(b.LITERAL, null);
408                 else b.add(b.EXPR, parseExpr());
409                 consume(SEMI);
410                 b.add(tok);
411                 break;
412             }
413
414             case BREAK: case CONTINUE: {
415                 getToken();
416                 if (peekToken() == NAME) consume(NAME);
417                 b.add(tok, string);
418                 consume(SEMI);
419                 break;
420             }
421                 
422             case SEMI:
423                 consume(SEMI);
424                 if (!braced) return;
425                 break;
426                 
427             case VAR: {
428                 consume(VAR);
429                 b.add(THIS);                               // push the current scope
430                 while(true) {
431                     consume(NAME);
432                     String name = string;
433                     b.add(b.LITERAL, name);                // push the name to be declared
434                     b.add(b.DECLARE);                      // declare it
435                     if (peekToken() == ASSIGN) {           // if there is an '=' after the variable name
436                         b.add(b.LITERAL, name);            // put the var name back on the stack
437                         consume(ASSIGN);
438                         b.add(b.EXPR, parseExpr());
439                         b.add(b.PUT);
440                         b.add(b.POP);
441                     }
442                     if (peekToken() != COMMA) break;
443                     consume(COMMA);
444                 }
445                 b.add(b.POP);
446                 if (peekToken() == SEMI) consume(SEMI);
447                 break;
448             }
449                 
450             case IF: {
451                 consume(IF);
452                 consume(LP);
453                 b.add(b.EXPR, parseExpr());
454                 consume(RP);
455                 b.add(b.JF, new Integer(4));
456                 b.add(b.EXPR, parseStatement());
457                 b.add(b.POP);
458                 b.add(b.JMP, new Integer(3));
459                 if (peekToken() == ELSE) {
460                     consume(ELSE);
461                     b.add(b.EXPR, parseStatement());
462                     b.add(b.POP);
463                 } else {
464                     b.add(b.JMP, new Integer(1));  // nop
465                     b.add(b.JMP, new Integer(1));  // nop
466                 }
467                 break;
468             }
469
470             case WHILE: {
471                 consume(WHILE);
472                 consume(LP);
473                 ForthBlock loop = new ForthBlock(curLine, sourceName);
474                 b.add(loop.LOOP, loop);
475                 
476                 loop.add(loop.POP);
477                 loop.add(loop.EXPR, parseExpr());
478                 loop.add(loop.JT, new Integer(2));
479                 loop.add(Lexer.BREAK);
480                 consume(RP);
481                 parseStatement(false, loop);
482                 
483                 // if we fall out of the end, definately continue
484                 loop.add(CONTINUE);
485                 break;
486             }
487
488             case SWITCH: {
489                 consume(SWITCH);
490                 consume(LP);
491                 ForthBlock loop = new ForthBlock(curLine, sourceName);
492                 b.add(loop.LOOP, loop);
493                 loop.add(loop.EXPR, parseExpr());
494                 consume(RP);
495                 consume(LC);
496                 while(true) {
497                     ForthBlock caseForthBlock;
498                     tok = getToken();
499                     if (tok == CASE) {
500                         loop.add(loop.DUP);
501                         loop.add(loop.EXPR, parseExpr());
502                         loop.add(EQ);
503                         loop.add(loop.JF, new Integer(2));
504                     } else if (tok != DEFAULT) throw new ParserException("expected CASE or DEFAULT");
505                     consume(COLON);
506                     ForthBlock b2 = new ForthBlock(curLine, sourceName);
507                     ForthBlock e1 = null;
508                     while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) {
509                         if ((e1 = parseStatement()) == null) break;
510                         b2.add(b.EXPR, e1);
511                         b2.add(b.POP);
512                     }
513                     loop.add(loop.EXPR, b2);
514                     if (peekToken() == RC) {
515                         consume(RC);
516                         loop.add(BREAK);
517                         break;
518                     }
519                 }
520                 break;
521             }
522                 
523             case DO: {
524                 consume(DO);
525                 ForthBlock loop = new ForthBlock(curLine, sourceName);
526                 b.add(loop.LOOP, loop);
527                 
528                 parseStatement(false, loop);
529                 consume(WHILE);
530                 consume(LP);
531                 loop.add(loop.EXPR, parseExpr());
532                 loop.add(loop.JT, new Integer(2));
533                 loop.add(Lexer.BREAK);
534                 loop.add(Lexer.CONTINUE);
535                 consume(RP);
536                 consume(SEMI);
537                 break;
538             }
539                 
540             case TRY: {
541                 // FIXME: don't just ignore this!
542                 // We deliberately allow you to omit braces in catch{}/finally{} if they are single statements...
543                 consume(TRY);
544                 parseStatement(true, b);
545                 
546                 if (peekToken() == CATCH) {
547                     getToken();
548                     consume(LP);
549                     consume(NAME);
550                     consume(RP);
551                     parseStatement();   // just discard the catchblock
552                 }
553
554                 if (peekToken() == FINALLY) {
555                     consume(FINALLY);
556                     parseStatement(false, b);
557                 }
558                 break;
559             }
560
561         case FOR: {
562             consume(FOR);
563             consume(LP);
564
565             tok = getToken();
566             if (tok == VAR) tok = getToken();
567             String varName = string;
568             boolean forIn = peekToken() == IN;
569             pushBackToken(tok, varName);
570
571             if (forIn) {
572                 consume(NAME);
573                 consume(IN);
574                 b.add(b.EXPR, parseExpr());
575                 b.add(b.PUSHKEYS);
576                 b.add(b.LITERAL, "length");
577                 b.add(b.GET);
578                 consume(RP);
579                 ForthBlock b2 = new ForthBlock(curLine, sourceName);
580                 b.add(b.SCOPE, b2);
581                 b2.add(b.LITERAL, new Integer(1));
582                 b2.add(SUB);
583                 b2.add(b.DUP);
584                 b2.add(b.LITERAL, new Integer(0));
585                 b2.add(LT);
586                 b2.add(b.JT, new Integer(7));
587                 b2.add(b.GET_PRESERVE);
588                 b2.add(b.LITERAL, varName);
589                 b2.add(b.LITERAL, varName);
590                 b2.add(b.DECLARE);
591                 b2.add(b.PUT);
592                 b2.add(b.EXPR, parseStatement());
593                 //b2.add(b.LITERAL, null);
594                 break;
595                 
596             } else {
597                 ForthBlock b2 = new ForthBlock(curLine, sourceName);
598                 b.add(b.SCOPE, b2);
599                 b.add(b.POP);
600
601                 ForthBlock e1 = parseExpr();
602                 if (e1 == null) e1 = new ForthBlock(curLine, sourceName, b.LITERAL, null);
603
604                 b2.add(b.EXPR, e1);
605                 b2.add(b.POP);
606                 consume(SEMI);
607                 ForthBlock e2 = parseExpr();
608                 consume(SEMI);
609                 ForthBlock e3 = parseExpr();
610                 consume(RP);
611
612                 if (e2 == null) e2 = new ForthBlock(curLine, sourceName, b.LITERAL, null);
613                 if (e3 == null) e3 = new ForthBlock(curLine, sourceName, b.LITERAL, null);
614
615                 ForthBlock b3 = new ForthBlock(curLine, sourceName);
616                 b2.add(b.LOOP, b3);
617                 b2.add(b.LITERAL, null);
618                 b3.add(b.JT, new Integer(3));
619                 b3.add(b.EXPR, e3);
620                 b3.add(b.POP);
621                 b3.add(b.EXPR, e2);
622                 b3.add(b.JT, new Integer(2));
623                 b3.add(BREAK);
624                 parseStatement(false, b3);
625                 b3.add(BREAK);
626                 break;
627             }
628         }
629             
630             case NAME: {
631                 consume(NAME);
632                 String name = string;
633                 if (peekToken() == COLON) {
634                     consume(COLON);
635                     b.add(ForthBlock.LABEL, string);
636                     break;
637                 } else {
638                     pushBackToken(NAME, name);
639                     // fall through to default case
640                 }
641             }
642             // fall through
643             case RC:
644                 if (tok == RC && braced) { consume(RC); return; }
645             // fall through
646             default: {
647                 ForthBlock ret = parseExpr();
648                 if (ret == null) return;
649                 b.add(b.EXPR, ret);
650                 b.add(b.POP);
651                 if (peekToken() == SEMI) consume(SEMI);
652                 break;
653             }
654             }
655             
656             if (!braced) return;
657         }
658     }
659     
660     class ParserException extends RuntimeException {
661         public ParserException(String s) { super(sourceName + ":" + line + " " + s); }
662     }
663     
664 }
665