1 // Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
8 /** parses a stream of lexed tokens into a tree of Expr's */
9 public class Parser extends Lexer {
11 // Constructors //////////////////////////////////////////////////////
13 public Parser(Reader r, String sourceName, int line) throws IOException {
15 this.sourceName = sourceName;
20 public static void main(String[] s) throws Exception {
21 Parser p = new Parser(new InputStreamReader(System.in), "stdin", 0);
23 Expr block = p.parseBlock(false);
24 if (block == null) return;
25 System.out.println(block);
26 if (p.peekToken() == -1) return;
31 // Statics ////////////////////////////////////////////////////////////
33 static byte[] precedence = new byte[MAX_TOKEN + 1];
34 static boolean[] isRightAssociative = new boolean[MAX_TOKEN + 1];
36 precedence[ASSIGN] = 1;
37 isRightAssociative[ASSIGN] = true;
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;
59 // Parsing Logic /////////////////////////////////////////////////////////
61 public void consume(int code) throws IOException {
62 if (getToken() != code)
63 throw new ParserException("expected " + codeToString[code] + ", got " + (op == -1 ? "EOL" : codeToString[op]));
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 {
70 if (peekToken() == -1) break;
72 prefix = parseSingleExpr(prefix, minPrecedence);
73 if (save == prefix) break;
74 if (prefix == null) throw new ParserException("parseSingleExpr() returned null");
79 public Expr parseSingleExpr(Expr prefix, int minPrecedence) throws IOException {
80 Expr e1 = null, e2 = null, e3 = null, head = null, tail = null, ret = null;
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))) {
94 // these case arms match the precedence of operators; each arm is a precedence level.
98 if (prefix != null) { pushBackToken(); return prefix; }
99 ByteCode b = new ByteCode(curLine);
100 b.add(b.THIS, NO_ARG);
103 String name = string;
104 b.add(b.DECLARE, name);
105 if (peekToken() == ASSIGN) {
106 b.add(b.LITERAL, name);
108 b.add(b.EXPR, parseMaximalExpr());
109 b.add(b.PUT, NO_ARG);
110 b.add(b.POP, NO_ARG);
112 if (peekToken() != COMMA) break;
118 case IN: pushBackToken(); return prefix;
121 if (prefix != null) { pushBackToken(); return prefix; }
122 ByteCode b = new ByteCode(curLine);
124 b.add(b.EXPR, parseMaximalExpr());
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);
131 b.add(b.EXPR, parseBlock(false));
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;
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]));
152 b.add(b.PUT, NO_ARG);
153 b.add(b.SWAP, NO_ARG);
154 b.add(b.POP, NO_ARG);
159 if (prefix == null) {
161 ByteCode b = (ByteCode)parseMaximalExpr(null, precedence[tok]);
162 b.set(b.size() - 1, tok, new Boolean(true));
166 ByteCode b = (ByteCode)prefix;
167 b.set(b.size() - 1, tok, new Boolean(false));
172 if (prefix == null) { // grouping
173 ByteCode b = new ByteCode(curLine, ByteCode.EXPR, parseMaximalExpr());
177 } else { // invocation
178 ByteCode b = new ByteCode(curLine);
180 b.add(b.EXPR, prefix);
181 while(peekToken() != RP) {
182 b.add(b.EXPR, parseMaximalExpr());
184 if (peekToken() == RP) break;
188 b.add(b.CALL, new Integer(i));
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]));
201 if (prefix == null && peekToken() == NUMBER) {
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]));
216 // includes short-circuit logic
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]));
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);
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");
244 if (prefix != null) { pushBackToken(); return prefix; }
245 return new ByteCode(curLine, ByteCode.LITERAL, number);
248 if (prefix != null) { pushBackToken(); return prefix; }
249 return new ByteCode(curLine, ByteCode.LITERAL, string);
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));
255 case COMMA: pushBackToken(); return prefix;
258 if (prefix != null) { pushBackToken(); return prefix; }
259 return new ByteCode(curLine, ByteCode.THIS, NO_ARG);
264 if (prefix != null) { pushBackToken(); return prefix; }
265 String name = string;
266 ByteCode b = new ByteCode(curLine);
267 if (peekToken() == 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);
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);
286 String target = string;
287 ByteCode b = new ByteCode(curLine);
288 b.add(b.EXPR, prefix);
289 if (peekToken() == ASSIGN) {
291 Expr val = parseMaximalExpr();
292 b.add(b.LITERAL, target);
294 b.add(b.PUT, NO_ARG);
295 b.add(b.SWAP, NO_ARG);
296 b.add(b.POP, NO_ARG);
298 b.add(b.LITERAL, target);
299 b.add(b.GET, NO_ARG);
305 ByteCode b = new ByteCode(curLine);
306 if (prefix == null) {
307 b.add(b.ARRAY, new Integer(0));
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; }
321 b.add(b.EXPR, prefix);
322 b.add(b.EXPR, parseMaximalExpr());
324 if (peekToken() == 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);
331 b.add(b.GET, NO_ARG);
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; }
343 if (peekToken() != NAME && peekToken() != STRING) throw new Error("expected NAME or STRING");
345 b.add(b.LITERAL, string);
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; }
352 if (peekToken() == RC) { consume(RC); return b; }
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));
363 b.add(b.EXPR, parseMaximalExpr());
368 if (prefix != null) { pushBackToken(); return prefix; }
370 ByteCode b = new ByteCode(curLine);
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);
383 if (peekToken() == COMMA) {
389 b.add(b.LITERAL, string);
390 b.add(b.DECLARE, NO_ARG);
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);
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);
406 b.add(b.POP, NO_ARG);
407 b.add(b.POP, NO_ARG);
409 if (peekToken() == RP) { consume(RP); break; }
414 // pop off the arguments array
415 b.add(b.POP, NO_ARG);
417 return new ByteCode(curLine, b.FUNCTION, b);
421 if (prefix != null) { pushBackToken(); return prefix; }
423 ByteCode r = new ByteCode(curLine);
424 ByteCode loop = new ByteCode(curLine);
425 r.add(loop.LOOP, loop);
426 r.add(r.LITERAL, null);
428 loop.add(loop.EXPR, parseMaximalExpr());
429 loop.add(loop.JT, new Integer(2));
430 loop.add(Lexer.BREAK, NO_ARG);
432 parseBlock(false, loop);
434 // if we fall out of the end, definately continue
435 loop.add(CONTINUE, NO_ARG);
440 if (prefix != null) { pushBackToken(); return prefix; }
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());
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");
459 ByteCode b = new ByteCode(curLine);
460 while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) {
461 if ((e1 = parseBlock(false)) == null) break;
464 loop.add(loop.EXPR, b);
465 if (peekToken() == RC) {
467 r.add(BREAK, NO_ARG);
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);
480 parseBlock(false, loop);
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);
493 // FIXME: don't just ignore this!
494 // We deliberately allow you to omit braces in catch{}/finally{} if they are single statements...
495 if (prefix != null) { pushBackToken(); return prefix; }
496 Expr tryBlock = parseBlock(true);
501 if (getToken() != LP) throw new ParserException("expected (");
502 if (getToken() != NAME) throw new ParserException("expected name");
503 if (getToken() != RP) throw new ParserException("expected )");
506 if (tok == FINALLY) getToken();
512 if (prefix != null) { pushBackToken(); return prefix; }
513 if (getToken() != LP) throw new ParserException("expected left paren");
516 if (tok == VAR) tok = getToken();
517 String varName = string;
518 boolean forIn = peekToken() == IN;
519 pushBackToken(tok, varName);
521 ByteCode b = new ByteCode(curLine);
525 b.add(b.EXPR, parseMaximalExpr());
526 b.add(b.PUSHKEYS, NO_ARG);
527 b.add(b.LITERAL, "length");
528 b.add(b.GET, NO_ARG);
530 ByteCode b2 = new ByteCode(curLine);
532 b2.add(b.LITERAL, new Integer(1));
534 b2.add(b.DUP, NO_ARG);
535 b2.add(b.LITERAL, new Integer(0));
537 b2.add(b.JT, new Integer(7));
538 b2.add(b.GET_PRESERVE, NO_ARG);
539 b2.add(b.LITERAL, varName);
540 b2.add(b.LITERAL, varName);
541 b2.add(b.DECLARE, NO_ARG);
542 b2.add(b.PUT, NO_ARG);
543 b2.add(b.EXPR, parseBlock(false));
544 b2.add(b.LITERAL, null);
548 ByteCode b2 = new ByteCode(curLine);
550 b2.add(b.EXPR, parseMaximalExpr());
551 b2.add(b.POP, NO_ARG);
553 ByteCode b3 = new ByteCode(curLine);
555 b2.add(b.LITERAL, null);
556 b3.add(b.EXPR, parseMaximalExpr());
558 b3.add(b.JT, new Integer(2));
559 b3.add(BREAK, NO_ARG);
560 b3.add(b.JT, new Integer(2));
561 b3.add(b.EXPR, parseMaximalExpr());
563 parseBlock(false, b3);
574 // Expr //////////////////////////////////////////////////////////////////////
576 public static final Object NO_ARG = new Object();
578 class ByteCode extends Expr {
580 // This class interprets a small forthlike bytecode language
581 // we use to represent JavaScript. It's a stack machine.
583 // Each instruction is an opcode and a literal. Any operation
584 // that accesses the top of the stack and does not have a
585 // mandatory literal can be performed on a literal instead of
586 // the top of the stack.
589 public static final byte arithmetic = -1; // -- arithmetic operators from parser
590 public static final byte LITERAL = -2; // < String | Number | null > -- push a literal onto the stack
591 public static final byte ARRAY = -3; // < size > -- create a new array of size <size>
592 public static final byte OBJECT = -4; // -- push an empty object onto the stack
593 public static final byte FUNCTION = -5; // < bytecode_block > -- push a new instance of a function with the given bytecode
594 public static final byte DECLARE = -6; // < name > -- declare <name> in the current scope
595 public static final byte THIS = -7; // -- push the topmost non-transparent scope onto the stack
596 public static final byte GET = -8; // -- get stack[0] from stack[1]
597 public static final byte GET_PRESERVE = -80; // -- get stack[0] from stack[1]
598 public static final byte PUT = -9; // -- put stack[1] to key stack[0] on stack[2]; leaves object on the stack
599 public static final byte JT = -13; // < relative_address > -- pop the stack; if true, jump to <relative_address>
600 public static final byte JF = -21; // < relative_address > -- pop the stack; if false, jump to <relative_address>
601 public static final byte JMP = -22; // < relative_address > -- jump to <relative_address>
602 static public final byte POP = -14; // -- discard the top element on the stack
604 public static final byte CALL = -15; // < numargs > -- call stack[0] with the topmost <numargs> values as arguments
606 public static final byte PUSHKEYS = -19; // -- ??
607 public static final byte EXPR = -20; // -- transitional
608 public static final byte SWAP = -23; // -- transitional
609 public static final byte SCOPE = -30; // -- transitional
610 public static final byte LOOP = -40; // -- transitional
611 public static final byte DUP = -50; // -- transitional
612 public static final byte LABEL = -60; // -- transitional
614 int[] op = new int[10];
615 Object[] arg = new Object[10];
618 public ByteCode(int line) { super(line, "foo"); }
619 public ByteCode(int line, int op_, Object arg_) { this(line); add(op_, arg_); }
621 public int size() { return size; }
622 public void set(int pos, int op_, Object arg_) { op[pos] = op_; arg[pos] = arg_; }
623 public ByteCode add(int op_, Object arg_) {
624 if (size == op.length - 1) {
625 int[] op2 = new int[op.length * 2]; System.arraycopy(op, 0, op2, 0, op.length); op = op2;
626 Object[] arg2 = new Object[op.length * 2]; System.arraycopy(arg, 0, arg2, 0, arg.length); arg = arg2;
634 public Object eval(final JS.Scope s) throws ControlTransferException, JS.Exn {
635 return eval(s, new Parser.Thread());
637 public Object eval(final JS.Scope s, Parser.Thread t) throws ControlTransferException {
638 for(int i=0; i<size; i++)
640 case LABEL: break; // FIXME
641 case LITERAL: t.push(arg[i]); break;
642 case OBJECT: t.push(new JS.Obj()); break;
643 case ARRAY: t.push(new JS.Array(toNumber(arg[i]).intValue())); break;
644 case DECLARE: s.declare(arg[i] == NO_ARG ? (String)t.pop() : (String)arg[i]); break;
645 case THIS: t.push(s); break; // FIXME: transparents
646 case JT: if (toBoolean(t.pop())) i += toNumber(arg[i]).intValue() - 1; break;
647 case JF: if (!toBoolean(t.pop())) i += toNumber(arg[i]).intValue() - 1; break;
648 case JMP: i += toNumber(arg[i]).intValue() - 1; break;
649 case POP: t.pop(); break;
650 case SWAP: t.swap(); break;
651 case DUP: t.push(t.peek()); break;
653 case EXPR: t.push(((Expr)arg[i]).eval(s)); break;
654 case SCOPE: t.push(((ByteCode)arg[i]).eval(new JS.Scope(s), t)); break;
656 case ASSERT: if (!toBoolean(t.pop())) throw new EvaluatorException("assertion failed"); break;
657 case RETURN: throw new ReturnException(t.pop());
658 case THROW: throw new JS.Exn(t.pop());
661 case INSTANCEOF: break;
665 Object[] keys = ((JS)o).keys();
666 JS.Array a = new JS.Array();
667 a.setSize(keys.length);
668 for(int j=0; j<keys.length; j++) a.setElementAt(keys[j], j);
673 case Lexer.BITNOT: t.push(new Long(~toLong(t.pop()))); break;
674 case Lexer.BANG: t.push(new Boolean(!toBoolean(t.pop()))); break;
677 // FIXME: make sure this can only appear in proper places
678 return Boolean.FALSE;
680 case Lexer.CONTINUE: {
681 // FIXME: make sure this can only appear in proper places
685 ByteCode loop = (ByteCode)arg[i];
686 Parser.Thread t2 = new Parser.Thread();
687 t2.push(Boolean.TRUE);
691 result = (Boolean)loop.eval(new JS.Scope(s), t2);
692 } catch (ContinueException c) {
693 result = Boolean.TRUE;
694 } catch (BreakException b) {
695 result = Boolean.FALSE;
697 if (result == Boolean.FALSE) break;
698 t2 = new Parser.Thread();
699 t2.push(Boolean.FALSE);
705 Object val = arg[i] == NO_ARG ? t.pop() : arg[i];
706 Object key = t.pop();
707 ((JS)t.peek()).put(key, val);
713 Object v = arg[i] == NO_ARG ? t.pop() : arg[i];
728 JS.Array arguments = new JS.Array();
729 int numArgs = toNumber(arg[i]).intValue();
730 arguments.setSize(numArgs);
731 for(int j=numArgs - 1; j >= 0; j--) arguments.setElementAt(t.pop(), j);
732 JS.Function f = (JS.Function)t.pop();
733 if (f == null) throw new JS.Exn(new EvaluatorException("attempted to call null"));
734 t.push(f.call(arguments));
739 final ByteCode myBytes = (ByteCode)arg[i];
740 t.push(new JS.Function() {
741 public String toString() { return sourceName + ":" + line; }
742 public String getSourceName() throws JS.Exn { return sourceName; }
743 public Object _call(final JS.Array args) throws JS.Exn {
744 Function save = JS.getCurrentFunction();
745 JS.currentFunction.put(java.lang.Thread.currentThread(), this);
746 JS.Scope scope = new JS.Scope(s) {
748 public String getSourceName() { return sourceName; }
749 public Object get(Object key) throws JS.Exn {
750 if (key.equals("trapee")) return org.xwt.Trap.currentTrapee();
751 else if (key.equals("cascade")) return org.xwt.Trap.cascadeFunction;
752 return super.get(key);
755 Parser.Thread t0 = new Parser.Thread();
758 return myBytes.eval(scope, t0);
759 } catch (ReturnException r) {
761 } catch (ControlTransferException c) {
762 throw new EvaluatorException("error, ControlTransferException tried to leave a function: " + c);
764 if (save == null) JS.currentFunction.remove(java.lang.Thread.currentThread());
765 else JS.currentFunction.put(java.lang.Thread.currentThread(), save);
772 case Lexer.INC: case Lexer.DEC: {
773 boolean isPrefix = toBoolean(arg[i]);
774 Object key = t.pop();
775 JS obj = (JS)t.pop();
776 Number num = toNumber(obj.get(key));
777 Number val = new Double(op[i] == Lexer.INC ? num.doubleValue() + 1.0 : num.doubleValue() - 1.0);
779 t.push(isPrefix ? val : num);
784 Object right = t.pop();
785 Object left = t.pop();
788 case Lexer.BITOR: t.push(new Long(toLong(left) | toLong(right))); break;
789 case Lexer.BITXOR: t.push(new Long(toLong(left) ^ toLong(right))); break;
790 case Lexer.BITAND: t.push(new Long(toLong(left) & toLong(right))); break;
795 if (l instanceof String || r instanceof String) {
796 if (l == null) l = "null";
797 if (r == null) r = "null";
798 if (l instanceof Number && ((Number)l).doubleValue() == ((Number)l).longValue())
799 l = new Long(((Number)l).longValue());
800 if (r instanceof Number && ((Number)r).doubleValue() == ((Number)r).longValue())
801 r = new Long(((Number)r).longValue());
802 t.push(l.toString() + r.toString()); break;
804 t.push(new Double(toDouble(l) + toDouble(r))); break;
807 case Lexer.SUB: t.push(new Double(toDouble(left) - toDouble(right))); break;
808 case Lexer.MUL: t.push(new Double(toDouble(left) * toDouble(right))); break;
809 case Lexer.DIV: t.push(new Double(toDouble(left) / toDouble(right))); break;
810 case Lexer.MOD: t.push(new Double(toDouble(left) % toDouble(right))); break;
812 case Lexer.LSH: t.push(new Long(toLong(left) << toLong(right))); break;
813 case Lexer.RSH: t.push(new Long(toLong(left) >> toLong(right))); break;
814 case Lexer.URSH: t.push(new Long(toLong(left) >>> toLong(right))); break;
816 // FIXME: these need to work on strings
817 case Lexer.LT: t.push(toDouble(left) < toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break;
818 case Lexer.LE: t.push(toDouble(left) <= toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break;
819 case Lexer.GT: t.push(toDouble(left) > toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break;
820 case Lexer.GE: t.push(toDouble(left) >= toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break;
824 // FIXME: should use Javascript coercion-equality rules
828 if (l == null) { Object tmp = r; r = l; l = tmp; }
829 if (l == null && r == null) ret = true;
830 else if (l instanceof Boolean) ret = new Boolean(toBoolean(r)).equals(l);
831 else if (l instanceof Number) ret = toNumber(r).doubleValue() == toNumber(l).doubleValue();
832 else if (l instanceof String) ret = r != null && l.equals(r.toString());
833 else ret = l.equals(r);
834 t.push(new Boolean(op[i] == Lexer.EQ ? ret : !ret)); break;
837 default: throw new Error("unknown opcode " + op[i]);
841 throw new EvaluatorException("eval() terminated with " + t.size() + " elements on the stack; one expected");
846 public Object doGet(final Object o, final Object v) {
847 if (o == null) throw new EvaluatorException("tried to get property \"" + v + "\" from the null value");
848 if (o instanceof String) {
849 if (v.equals("length")) return new Integer(((String)o).length());
850 else if (v.equals("substring")) return new JS.Function() {
851 public Object _call(JS.Array args) {
852 if (args.length() == 1) return ((String)o).substring(toNumber(args.elementAt(0)).intValue());
853 else if (args.length() == 2) return ((String)o).substring(toNumber(args.elementAt(0)).intValue(),
854 toNumber(args.elementAt(1)).intValue());
855 else throw new Error("String.substring() can only take one or two arguments");
858 else if (v.equals("toLowerCase")) return new JS.Function() {
859 public Object _call(JS.Array args) {
860 return ((String)o).toLowerCase();
862 else if (v.equals("toUpperCase")) return new JS.Function() {
863 public Object _call(JS.Array args) {
864 return ((String)o).toString().toUpperCase();
866 else if (v.equals("charAt")) return new JS.Function() {
867 public Object _call(JS.Array args) {
868 return ((String)o).charAt(toNumber(args.elementAt(0)).intValue()) + "";
870 else if (v.equals("lastIndexOf")) return new JS.Function() {
871 public Object _call(JS.Array args) {
872 if (args.length() != 1) return null;
873 return new Integer(((String)o).lastIndexOf(args.elementAt(0).toString()));
875 else if (v.equals("indexOf")) return new JS.Function() {
876 public Object _call(JS.Array args) {
877 if (args.length() != 1) return null;
878 return new Integer(((String)o).indexOf(args.elementAt(0).toString()));
880 throw new Error("Not Implemented: propery " + v + " on String objects");
881 } else if (o instanceof Boolean) {
882 throw new Error("Not Implemented: properties on Boolean objects");
883 } else if (o instanceof Number) {
884 Log.log(this, "Not Implemented: properties on Number objects");
886 //throw new Error("Not Implemented: properties on Number objects");
887 } else if (o instanceof JS) {
888 return ((JS)o).get(v);
895 public Object[] os = new Object[256];
896 private int size = 0;
897 public void push(Object o) {
900 public Object pop() { return os[--size]; }
901 public Object peek() { return os[size - 1]; }
902 public void swap() { Object temp = os[size - 1]; os[size - 1] = os[size - 2]; os[size - 2] = temp; }
903 public int size() { return size; }
906 class ExprList extends Expr {
908 public ExprList(int curLine, int code) { super(curLine, code); }
909 public void add(Expr e) { v.addElement(e); }
910 public int numExprs() { return v.size(); }
911 public int size() { return v.size(); }
912 public Expr elementAt(int i) { return (Expr)v.elementAt(i); }
913 public Object eval(final JS.Scope s) throws ControlTransferException, JS.Exn {
917 JS.Scope scope = new JS.Scope(s);
918 for(int i=0; i<v.size(); i++) ((Expr)v.elementAt(i)).eval(scope);
923 JS.Array ret = new JS.Array();
924 for(int i=0; i<numExprs(); i++) ret.addElement(elementAt(i).eval(s));
929 JS.Obj ret = new JS.Obj();
930 for(int i=0; i<numExprs(); i++) {
931 Expr e = elementAt(i);
932 Object key = e.left.string;
933 Object val = e.right.eval(s);
941 boolean first = true;
943 ExprList list = this;
944 Expr whileExpr = list.elementAt(0);
945 Expr bodyExpr = list.elementAt(1);
946 Expr incExpr = list.elementAt(2);
947 for(;(first && code == Lexer.DO) || toBoolean(whileExpr.eval(s)); bogus = incExpr.eval(s)) {
951 } catch (ContinueException c) {
952 if (c.label == null || c.label.equals(string)) continue;
953 } catch (BreakException b) {
954 if (b.label == null || b.label.equals(string)) return null;
955 throw (BreakException)b.fillInStackTrace();
958 } catch (BreakException e) {
959 if (e.label != null && !e.label.equals(string)) throw e;
963 for(int i=0; i<size(); i++) {
964 Expr e = elementAt(i);
965 s.declare(e.left.string);
966 if (e.right != null) e.right.eval(s);
969 default: throw new Error("augh!");
974 /** a block is either a single statement or a list of statements surrounded by curly braces; all expressions are also statements */
975 public Expr parseBlock(boolean requireBraces) throws IOException { return parseBlock(requireBraces, null); }
976 public Expr parseBlock(boolean requireBraces, ByteCode b) throws IOException {
978 int tok = peekToken();
979 if (tok == -1) return null;
980 boolean braced = tok == LC;
981 if (requireBraces && !braced) throw new ParserException("expected {, got " + codeToString[tok]);
982 if (braced) consume(LC);
984 ByteCode ret = new ByteCode(curLine);
985 ByteCode block = b == null ? new ByteCode(curLine) : b;
986 block.add(ret.LITERAL, Boolean.TRUE);
987 ret.add(block.SCOPE, block);
989 switch(tok = peekToken()) {
991 case LC: smt = parseBlock(true); break;
992 case GOTO: throw new ParserException("goto not supported");
994 case THROW: case RETURN: case ASSERT: {
996 ByteCode r = new ByteCode(curLine);
997 if (tok == RETURN && peekToken() == SEMI) r.add(b.LITERAL, null);
998 else r.add(b.EXPR, parseMaximalExpr());
1005 case BREAK: case CONTINUE: {
1007 if (peekToken() == NAME) consume(NAME);
1008 smt = new ByteCode(curLine, tok, string);
1014 if (braced) consume(RC);
1015 return block.size() == 0 ? null : ret;
1019 if (!braced) return block.size() == 0 ? null : ret;
1023 String name = string;
1025 if (peekToken() == COLON) {
1027 smt = new ByteCode(curLine, ByteCode.LABEL, string);
1030 pushBackToken(NAME, name);
1031 // fall through to default case
1037 smt = parseMaximalExpr();
1038 if (smt == null) return block.size() == 0 ? null : ret;
1039 if (peekToken() == SEMI) getToken();
1043 if (!braced) return smt;
1044 block.add(block.EXPR, smt);
1045 block.add(block.POP, NO_ARG);
1050 /** sorta like gcc trees */
1057 String sourceName = "unknown";
1059 String string = null;
1060 Number number = null;
1062 public String toString() { return toString(0); }
1063 public String toString(int indent) {
1065 for(int i=0; i<indent; i++) ret += " ";
1066 ret += Lexer.codeToString[code];
1067 if (code == Lexer.NUMBER) ret += " " + number;
1068 else if (string != null) ret += " \"" + string + "\"";
1070 if (left != null) ret += left.toString(indent + 2);
1071 if (right != null) ret += right.toString(indent + 2);
1075 public Expr(int line, String s) { this(line, Lexer.STRING); this.string = s; } // an identifier or label
1076 public Expr(int line, int code, String s) { this(line, code); this.string = s; }
1077 public Expr(int line, Number n) { this(line, Lexer.NUMBER); this.number = n; } // an identifier or label
1078 public Expr(int line, int code) { this(line, code, null, null); }
1079 public Expr(int line, int code, Expr left) { this(line, code, left, null); }
1080 public Expr(int line, int code, Expr left, Expr right) {
1081 this.code = code; this.left = left; this.right = right;
1083 this.sourceName = Parser.this.sourceName;
1086 public double toDouble(Object o) { return toNumber(o).doubleValue(); }
1087 public long toLong(Object o) { return toNumber(o).longValue(); }
1088 public boolean toBoolean(Object o) {
1089 if (o == null) return false;
1090 if (o instanceof Boolean) return ((Boolean)o).booleanValue();
1091 if (o instanceof Number) return o.equals(new Integer(0));
1095 public Object eval(final JS.Scope s) throws ControlTransferException, JS.Exn {
1098 case Lexer.NULL: return null;
1099 case Lexer.TYPEOF: {
1100 Object o = left.eval(s);
1101 if (o == null) return "null";
1102 if (o.getClass() == String.class) return "string";
1103 if (o.getClass() == Boolean.class) return "boolean";
1104 if (o instanceof Number) return "number";
1105 if (o instanceof JS.Array) return "array";
1106 if (o instanceof JS) return "object";
1107 throw new EvaluatorException("typeof " + o.getClass().getName() + " unknown");
1111 boolean safeToExit = false;
1113 Object ret = left.eval(s);
1116 } catch (JS.Exn e) {
1117 ExprList list = (ExprList)right;
1118 Expr c = list.elementAt(0);
1119 if (c.code == Lexer.CATCH) {
1120 JS.Scope scope = new JS.Scope(s);
1121 s.put(c.left.string, e);
1122 c.right.eval(scope);
1123 c = list.elementAt(1);
1125 if (c.code == Lexer.FINALLY) {
1126 JS.Scope scope = new JS.Scope(s);
1130 if (!safeToExit) Log.log(this, "WARNING: Java exception penetrated a JavaScript try{} block");
1136 Object[] keys = ((JS)left.right.eval(s)).keys();
1137 for(int i=0; i<keys.length; i++) {
1138 JS.Scope scope = new JS.Scope(s);
1139 scope.declare(left.string);
1140 scope.put(left.string, keys[i]);
1143 } catch (ContinueException c) {
1144 if (c.label == null || c.label.equals(string)) continue;
1145 } catch (BreakException b) {
1146 if (b.label == null || b.label.equals(string)) return null;
1147 throw (BreakException)b.fillInStackTrace();
1152 default: throw new EvaluatorException("don't know how to eval an Expr with code " + Lexer.codeToString[code] + "\n" + this);
1156 class EvaluatorException extends RuntimeException {
1157 public EvaluatorException(String s) { super(sourceName + ":" + line + " " + s); }
1161 static abstract class ControlTransferException extends Exception { }
1162 static class BreakException extends ControlTransferException {
1163 public String label;
1164 BreakException(String label) { this.label = label; }
1166 static class ContinueException extends ControlTransferException {
1167 public String label;
1168 ContinueException(String label) { this.label = label; }
1170 static class ReturnException extends ControlTransferException {
1171 public Object retval;
1172 ReturnException(Object retval) { this.retval = retval; }
1175 class ParserException extends RuntimeException {
1176 public ParserException(String s) { super(sourceName + ":" + line + " " + s); }
1179 public static Number toNumber(Object o) {
1180 if (o == null) return new Long(0);
1181 if (o instanceof Number) return ((Number)o);
1182 if (o instanceof String) try { return new Double((String)o); } catch (NumberFormatException e) { return new Double(0); }
1183 if (o instanceof Boolean) return ((Boolean)o).booleanValue() ? new Long(1) : new Long(0);
1184 if (o instanceof JS) return ((JS)o).coerceToNumber();
1186 throw new Error("toNumber() got object of type " + o.getClass().getName());