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 expect(int code) throws IOException {
66 int got = peekToken();
67 if (got != code) throw new ParserException("expected " + codeToString[code] + ", got " + (got == -1 ? "EOL" : codeToString[got]));
70 /** parses the largest possible expression */
71 public Expr parseMaximalExpr() throws IOException { return parseMaximalExpr(null, -1); }
72 public Expr parseMaximalExpr(Expr prefix, int minPrecedence) throws IOException {
74 if (peekToken() == -1) break;
76 prefix = parseSingleExpr(prefix, minPrecedence);
77 if (save == prefix) break;
78 if (prefix == null) throw new ParserException("parseSingleExpr() returned null");
83 public Expr parseSingleExpr(Expr prefix, int minPrecedence) throws IOException {
84 Expr e1 = null, e2 = null, e3 = null, head = null, tail = null, ret = null;
86 int tok = peekToken();
87 if (minPrecedence > 0 &&
88 tok < precedence.length &&
89 precedence[tok] != 0 &&
90 (isRightAssociative[tok] ?
91 (precedence[tok] < minPrecedence) :
92 (precedence[tok] <= minPrecedence))) {
98 // these case arms match the precedence of operators; each arm is a precedence level.
101 case WITH: throw new ParserException("XWT does not allow the WITH keyword");
102 case VOID: case RESERVED: throw new ParserException("reserved word that you shouldn't be using");
103 case NAME: if (prefix != null) { pushBackToken(); return prefix; } else return parseMaximalExpr(new Expr(curLine, NAME, string), minPrecedence);
104 case STRING: if (prefix != null) { pushBackToken(); return prefix; } else return new Expr(curLine, string);
105 case NUMBER: if (prefix != null) { pushBackToken(); return prefix; } else return new Expr(curLine, number);
106 case NULL: case TRUE: case FALSE: case NOP: if (prefix != null) { pushBackToken(); return prefix; } else return new Expr(curLine, tok);
108 case ASSIGN_BITOR: case ASSIGN_BITXOR: case ASSIGN_BITAND: case ASSIGN_LSH:
109 case ASSIGN_RSH: case ASSIGN_URSH: case ASSIGN_ADD: case ASSIGN_SUB: case ASSIGN_MUL: case ASSIGN_DIV: case ASSIGN_MOD:
110 return new Expr(curLine, ASSIGN, prefix, new Expr(curLine, tok - 1, prefix, parseMaximalExpr(null, precedence[ASSIGN])));
112 case COMMA: pushBackToken();return prefix;
115 if (prefix != null) { pushBackToken(); return prefix; }
116 return new Expr(curLine, THIS);
119 if (prefix == null && peekToken() == NUMBER) {
121 return new Expr(curLine, new Double(number.doubleValue() * -1));
122 } // else fall through
123 case BITOR: case BITXOR: case BITAND: case SHEQ: case SHNE: case LSH:
124 case RSH: case URSH: case ADD: case MUL: case DIV: case MOD:
125 case ASSIGN: case GT: case GE: case OR: case AND:
126 case EQ: case NE: case LT: case LE:
127 if (prefix == null) throw new ParserException("the " + codeToString[tok] + " token cannot start an expression");
128 return new Expr(curLine, tok, prefix, parseMaximalExpr(null, precedence[tok]));
131 //e1 = parseMaximalExpr(null, precedence[DOT]);
132 //if (e1.code == NAME) e1.code = STRING;
133 //else throw new ParserException("argument to DOT must be a NAME; got a " + codeToString[e1.code]);
134 //return new Expr(curLine, DOT, prefix, e1);
136 if (tok != NAME) throw new ParserException("DOT must be followed by a NAME");
137 return new Expr(curLine, DOT, prefix, new Expr(curLine, STRING, string));
140 case BANG: case BITNOT: case INSTANCEOF: case TYPEOF:
141 if (prefix != null) { pushBackToken(); return prefix; }
142 return new Expr(curLine, tok, parseMaximalExpr(null, precedence[tok]));
145 if (prefix == null) {
147 e1 = parseMaximalExpr(null, precedence[tok]);
148 return new Expr(curLine, ASSIGN, e1, new Expr(curLine, tok == INC ? ADD : SUB, e1, new Expr(curLine, new Integer(1))));
151 return new Expr(curLine, tok, prefix);
155 if (prefix == null) { // grouping
156 Expr r = parseMaximalExpr();
157 expect(RP); getToken();
160 } else { // invocation
161 ExprList list = new ExprList(curLine, LP);
162 while(peekToken() != RP) {
163 list.add(parseMaximalExpr());
164 if (peekToken() == RP) break;
168 return new Expr(curLine, LP, prefix, list);
172 if (prefix == null) {
174 ExprList list = new ExprList(curLine, LB);
176 Expr e = parseMaximalExpr();
180 if (peekToken() == COMMA) list.add(new Expr(curLine, NULL));
182 if (peekToken() == RB) { consume(RB); return list; }
187 e1 = parseMaximalExpr();
188 if (getToken() != RB) throw new ParserException("expected a right brace");
189 return new Expr(curLine, DOT, prefix, e1);
194 if (prefix != null) { pushBackToken(); return prefix; }
196 ExprList list = new ExprList(curLine, RC);
197 if (tok == RC) return list;
199 if (tok != NAME && tok != STRING) throw new ParserException("expecting name, got " + codeToString[tok]);
200 Expr name = new Expr(curLine, NAME, string);
201 if (getToken() != COLON) throw new ParserException("expecting colon");
202 e1 = new Expr(curLine, COLON, name, parseMaximalExpr());
205 if (tok != COMMA && tok != RC) throw new ParserException("expected right curly or comma, got " + codeToString[tok]);
206 if (tok == RC) return list;
208 if (tok == RC) return list;
213 e2 = parseMaximalExpr();
214 if (getToken() != COLON) throw new ParserException("expected colon to close ?: expression");
215 e3 = parseMaximalExpr();
216 return new Expr(curLine, HOOK, prefix, new Expr(curLine, ELSE, e2, e3));
219 if (prefix != null) { pushBackToken(); return prefix; }
220 if (getToken() != LP) throw new ParserException("expected left paren");
221 Expr switchExpr = parseMaximalExpr();
222 if (getToken() != RP) throw new ParserException("expected left paren");
223 if (getToken() != LC) throw new ParserException("expected left brace");
224 ExprList toplevel = new ExprList(curLine, LC);
228 if (tok == DEFAULT) caseExpr = null;
229 else if (tok == CASE) caseExpr = parseMaximalExpr();
230 else throw new ParserException("expected CASE or DEFAULT");
232 // FIXME: we shouldn't be creating a scope here
233 ExprList list = new ExprList(curLine, LC);
234 while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) {
235 if ((e1 = parseBlock(false)) == null) break;
238 toplevel.add(new Expr(curLine, tok, caseExpr, list));
239 if (peekToken() == RC) { consume(RC); return new Expr(curLine, SWITCH, switchExpr, toplevel); }
244 if (prefix != null) { pushBackToken(); return prefix; }
246 ExprList list = new ExprList(curLine, LC);
247 if (peekToken() == RP) consume(RP);
252 list.add(new Expr(curLine, NULL));
255 list.add(new Expr(curLine, NAME, string));
256 if (peekToken() == RP) { consume(RP); break; }
260 return new Expr(curLine, FUNCTION, list, parseBlock(true));
264 if (prefix != null) { pushBackToken(); return prefix; }
265 ExprList list = new ExprList(curLine, VAR);
267 if (getToken() != NAME) throw new ParserException("variable declarations must start with a variable name");
268 String name = string;
274 initVal = parseMaximalExpr();
276 e = new Expr(curLine, VAR,
277 new Expr(curLine, name),
278 new Expr(curLine, ASSIGN,
279 new Expr(curLine, name),
282 e = new Expr(curLine, VAR, new Expr(curLine, name));
285 if (tok != COMMA) break;
292 // We deliberately allow you to omit braces in catch{}/finally{} if they are single statements...
293 if (prefix != null) { pushBackToken(); return prefix; }
294 Expr tryBlock = parseBlock(true);
297 ExprList list = new ExprList(curLine, TRY);
300 if (getToken() != LP) throw new ParserException("expected (");
301 if (getToken() != NAME) throw new ParserException("expected name");
302 Expr name = new Expr(curLine, NAME, string);
303 if (getToken() != RP) throw new ParserException("expected )");
304 list.add(new Expr(curLine, CATCH, name, parseBlock(false)));
307 if (tok == FINALLY) {
309 list.add(new Expr(curLine, FINALLY, parseBlock(false)));
312 if (list.size() == 0) throw new ParserException("try without catch or finally");
313 return new Expr(curLine, TRY, tryBlock, list);
316 case IF: case WHILE: {
317 if (prefix != null) { pushBackToken(); return prefix; }
318 if (getToken() != LP) throw new ParserException("expected left paren");
319 Expr parenExpr = parseMaximalExpr();
321 if ((t = getToken()) != RP) throw new ParserException("expected right paren, but got " + codeToString[t]);
322 Expr firstBlock = parseBlock(false);
323 if (tok == IF && peekToken() == ELSE) {
325 return new Expr(curLine, tok, parenExpr, new Expr(curLine, ELSE, firstBlock, parseBlock(false)));
327 if (tok == IF) return new Expr(curLine, tok, parenExpr, firstBlock);
329 ExprList list = new ExprList(curLine, WHILE);
331 list.add(firstBlock);
332 list.add(new Expr(curLine, NULL));
338 case IN: pushBackToken(); return prefix;
341 if (prefix != null) { pushBackToken(); return prefix; }
342 if (getToken() != LP) throw new ParserException("expected left paren");
343 e1 = parseMaximalExpr();
344 if (peekToken() == IN) {
346 e2 = parseMaximalExpr();
347 if (getToken() != RP) throw new ParserException("expected right paren");
348 return new Expr(curLine, FOR, new Expr(curLine, IN, e1.left, e2), parseBlock(false));
352 if (initExpr == null) initExpr = new Expr(curLine, NULL);
353 expect(SEMI); getToken();
354 Expr whileExpr = parseMaximalExpr();
355 expect(SEMI); getToken();
356 Expr incExpr = parseMaximalExpr();
357 expect(RP); getToken();
358 Expr body = parseBlock(false);
359 Expr loop = new Expr(curLine, WHILE, whileExpr, body);
360 ExprList list = new ExprList(curLine, LC);
362 ExprList list2 = new ExprList(curLine, WHILE);
364 list2.add(whileExpr);
371 if (prefix != null) { pushBackToken(); return prefix; }
372 ExprList list = new ExprList(curLine, DO);
373 Expr body = parseBlock(false);
376 list.add(parseMaximalExpr());
378 list.add(new Expr(curLine, NULL));
390 // Expr //////////////////////////////////////////////////////////////////////
392 class ExprList extends Expr {
394 public ExprList(int curLine, int code) { super(curLine, code); }
395 public void add(Expr e) { v.addElement(e); }
396 public int numExprs() { return v.size(); }
397 public int size() { return v.size(); }
398 public Expr elementAt(int i) { return (Expr)v.elementAt(i); }
399 public Object eval(final JS.Scope s) throws ControlTransferException, JS.Exn {
403 JS.Scope scope = new JS.Scope(s);
404 for(int i=0; i<v.size(); i++) ((Expr)v.elementAt(i)).eval(scope);
409 JS.Array ret = new JS.Array();
410 for(int i=0; i<numExprs(); i++) ret.addElement(elementAt(i).eval(s));
415 JS.Obj ret = new JS.Obj();
416 for(int i=0; i<numExprs(); i++) {
417 Expr e = elementAt(i);
418 Object key = e.left.string;
419 Object val = e.right.eval(s);
427 boolean first = true;
429 ExprList list = this;
430 Expr whileExpr = list.elementAt(0);
431 Expr bodyExpr = list.elementAt(1);
432 Expr incExpr = list.elementAt(2);
433 for(;(first && code == Lexer.DO) || toBoolean(whileExpr.eval(s)); bogus = incExpr.eval(s)) {
435 try { bodyExpr.eval(s);
436 } catch (ContinueException c) {
437 if (c.label == null || c.label.equals(string)) continue;
438 } catch (BreakException b) {
439 if (b.label == null || b.label.equals(string)) return null;
440 throw (BreakException)b.fillInStackTrace();
443 } catch (BreakException e) {
444 if (e.label != null && !e.label.equals(string)) throw e;
448 for(int i=0; i<size(); i++) {
449 Expr e = elementAt(i);
450 s.declare(e.left.string);
451 if (e.right != null) e.right.eval(s);
454 default: throw new Error("augh!");
459 /** a block is either a single statement or a list of statements surrounded by curly braces; all expressions are also statements */
460 public Expr parseBlock(boolean requireBraces) throws IOException {
462 int tok = peekToken();
463 if (tok == -1) return null;
464 boolean braced = tok == LC;
465 if (requireBraces && !braced) throw new ParserException("expected {, got " + codeToString[tok]);
466 if (braced) getToken();
468 ExprList block = new ExprList(curLine, LC);
470 switch(tok = peekToken()) {
476 smt = parseBlock(true); break;
478 case THROW: case RETURN: case ASSERT:
480 if (peekToken() == SEMI) {
481 if (tok == THROW || tok == ASSERT) throw new ParserException(codeToString[tok] + " requires an argument");
483 smt = new Expr(curLine, tok);
485 smt = new Expr(curLine, tok, parseMaximalExpr());
490 case GOTO: case BREAK: case CONTINUE: {
495 smt = new Expr(curLine, tok, new Expr(curLine, string));
496 } else if (tok == GOTO) {
497 throw new ParserException("goto must be followed by a label");
499 smt = new Expr(curLine, tok, new Expr(curLine, string));
505 if (braced) consume(RC);
510 if (!braced) return block;
515 String name = string;
517 if (peekToken() == COLON) {
518 smt = new Expr(curLine, COLON, new Expr(curLine, name), parseBlock(false));
523 // fall through to default case
528 smt = parseMaximalExpr();
530 if (block.numExprs() == 0) return null;
533 if (peekToken() == SEMI) getToken();
537 if (!braced) return smt;
543 /** sorta like gcc trees */
550 String sourceName = "unknown";
552 String string = null;
553 Number number = null;
555 public String toString() { return toString(0); }
556 public String toString(int indent) {
558 for(int i=0; i<indent; i++) ret += " ";
559 ret += Lexer.codeToString[code];
560 if (code == Lexer.NUMBER) ret += " " + number;
561 else if (string != null) ret += " \"" + string + "\"";
563 if (left != null) ret += left.toString(indent + 2);
564 if (right != null) ret += right.toString(indent + 2);
568 public Expr(int line, String s) { this(line, Lexer.STRING); this.string = s; } // an identifier or label
569 public Expr(int line, int code, String s) { this(line, code); this.string = s; }
570 public Expr(int line, Number n) { this(line, Lexer.NUMBER); this.number = n; } // an identifier or label
571 public Expr(int line, int code) { this(line, code, null, null); }
572 public Expr(int line, int code, Expr left) { this(line, code, left, null); }
573 public Expr(int line, int code, Expr left, Expr right) {
574 this.code = code; this.left = left; this.right = right;
576 this.sourceName = Parser.this.sourceName;
579 public double toDouble(Object o) { return toNumber(o).doubleValue(); }
580 public long toLong(Object o) { return toNumber(o).longValue(); }
581 public boolean toBoolean(Object o) {
582 if (o == null) return false;
583 if (o instanceof Boolean) return ((Boolean)o).booleanValue();
584 if (o instanceof Number) return o.equals(new Integer(0));
588 public Object eval(final JS.Scope s) throws ControlTransferException, JS.Exn {
591 case Lexer.BITOR: return new Long(toLong(left.eval(s)) | toLong(right.eval(s)));
592 case Lexer.BITXOR: return new Long(toLong(left.eval(s)) ^ toLong(right.eval(s)));
593 case Lexer.BITAND: return new Long(toLong(left.eval(s)) & toLong(right.eval(s)));
594 case Lexer.BITNOT: return new Long(~toLong(left.eval(s)));
597 Object l = left.eval(s);
598 Object r = right.eval(s);
599 if (l instanceof String || r instanceof String) {
600 if (l == null) l = "null";
601 if (r == null) r = "null";
602 if (l instanceof Number && ((Number)l).doubleValue() == ((Number)l).longValue())
603 l = new Long(((Number)l).longValue());
604 if (r instanceof Number && ((Number)r).doubleValue() == ((Number)r).longValue())
605 r = new Long(((Number)r).longValue());
606 return l.toString() + r.toString();
608 return new Double(toDouble(l) + toDouble(r));
611 case Lexer.SUB: return new Double(toDouble(left.eval(s)) - toDouble(right.eval(s)));
612 case Lexer.MUL: return new Double(toDouble(left.eval(s)) * toDouble(right.eval(s)));
613 case Lexer.DIV: return new Double(toDouble(left.eval(s)) / toDouble(right.eval(s)));
614 case Lexer.MOD: return new Double(toDouble(left.eval(s)) % toDouble(right.eval(s)));
616 case Lexer.LSH: return new Long(toLong(left.eval(s)) << toLong(right.eval(s)));
617 case Lexer.RSH: return new Long(toLong(left.eval(s)) >> toLong(right.eval(s)));
618 case Lexer.URSH: return new Long(toLong(left.eval(s)) >>> toLong(right.eval(s)));
620 // FIXME: these need to work on strings
621 case Lexer.LT: return toDouble(left.eval(s)) < toDouble(right.eval(s)) ? Boolean.TRUE : Boolean.FALSE;
622 case Lexer.LE: return toDouble(left.eval(s)) <= toDouble(right.eval(s)) ? Boolean.TRUE : Boolean.FALSE;
623 case Lexer.GT: return toDouble(left.eval(s)) > toDouble(right.eval(s)) ? Boolean.TRUE : Boolean.FALSE;
624 case Lexer.GE: return toDouble(left.eval(s)) >= toDouble(right.eval(s)) ? Boolean.TRUE : Boolean.FALSE;
627 boolean b1 = toBoolean(left.eval(s));
628 if (b1) return Boolean.TRUE;
629 return new Boolean(b1 || toBoolean(right.eval(s)));
633 boolean b1 = toBoolean(left.eval(s));
634 if (!b1) return Boolean.FALSE;
635 return new Boolean(b1 && toBoolean(right.eval(s)));
638 case Lexer.BANG: return new Boolean(!toBoolean(left.eval(s)));
642 // FIXME: should use Javascript coercion-equality rules
643 Object l = left.eval(s);
644 Object r = right.eval(s);
646 if (l == null) { Object t = r; r = l; l = t; }
647 if (l == null && r == null) ret = true;
648 else if (l instanceof Boolean) ret = new Boolean(toBoolean(r)).equals(l);
649 else if (l instanceof Number) ret = toNumber(r).doubleValue() == toNumber(l).doubleValue();
650 else if (l instanceof String) ret = r != null && l.equals(r.toString());
651 else ret = l.equals(r);
653 return new Boolean(code == Lexer.EQ ? ret : !ret);
659 Object v = (code == Lexer.ASSIGN) ? right.eval(s) : new Double(toDouble(left.eval(s)) + (code == Lexer.INC ? 1 : -1));
660 if (left.code == Lexer.DOT) {
661 Object o = left.left.eval(s);
662 if (o instanceof String) {
663 throw new EvaluatorException("can't set properties on a String");
664 } else if (o instanceof Number) {
665 throw new EvaluatorException("can't set properties on a Number");
666 } else if (o instanceof Boolean) {
667 throw new EvaluatorException("can't set properties on a Boolean");
670 if (target == null) throw new JS.Exn(new EvaluatorException("attempted to put to the null value"));
671 target.put(left.right.eval(s), v);
675 s.put(left.string, v);
681 Object o = left.eval(s);
682 if (o == null) return "null";
683 if (o.getClass() == String.class) return "string";
684 if (o.getClass() == Boolean.class) return "boolean";
685 if (o instanceof Number) return "number";
686 if (o instanceof JS.Array) return "array";
687 if (o instanceof JS) return "object";
688 throw new EvaluatorException("typeof " + o.getClass().getName() + " unknown");
691 case Lexer.NUMBER: return number;
692 case Lexer.STRING: return string;
694 case Lexer.NULL: return null;
695 case Lexer.FALSE: return Boolean.FALSE;
696 case Lexer.TRUE: return Boolean.TRUE;
697 case Lexer.ASSERT: if (!toBoolean(left.eval(s))) throw new EvaluatorException("assertion failed");
698 case Lexer.THROW: throw new JS.Exn(left.eval(s));
699 case Lexer.NAME: return s.get(string);
700 case Lexer.THIS: return s.isTransparent() ? s : this.eval(s.getParentScope());
702 final Object o = left.eval(s);
703 if (o == null) throw new EvaluatorException("tried to get a property from the null value");
704 Object v = (right.code == Lexer.STRING) ? right.string : right.eval(s);
705 if (o instanceof String) {
706 if (v.equals("length")) return new Integer(((String)o).length());
707 else if (v.equals("substring")) return new JS.Function() {
708 public Object _call(JS.Array args) {
709 if (args.length() == 1) return ((String)o).substring(toNumber(args.elementAt(0)).intValue());
710 else if (args.length() == 2) return ((String)o).substring(toNumber(args.elementAt(0)).intValue(),
711 toNumber(args.elementAt(1)).intValue());
712 else throw new EvaluatorException("String.substring() can only take one or two arguments");
715 else if (v.equals("toLowerCase")) return new JS.Function() {
716 public Object _call(JS.Array args) {
717 return ((String)o).toLowerCase();
719 else if (v.equals("toUpperCase")) return new JS.Function() {
720 public Object _call(JS.Array args) {
721 return ((String)o).toString().toUpperCase();
723 else if (v.equals("charAt")) return new JS.Function() {
724 public Object _call(JS.Array args) {
725 return ((String)o).charAt(toNumber(args.elementAt(0)).intValue()) + "";
727 else if (v.equals("lastIndexOf")) return new JS.Function() {
728 public Object _call(JS.Array args) {
729 if (args.length() != 1) return null;
730 return new Integer(((String)o).lastIndexOf(args.elementAt(0).toString()));
732 else if (v.equals("indexOf")) return new JS.Function() {
733 public Object _call(JS.Array args) {
734 if (args.length() != 1) return null;
735 return new Integer(((String)o).indexOf(args.elementAt(0).toString()));
737 throw new EvaluatorException("Not Implemented: properties on String objects");
738 } else if (o instanceof Boolean) {
739 throw new EvaluatorException("Not Implemented: properties on Boolean objects");
740 } else if (o instanceof Number) {
741 Log.log(this, "Not Implemented: properties on Number objects");
743 //throw new EvaluatorException("Not Implemented: properties on Number objects");
744 } else if (o instanceof JS) {
745 return ((JS)o).get(v);
750 boolean safeToExit = false;
752 Object ret = left.eval(s);
756 ExprList list = (ExprList)right;
757 Expr c = list.elementAt(0);
758 if (c.code == Lexer.CATCH) {
759 JS.Scope scope = new JS.Scope(s);
760 s.put(c.left.string, e);
762 c = list.elementAt(1);
764 if (c.code == Lexer.FINALLY) {
765 JS.Scope scope = new JS.Scope(s);
769 if (!safeToExit) Log.log(this, "WARNING: Java exception penetrated a JavaScript try{} block");
775 JS.Function f = (JS.Function)left.eval(s);
776 JS.Array arguments = new JS.Array();
777 for(int i=0; i<((ExprList)right).numExprs(); i++) arguments.addElement(((ExprList)right).elementAt(i).eval(s));
778 if (f == null) throw new JS.Exn(new EvaluatorException("attempted to call null"));
779 return f.call(arguments);
782 return new JS.Function() {
783 public String toString() { return right.sourceName + ":" + right.line; }
784 public String getSourceName() throws JS.Exn { return right.sourceName; }
785 public Object _call(final JS.Array args) throws JS.Exn {
786 Function save = JS.getCurrentFunction();
787 JS.currentFunction.put(Thread.currentThread(), this);
788 JS.Scope scope = new JS.Scope(s) {
790 public String getSourceName() { return sourceName; }
791 public Object get(Object key) throws JS.Exn {
792 if (key.equals("trapee")) return org.xwt.Trap.currentTrapee();
793 else if (key.equals("cascade")) return org.xwt.Trap.cascadeFunction;
794 else if (key.equals("arguments")) return args;
795 return super.get(key);
799 ExprList list = (ExprList)left;
800 for(i=0; i<list.size(); i++) {
801 scope.declare(list.elementAt(i).string);
802 scope.put(list.elementAt(i).string, args.get(new Integer(i)));
805 return right.eval(scope);
806 } catch (ReturnException r) {
808 } catch (ControlTransferException c) {
809 throw new EvaluatorException("error, ControlTransferException tried to leave a function: "
812 if (save == null) JS.currentFunction.remove(Thread.currentThread());
813 else JS.currentFunction.put(Thread.currentThread(), save);
819 Object[] keys = ((JS)left.right.eval(s)).keys();
820 for(int i=0; i<keys.length; i++) {
821 JS.Scope scope = new JS.Scope(s);
822 scope.declare(left.string);
823 scope.put(left.string, keys[i]);
826 } catch (ContinueException c) {
827 if (c.label == null || c.label.equals(string)) continue;
828 } catch (BreakException b) {
829 if (b.label == null || b.label.equals(string)) return null;
830 throw (BreakException)b.fillInStackTrace();
836 Object switchVal = left.eval(s);
839 ExprList list = (ExprList)right;
840 for(int i=0; i<list.size(); i++) {
841 Expr e = list.elementAt(i);
842 if (go || e.code == Lexer.DEFAULT || e.left.eval(s).equals(switchVal)) go = true;
843 if (go) e.right.eval(s);
845 } catch (BreakException b) {
846 if (b.label == null || b.label.equals(string)) return null;
847 throw (BreakException)b.fillInStackTrace();
851 case Lexer.HOOK: return toBoolean(left.eval(s)) ? right.left.eval(s) : right.right.eval(s);
853 if (right.code == ELSE) {
854 if (toBoolean(left.eval(s))) right.left.eval(s);
855 else right.right.eval(s);
858 if (toBoolean(left.eval(s))) right.eval(s);
861 case Lexer.BREAK: throw new BreakException(string);
862 case Lexer.CONTINUE: throw new ContinueException(string);
863 case Lexer.RETURN: throw new ReturnException(left == null ? null : left.eval(s));
865 default: throw new EvaluatorException("don't know how to eval an Expr with code " + Lexer.codeToString[code] + "\n" + this);
869 class EvaluatorException extends RuntimeException {
870 public EvaluatorException(String s) { super(sourceName + ":" + line + " " + s); }
874 static abstract class ControlTransferException extends Exception { }
875 static class BreakException extends ControlTransferException {
877 BreakException(String label) { this.label = label; }
879 static class ContinueException extends ControlTransferException {
881 ContinueException(String label) { this.label = label; }
883 static class ReturnException extends ControlTransferException {
884 public Object retval;
885 ReturnException(Object retval) { this.retval = retval; }
888 class ParserException extends RuntimeException {
889 public ParserException(String s) { super(sourceName + ":" + line + " " + s); }
892 public static Number toNumber(Object o) {
893 if (o == null) return new Long(0);
894 if (o instanceof Number) return ((Number)o);
895 if (o instanceof String) try { return new Double((String)o); } catch (NumberFormatException e) { return new Double(0); }
896 if (o instanceof Boolean) return ((Boolean)o).booleanValue() ? new Long(1) : new Long(0);
897 if (o instanceof JS) return ((JS)o).coerceToNumber();
899 throw new Error("toNumber() got object of type " + o.getClass().getName());