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