2003/06/04 03:31:42
[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
8 /** parses a stream of lexed tokens into a tree of Expr's */
9 public class Parser extends Lexer {
10
11     // Constructors //////////////////////////////////////////////////////
12
13     public Parser(Reader r, String sourceName, int line) throws IOException {
14         super(r);
15         this.sourceName = sourceName;
16         this.line = line;
17     }
18
19     /** for debugging */
20     public static void main(String[] s) throws Exception {
21         Parser p = new Parser(new InputStreamReader(System.in), "stdin", 0);
22         while(true) {
23             Expr block = p.parseBlock(false);
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 {
36         precedence[ASSIGN] = 1;
37         isRightAssociative[ASSIGN] = true;
38         precedence[HOOK] = 2;
39         precedence[COMMA] = 3;
40         precedence[OR] = precedence[AND] = 4;
41         precedence[GT] = precedence[GE] = 5;
42         precedence[BITOR] = 6;
43         precedence[BITXOR] = 7;
44         precedence[BITAND] = 8;
45         precedence[EQ] = precedence[NE] = 9;
46         precedence[LT] = precedence[LE] = 10;
47         precedence[SHEQ] = precedence[SHNE] = 11;
48         precedence[LSH] = precedence[RSH] = precedence[URSH] = 12;
49         precedence[ADD] = precedence[SUB] = 13;
50         precedence[MUL] = precedence[DIV] = precedence[MOD] = 14;
51         precedence[BITNOT] = precedence[INSTANCEOF] = 15;
52         precedence[INC] = precedence[DEC] = 16;
53         precedence[LP] = 17;
54         precedence[LB] = 18;
55         precedence[DOT] = 19;
56     }
57
58
59     // Parsing Logic /////////////////////////////////////////////////////////
60
61     public void consume(int code) throws IOException {
62         if (getToken() != code)
63             throw new ParserException("expected " + codeToString[op] + ", got " + (op == -1 ? "EOL" : codeToString[op]));
64     }
65
66     /** parses the largest possible expression */
67     public Expr parseMaximalExpr() throws IOException { return parseMaximalExpr(null, -1); }
68     public Expr parseMaximalExpr(Expr prefix, int minPrecedence) throws IOException {
69         while(true) {
70             if (peekToken() == -1) break;
71             Expr save = prefix;
72             prefix = parseSingleExpr(prefix, minPrecedence);
73             if (save == prefix) break;
74             if (prefix == null) throw new ParserException("parseSingleExpr() returned null");
75         }
76         return prefix;
77     }
78
79     public Expr parseSingleExpr(Expr prefix, int minPrecedence) throws IOException {
80         Expr e1 = null, e2 = null, e3 = null, head = null, tail = null, ret = null;
81
82         int tok = peekToken();
83         if (minPrecedence > 0 &&
84             tok < precedence.length &&
85             precedence[tok] != 0 &&
86             (isRightAssociative[tok] ?
87              (precedence[tok] < minPrecedence) :
88              (precedence[tok] <= minPrecedence))) {
89             return prefix;
90              }
91         getToken();
92         int curLine = line;
93
94         // these case arms match the precedence of operators; each arm is a precedence level.
95         switch (tok) {
96
97         case VAR: {
98             if (prefix != null) { pushBackToken(); return prefix; }
99             ByteCode b = new ByteCode(curLine);
100             b.add(b.THIS, NO_ARG);
101             while(true) {
102                 consume(NAME);
103                 String name = string;
104                 b.add(b.DECLARE, name);
105                 if (peekToken() == ASSIGN) {
106                     b.add(b.LITERAL, name);
107                     consume(ASSIGN);
108                     b.add(b.EXPR, parseMaximalExpr());
109                     b.add(b.PUT, NO_ARG);
110                     b.add(b.POP, NO_ARG);
111                 }
112                 if (peekToken() != COMMA) break;
113                 consume(COMMA);
114             }
115             return b;
116         }
117
118         case IN: pushBackToken(); return prefix;
119
120         case IF: {
121             if (prefix != null) { pushBackToken(); return prefix; }
122             ByteCode b = new ByteCode(curLine);
123             consume(LP);
124             b.add(b.EXPR, parseMaximalExpr());
125             consume(RP);
126             b.add(b.JF, new Integer(3));
127             b.add(b.EXPR, parseBlock(false));
128             b.add(b.JMP, new Integer(2));
129             if (peekToken() != ELSE) return b.add(b.LITERAL, null);
130             consume(ELSE);
131             b.add(b.EXPR, parseBlock(false));
132             return b;
133         }
134
135             // FIXME: ugly hack!!
136         case ASSIGN_BITOR: if (tok == ASSIGN_BITOR) tok = BITOR;
137         case ASSIGN_BITXOR: if (tok == ASSIGN_BITXOR) tok = BITXOR;
138         case ASSIGN_BITAND: if (tok == ASSIGN_BITAND) tok = BITAND;
139         case ASSIGN_LSH: if (tok == ASSIGN_LSH) tok = LSH;
140         case ASSIGN_RSH: if (tok == ASSIGN_RSH) tok = RSH;
141         case ASSIGN_URSH: if (tok == ASSIGN_URSH) tok = URSH;
142         case ASSIGN_ADD: if (tok == ASSIGN_ADD) tok = ADD;
143         case ASSIGN_SUB: if (tok == ASSIGN_SUB) tok = SUB;
144         case ASSIGN_MUL: if (tok == ASSIGN_MUL) tok = MUL;
145         case ASSIGN_DIV: if (tok == ASSIGN_DIV) tok = DIV;
146         case ASSIGN_MOD: if (tok == ASSIGN_MOD) tok = MOD;
147             {
148                 ByteCode b = (ByteCode)prefix;
149                 b.set(b.size() - 1, b.GET_PRESERVE, new Boolean(true));
150                 b.add(b.EXPR, parseMaximalExpr(null, precedence[tok]));
151                 b.add(tok, NO_ARG);
152                 b.add(b.PUT, NO_ARG);
153                 b.add(b.SWAP, NO_ARG);
154                 b.add(b.POP, NO_ARG);
155                 return b;
156             }
157
158         case INC: case DEC:
159             if (prefix == null) {
160                 // prefix
161                 ByteCode b = (ByteCode)parseMaximalExpr(null, precedence[tok]);
162                 b.set(b.size() - 1, tok, new Boolean(true));
163                 return b;
164             } else {
165                 // postfix
166                 ByteCode b = (ByteCode)prefix;
167                 b.set(b.size() - 1, tok, new Boolean(false));
168                 return b;
169             }
170
171         case LP:
172             if (prefix == null) {  // grouping
173                 ByteCode b = new ByteCode(curLine, ByteCode.EXPR, parseMaximalExpr());
174                 consume(RP);
175                 return b;
176
177             } else {  // invocation
178                 ByteCode b = new ByteCode(curLine);
179                 int i = 0;
180                 b.add(b.EXPR, prefix);
181                 while(peekToken() != RP) {
182                     b.add(b.EXPR, parseMaximalExpr());
183                     i++;
184                     if (peekToken() == RP) break;
185                     consume(COMMA);
186                 }
187                 consume(RP);
188                 b.add(b.CALL, new Integer(i));
189                 return b;
190             }
191
192         case BANG: case BITNOT: case INSTANCEOF: case TYPEOF: {
193             if (prefix != null) { pushBackToken(); return prefix; }
194             ByteCode b = new ByteCode(curLine);
195             b.add(b.EXPR, parseMaximalExpr(null, precedence[tok]));
196             b.add(tok, NO_ARG);
197             return b;
198         }
199
200         case SUB:
201             if (prefix == null && peekToken() == NUMBER) {
202                 getToken();
203                 return new ByteCode(curLine, ByteCode.LITERAL, new Double(number.doubleValue() * -1));
204             } // else fall through
205         case BITOR: case BITXOR: case BITAND: case SHEQ: case SHNE: case LSH:
206         case RSH: case URSH: case ADD: case MUL: case DIV: case MOD:
207         case GT: case GE: case EQ: case NE: case LT: case LE: {
208             if (prefix == null) throw new ParserException("the " + codeToString[tok] + " token cannot start an expression");
209             ByteCode b = new ByteCode(curLine);
210             b.add(b.EXPR, prefix);
211             b.add(b.EXPR, parseMaximalExpr(null, precedence[tok]));
212             b.add(tok, NO_ARG);
213             return b;
214         }
215
216             // includes short-circuit logic
217         case OR: case AND: {
218             if (prefix == null) throw new ParserException("the " + codeToString[tok] + " token cannot start an expression");
219             ByteCode b = new ByteCode(curLine);
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, NO_ARG);
224             b.add(b.EXPR, parseMaximalExpr(null, precedence[tok]));
225             return b;
226         }
227
228         case ASSIGN: {
229             if (prefix == null) throw new ParserException("the " + codeToString[tok] + " token cannot start an expression");
230             ByteCode b = new ByteCode(curLine);
231             b.add(b.THIS, NO_ARG);
232             b.add(b.LITERAL, prefix.string);   // FIXME, this is ass-ugly
233             b.add(b.EXPR, parseMaximalExpr(null, precedence[ASSIGN]));
234             b.add(b.PUT, NO_ARG);
235             b.add(b.SWAP, NO_ARG);
236             b.add(b.POP, NO_ARG);
237             return b;
238         }
239
240         case WITH: throw new ParserException("XWT does not allow the WITH keyword");
241         case VOID: case RESERVED: throw new ParserException("reserved word that you shouldn't be using");
242
243         case NUMBER:
244             if (prefix != null) { pushBackToken(); return prefix; }
245             return new ByteCode(curLine, ByteCode.LITERAL, number);
246
247         case STRING:
248             if (prefix != null) { pushBackToken(); return prefix; }
249             return new ByteCode(curLine, ByteCode.LITERAL, string);
250
251         case NULL: case TRUE: case FALSE: case NOP:
252             if (prefix != null) { pushBackToken(); return prefix; }
253             return new ByteCode(curLine, ByteCode.LITERAL, (tok == NULL || tok == NOP) ? null : new Boolean(tok == TRUE));
254
255         case COMMA: pushBackToken(); return prefix;
256
257         case THIS:
258             if (prefix != null) { pushBackToken(); return prefix; } 
259             return new ByteCode(curLine, ByteCode.THIS, NO_ARG);
260
261             // potential lvalues
262
263         case NAME: {
264             if (prefix != null) { pushBackToken(); return prefix; }
265             String name = string;
266             ByteCode b = new ByteCode(curLine);
267             if (peekToken() == ASSIGN) {
268                 consume(ASSIGN);
269                 b.add(ByteCode.THIS, NO_ARG);
270                 b.add(ByteCode.LITERAL, name);
271                 b.add(ByteCode.EXPR, parseMaximalExpr(null, minPrecedence));
272                 b.add(ByteCode.PUT, NO_ARG);
273                 b.add(ByteCode.SWAP, NO_ARG);
274                 b.add(ByteCode.POP, NO_ARG);
275                 return b;
276             } else {
277                 b.add(ByteCode.THIS, NO_ARG);
278                 b.add(ByteCode.LITERAL, name);
279                 b.add(ByteCode.GET, NO_ARG);
280                 return parseMaximalExpr(b, minPrecedence);
281             }
282         }
283
284         case DOT: {
285             consume(NAME);
286             String target = string;
287             ByteCode b = new ByteCode(curLine);
288             b.add(b.EXPR, prefix);
289             if (peekToken() == ASSIGN) {
290                 consume(ASSIGN);
291                 Expr val = parseMaximalExpr();
292                 b.add(b.LITERAL, target);
293                 b.add(b.EXPR, val);
294                 b.add(b.PUT, NO_ARG);
295                 b.add(b.SWAP, NO_ARG);
296                 b.add(b.POP, NO_ARG);
297             } else {
298                 b.add(b.LITERAL, target);
299                 b.add(b.GET, NO_ARG);
300             }
301             return b;
302         }
303
304         case LB: {
305             ByteCode b = new ByteCode(curLine);
306             if (prefix == null) {
307                 b.add(b.ARRAY, new Integer(0));
308                 int i = 0;
309                 while(true) {
310                     Expr e = parseMaximalExpr();
311                     if (e == null && peekToken() == RB) { consume(RB); return b; }
312                     b.add(b.LITERAL, new Integer(i++));
313                     if (e == null) b.add(b.LITERAL, null);
314                     else b.add(b.EXPR, e);
315                     b.add(b.PUT, NO_ARG);
316                     b.add(b.POP, NO_ARG);
317                     if (peekToken() == RB) { consume(RB); return b; }
318                     consume(COMMA);
319                 }
320             } else {
321                 b.add(b.EXPR, prefix);
322                 b.add(b.EXPR, parseMaximalExpr());
323                 consume(RB);
324                 if (peekToken() == ASSIGN) {
325                     consume(ASSIGN);
326                     b.add(b.EXPR, parseMaximalExpr());
327                     b.add(b.PUT, NO_ARG);
328                     b.add(b.SWAP, NO_ARG);
329                     b.add(b.POP, NO_ARG);
330                 } else {
331                     b.add(b.GET, NO_ARG);
332                 }
333                 return b;
334             }
335         }
336
337         case LC: {
338             if (prefix != null) { pushBackToken(); return prefix; }
339             ByteCode b = new ByteCode(curLine);
340             b.add(b.OBJECT, null);
341             if (peekToken() == RC) { consume(RC); return b; }
342             while(true) {
343                 if (peekToken() != NAME && peekToken() != STRING) throw new Error("expected NAME or STRING");
344                 getToken();
345                 b.add(b.LITERAL, string);
346                 consume(COLON);
347                 b.add(b.EXPR, parseMaximalExpr());
348                 b.add(b.PUT, NO_ARG);
349                 b.add(b.POP, NO_ARG);
350                 if (peekToken() == RC) { consume(RC); return b; }
351                 consume(COMMA);
352                 if (peekToken() == RC) { consume(RC); return b; }
353             }
354         }
355             
356         case HOOK: {
357             ByteCode b = new ByteCode(curLine);
358             b.add(b.EXPR, prefix);
359             b.add(b.JF, new Integer(3));
360             b.add(b.EXPR, parseMaximalExpr());
361             b.add(b.JMP, new Integer(2));
362             consume(COLON);
363             b.add(b.EXPR, parseMaximalExpr());
364             return b;
365         }
366             
367         case FUNCTION: {
368             if (prefix != null) { pushBackToken(); return prefix; }
369             consume(LP);
370             ByteCode b = new ByteCode(curLine);
371             int numArgs = 0;
372             b.add(b.THIS, NO_ARG);
373             b.add(b.SWAP, NO_ARG);
374             b.add(b.LITERAL, "arguments");
375             b.add(b.LITERAL, "arguments");
376             b.add(b.DECLARE, NO_ARG);
377             b.add(b.SWAP, NO_ARG);
378             b.add(b.PUT, NO_ARG);
379             b.add(b.SWAP, NO_ARG);
380             b.add(b.POP, NO_ARG);
381             if (peekToken() == RP) consume(RP);
382             else while(true) {
383                 if (peekToken() == COMMA) {
384                     consume(COMMA);
385                 } else {
386                     consume(NAME);
387
388                     // declare the name
389                     b.add(b.LITERAL, string);
390                     b.add(b.DECLARE, NO_ARG);
391
392                     // retrieve it from the arguments array
393                     b.add(b.LITERAL, new Integer(numArgs));
394                     b.add(b.GET_PRESERVE, NO_ARG);
395                     b.add(b.SWAP, NO_ARG);
396                     b.add(b.POP, NO_ARG);
397
398                     // put it to the current scope
399                     b.add(b.THIS, NO_ARG);
400                     b.add(b.SWAP, NO_ARG);
401                     b.add(b.LITERAL, string);
402                     b.add(b.SWAP, NO_ARG);
403                     b.add(b.PUT, NO_ARG);
404
405                     // clean the stack
406                     b.add(b.POP, NO_ARG);
407                     b.add(b.POP, NO_ARG);
408
409                     if (peekToken() == RP) { consume(RP); break; }
410                     consume(COMMA);
411                 }
412                 numArgs++;
413             }
414             // pop off the arguments array
415             b.add(b.POP, NO_ARG);
416             parseBlock(true, b);
417             return new ByteCode(curLine, b.FUNCTION, b);
418         }
419             
420         case WHILE: {
421             if (prefix != null) { pushBackToken(); return prefix; }
422             consume(LP);
423             ByteCode r = new ByteCode(curLine);
424             ByteCode loop = new ByteCode(curLine);
425             r.add(loop.LOOP, loop);
426             r.add(r.LITERAL, null);
427
428             loop.add(loop.EXPR, parseMaximalExpr());
429             loop.add(loop.JT, new Integer(2));
430             loop.add(Lexer.BREAK, NO_ARG);
431             consume(RP);
432             parseBlock(false, loop);
433
434             // if we fall out of the end, definately continue
435             loop.add(CONTINUE, NO_ARG);
436             return r;
437         }
438
439         case SWITCH: {
440             if (prefix != null) { pushBackToken(); return prefix; }
441             consume(LP);
442             ByteCode r = new ByteCode(curLine);
443             ByteCode loop = new ByteCode(curLine);
444             r.add(loop.LOOP, loop);
445             r.add(r.LITERAL, null);
446             loop.add(loop.EXPR, parseMaximalExpr());
447             consume(RP);
448             consume(LC);
449             while(true) {
450                 Expr caseExpr;
451                 tok = getToken();
452                 if (tok == CASE) {
453                     loop.add(loop.DUP, NO_ARG);
454                     loop.add(loop.EXPR, parseMaximalExpr());
455                     loop.add(EQ, NO_ARG);
456                     loop.add(loop.JF, new Integer(2));
457                 } else if (tok != DEFAULT) throw new ParserException("expected CASE or DEFAULT");
458                 consume(COLON);
459                 ByteCode b = new ByteCode(curLine);
460                 while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) {
461                     if ((e1 = parseBlock(false)) == null) break;
462                     b.add(b.EXPR, e1);
463                 }
464                 loop.add(loop.EXPR, b);
465                 if (peekToken() == RC) {
466                     consume(RC);
467                     r.add(BREAK, NO_ARG);
468                     return r;
469                 }
470             }
471         }
472             
473         case DO: {
474             if (prefix != null) { pushBackToken(); return prefix; }
475             ByteCode r = new ByteCode(curLine);
476             ByteCode loop = new ByteCode(curLine);
477             r.add(loop.LOOP, loop);
478             r.add(r.LITERAL, null);
479
480             parseBlock(false, loop);
481             consume(WHILE);
482             consume(LP);
483             loop.add(loop.EXPR, parseMaximalExpr());
484             loop.add(loop.JT, new Integer(2));
485             loop.add(Lexer.BREAK, NO_ARG);
486             loop.add(Lexer.CONTINUE, NO_ARG);
487             consume(RP);
488             consume(SEMI);
489             return r;
490         }
491
492             // Needs break //
493
494         case FOR:
495             if (prefix != null) { pushBackToken(); return prefix; }
496             if (getToken() != LP) throw new ParserException("expected left paren");
497             e1 = parseMaximalExpr();
498             if (peekToken() == IN) {
499                 getToken();
500                 e2 = parseMaximalExpr();
501                 if (getToken() != RP) throw new ParserException("expected right paren");
502                 return new Expr(curLine, FOR, new Expr(curLine, IN, e1.left, e2), parseBlock(false));
503                 
504             } else {
505                 Expr initExpr = e1;
506                 if (initExpr == null) initExpr = new Expr(curLine, NULL);
507                 consume(SEMI);
508                 Expr whileExpr = parseMaximalExpr();
509                 consume(SEMI);
510                 Expr incExpr = parseMaximalExpr();
511                 consume(RP);
512                 Expr body = parseBlock(false);
513                 Expr loop = new Expr(curLine, WHILE, whileExpr, body);
514                 ExprList list = new ExprList(curLine, LC);
515                 list.add(initExpr);
516                 ExprList list2 = new ExprList(curLine, WHILE);
517                 list.add(list2);
518                 list2.add(whileExpr);
519                 list2.add(body);
520                 list2.add(incExpr);
521                 return list;
522             }
523             
524         case TRY: {
525             // We deliberately allow you to omit braces in catch{}/finally{} if they are single statements...
526             if (prefix != null) { pushBackToken(); return prefix; }
527             Expr tryBlock = parseBlock(true);
528             
529             tok = peekToken();
530             ExprList list = new ExprList(curLine, TRY);
531             if (tok == CATCH) {
532                 getToken();
533                 if (getToken() != LP) throw new ParserException("expected (");
534                 if (getToken() != NAME) throw new ParserException("expected name");
535                 Expr name = new Expr(curLine, NAME, string);
536                 if (getToken() != RP) throw new ParserException("expected )");
537                 list.add(new Expr(curLine, CATCH, name, parseBlock(false)));
538                 tok = peekToken();
539             }
540             if (tok == FINALLY) {
541                 getToken();
542                 list.add(new Expr(curLine, FINALLY, parseBlock(false)));
543             }
544
545             if (list.size() == 0) throw new ParserException("try without catch or finally");
546             return new Expr(curLine, TRY, tryBlock, list);
547         }
548
549         default:
550             pushBackToken();
551             return prefix;
552         }
553     }
554     
555     // Expr //////////////////////////////////////////////////////////////////////
556
557     public static final Object NO_ARG = new Object();
558
559     class ByteCode extends Expr {
560
561         // This class interprets a small forthlike bytecode language
562         // we use to represent JavaScript.  It's a stack machine.
563
564         // Each instruction is an opcode and a literal.  Any operation
565         // that accesses the top of the stack and does not have a
566         // mandatory literal can be performed on a literal instead of
567         // the top of the stack.
568
569         // opcodes:
570         public static final byte arithmetic = -1;  //                               -- arithmetic operators from parser
571         public static final byte LITERAL = -2;     // < String | Number | null >    -- push a literal onto the stack
572         public static final byte ARRAY = -3;       // < size >                      -- create a new array of size <size>
573         public static final byte OBJECT = -4;      //                               -- push an empty object onto the stack
574         public static final byte FUNCTION = -5;    // < bytecode_block >            -- push a new instance of a function with the given bytecode
575         public static final byte DECLARE = -6;     // < name >                      -- declare <name> in the current scope
576         public static final byte THIS = -7;        //                               -- push the topmost non-transparent scope onto the stack
577         public static final byte GET = -8;         //                               -- get stack[0] from stack[1]
578         public static final byte GET_PRESERVE = -80;         //                               -- get stack[0] from stack[1]
579         public static final byte PUT = -9;         //                               -- put stack[1] to key stack[0] on stack[2]; leaves object on the stack
580         public static final byte THROW = -10;      //                               -- throw topmost element
581         public static final byte RETURN = -11;     //                               -- return the topmost value on the stack
582         public static final byte ASSERT = -12;     //                               -- fail if topmost element is not true
583         public static final byte JT = -13;         // < relative_address >          -- pop the stack; if true, jump to <relative_address>
584         public static final byte JF = -21;         // < relative_address >          -- pop the stack; if false, jump to <relative_address>
585         public static final byte JMP = -22;        // < relative_address >          -- jump to <relative_address>
586         static public final byte POP = -14;        //                               -- discard the top element on the stack
587
588         public static final byte CALL = -15;       // < numargs >                   -- call stack[0] with the topmost <numargs> values as arguments
589         public static final byte TRY = -16;        // < bytecode_block >            -- run the block pointed to; returns with thrown exn on top of stack
590
591         public static final byte INSTANCEOF = -17; //                               -- ??
592         public static final byte TYPEOF = -18;     //                               --
593
594         public static final byte FOR__IN = -19;    //                               -- ??
595         public static final byte EXPR = -20;       //                               -- transitional
596         public static final byte SWAP = -23;       //                               -- transitional
597         public static final byte SCOPE = -30;       //                               -- transitional
598         public static final byte LOOP = -40;       //                               -- transitional
599         public static final byte DUP = -50;       //                               -- transitional
600
601         int[] op = new int[10];
602         Object[] arg = new Object[10];
603         int size = 0;
604
605         public ByteCode(int line) { super(line, "foo"); }
606         public ByteCode(int line, int op_, Object arg_) { this(line); add(op_, arg_); }
607
608         public int size() { return size; }
609         public void set(int pos, int op_, Object arg_) { op[pos] = op_; arg[pos] = arg_; }
610         public ByteCode add(int op_, Object arg_) {
611             if (size == op.length - 1) {
612                 int[] op2 = new int[op.length * 2]; System.arraycopy(op, 0, op2, 0, op.length); op = op2;
613                 Object[] arg2 = new Object[op.length * 2]; System.arraycopy(arg, 0, arg2, 0, arg.length); arg = arg2;
614             }
615             op[size] = op_;
616             arg[size] = arg_;
617             size++;
618             return this;
619         }
620         
621         public Object eval(final JS.Scope s) throws ControlTransferException, JS.Exn {
622             return eval(s, new Parser.Thread());
623         }
624         public Object eval(final JS.Scope s, Parser.Thread t) throws ControlTransferException {
625             for(int i=0; i<size; i++)
626                 switch(op[i]) {
627                 case arithmetic: break;
628                 case LITERAL:
629                     t.push(arg[i]);
630                     break;
631                 case OBJECT: t.push(new JS.Obj()); break;
632                 case ARRAY: t.push(new JS.Array(toNumber(arg[i]).intValue())); break;
633                 case DECLARE: s.declare(arg[i] == NO_ARG ? (String)t.pop() : (String)arg[i]); break;
634                 case THIS: t.push(s); break;   // FIXME: transparents
635                 case ASSERT: if (!toBoolean(t.pop())) throw new EvaluatorException("assertion failed"); break;
636                 case THROW: throw new JS.Exn(t.pop());
637                 case JT: if (toBoolean(t.pop())) i += toNumber(arg[i]).intValue() - 1; break;
638                 case JF: if (!toBoolean(t.pop())) i += toNumber(arg[i]).intValue() - 1; break;
639                 case JMP: i += toNumber(arg[i]).intValue() - 1; break;
640                 case POP: t.pop(); break;
641                 case SWAP: t.swap(); break;
642                 case DUP: t.push(t.peek()); break;
643                     
644                 case Lexer.BREAK: {
645                     // FIXME: make sure this can only appear in proper places
646                     return Boolean.FALSE;
647                 }
648                 case Lexer.CONTINUE: {
649                     // FIXME: make sure this can only appear in proper places
650                     return Boolean.TRUE;
651                 }
652                 case LOOP: {
653                     ByteCode loop = (ByteCode)arg[i];
654                     Parser.Thread t2 = new Parser.Thread();
655                     t2.push(Boolean.TRUE);
656                     while (true) {
657                         Boolean result;
658                         try {
659                             result = (Boolean)loop.eval(new JS.Scope(s), t2);
660                         } catch (ContinueException c) {
661                             result = Boolean.TRUE;
662                         } catch (BreakException b) {
663                             result = Boolean.FALSE;
664                         }
665                         if (result == Boolean.FALSE) break;
666                         t2 = new Parser.Thread();
667                         t2.push(Boolean.FALSE);
668                     }
669                     break;
670                 }
671
672                 case PUT: {
673                     Object val = arg[i] == NO_ARG ? t.pop() : arg[i];
674                     Object key = t.pop();
675                     ((JS)t.peek()).put(key, val);
676                     t.push(val);
677                     break;
678                 }
679
680                 case GET: {
681                     Object v = arg[i] == NO_ARG ? t.pop() : arg[i];
682                     Object o = t.pop();
683                     t.push(doGet(o, v));
684                     break;
685                 }
686                 case GET_PRESERVE: {
687                     Object v = t.pop();
688                     Object o = t.peek();
689                     t.push(v);
690                     t.push(doGet(o, v));
691                     break;
692                 }
693                 case NOP: break;
694                     
695                 case EXPR: t.push(((Expr)arg[i]).eval(s)); break;
696                 case SCOPE: t.push(((Expr)arg[i]).eval(new JS.Scope(s))); break;
697                     
698                 case CALL:
699                     JS.Array arguments = new JS.Array();
700                     int numArgs = toNumber(arg[i]).intValue();
701                     arguments.setSize(numArgs);
702                     for(int j=numArgs - 1; j >= 0; j--) arguments.setElementAt(t.pop(), j);
703                     JS.Function f = (JS.Function)t.pop();
704                     if (f == null) throw new JS.Exn(new EvaluatorException("attempted to call null"));
705                     t.push(f.call(arguments));
706                     break;
707
708                 case FUNCTION: {
709                     final ByteCode myBytes = (ByteCode)arg[i];
710                     t.push(new JS.Function() {
711                         public String toString() { return sourceName + ":" + line; }
712                         public String getSourceName() throws JS.Exn { return sourceName; }
713                         public Object _call(final JS.Array args) throws JS.Exn {
714                             Function save = JS.getCurrentFunction();
715                             JS.currentFunction.put(java.lang.Thread.currentThread(), this);
716                             JS.Scope scope = new JS.Scope(s) {
717                                     // FIXME
718                                     public String getSourceName() { return sourceName; }
719                                     public Object get(Object key) throws JS.Exn {
720                                         if (key.equals("trapee")) return org.xwt.Trap.currentTrapee();
721                                         else if (key.equals("cascade")) return org.xwt.Trap.cascadeFunction;
722                                         return super.get(key);
723                                     }
724                                 };
725                             Parser.Thread t0 = new Parser.Thread();
726                             t0.push(args);
727                             try {
728                                 return myBytes.eval(scope, t0);
729                             } catch (ReturnException r) {
730                                 return r.retval;
731                             } catch (ControlTransferException c) {
732                                 throw new EvaluatorException("error, ControlTransferException tried to leave a function: " + c);
733                             } finally {
734                                 if (save == null) JS.currentFunction.remove(java.lang.Thread.currentThread());
735                                 else JS.currentFunction.put(java.lang.Thread.currentThread(), save);
736                             }
737                         }
738                     });
739                     break;
740                 }
741
742                 case RETURN: break;
743                 case TRY: break;
744                 case INSTANCEOF: break;
745                 case TYPEOF: break;
746                 case FOR__IN: break;
747
748                 case Lexer.BITNOT: t.push(new Long(~toLong(t.pop()))); break;
749                 case Lexer.BANG: t.push(new Boolean(!toBoolean(t.pop()))); break;
750
751                 case Lexer.INC: case Lexer.DEC: {
752                     boolean isPrefix = toBoolean(arg[i]);
753                     Object key = t.pop();
754                     JS obj = (JS)t.pop();
755                     Number num = toNumber(obj.get(key));
756                     Number val = new Double(op[i] == Lexer.INC ? num.doubleValue() + 1.0 : num.doubleValue() - 1.0);
757                     obj.put(key, val);
758                     t.push(isPrefix ? val : num);
759                     break;
760                 }
761
762                 default: {
763                     Object right = t.pop();
764                     Object left = t.pop();
765                     switch(op[i]) {
766
767                     case Lexer.BITOR: t.push(new Long(toLong(left) | toLong(right))); break;
768                     case Lexer.BITXOR: t.push(new Long(toLong(left) ^ toLong(right))); break;
769                     case Lexer.BITAND: t.push(new Long(toLong(left) & toLong(right))); break;
770
771                     case Lexer.ADD: {
772                         Object l = left;
773                         Object r = right;
774                         if (l instanceof String || r instanceof String) {
775                             if (l == null) l = "null";
776                             if (r == null) r = "null";
777                             if (l instanceof Number && ((Number)l).doubleValue() == ((Number)l).longValue())
778                                 l = new Long(((Number)l).longValue());
779                             if (r instanceof Number && ((Number)r).doubleValue() == ((Number)r).longValue())
780                                 r = new Long(((Number)r).longValue());
781                             t.push(l.toString() + r.toString()); break;
782                         }
783                         t.push(new Double(toDouble(l) + toDouble(r))); break;
784                     }
785                         
786                     case Lexer.SUB: t.push(new Double(toDouble(left) - toDouble(right))); break;
787                     case Lexer.MUL: t.push(new Double(toDouble(left) * toDouble(right))); break;
788                     case Lexer.DIV: t.push(new Double(toDouble(left) / toDouble(right))); break;
789                     case Lexer.MOD: t.push(new Double(toDouble(left) % toDouble(right))); break;
790                         
791                     case Lexer.LSH: t.push(new Long(toLong(left) << toLong(right))); break;
792                     case Lexer.RSH: t.push(new Long(toLong(left) >> toLong(right))); break;
793                     case Lexer.URSH: t.push(new Long(toLong(left) >>> toLong(right))); break;
794                         
795                         // FIXME: these need to work on strings
796                     case Lexer.LT: t.push(toDouble(left) < toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break;
797                     case Lexer.LE: t.push(toDouble(left) <= toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break;
798                     case Lexer.GT: t.push(toDouble(left) > toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break;
799                     case Lexer.GE: t.push(toDouble(left) >= toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break;
800                     
801                     case Lexer.EQ:
802                     case Lexer.NE: {
803                         // FIXME: should use Javascript coercion-equality rules
804                         Object l = left;
805                         Object r = right;
806                         boolean ret;
807                         if (l == null) { Object tmp = r; r = l; l = tmp; }
808                         if (l == null && r == null) ret = true;
809                         else if (l instanceof Boolean) ret = new Boolean(toBoolean(r)).equals(l);
810                         else if (l instanceof Number) ret = toNumber(r).doubleValue() == toNumber(l).doubleValue();
811                         else if (l instanceof String) ret = r != null && l.equals(r.toString());
812                         else ret = l.equals(r);
813                         t.push(new Boolean(op[i] == Lexer.EQ ? ret : !ret)); break;
814                     }
815
816                     default: throw new Error("unknown opcode " + op[i]);
817                     } }
818                 }
819             if (t.size() != 1) {
820                 throw new EvaluatorException("eval() terminated with " + t.size() + " elements on the stack; one expected");
821             }
822             return t.pop();
823         }
824
825         public Object doGet(final Object o, final Object v) {
826             if (o == null) throw new EvaluatorException("tried to get property \"" + v + "\" from the null value");
827             if (o instanceof String) {
828                 if (v.equals("length")) return new Integer(((String)o).length());
829                 else if (v.equals("substring")) return new JS.Function() {
830                         public Object _call(JS.Array args) {
831                             if (args.length() == 1) return ((String)o).substring(toNumber(args.elementAt(0)).intValue());
832                             else if (args.length() == 2) return ((String)o).substring(toNumber(args.elementAt(0)).intValue(),
833                                                                                       toNumber(args.elementAt(1)).intValue());
834                             else throw new Error("String.substring() can only take one or two arguments");
835                         }
836                     };
837                 else if (v.equals("toLowerCase")) return new JS.Function() {
838                         public Object _call(JS.Array args) {
839                             return ((String)o).toLowerCase();
840                         } };
841                 else if (v.equals("toUpperCase")) return new JS.Function() {
842                         public Object _call(JS.Array args) {
843                             return ((String)o).toString().toUpperCase();
844                         } };
845                 else if (v.equals("charAt")) return new JS.Function() {
846                         public Object _call(JS.Array args) {
847                             return ((String)o).charAt(toNumber(args.elementAt(0)).intValue()) + "";
848                         } };
849                 else if (v.equals("lastIndexOf")) return new JS.Function() {
850                         public Object _call(JS.Array args) {
851                             if (args.length() != 1) return null;
852                             return new Integer(((String)o).lastIndexOf(args.elementAt(0).toString()));
853                         } };
854                 else if (v.equals("indexOf")) return new JS.Function() {
855                         public Object _call(JS.Array args) {
856                             if (args.length() != 1) return null;
857                             return new Integer(((String)o).indexOf(args.elementAt(0).toString()));
858                         } };
859                 throw new Error("Not Implemented: propery " + v + " on String objects");
860             } else if (o instanceof Boolean) {
861                 throw new Error("Not Implemented: properties on Boolean objects");
862             } else if (o instanceof Number) {
863                 Log.log(this, "Not Implemented: properties on Number objects");
864                 return null;
865                 //throw new Error("Not Implemented: properties on Number objects");
866             } else if (o instanceof JS) {
867                 return ((JS)o).get(v);
868             }
869             return null;
870         }
871     }
872
873     class Thread {
874         public Object[] os = new Object[256];
875         private int size = 0;
876         public void push(Object o) {
877             os[size++] = o;
878         }
879         public Object pop() {
880             return os[--size];
881         }
882         public Object peek() { return os[size - 1]; }
883         public void swap() { Object temp = os[size - 1]; os[size - 1] = os[size - 2]; os[size - 2] = temp; }
884         public int size() { return size; }
885     }
886
887     class ExprList extends Expr {
888         Vec v = new Vec();
889         public ExprList(int curLine, int code) { super(curLine, code); }
890         public void add(Expr e) { v.addElement(e); }
891         public int numExprs() { return v.size(); }
892         public int size() { return v.size(); }
893         public Expr elementAt(int i) { return (Expr)v.elementAt(i); }
894         public Object eval(final JS.Scope s) throws ControlTransferException, JS.Exn {
895             switch(code) {
896             case LC: {
897                 // Block
898                 JS.Scope scope = new JS.Scope(s);
899                 for(int i=0; i<v.size(); i++) ((Expr)v.elementAt(i)).eval(scope);
900                 return null;
901             }
902             case Lexer.LB: {
903                 // Array ctor
904                 JS.Array ret = new JS.Array();
905                 for(int i=0; i<numExprs(); i++) ret.addElement(elementAt(i).eval(s));
906                 return ret;
907             }
908             case Lexer.RC: {
909                 // Object ctor
910                 JS.Obj ret = new JS.Obj();
911                 for(int i=0; i<numExprs(); i++) {
912                     Expr e = elementAt(i);
913                     Object key = e.left.string;
914                     Object val = e.right.eval(s);
915                     ret.put(key, val);
916                 }
917                 return ret;
918             }
919             case Lexer.WHILE:
920             case Lexer.DO:
921                 try {
922                     boolean first = true;
923                     Object bogus = null;
924                     ExprList list = this;
925                     Expr whileExpr = list.elementAt(0);
926                     Expr bodyExpr = list.elementAt(1);
927                     Expr incExpr = list.elementAt(2);
928                     for(;(first && code == Lexer.DO) || toBoolean(whileExpr.eval(s)); bogus = incExpr.eval(s)) {
929                         first = false;
930                         try {
931                             bodyExpr.eval(s);
932                         } catch (ContinueException c) {
933                             if (c.label == null || c.label.equals(string)) continue;
934                         } catch (BreakException b) {
935                             if (b.label == null || b.label.equals(string)) return null;
936                             throw (BreakException)b.fillInStackTrace();
937                         }
938                     }
939                 } catch (BreakException e) {
940                     if (e.label != null && !e.label.equals(string)) throw e;
941                 }
942                 return null;
943             case Lexer.VAR:
944                 for(int i=0; i<size(); i++) {
945                     Expr e = elementAt(i);
946                     s.declare(e.left.string);
947                     if (e.right != null) e.right.eval(s);
948                 }
949                 return null;
950             default: throw new Error("augh!");
951             }
952         }
953     }
954     
955     /** a block is either a single statement or a list of statements surrounded by curly braces; all expressions are also statements */
956     public Expr parseBlock(boolean requireBraces) throws IOException { return parseBlock(requireBraces, null); }
957     public Expr parseBlock(boolean requireBraces, ByteCode b) throws IOException {
958         Expr smt = null;
959         int tok = peekToken();
960         if (tok == -1) return null;
961         boolean braced = tok == LC;
962         if (requireBraces && !braced) throw new ParserException("expected {, got " + codeToString[tok]);
963         if (braced) consume(LC);
964         int curLine = line;
965         ByteCode ret = new ByteCode(curLine);
966         ByteCode block = b == null ? new ByteCode(curLine) : b;
967         block.add(ret.LITERAL, Boolean.TRUE);
968         ret.add(block.SCOPE, block);
969         while(true) {
970             switch(tok = peekToken()) {
971
972             case LC:
973                 smt = parseBlock(true); break;
974
975             case THROW: case RETURN: case ASSERT:
976                 getToken();
977                 if (peekToken() == SEMI) {
978                     if (tok == THROW || tok == ASSERT) throw new ParserException(codeToString[tok] + " requires an argument");
979                     consume(SEMI);
980                     smt = new Expr(curLine, tok);
981                 } else {
982                     smt = new Expr(curLine, tok, parseMaximalExpr());
983                     consume(SEMI);
984                 }
985                 break;
986
987             case GOTO: case BREAK: case CONTINUE: {
988                 getToken();
989                 int t = peekToken();
990                 if (t == NAME) {
991                     getToken();
992                     smt = new Expr(curLine, tok, new Expr(curLine, string));
993                 } else if (tok == GOTO) {
994                     throw new ParserException("goto must be followed by a label");
995                 }
996                 smt = new Expr(curLine, tok, new Expr(curLine, string));
997                 consume(SEMI);
998                 break;
999             }
1000                 
1001             case RC:
1002                 if (braced) consume(RC);
1003                 return block.size() == 0 ? null : ret;
1004                 
1005             case SEMI:
1006                 consume(SEMI);
1007                 if (!braced) return block.size() == 0 ? null : ret;
1008                 continue;
1009                 
1010             case NAME: {
1011                 String name = string;
1012                 consume(NAME);
1013                 if (peekToken() == COLON) {
1014                     consume(COLON);
1015                     smt = new Expr(curLine, COLON, new Expr(curLine, name), parseBlock(false));
1016                     break;
1017                 } else {
1018                     pushBackToken(NAME, name);
1019                     // fall through to default case
1020                 }
1021             }
1022
1023             case -1:
1024             default:
1025                 smt = parseMaximalExpr();
1026                 if (smt == null) return block.size() == 0 ? null : ret;
1027                 if (peekToken() == SEMI) getToken();
1028                 break;
1029             }
1030
1031             if (!braced) return smt;
1032             block.add(block.EXPR, smt);
1033             block.add(block.POP, NO_ARG);
1034         }
1035     }
1036     
1037
1038     /** sorta like gcc trees */
1039     class Expr {
1040         int code = -1;
1041     
1042         final Expr left;
1043         final Expr right;
1044         int line = -1;
1045         String sourceName = "unknown";
1046     
1047         String string = null;
1048         Number number = null;
1049     
1050         public String toString() { return toString(0); }
1051         public String toString(int indent) {
1052             String ret = "";
1053             for(int i=0; i<indent; i++) ret += " ";
1054             ret += Lexer.codeToString[code];
1055             if (code == Lexer.NUMBER) ret += " " + number;
1056             else if (string != null) ret += " \"" + string + "\"";
1057             ret += "\n";
1058             if (left != null) ret += left.toString(indent + 2);
1059             if (right != null) ret += right.toString(indent + 2);
1060             return ret;
1061         }
1062     
1063         public Expr(int line, String s) { this(line, Lexer.STRING); this.string = s; }  // an identifier or label
1064         public Expr(int line, int code, String s) { this(line, code); this.string = s; }
1065         public Expr(int line, Number n) { this(line, Lexer.NUMBER); this.number = n; }  // an identifier or label
1066         public Expr(int line, int code) { this(line, code, null, null); }
1067         public Expr(int line, int code, Expr left) { this(line, code, left, null); }
1068         public Expr(int line, int code, Expr left, Expr right) {
1069             this.code = code; this.left = left; this.right = right;
1070             this.line = line;
1071             this.sourceName = Parser.this.sourceName;
1072         }
1073
1074         public double toDouble(Object o) { return toNumber(o).doubleValue(); }
1075         public long toLong(Object o) { return toNumber(o).longValue(); }
1076         public boolean toBoolean(Object o) {
1077             if (o == null) return false;
1078             if (o instanceof Boolean) return ((Boolean)o).booleanValue();
1079             if (o instanceof Number) return o.equals(new Integer(0));
1080             return true;
1081         }
1082
1083         public Object eval(final JS.Scope s) throws ControlTransferException, JS.Exn {
1084             switch(code) {
1085
1086             case Lexer.NULL: return null;
1087             case Lexer.TYPEOF: {
1088                 Object o = left.eval(s);
1089                 if (o == null) return "null";
1090                 if (o.getClass() == String.class) return "string";
1091                 if (o.getClass() == Boolean.class) return "boolean";
1092                 if (o instanceof Number) return "number";
1093                 if (o instanceof JS.Array) return "array";
1094                 if (o instanceof JS) return "object";
1095                 throw new EvaluatorException("typeof " + o.getClass().getName() + " unknown");
1096             }
1097
1098             case Lexer.THROW: throw new JS.Exn(left.eval(s));
1099
1100             case Lexer.TRY: {
1101                 boolean safeToExit = false;
1102                 try {
1103                     Object ret = left.eval(s);
1104                     safeToExit = true;
1105                     return ret;
1106                 } catch (JS.Exn e) {
1107                     ExprList list = (ExprList)right;
1108                     Expr c = list.elementAt(0);
1109                     if (c.code == Lexer.CATCH) {
1110                         JS.Scope scope = new JS.Scope(s);
1111                         s.put(c.left.string, e);
1112                         c.right.eval(scope);
1113                         c = list.elementAt(1);
1114                     }
1115                     if (c.code == Lexer.FINALLY) {
1116                         JS.Scope scope = new JS.Scope(s);
1117                         c.left.eval(scope);
1118                     }
1119                 } finally {
1120                     if (!safeToExit) Log.log(this, "WARNING: Java exception penetrated a JavaScript try{} block");
1121                 }
1122                 return null;
1123             }
1124
1125             case Lexer.FOR:
1126                 Object[] keys = ((JS)left.right.eval(s)).keys();
1127                 for(int i=0; i<keys.length; i++) {
1128                     JS.Scope scope = new JS.Scope(s);
1129                     scope.declare(left.string);
1130                     scope.put(left.string, keys[i]);
1131                     try {
1132                         right.eval(scope);
1133                     } catch (ContinueException c) {
1134                         if (c.label == null || c.label.equals(string)) continue;
1135                     } catch (BreakException b) {
1136                         if (b.label == null || b.label.equals(string)) return null;
1137                         throw (BreakException)b.fillInStackTrace();
1138                     }
1139                 }
1140                 return null;
1141
1142             case Lexer.SWITCH:
1143                 Object switchVal = left.eval(s);
1144                 boolean go = false;
1145                 try {
1146                     ExprList list = (ExprList)right;
1147                     for(int i=0; i<list.size(); i++) {
1148                         Expr e = list.elementAt(i);
1149                         if (go || e.code == Lexer.DEFAULT || e.left.eval(s).equals(switchVal)) go = true;
1150                         if (go) e.right.eval(s);
1151                     }
1152                 } catch (BreakException b) {
1153                     if (b.label == null || b.label.equals(string)) return null;
1154                     throw (BreakException)b.fillInStackTrace();
1155                 }
1156                 return null;
1157
1158             case Lexer.BREAK: throw new BreakException(string);
1159             case Lexer.CONTINUE: throw new ContinueException(string);
1160             case Lexer.RETURN: throw new ReturnException(left == null ? null : left.eval(s));
1161
1162             default: throw new EvaluatorException("don't know how to eval an Expr with code " + Lexer.codeToString[code] + "\n" + this);
1163             }
1164         }
1165
1166         class EvaluatorException extends RuntimeException {
1167             public EvaluatorException(String s) { super(sourceName + ":" + line + " " + s); }
1168         }
1169     }
1170
1171         static abstract class ControlTransferException extends Exception { }
1172         static class BreakException extends ControlTransferException {
1173             public String label;
1174             BreakException(String label) { this.label = label; }
1175         }
1176         static class ContinueException extends ControlTransferException {
1177             public String label;
1178             ContinueException(String label) { this.label = label; }
1179         }
1180         static class ReturnException extends ControlTransferException {
1181             public Object retval;
1182             ReturnException(Object retval) { this.retval = retval; }
1183         }
1184
1185     class ParserException extends RuntimeException {
1186         public ParserException(String s) { super(sourceName + ":" + line + " " + s); }
1187     }
1188
1189         public static Number toNumber(Object o) {
1190             if (o == null) return new Long(0);
1191             if (o instanceof Number) return ((Number)o);
1192             if (o instanceof String) try { return new Double((String)o); } catch (NumberFormatException e) { return new Double(0); }
1193             if (o instanceof Boolean) return ((Boolean)o).booleanValue() ? new Long(1) : new Long(0);
1194             if (o instanceof JS) return ((JS)o).coerceToNumber();
1195             // FIXME
1196             throw new Error("toNumber() got object of type " + o.getClass().getName());
1197         }
1198  }
1199