2003/06/05 01:17:48
[org.ibex.core.git] / src / org / xwt / js / Parser.java
1 // Copyright 2002 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 = p.parseStatement(false);
23             if (block == null) return;
24             System.out.println(block);
25             if (p.peekToken() == -1) return;
26         }
27     }
28
29
30     // Statics ////////////////////////////////////////////////////////////
31
32     static byte[] precedence = new byte[MAX_TOKEN + 1];
33     static boolean[] isRightAssociative = new boolean[MAX_TOKEN + 1];
34     static {
35         precedence[ASSIGN] = 1;
36         isRightAssociative[ASSIGN] = true;
37         precedence[HOOK] = 2;
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;
52         precedence[LP] = 17;
53         precedence[LB] = 18;
54         precedence[DOT] = 19;
55     }
56
57
58     // Parsing Logic /////////////////////////////////////////////////////////
59
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]));
63     }
64
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;
69
70         int tok = peekToken();
71         if (minPrecedence > 0 && tok < precedence.length && precedence[tok] != 0 &&
72             (isRightAssociative[tok] ? (precedence[tok] < minPrecedence) : (precedence[tok] <= minPrecedence)))
73             return prefix;
74
75         getToken();
76         int curLine = line;
77
78         switch (tok) {
79
80         case VAR: {
81             if (prefix != null) { pushBackToken(); return prefix; }
82             ForthBlock b = new ForthBlock(curLine, sourceName);
83             b.add(OpCodes.THIS);
84             while(true) {
85                 consume(NAME);
86                 String name = string;
87                 b.add(b.LITERAL, name);
88                 b.add(b.DECLARE);
89                 if (peekToken() == ASSIGN) {
90                     b.add(b.LITERAL, name);
91                     consume(ASSIGN);
92                     b.add(b.EXPR, parseExpr());
93                     b.add(b.PUT);
94                     b.add(b.POP);
95                 }
96                 if (peekToken() != COMMA) break;
97                 consume(COMMA);
98             }
99             return parseExpr(b, minPrecedence);
100         }
101
102         case IN: pushBackToken(); return prefix;
103
104         case IF: {
105             if (prefix != null) { pushBackToken(); return prefix; }
106             ForthBlock b = new ForthBlock(curLine, sourceName);
107             consume(LP);
108             b.add(b.EXPR, parseExpr());
109             consume(RP);
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);
114             consume(ELSE);
115             b.add(b.EXPR, parseStatement(false));
116             return parseExpr(b, minPrecedence);
117         }
118
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;
131             {
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]));
135                 b.add(tok);
136                 b.add(b.PUT);
137                 b.add(b.SWAP);
138                 b.add(b.POP);
139                 return parseExpr(b, minPrecedence);
140             }
141
142         case INC: case DEC:
143             if (prefix == null) {
144                 // prefix
145                 ForthBlock b = (ForthBlock)parseExpr(null, precedence[tok]);
146                 b.set(b.size() - 1, tok, new Boolean(true));
147                 return parseExpr(b, minPrecedence);
148             } else {
149                 // postfix
150                 ForthBlock b = (ForthBlock)prefix;
151                 b.set(b.size() - 1, tok, new Boolean(false));
152                 return parseExpr(b, minPrecedence);
153             }
154
155         case LP:
156             if (prefix == null) {  // grouping
157                 ForthBlock b = new ForthBlock(curLine, sourceName, ForthBlock.EXPR, parseExpr());
158                 consume(RP);
159                 return parseExpr(b, minPrecedence);
160
161             } else {  // invocation
162                 ForthBlock b = new ForthBlock(curLine, sourceName);
163                 int i = 0;
164                 b.add(b.EXPR, prefix);
165                 while(peekToken() != RP) {
166                     b.add(b.EXPR, parseExpr());
167                     i++;
168                     if (peekToken() == RP) break;
169                     consume(COMMA);
170                 }
171                 consume(RP);
172                 b.add(b.CALL, new Integer(i));
173                 return parseExpr(b, minPrecedence);
174             }
175
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]));
180             b.add(tok);
181             return parseExpr(b, minPrecedence);
182         }
183
184         case SUB:
185             if (prefix == null && peekToken() == NUMBER) {
186                 getToken();
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]));
196             b.add(tok);
197             return parseExpr(b, minPrecedence);
198         }
199
200             // includes short-circuit logic
201         case OR: case AND: {
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));
207             b.add(b.POP);
208             b.add(b.EXPR, parseExpr(null, precedence[tok]));
209             return parseExpr(b, minPrecedence);
210         }
211
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");
214
215         case NUMBER:
216             if (prefix != null) { pushBackToken(); return prefix; }
217             return parseExpr(new ForthBlock(curLine, sourceName, ForthBlock.LITERAL, number), minPrecedence);
218
219         case STRING:
220             if (prefix != null) { pushBackToken(); return prefix; }
221             return parseExpr(new ForthBlock(curLine, sourceName, ForthBlock.LITERAL, string), minPrecedence);
222
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);
226
227         case COMMA: pushBackToken(); return prefix;
228
229         case Tokens.THIS:
230             if (prefix != null) { pushBackToken(); return prefix; } 
231             return parseExpr(new ForthBlock(curLine, sourceName, OpCodes.THIS, null), minPrecedence);
232
233         case NAME: {
234             if (prefix != null) { pushBackToken(); return prefix; }
235             String name = string;
236             ForthBlock b = new ForthBlock(curLine, sourceName);
237             if (peekToken() == ASSIGN) {
238                 consume(ASSIGN);
239                 b.add(OpCodes.THIS);
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);
246             } else {
247                 b.add(OpCodes.THIS);
248                 b.add(ForthBlock.LITERAL, name);
249                 b.add(ForthBlock.GET);
250                 return parseExpr(parseExpr(b, minPrecedence), minPrecedence);
251             }
252         }
253
254         case DOT: {
255             consume(NAME);
256             String target = string;
257             ForthBlock b = new ForthBlock(curLine, sourceName);
258             b.add(b.EXPR, prefix);
259             if (peekToken() == ASSIGN) {
260                 consume(ASSIGN);
261                 ForthBlock val = parseExpr();
262                 b.add(b.LITERAL, target);
263                 b.add(b.EXPR, val);
264                 b.add(b.PUT);
265                 b.add(b.SWAP);
266                 b.add(b.POP);
267             } else {
268                 b.add(b.LITERAL, target);
269                 b.add(b.GET);
270             }
271             return parseExpr(b, minPrecedence);
272         }
273
274         case LB: {
275             ForthBlock b = new ForthBlock(curLine, sourceName);
276             if (prefix == null) {
277                 b.add(b.ARRAY, new Integer(0));
278                 int i = 0;
279                 while(true) {
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);
285                     b.add(b.PUT);
286                     b.add(b.POP);
287                     if (peekToken() == RB) { consume(RB); return parseExpr(b, minPrecedence); }
288                     consume(COMMA);
289                 }
290             } else {
291                 b.add(b.EXPR, prefix);
292                 b.add(b.EXPR, parseExpr());
293                 consume(RB);
294                 if (peekToken() == ASSIGN) {
295                     consume(ASSIGN);
296                     b.add(b.EXPR, parseExpr());
297                     b.add(b.PUT);
298                     b.add(b.SWAP);
299                     b.add(b.POP);
300                 } else {
301                     b.add(b.GET);
302                 }
303                 return parseExpr(b, minPrecedence);
304             }
305         }
306
307         case LC: {
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); }
312             while(true) {
313                 if (peekToken() != NAME && peekToken() != STRING) throw new Error("expected NAME or STRING");
314                 getToken();
315                 b.add(b.LITERAL, string);
316                 consume(COLON);
317                 b.add(b.EXPR, parseExpr());
318                 b.add(b.PUT);
319                 b.add(b.POP);
320                 if (peekToken() == RC) { consume(RC); return parseExpr(b, minPrecedence); }
321                 consume(COMMA);
322                 if (peekToken() == RC) { consume(RC); return parseExpr(b, minPrecedence); }
323             }
324         }
325             
326         case HOOK: {
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));
332             consume(COLON);
333             b.add(b.EXPR, parseExpr());
334             return parseExpr(b, minPrecedence);
335         }
336             
337         case Tokens.FUNCTION: {
338             if (prefix != null) { pushBackToken(); return prefix; }
339             consume(LP);
340             ForthBlock b = new ForthBlock(curLine, sourceName);
341             int numArgs = 0;
342             b.add(OpCodes.THIS);
343             b.add(b.SWAP);
344             b.add(b.LITERAL, "arguments");
345             b.add(b.LITERAL, "arguments");
346             b.add(b.DECLARE);
347             b.add(b.SWAP);
348             b.add(b.PUT);
349             b.add(b.SWAP);
350             b.add(b.POP);
351             if (peekToken() == RP) consume(RP);
352             else while(true) {
353                 if (peekToken() == COMMA) {
354                     consume(COMMA);
355                 } else {
356                     consume(NAME);
357
358                     // declare the name
359                     b.add(b.LITERAL, string);
360                     b.add(b.DECLARE);
361
362                     // retrieve it from the arguments array
363                     b.add(b.LITERAL, new Integer(numArgs));
364                     b.add(b.GET_PRESERVE);
365                     b.add(b.SWAP);
366                     b.add(b.POP);
367
368                     // put it to the current scope
369                     b.add(OpCodes.THIS);
370                     b.add(b.SWAP);
371                     b.add(b.LITERAL, string);
372                     b.add(b.SWAP);
373                     b.add(b.PUT);
374
375                     // clean the stack
376                     b.add(b.POP);
377                     b.add(b.POP);
378
379                     if (peekToken() == RP) { consume(RP); break; }
380                     consume(COMMA);
381                 }
382                 numArgs++;
383             }
384             // pop off the arguments array
385             b.add(b.POP);
386             parseStatement(true, b);
387             return parseExpr(new ForthBlock(curLine, sourceName, OpCodes.FUNCTION, b), minPrecedence);
388         }
389             
390         case WHILE: {
391             if (prefix != null) { pushBackToken(); return prefix; }
392             consume(LP);
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);
397
398             loop.add(loop.EXPR, parseExpr());
399             loop.add(loop.JT, new Integer(2));
400             loop.add(Lexer.BREAK);
401             consume(RP);
402             parseStatement(false, loop);
403
404             // if we fall out of the end, definately continue
405             loop.add(CONTINUE);
406             return parseExpr(r, minPrecedence);
407         }
408
409         case SWITCH: {
410             if (prefix != null) { pushBackToken(); return prefix; }
411             consume(LP);
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());
417             consume(RP);
418             consume(LC);
419             while(true) {
420                 ForthBlock caseForthBlock;
421                 tok = getToken();
422                 if (tok == CASE) {
423                     loop.add(loop.DUP);
424                     loop.add(loop.EXPR, parseExpr());
425                     loop.add(EQ);
426                     loop.add(loop.JF, new Integer(2));
427                 } else if (tok != DEFAULT) throw new ParserException("expected CASE or DEFAULT");
428                 consume(COLON);
429                 ForthBlock b = new ForthBlock(curLine, sourceName);
430                 while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) {
431                     if ((e1 = parseStatement(false)) == null) break;
432                     b.add(b.EXPR, e1);
433                 }
434                 loop.add(loop.EXPR, b);
435                 if (peekToken() == RC) {
436                     consume(RC);
437                     r.add(BREAK);
438                     return parseExpr(r, minPrecedence);
439                 }
440             }
441         }
442             
443         case DO: {
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);
449
450             parseStatement(false, loop);
451             consume(WHILE);
452             consume(LP);
453             loop.add(loop.EXPR, parseExpr());
454             loop.add(loop.JT, new Integer(2));
455             loop.add(Lexer.BREAK);
456             loop.add(Lexer.CONTINUE);
457             consume(RP);
458             consume(SEMI);
459             return parseExpr(r, minPrecedence);
460         }
461
462         case TRY: {
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);
467             
468             tok = peekToken();
469             if (tok == CATCH) {
470                 getToken();
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 )");
474                 tok = peekToken();
475             }
476             if (tok == FINALLY) getToken();
477
478             return parseExpr(tryBlock, minPrecedence);
479         }
480
481         case FOR: {
482             if (prefix != null) { pushBackToken(); return prefix; }
483             if (getToken() != LP) throw new ParserException("expected left paren");
484
485             tok = getToken();
486             if (tok == VAR) tok = getToken();
487             String varName = string;
488             boolean forIn = peekToken() == IN;
489             pushBackToken(tok, varName);
490
491             ForthBlock b = new ForthBlock(curLine, sourceName);
492             if (forIn) {
493                 consume(NAME);
494                 consume(IN);
495                 b.add(b.EXPR, parseExpr());
496                 b.add(b.PUSHKEYS);
497                 b.add(b.LITERAL, "length");
498                 b.add(b.GET);
499                 consume(RP);
500                 ForthBlock b2 = new ForthBlock(curLine, sourceName);
501                 b.add(b.SCOPE, b2);
502                 b2.add(b.LITERAL, new Integer(1));
503                 b2.add(SUB);
504                 b2.add(b.DUP);
505                 b2.add(b.LITERAL, new Integer(0));
506                 b2.add(LT);
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);
511                 b2.add(b.DECLARE);
512                 b2.add(b.PUT);
513                 b2.add(b.EXPR, parseStatement(false));
514                 b2.add(b.LITERAL, null);
515                 return parseExpr(b, minPrecedence);
516                 
517             } else {
518                 ForthBlock b2 = new ForthBlock(curLine, sourceName);
519                 b.add(b.SCOPE, b2);
520
521                 e1 = parseExpr();
522                 if (e1 == null) e1 = new ForthBlock(curLine, sourceName, b.LITERAL, null);
523
524                 b2.add(b.EXPR, e1);
525                 b2.add(b.POP);
526                 consume(SEMI);
527                 e2 = parseExpr();
528                 consume(SEMI);
529                 e3 = parseExpr();
530                 consume(RP);
531
532                 if (e2 == null) e2 = new ForthBlock(curLine, sourceName, b.LITERAL, null);
533                 if (e3 == null) e3 = new ForthBlock(curLine, sourceName, b.LITERAL, null);
534
535                 ForthBlock b3 = new ForthBlock(curLine, sourceName);
536                 b2.add(b.LOOP, b3);
537                 b2.add(b.LITERAL, null);
538                 b3.add(b.JT, new Integer(3));
539                 b3.add(b.EXPR, e3);
540                 b3.add(b.POP);
541                 b3.add(b.EXPR, e2);
542                 b3.add(b.JT, new Integer(2));
543                 b3.add(BREAK);
544                 parseStatement(false, b3);
545                 return parseExpr(b, minPrecedence);
546             }
547         }
548             
549         default:
550             pushBackToken();
551             return prefix;
552         }
553     }
554     
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);
564         int curLine = line;
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);
569         while(true) {
570             switch(tok = peekToken()) {
571
572             case LC: smt = parseStatement(true); break;
573             case GOTO: throw new ParserException("goto not supported");
574
575             case THROW: case RETURN: case ASSERT: {
576                 getToken();
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());
580                 consume(SEMI);
581                 r.add(tok);
582                 smt = r;
583                 break;
584             }
585
586             case BREAK: case CONTINUE: {
587                 getToken();
588                 if (peekToken() == NAME) consume(NAME);
589                 smt = new ForthBlock(curLine, sourceName, tok, string);
590                 consume(SEMI);
591                 break;
592             }
593                 
594             case RC:
595                 if (braced) consume(RC);
596                 return block.size() == 0 ? null : ret;
597                 
598             case SEMI:
599                 consume(SEMI);
600                 if (!braced) return block.size() == 0 ? null : ret;
601                 continue;
602                 
603             case NAME: {
604                 String name = string;
605                 consume(NAME);
606                 if (peekToken() == COLON) {
607                     consume(COLON);
608                     smt = new ForthBlock(curLine, sourceName, ForthBlock.LABEL, string);
609                     break;
610                 } else {
611                     pushBackToken(NAME, name);
612                     // fall through to default case
613                 }
614             }
615
616             case -1:
617             default:
618                 smt = parseExpr();
619                 if (smt == null) return block.size() == 0 ? null : ret;
620                 if (peekToken() == SEMI) getToken();
621                 break;
622             }
623
624             if (!braced) return smt;
625             block.add(block.EXPR, smt);
626             block.add(block.POP);
627         }
628     }
629     
630     class ParserException extends RuntimeException {
631         public ParserException(String s) { super(sourceName + ":" + line + " " + s); }
632     }
633     
634 }
635