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 {
63 if (got != code) throw new ParserException("expected " + codeToString[code] + ", got " + (got == -1 ? "EOL" : codeToString[got]));
65 public void consume(int code, int code2) throws IOException {
67 if (got != code && got != code2)
68 throw new ParserException("expected " + codeToString[code] + " or " + codeToString[code2] +
69 ", got " + (got == -1 ? "EOL" : codeToString[got]));
71 public void expect(int code) throws IOException {
72 int got = peekToken();
73 if (got != code) throw new ParserException("expected " + codeToString[code] + ", got " + (got == -1 ? "EOL" : codeToString[got]));
76 /** parses the largest possible expression */
77 public Expr parseMaximalExpr() throws IOException { return parseMaximalExpr(null, -1); }
78 public Expr parseMaximalExpr(Expr prefix, int minPrecedence) throws IOException {
80 if (peekToken() == -1) break;
82 prefix = parseSingleExpr(prefix, minPrecedence);
83 if (save == prefix) break;
84 if (prefix == null) throw new ParserException("parseSingleExpr() returned null");
89 public Expr parseSingleExpr(Expr prefix, int minPrecedence) throws IOException {
90 Expr e1 = null, e2 = null, e3 = null, head = null, tail = null, ret = null;
92 int tok = peekToken();
93 if (minPrecedence > 0 &&
94 tok < precedence.length &&
95 precedence[tok] != 0 &&
96 (isRightAssociative[tok] ?
97 (precedence[tok] < minPrecedence) :
98 (precedence[tok] <= minPrecedence))) {
104 // these case arms match the precedence of operators; each arm is a precedence level.
108 if (prefix != null) { pushBackToken(); return prefix; }
109 ByteCode b = new ByteCode(curLine);
110 b.add(b.THIS, NO_ARG);
113 String name = string;
114 b.add(b.DECLARE, name);
115 if (peekToken() == ASSIGN) {
116 b.add(b.LITERAL, name);
118 b.add(b.EXPR, parseMaximalExpr());
119 b.add(b.PUT, NO_ARG);
120 b.add(b.POP, NO_ARG);
122 if (peekToken() != COMMA) break;
128 case IN: pushBackToken(); return prefix;
131 if (prefix != null) { pushBackToken(); return prefix; }
132 ByteCode b = new ByteCode(curLine);
134 b.add(b.EXPR, parseMaximalExpr());
136 b.add(b.JF, new Integer(3));
137 b.add(b.EXPR, parseBlock(false));
138 b.add(b.JMP, new Integer(2));
139 if (peekToken() != ELSE) return b.add(b.LITERAL, null);
141 b.add(b.EXPR, parseBlock(false));
145 // FIXME: ugly hack!!
146 case ASSIGN_BITOR: if (tok == ASSIGN_BITOR) tok = BITOR;
147 case ASSIGN_BITXOR: if (tok == ASSIGN_BITXOR) tok = BITXOR;
148 case ASSIGN_BITAND: if (tok == ASSIGN_BITAND) tok = BITAND;
149 case ASSIGN_LSH: if (tok == ASSIGN_LSH) tok = LSH;
150 case ASSIGN_RSH: if (tok == ASSIGN_RSH) tok = RSH;
151 case ASSIGN_URSH: if (tok == ASSIGN_URSH) tok = URSH;
152 case ASSIGN_ADD: if (tok == ASSIGN_ADD) tok = ADD;
153 case ASSIGN_SUB: if (tok == ASSIGN_SUB) tok = SUB;
154 case ASSIGN_MUL: if (tok == ASSIGN_MUL) tok = MUL;
155 case ASSIGN_DIV: if (tok == ASSIGN_DIV) tok = DIV;
156 case ASSIGN_MOD: if (tok == ASSIGN_MOD) tok = MOD;
158 ByteCode b = (ByteCode)prefix;
159 b.set(b.size() - 1, b.GET_PRESERVE, new Boolean(true));
160 b.add(b.EXPR, parseMaximalExpr(null, precedence[tok]));
162 b.add(b.PUT, NO_ARG);
163 b.add(b.SWAP, NO_ARG);
164 b.add(b.POP, NO_ARG);
169 if (prefix == null) {
171 ByteCode b = (ByteCode)parseMaximalExpr(null, precedence[tok]);
172 b.set(b.size() - 1, tok, new Boolean(true));
176 ByteCode b = (ByteCode)prefix;
177 b.set(b.size() - 1, tok, new Boolean(false));
182 if (prefix == null) { // grouping
183 ByteCode b = new ByteCode(curLine, ByteCode.EXPR, parseMaximalExpr());
187 } else { // invocation
188 ByteCode b = new ByteCode(curLine);
190 b.add(b.EXPR, prefix);
191 while(peekToken() != RP) {
192 b.add(b.EXPR, parseMaximalExpr());
194 if (peekToken() == RP) break;
198 b.add(b.CALL, new Integer(i));
202 case BANG: case BITNOT: case INSTANCEOF: case TYPEOF: {
203 if (prefix != null) { pushBackToken(); return prefix; }
204 ByteCode b = new ByteCode(curLine);
205 b.add(b.EXPR, parseMaximalExpr(null, precedence[tok]));
211 if (prefix == null && peekToken() == NUMBER) {
213 return new ByteCode(curLine, ByteCode.LITERAL, new Double(number.doubleValue() * -1));
214 } // else fall through
215 case BITOR: case BITXOR: case BITAND: case SHEQ: case SHNE: case LSH:
216 case RSH: case URSH: case ADD: case MUL: case DIV: case MOD:
217 case GT: case GE: case EQ: case NE: case LT: case LE: {
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.EXPR, prefix);
221 b.add(b.EXPR, parseMaximalExpr(null, precedence[tok]));
226 // includes short-circuit logic
228 if (prefix == null) throw new ParserException("the " + codeToString[tok] + " token cannot start an expression");
229 ByteCode b = new ByteCode(curLine);
230 b.add(b.LITERAL, tok == AND ? new Boolean(false) : new Boolean(true));
231 b.add(b.EXPR, prefix);
232 b.add(tok == AND ? b.JF : b.JT, new Integer(3));
233 b.add(b.POP, NO_ARG);
234 b.add(b.EXPR, parseMaximalExpr(null, precedence[tok]));
239 if (prefix == null) throw new ParserException("the " + codeToString[tok] + " token cannot start an expression");
240 ByteCode b = new ByteCode(curLine);
241 b.add(b.THIS, NO_ARG);
242 b.add(b.LITERAL, prefix.string); // FIXME, this is ass-ugly
243 b.add(b.EXPR, parseMaximalExpr(null, precedence[ASSIGN]));
244 b.add(b.PUT, NO_ARG);
245 b.add(b.SWAP, NO_ARG);
246 b.add(b.POP, NO_ARG);
250 case WITH: throw new ParserException("XWT does not allow the WITH keyword");
251 case VOID: case RESERVED: throw new ParserException("reserved word that you shouldn't be using");
254 if (prefix != null) { pushBackToken(); return prefix; }
255 return new ByteCode(curLine, ByteCode.LITERAL, number);
258 if (prefix != null) { pushBackToken(); return prefix; }
259 return new ByteCode(curLine, ByteCode.LITERAL, string);
261 case NULL: case TRUE: case FALSE: case NOP:
262 if (prefix != null) { pushBackToken(); return prefix; }
263 return new ByteCode(curLine, ByteCode.LITERAL, (tok == NULL || tok == NOP) ? null : new Boolean(tok == TRUE));
265 case COMMA: pushBackToken(); return prefix;
268 if (prefix != null) { pushBackToken(); return prefix; }
269 return new ByteCode(curLine, ByteCode.THIS, NO_ARG);
274 if (prefix != null) { pushBackToken(); return prefix; }
275 String name = string;
276 ByteCode b = new ByteCode(curLine);
277 if (peekToken() == ASSIGN) {
279 b.add(ByteCode.THIS, NO_ARG);
280 b.add(ByteCode.LITERAL, name);
281 b.add(ByteCode.EXPR, parseMaximalExpr(null, minPrecedence));
282 b.add(ByteCode.PUT, NO_ARG);
283 b.add(ByteCode.SWAP, NO_ARG);
284 b.add(ByteCode.POP, NO_ARG);
287 b.add(ByteCode.THIS, NO_ARG);
288 b.add(ByteCode.LITERAL, name);
289 b.add(ByteCode.GET, NO_ARG);
290 return parseMaximalExpr(b, minPrecedence);
296 String target = string;
297 ByteCode b = new ByteCode(curLine);
298 b.add(b.EXPR, prefix);
299 if (peekToken() == ASSIGN) {
301 Expr val = parseMaximalExpr();
302 b.add(b.LITERAL, target);
304 b.add(b.PUT, NO_ARG);
305 b.add(b.SWAP, NO_ARG);
306 b.add(b.POP, NO_ARG);
308 b.add(b.LITERAL, target);
309 b.add(b.GET, NO_ARG);
315 ByteCode b = new ByteCode(curLine);
316 if (prefix == null) {
317 b.add(b.ARRAY, new Integer(0));
320 Expr e = parseMaximalExpr();
321 if (e == null && peekToken() == RB) { consume(RB); return b; }
322 b.add(b.LITERAL, new Integer(i++));
323 if (e == null) b.add(b.LITERAL, null);
324 else b.add(b.EXPR, e);
325 b.add(b.PUT, NO_ARG);
326 b.add(b.POP, NO_ARG);
327 if (peekToken() == RB) { consume(RB); return b; }
331 b.add(b.EXPR, prefix);
332 b.add(b.EXPR, parseMaximalExpr());
334 if (peekToken() == ASSIGN) {
336 b.add(b.EXPR, parseMaximalExpr());
337 b.add(b.PUT, NO_ARG);
338 b.add(b.SWAP, NO_ARG);
339 b.add(b.POP, NO_ARG);
341 b.add(b.GET, NO_ARG);
350 if (prefix != null) { pushBackToken(); return prefix; }
351 ByteCode b = new ByteCode(curLine);
352 b.add(b.OBJECT, null);
353 if (peekToken() == RC) { consume(RC); return b; }
355 consume(NAME, STRING);
356 b.add(b.LITERAL, string);
358 b.add(b.EXPR, parseMaximalExpr());
359 b.add(b.PUT, NO_ARG);
360 b.add(b.POP, NO_ARG);
361 if (peekToken() == RC) { consume(RC); return b; }
363 if (peekToken() == RC) { consume(RC); return b; }
368 ByteCode b = new ByteCode(curLine);
369 b.add(b.EXPR, prefix);
370 b.add(b.JF, new Integer(3));
371 b.add(b.EXPR, parseMaximalExpr());
372 b.add(b.JMP, new Integer(2));
374 b.add(b.EXPR, parseMaximalExpr());
379 if (prefix != null) { pushBackToken(); return prefix; }
381 ByteCode b = new ByteCode(curLine);
383 b.add(b.THIS, NO_ARG);
384 b.add(b.SWAP, NO_ARG);
385 b.add(b.LITERAL, "arguments");
386 b.add(b.LITERAL, "arguments");
387 b.add(b.DECLARE, NO_ARG);
388 b.add(b.SWAP, NO_ARG);
389 b.add(b.PUT, NO_ARG);
390 b.add(b.SWAP, NO_ARG);
391 b.add(b.POP, NO_ARG);
392 if (peekToken() == RP) consume(RP);
394 if (peekToken() == COMMA) {
400 b.add(b.LITERAL, string);
401 b.add(b.DECLARE, NO_ARG);
403 // retrieve it from the arguments array
404 b.add(b.LITERAL, new Integer(numArgs));
405 b.add(b.GET_PRESERVE, NO_ARG);
406 b.add(b.SWAP, NO_ARG);
407 b.add(b.POP, NO_ARG);
409 // put it to the current scope
410 b.add(b.THIS, NO_ARG);
411 b.add(b.SWAP, NO_ARG);
412 b.add(b.LITERAL, string);
413 b.add(b.SWAP, NO_ARG);
414 b.add(b.PUT, NO_ARG);
417 b.add(b.POP, NO_ARG);
418 b.add(b.POP, NO_ARG);
420 if (peekToken() == RP) { consume(RP); break; }
425 // pop off the arguments array
426 b.add(b.POP, NO_ARG);
428 return new ByteCode(curLine, b.FUNCTION, b);
434 if (prefix != null) { pushBackToken(); return prefix; }
436 Expr switchExpr = parseMaximalExpr();
439 ExprList toplevel = new ExprList(curLine, LC);
443 if (tok == DEFAULT) caseExpr = null;
444 else if (tok == CASE) caseExpr = parseMaximalExpr();
445 else throw new ParserException("expected CASE or DEFAULT");
447 // FIXME: we shouldn't be creating a scope here
448 ExprList list = new ExprList(curLine, LC);
449 while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) {
450 if ((e1 = parseBlock(false)) == null) break;
453 toplevel.add(new Expr(curLine, tok, caseExpr, list));
454 if (peekToken() == RC) { consume(RC); return new Expr(curLine, SWITCH, switchExpr, toplevel); }
459 // We deliberately allow you to omit braces in catch{}/finally{} if they are single statements...
460 if (prefix != null) { pushBackToken(); return prefix; }
461 Expr tryBlock = parseBlock(true);
464 ExprList list = new ExprList(curLine, TRY);
467 if (getToken() != LP) throw new ParserException("expected (");
468 if (getToken() != NAME) throw new ParserException("expected name");
469 Expr name = new Expr(curLine, NAME, string);
470 if (getToken() != RP) throw new ParserException("expected )");
471 list.add(new Expr(curLine, CATCH, name, parseBlock(false)));
474 if (tok == FINALLY) {
476 list.add(new Expr(curLine, FINALLY, parseBlock(false)));
479 if (list.size() == 0) throw new ParserException("try without catch or finally");
480 return new Expr(curLine, TRY, tryBlock, list);
484 if (prefix != null) { pushBackToken(); return prefix; }
486 Expr parenExpr = parseMaximalExpr();
488 if ((t = getToken()) != RP)
489 throw new ParserException("expected right paren, but got " + codeToString[t]);
490 Expr firstBlock = parseBlock(false);
491 ExprList list = new ExprList(curLine, WHILE);
493 list.add(firstBlock);
494 list.add(new Expr(curLine, NULL));
499 if (prefix != null) { pushBackToken(); return prefix; }
500 if (getToken() != LP) throw new ParserException("expected left paren");
501 e1 = parseMaximalExpr();
502 if (peekToken() == IN) {
504 e2 = parseMaximalExpr();
505 if (getToken() != RP) throw new ParserException("expected right paren");
506 return new Expr(curLine, FOR, new Expr(curLine, IN, e1.left, e2), parseBlock(false));
510 if (initExpr == null) initExpr = new Expr(curLine, NULL);
511 expect(SEMI); getToken();
512 Expr whileExpr = parseMaximalExpr();
513 expect(SEMI); getToken();
514 Expr incExpr = parseMaximalExpr();
515 expect(RP); getToken();
516 Expr body = parseBlock(false);
517 Expr loop = new Expr(curLine, WHILE, whileExpr, body);
518 ExprList list = new ExprList(curLine, LC);
520 ExprList list2 = new ExprList(curLine, WHILE);
522 list2.add(whileExpr);
529 if (prefix != null) { pushBackToken(); return prefix; }
530 ExprList list = new ExprList(curLine, DO);
531 Expr body = parseBlock(false);
534 list.add(parseMaximalExpr());
536 list.add(new Expr(curLine, NULL));
548 // Expr //////////////////////////////////////////////////////////////////////
550 public static final Object NO_ARG = new Object();
552 class ByteCode extends Expr {
554 // This class interprets a small forthlike bytecode language
555 // we use to represent JavaScript. It's a stack machine.
557 // Each instruction is an opcode and a literal. Any operation
558 // that accesses the top of the stack and does not have a
559 // mandatory literal can be performed on a literal instead of
560 // the top of the stack.
563 public static final byte arithmetic = -1; // -- arithmetic operators from parser
564 public static final byte LITERAL = -2; // < String | Number | null > -- push a literal onto the stack
565 public static final byte ARRAY = -3; // < size > -- create a new array of size <size>
566 public static final byte OBJECT = -4; // -- push an empty object onto the stack
567 public static final byte FUNCTION = -5; // < bytecode_block > -- push a new instance of a function with the given bytecode
568 public static final byte DECLARE = -6; // < name > -- declare <name> in the current scope
569 public static final byte THIS = -7; // -- push the topmost non-transparent scope onto the stack
570 public static final byte GET = -8; // -- get stack[0] from stack[1]
571 public static final byte GET_PRESERVE = -80; // -- get stack[0] from stack[1]
572 public static final byte PUT = -9; // -- put stack[1] to key stack[0] on stack[2]; leaves object on the stack
573 public static final byte THROW = -10; // -- throw topmost element
574 public static final byte RETURN = -11; // -- return the topmost value on the stack
575 public static final byte ASSERT = -12; // -- fail if topmost element is not true
576 public static final byte JT = -13; // < relative_address > -- pop the stack; if true, jump to <relative_address>
577 public static final byte JF = -21; // < relative_address > -- pop the stack; if false, jump to <relative_address>
578 public static final byte JMP = -22; // < relative_address > -- jump to <relative_address>
579 static public final byte POP = -14; // -- discard the top element on the stack
581 public static final byte CALL = -15; // < numargs > -- call stack[0] with the topmost <numargs> values as arguments
582 public static final byte TRY = -16; // < bytecode_block > -- run the block pointed to; returns with thrown exn on top of stack
584 public static final byte INSTANCEOF = -17; // -- ??
585 public static final byte TYPEOF = -18; // --
587 public static final byte FOR__IN = -19; // -- ??
588 public static final byte EXPR = -20; // -- transitional
589 public static final byte SWAP = -23; // -- transitional
590 public static final byte SCOPE = -30; // -- transitional
592 int[] op = new int[10];
593 Object[] arg = new Object[10];
596 public ByteCode(int line) { super(line, "foo"); }
597 public ByteCode(int line, int op_, Object arg_) { this(line); add(op_, arg_); }
599 public int size() { return size; }
600 public void set(int pos, int op_, Object arg_) { op[pos] = op_; arg[pos] = arg_; }
601 public ByteCode add(int op_, Object arg_) {
602 if (size == op.length - 1) {
603 int[] op2 = new int[op.length * 2]; System.arraycopy(op, 0, op2, 0, op.length); op = op2;
604 Object[] arg2 = new Object[op.length * 2]; System.arraycopy(arg, 0, arg2, 0, arg.length); arg = arg2;
612 public Object eval(final JS.Scope s) throws ControlTransferException, JS.Exn {
613 return eval(s, new Parser.Thread());
615 public Object eval(final JS.Scope s, Parser.Thread t) throws ControlTransferException {
616 for(int i=0; i<size; i++)
618 case arithmetic: break;
622 case OBJECT: t.push(new JS.Obj()); break;
623 case ARRAY: t.push(new JS.Array(toNumber(arg[i]).intValue())); break;
624 case DECLARE: s.declare(arg[i] == NO_ARG ? (String)t.pop() : (String)arg[i]); break;
625 case THIS: t.push(s); break; // FIXME: transparents
626 case ASSERT: if (!toBoolean(t.pop())) throw new EvaluatorException("assertion failed"); break;
627 case THROW: throw new JS.Exn(t.pop());
628 case JT: if (toBoolean(t.pop())) i += toNumber(arg[i]).intValue() - 1; break;
629 case JF: if (!toBoolean(t.pop())) i += toNumber(arg[i]).intValue() - 1; break;
630 case JMP: i += toNumber(arg[i]).intValue() - 1; break;
631 case POP: t.pop(); break;
632 case SWAP: t.swap(); break;
635 Object val = arg[i] == NO_ARG ? t.pop() : arg[i];
636 Object key = t.pop();
637 ((JS)t.peek()).put(key, val);
643 Object v = arg[i] == NO_ARG ? t.pop() : arg[i];
657 case EXPR: t.push(((Expr)arg[i]).eval(s)); break;
658 case SCOPE: t.push(((Expr)arg[i]).eval(new JS.Scope(s))); break;
661 JS.Array arguments = new JS.Array();
662 int numArgs = toNumber(arg[i]).intValue();
663 arguments.setSize(numArgs);
664 for(int j=numArgs - 1; j >= 0; j--) arguments.setElementAt(t.pop(), j);
665 JS.Function f = (JS.Function)t.pop();
666 if (f == null) throw new JS.Exn(new EvaluatorException("attempted to call null"));
667 t.push(f.call(arguments));
671 final ByteCode myBytes = (ByteCode)arg[i];
672 t.push(new JS.Function() {
673 public String toString() { return sourceName + ":" + line; }
674 public String getSourceName() throws JS.Exn { return sourceName; }
675 public Object _call(final JS.Array args) throws JS.Exn {
676 Function save = JS.getCurrentFunction();
677 JS.currentFunction.put(java.lang.Thread.currentThread(), this);
678 JS.Scope scope = new JS.Scope(s) {
680 public String getSourceName() { return sourceName; }
681 public Object get(Object key) throws JS.Exn {
682 if (key.equals("trapee")) return org.xwt.Trap.currentTrapee();
683 else if (key.equals("cascade")) return org.xwt.Trap.cascadeFunction;
684 return super.get(key);
687 Parser.Thread t0 = new Parser.Thread();
690 return myBytes.eval(scope, t0);
691 } catch (ReturnException r) {
693 } catch (ControlTransferException c) {
694 throw new EvaluatorException("error, ControlTransferException tried to leave a function: " + c);
696 if (save == null) JS.currentFunction.remove(java.lang.Thread.currentThread());
697 else JS.currentFunction.put(java.lang.Thread.currentThread(), save);
706 case INSTANCEOF: break;
710 case Lexer.BITNOT: t.push(new Long(~toLong(t.pop()))); break;
711 case Lexer.BANG: t.push(new Boolean(!toBoolean(t.pop()))); break;
713 case Lexer.INC: case Lexer.DEC: {
714 boolean isPrefix = toBoolean(arg[i]);
715 Object key = t.pop();
716 JS obj = (JS)t.pop();
717 Number num = toNumber(obj.get(key));
718 Number val = new Double(op[i] == Lexer.INC ? num.doubleValue() + 1.0 : num.doubleValue() - 1.0);
720 t.push(isPrefix ? val : num);
725 Object right = t.pop();
726 Object left = t.pop();
729 case Lexer.BITOR: t.push(new Long(toLong(left) | toLong(right))); break;
730 case Lexer.BITXOR: t.push(new Long(toLong(left) ^ toLong(right))); break;
731 case Lexer.BITAND: t.push(new Long(toLong(left) & toLong(right))); break;
736 if (l instanceof String || r instanceof String) {
737 if (l == null) l = "null";
738 if (r == null) r = "null";
739 if (l instanceof Number && ((Number)l).doubleValue() == ((Number)l).longValue())
740 l = new Long(((Number)l).longValue());
741 if (r instanceof Number && ((Number)r).doubleValue() == ((Number)r).longValue())
742 r = new Long(((Number)r).longValue());
743 t.push(l.toString() + r.toString()); break;
745 t.push(new Double(toDouble(l) + toDouble(r))); break;
748 case Lexer.SUB: t.push(new Double(toDouble(left) - toDouble(right))); break;
749 case Lexer.MUL: t.push(new Double(toDouble(left) * toDouble(right))); break;
750 case Lexer.DIV: t.push(new Double(toDouble(left) / toDouble(right))); break;
751 case Lexer.MOD: t.push(new Double(toDouble(left) % toDouble(right))); break;
753 case Lexer.LSH: t.push(new Long(toLong(left) << toLong(right))); break;
754 case Lexer.RSH: t.push(new Long(toLong(left) >> toLong(right))); break;
755 case Lexer.URSH: t.push(new Long(toLong(left) >>> toLong(right))); break;
757 // FIXME: these need to work on strings
758 case Lexer.LT: t.push(toDouble(left) < toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break;
759 case Lexer.LE: t.push(toDouble(left) <= toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break;
760 case Lexer.GT: t.push(toDouble(left) > toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break;
761 case Lexer.GE: t.push(toDouble(left) >= toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break;
765 // FIXME: should use Javascript coercion-equality rules
769 if (l == null) { Object tmp = r; r = l; l = tmp; }
770 if (l == null && r == null) ret = true;
771 else if (l instanceof Boolean) ret = new Boolean(toBoolean(r)).equals(l);
772 else if (l instanceof Number) ret = toNumber(r).doubleValue() == toNumber(l).doubleValue();
773 else if (l instanceof String) ret = r != null && l.equals(r.toString());
774 else ret = l.equals(r);
775 t.push(new Boolean(op[i] == Lexer.EQ ? ret : !ret)); break;
778 default: throw new Error("unknown opcode " + op[i]);
782 throw new EvaluatorException("eval() terminated with " + t.size() + " elements on the stack; one expected");
787 public Object doGet(final Object o, final Object v) {
788 if (o == null) throw new EvaluatorException("tried to get property \"" + v + "\" from the null value");
789 if (o instanceof String) {
790 if (v.equals("length")) return new Integer(((String)o).length());
791 else if (v.equals("substring")) return new JS.Function() {
792 public Object _call(JS.Array args) {
793 if (args.length() == 1) return ((String)o).substring(toNumber(args.elementAt(0)).intValue());
794 else if (args.length() == 2) return ((String)o).substring(toNumber(args.elementAt(0)).intValue(),
795 toNumber(args.elementAt(1)).intValue());
796 else throw new Error("String.substring() can only take one or two arguments");
799 else if (v.equals("toLowerCase")) return new JS.Function() {
800 public Object _call(JS.Array args) {
801 return ((String)o).toLowerCase();
803 else if (v.equals("toUpperCase")) return new JS.Function() {
804 public Object _call(JS.Array args) {
805 return ((String)o).toString().toUpperCase();
807 else if (v.equals("charAt")) return new JS.Function() {
808 public Object _call(JS.Array args) {
809 return ((String)o).charAt(toNumber(args.elementAt(0)).intValue()) + "";
811 else if (v.equals("lastIndexOf")) return new JS.Function() {
812 public Object _call(JS.Array args) {
813 if (args.length() != 1) return null;
814 return new Integer(((String)o).lastIndexOf(args.elementAt(0).toString()));
816 else if (v.equals("indexOf")) return new JS.Function() {
817 public Object _call(JS.Array args) {
818 if (args.length() != 1) return null;
819 return new Integer(((String)o).indexOf(args.elementAt(0).toString()));
821 throw new Error("Not Implemented: propery " + v + " on String objects");
822 } else if (o instanceof Boolean) {
823 throw new Error("Not Implemented: properties on Boolean objects");
824 } else if (o instanceof Number) {
825 Log.log(this, "Not Implemented: properties on Number objects");
827 //throw new Error("Not Implemented: properties on Number objects");
828 } else if (o instanceof JS) {
829 return ((JS)o).get(v);
836 public Object[] os = new Object[256];
838 public void push(Object o) { os[size++] = o; }
839 public Object pop() { return os[--size]; }
840 public Object peek() { return os[size - 1]; }
841 public void swap() { Object temp = os[size - 1]; os[size - 1] = os[size - 2]; os[size - 2] = temp; }
842 public int size() { return size; }
845 class ExprList extends Expr {
847 public ExprList(int curLine, int code) { super(curLine, code); }
848 public void add(Expr e) { v.addElement(e); }
849 public int numExprs() { return v.size(); }
850 public int size() { return v.size(); }
851 public Expr elementAt(int i) { return (Expr)v.elementAt(i); }
852 public Object eval(final JS.Scope s) throws ControlTransferException, JS.Exn {
856 JS.Scope scope = new JS.Scope(s);
857 for(int i=0; i<v.size(); i++) ((Expr)v.elementAt(i)).eval(scope);
862 JS.Array ret = new JS.Array();
863 for(int i=0; i<numExprs(); i++) ret.addElement(elementAt(i).eval(s));
868 JS.Obj ret = new JS.Obj();
869 for(int i=0; i<numExprs(); i++) {
870 Expr e = elementAt(i);
871 Object key = e.left.string;
872 Object val = e.right.eval(s);
880 boolean first = true;
882 ExprList list = this;
883 Expr whileExpr = list.elementAt(0);
884 Expr bodyExpr = list.elementAt(1);
885 Expr incExpr = list.elementAt(2);
886 for(;(first && code == Lexer.DO) || toBoolean(whileExpr.eval(s)); bogus = incExpr.eval(s)) {
890 } catch (ContinueException c) {
891 if (c.label == null || c.label.equals(string)) continue;
892 } catch (BreakException b) {
893 if (b.label == null || b.label.equals(string)) return null;
894 throw (BreakException)b.fillInStackTrace();
897 } catch (BreakException e) {
898 if (e.label != null && !e.label.equals(string)) throw e;
902 for(int i=0; i<size(); i++) {
903 Expr e = elementAt(i);
904 s.declare(e.left.string);
905 if (e.right != null) e.right.eval(s);
908 default: throw new Error("augh!");
913 /** a block is either a single statement or a list of statements surrounded by curly braces; all expressions are also statements */
914 public Expr parseBlock(boolean requireBraces) throws IOException { return parseBlock(requireBraces, null); }
915 public Expr parseBlock(boolean requireBraces, ByteCode b) throws IOException {
917 int tok = peekToken();
918 if (tok == -1) return null;
919 boolean braced = tok == LC;
920 if (requireBraces && !braced) throw new ParserException("expected {, got " + codeToString[tok]);
921 if (braced) consume(LC);
923 ByteCode ret = new ByteCode(curLine);
924 ByteCode block = b == null ? new ByteCode(curLine) : b;
925 block.add(ret.LITERAL, Boolean.TRUE);
926 ret.add(block.SCOPE, block);
928 switch(tok = peekToken()) {
931 smt = parseBlock(true); break;
933 case THROW: case RETURN: case ASSERT:
935 if (peekToken() == SEMI) {
936 if (tok == THROW || tok == ASSERT) throw new ParserException(codeToString[tok] + " requires an argument");
938 smt = new Expr(curLine, tok);
940 smt = new Expr(curLine, tok, parseMaximalExpr());
945 case GOTO: case BREAK: case CONTINUE: {
950 smt = new Expr(curLine, tok, new Expr(curLine, string));
951 } else if (tok == GOTO) {
952 throw new ParserException("goto must be followed by a label");
954 smt = new Expr(curLine, tok, new Expr(curLine, string));
960 if (braced) consume(RC);
961 return block.size() == 0 ? null : ret;
965 if (!braced) return block.size() == 0 ? null : ret;
969 String name = string;
971 if (peekToken() == COLON) {
973 smt = new Expr(curLine, COLON, new Expr(curLine, name), parseBlock(false));
976 pushBackToken(NAME, name);
977 // fall through to default case
983 smt = parseMaximalExpr();
984 if (smt == null) return block.size() == 0 ? null : ret;
985 if (peekToken() == SEMI) getToken();
989 if (!braced) return smt;
990 block.add(block.EXPR, smt);
991 block.add(block.POP, NO_ARG);
996 /** sorta like gcc trees */
1003 String sourceName = "unknown";
1005 String string = null;
1006 Number number = null;
1008 public String toString() { return toString(0); }
1009 public String toString(int indent) {
1011 for(int i=0; i<indent; i++) ret += " ";
1012 ret += Lexer.codeToString[code];
1013 if (code == Lexer.NUMBER) ret += " " + number;
1014 else if (string != null) ret += " \"" + string + "\"";
1016 if (left != null) ret += left.toString(indent + 2);
1017 if (right != null) ret += right.toString(indent + 2);
1021 public Expr(int line, String s) { this(line, Lexer.STRING); this.string = s; } // an identifier or label
1022 public Expr(int line, int code, String s) { this(line, code); this.string = s; }
1023 public Expr(int line, Number n) { this(line, Lexer.NUMBER); this.number = n; } // an identifier or label
1024 public Expr(int line, int code) { this(line, code, null, null); }
1025 public Expr(int line, int code, Expr left) { this(line, code, left, null); }
1026 public Expr(int line, int code, Expr left, Expr right) {
1027 this.code = code; this.left = left; this.right = right;
1029 this.sourceName = Parser.this.sourceName;
1032 public double toDouble(Object o) { return toNumber(o).doubleValue(); }
1033 public long toLong(Object o) { return toNumber(o).longValue(); }
1034 public boolean toBoolean(Object o) {
1035 if (o == null) return false;
1036 if (o instanceof Boolean) return ((Boolean)o).booleanValue();
1037 if (o instanceof Number) return o.equals(new Integer(0));
1041 public Object eval(final JS.Scope s) throws ControlTransferException, JS.Exn {
1044 case Lexer.NULL: return null;
1045 case Lexer.TYPEOF: {
1046 Object o = left.eval(s);
1047 if (o == null) return "null";
1048 if (o.getClass() == String.class) return "string";
1049 if (o.getClass() == Boolean.class) return "boolean";
1050 if (o instanceof Number) return "number";
1051 if (o instanceof JS.Array) return "array";
1052 if (o instanceof JS) return "object";
1053 throw new EvaluatorException("typeof " + o.getClass().getName() + " unknown");
1056 case Lexer.THROW: throw new JS.Exn(left.eval(s));
1059 boolean safeToExit = false;
1061 Object ret = left.eval(s);
1064 } catch (JS.Exn e) {
1065 ExprList list = (ExprList)right;
1066 Expr c = list.elementAt(0);
1067 if (c.code == Lexer.CATCH) {
1068 JS.Scope scope = new JS.Scope(s);
1069 s.put(c.left.string, e);
1070 c.right.eval(scope);
1071 c = list.elementAt(1);
1073 if (c.code == Lexer.FINALLY) {
1074 JS.Scope scope = new JS.Scope(s);
1078 if (!safeToExit) Log.log(this, "WARNING: Java exception penetrated a JavaScript try{} block");
1084 Object[] keys = ((JS)left.right.eval(s)).keys();
1085 for(int i=0; i<keys.length; i++) {
1086 JS.Scope scope = new JS.Scope(s);
1087 scope.declare(left.string);
1088 scope.put(left.string, keys[i]);
1091 } catch (ContinueException c) {
1092 if (c.label == null || c.label.equals(string)) continue;
1093 } catch (BreakException b) {
1094 if (b.label == null || b.label.equals(string)) return null;
1095 throw (BreakException)b.fillInStackTrace();
1101 Object switchVal = left.eval(s);
1104 ExprList list = (ExprList)right;
1105 for(int i=0; i<list.size(); i++) {
1106 Expr e = list.elementAt(i);
1107 if (go || e.code == Lexer.DEFAULT || e.left.eval(s).equals(switchVal)) go = true;
1108 if (go) e.right.eval(s);
1110 } catch (BreakException b) {
1111 if (b.label == null || b.label.equals(string)) return null;
1112 throw (BreakException)b.fillInStackTrace();
1116 case Lexer.BREAK: throw new BreakException(string);
1117 case Lexer.CONTINUE: throw new ContinueException(string);
1118 case Lexer.RETURN: throw new ReturnException(left == null ? null : left.eval(s));
1120 default: throw new EvaluatorException("don't know how to eval an Expr with code " + Lexer.codeToString[code] + "\n" + this);
1124 class EvaluatorException extends RuntimeException {
1125 public EvaluatorException(String s) { super(sourceName + ":" + line + " " + s); }
1129 static abstract class ControlTransferException extends Exception { }
1130 static class BreakException extends ControlTransferException {
1131 public String label;
1132 BreakException(String label) { this.label = label; }
1134 static class ContinueException extends ControlTransferException {
1135 public String label;
1136 ContinueException(String label) { this.label = label; }
1138 static class ReturnException extends ControlTransferException {
1139 public Object retval;
1140 ReturnException(Object retval) { this.retval = retval; }
1143 class ParserException extends RuntimeException {
1144 public ParserException(String s) { super(sourceName + ":" + line + " " + s); }
1147 public static Number toNumber(Object o) {
1148 if (o == null) return new Long(0);
1149 if (o instanceof Number) return ((Number)o);
1150 if (o instanceof String) try { return new Double((String)o); } catch (NumberFormatException e) { return new Double(0); }
1151 if (o instanceof Boolean) return ((Boolean)o).booleanValue() ? new Long(1) : new Long(0);
1152 if (o instanceof JS) return ((JS)o).coerceToNumber();
1154 throw new Error("toNumber() got object of type " + o.getClass().getName());