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;
19 public static void main(String[] s) throws Exception {
20 Parser p = new Parser(new InputStreamReader(System.in), "stdin", 0);
22 Expr block = p.parseBlock(false);
23 if (block == null) return;
24 System.out.println(block);
25 if (p.peekToken() == -1) return;
30 // Statics ////////////////////////////////////////////////////////////
32 static byte[] precedence = new byte[MAX_TOKEN + 1];
33 static boolean[] isRightAssociative = new boolean[MAX_TOKEN + 1];
35 precedence[ASSIGN] = 1;
36 isRightAssociative[ASSIGN] = true;
38 precedence[COMMA] = 3;
39 precedence[OR] = precedence[AND] = 4;
40 precedence[GT] = precedence[GE] = 5;
41 precedence[BITOR] = 6;
42 precedence[BITXOR] = 7;
43 precedence[BITAND] = 8;
44 precedence[EQ] = precedence[NE] = 9;
45 precedence[LT] = precedence[LE] = 10;
46 precedence[SHEQ] = precedence[SHNE] = 11;
47 precedence[LSH] = precedence[RSH] = precedence[URSH] = 12;
48 precedence[ADD] = precedence[SUB] = 13;
49 precedence[MUL] = precedence[DIV] = precedence[MOD] = 14;
50 precedence[BITNOT] = precedence[INSTANCEOF] = 15;
51 precedence[INC] = precedence[DEC] = 16;
58 // Parsing Logic /////////////////////////////////////////////////////////
60 /** a block is either a single statement or a list of statements surrounded by curly braces; all expressions are also statements */
61 public Expr parseBlock(boolean requireBraces) throws IOException {
63 int tok = peekToken();
64 if (tok == -1) return null;
65 boolean braced = tok == LC;
66 if (requireBraces && !braced) throw new ParserException("expected {");
67 if (braced) getToken();
74 switch(tok = peekToken()) {
76 case LC: smt = parseBlock(true); break;
77 case THROW: case RETURN: case ASSERT:
79 if (peekToken() == SEMI) {
81 smt = new Expr(curLine, tok);
84 smt = new Expr(curLine, tok, parseMaximalExpr());
85 if (getToken() != SEMI) throw new ParserException("expected ;");
93 if (getToken() != COLON) throw new ParserException("expected COLON after label");
94 Expr labeledBlock = parseBlock(false);
95 if (labeledBlock.code != WHILE && labeledBlock.code != FOR && labeledBlock.code != SWITCH)
96 throw new ParserException("you can only label a WHILE, FOR, or SWITCH block");
97 labeledBlock.string = str;
102 case GOTO: case BREAK: case CONTINUE: {
107 smt = new Expr(curLine, tok, new Expr(curLine, string));
108 } else if (t == GOTO) {
109 throw new ParserException("goto must be followed by a label");
110 } else if (t == SEMI) {
112 smt = new Expr(curLine, tok);
114 throw new ParserException(codeToString[tok] + " followed by a " + codeToString[t] + "; expected NAME or SEMI");
120 if (braced) getToken();
125 if (!braced) break OUTER;
129 smt = parseMaximalExpr();
131 if (head == null) return null;
134 if (peekToken() == SEMI) getToken();
137 if (!braced) return smt;
138 if (head == null) head = tail = smt; else tail = (tail.next = smt);
140 return new Expr(curLine, LC, head);
143 /** throws an error if the next token is not <tt>code</tt> */
144 public void expect(int code) throws IOException {
145 int got = peekToken();
147 throw new ParserException("expected " + codeToString[code] + ", got " + (got == -1 ? "EOL" : codeToString[got]));
150 /** parses the largest possible expression */
151 public Expr parseMaximalExpr() throws IOException { return parseMaximalExpr(null, -1); }
152 public Expr parseMaximalExpr(Expr prefix, int minPrecedence) throws IOException {
154 if (peekToken() == -1) break;
156 prefix = parseSingleExpr(prefix, minPrecedence);
157 if (save == prefix) break;
158 if (prefix == null) throw new ParserException("parseSingleExpr() returned null");
163 public Expr parseSingleExpr(Expr prefix, int minPrecedence) throws IOException {
164 Expr e1 = null, e2 = null, e3 = null, head = null, tail = null, ret = null;
166 int tok = peekToken();
167 if (minPrecedence > 0 &&
168 tok < precedence.length &&
169 precedence[tok] != 0 &&
170 (isRightAssociative[tok] ?
171 (precedence[tok] < minPrecedence) :
172 (precedence[tok] <= minPrecedence))) {
178 // these case arms match the precedence of operators; each arm is a precedence level.
181 case WITH: throw new ParserException("XWT does not allow the WITH keyword");
182 case VOID: case RESERVED: throw new ParserException("reserved word that you shouldn't be using");
183 case NAME: if (prefix != null) { pushBackToken(); return prefix; } else return parseMaximalExpr(new Expr(curLine, NAME, string), minPrecedence);
184 case STRING: if (prefix != null) { pushBackToken(); return prefix; } else return new Expr(curLine, string);
185 case NUMBER: if (prefix != null) { pushBackToken(); return prefix; } else return new Expr(curLine, number);
186 case NULL: case TRUE: case FALSE: case NOP: if (prefix != null) { pushBackToken(); return prefix; } else return new Expr(curLine, tok);
188 case ASSIGN_BITOR: case ASSIGN_BITXOR: case ASSIGN_BITAND: case ASSIGN_LSH:
189 case ASSIGN_RSH: case ASSIGN_URSH: case ASSIGN_ADD: case ASSIGN_SUB: case ASSIGN_MUL: case ASSIGN_DIV: case ASSIGN_MOD:
190 return new Expr(curLine, ASSIGN, prefix, new Expr(curLine, tok - 1, prefix, parseMaximalExpr(null, precedence[ASSIGN])));
192 case COMMA: pushBackToken();return prefix;
195 if (prefix != null) { pushBackToken(); return prefix; }
196 return new Expr(curLine, THIS);
199 if (prefix == null && peekToken() == NUMBER) {
201 return new Expr(curLine, new Double(number.doubleValue() * -1));
202 } // else fall through
203 case BITOR: case BITXOR: case BITAND: case SHEQ: case SHNE: case LSH:
204 case RSH: case URSH: case ADD: case MUL: case DIV: case MOD:
205 case ASSIGN: case GT: case GE: case OR: case AND:
206 case EQ: case NE: case LT: case LE:
207 if (prefix == null) throw new ParserException("the " + codeToString[tok] + " token cannot start an expression");
208 return new Expr(curLine, tok, prefix, parseMaximalExpr(null, precedence[tok]));
211 //e1 = parseMaximalExpr(null, precedence[DOT]);
212 //if (e1.code == NAME) e1.code = STRING;
213 //else throw new ParserException("argument to DOT must be a NAME; got a " + codeToString[e1.code]);
214 //return new Expr(curLine, DOT, prefix, e1);
216 if (tok != NAME) throw new ParserException("DOT must be followed by a NAME");
217 return new Expr(curLine, DOT, prefix, new Expr(curLine, STRING, string));
220 case BANG: case BITNOT: case INSTANCEOF: case TYPEOF:
221 if (prefix != null) { pushBackToken(); return prefix; }
222 return new Expr(curLine, tok, parseMaximalExpr(null, precedence[tok]));
225 if (prefix == null) {
227 e1 = parseMaximalExpr(null, precedence[tok]);
228 return new Expr(curLine, ASSIGN, e1, new Expr(curLine, tok == INC ? ADD : SUB, e1, new Expr(curLine, new Integer(1))));
231 return new Expr(curLine, tok, prefix);
235 if (prefix == null) { // grouping
236 Expr r = parseMaximalExpr();
237 expect(RP); getToken();
240 } else { // invocation
241 while(peekToken() != RP) {
242 Expr e = parseMaximalExpr();
243 if (head == null) head = tail = e; else tail = tail.next = e;
245 if (tok == RP) { pushBackToken(); break; }
246 if (tok != COMMA) throw new ParserException("expected comma or right paren, got " + codeToString[tok]);
249 return new Expr(curLine, LP, prefix, head);
253 if (prefix != null) {
255 e1 = parseMaximalExpr();
256 if (getToken() != RB) throw new ParserException("expected a right brace");
257 return new Expr(curLine, DOT, prefix, e1);
261 if (peekToken() == RB) { getToken(); return new Expr(curLine, LB, prefix, head); }
262 Expr eee = parseMaximalExpr();
264 if (head == null) head = tail = eee;
265 else tail.next = tail = eee;
268 if (tok == RB) return new Expr(curLine, LB, prefix, head);
269 if (tok != COMMA) throw new ParserException("expected right bracket or comma");
274 if (prefix != null) { pushBackToken(); return prefix; }
276 if (tok == RC) return new Expr(curLine, RC, head);
278 if (tok != NAME && tok != STRING) throw new ParserException("expecting name, got " + codeToString[tok]);
279 Expr name = new Expr(curLine, NAME, string);
280 if (getToken() != COLON) throw new ParserException("expecting colon");
281 e1 = new Expr(curLine, COLON, name, parseMaximalExpr());
282 if (head == null) head = tail = e1; else tail = tail.next = e1;
284 if (tok != COMMA && tok != RC) throw new ParserException("expected right curly or comma, got " + codeToString[tok]);
285 if (tok == RC) return new Expr(curLine, RC, head);
287 if (tok == RC) return new Expr(curLine, RC, head);
291 e2 = parseMaximalExpr();
292 if (getToken() != COLON) throw new ParserException("expected colon to close ?: expression");
293 e3 = parseMaximalExpr();
294 return new Expr(curLine, HOOK, prefix, new Expr(curLine, ELSE, e2, e3));
297 if (prefix != null) { pushBackToken(); return prefix; }
298 if (getToken() != LP) throw new ParserException("expected left paren");
299 Expr switchExpr = parseMaximalExpr();
300 if (getToken() != RP) throw new ParserException("expected left paren");
301 if (getToken() != LC) throw new ParserException("expected left brace");
302 Expr firstExpr = null;
303 Expr lastExpr = null;
307 if (tok == DEFAULT) {
309 } else if (tok == CASE) {
310 // FIXME: we don't support non-brace-enclosed CASE blocks
311 caseExpr = parseMaximalExpr();
313 throw new ParserException("expected CASE");
315 expect(COLON); getToken();
317 while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) {
318 e1 = parseBlock(false);
319 if (e1 == null) break;
320 if (head == null) head = tail = e1; else tail = tail.next = e1;
322 e1 = new Expr(curLine, tok, caseExpr, new Expr(curLine, LC, head));
323 if (lastExpr == null) firstExpr = e1; else lastExpr.next = e1;
325 if (peekToken() == RC) {getToken(); return new Expr(curLine, SWITCH, switchExpr, firstExpr); }
330 if (prefix != null) { pushBackToken(); return prefix; }
331 if (getToken() != LP) throw new ParserException("function keyword must be followed by a left paren");
332 Expr formalArgs = null, cur = null;
335 if (tok != NAME) throw new ParserException("expected a variable name");
336 if (cur == null) { formalArgs = cur = new Expr(curLine, string); }
337 else { cur.next = new Expr(curLine, NAME, string); cur = cur.next; }
339 if (tok == RP) break;
340 if (tok != COMMA) throw new ParserException("function argument list must consist of alternating NAMEs and COMMAs");
343 return new Expr(curLine, FUNCTION, formalArgs, parseBlock(true));
347 if (prefix != null) { pushBackToken(); return prefix; }
349 if (getToken() != NAME) throw new ParserException("variable declarations must start with a variable name");
350 String name = string;
356 initVal = parseMaximalExpr();
358 e = new Expr(curLine, VAR,
359 new Expr(curLine, name),
360 new Expr(curLine, ASSIGN,
361 new Expr(curLine, name),
364 e = new Expr(curLine, VAR, new Expr(curLine, name));
366 if (head == null) head = tail = e; else tail = tail.next = e;
367 if (tok != COMMA) break;
370 return new Expr(curLine, VAR, head);
373 // We deliberately allow you to omit braces in catch{}/finally{} if they are single statements...
374 if (prefix != null) { pushBackToken(); return prefix; }
375 Expr tryBlock = parseBlock(true);
380 if (getToken() != LP) throw new ParserException("expected (");
381 if (getToken() != NAME) throw new ParserException("expected name");
382 Expr name = new Expr(curLine, NAME, string);
383 if (getToken() != RP) throw new ParserException("expected )");
384 head = tail = new Expr(curLine, CATCH, name, parseBlock(false));
387 if (tok == FINALLY) {
389 e1 = new Expr(curLine, FINALLY, parseBlock(false));
390 if (head == null) head = tail = e1; else tail = tail.next = e1;
393 if (head == null) throw new ParserException("try without catch or finally");
394 return new Expr(curLine, TRY, tryBlock, head);
397 case IF: case WHILE: {
398 if (prefix != null) { pushBackToken(); return prefix; }
399 if (getToken() != LP) throw new ParserException("expected left paren");
400 Expr parenExpr = parseMaximalExpr();
402 if ((t = getToken()) != RP) throw new ParserException("expected right paren, but got " + codeToString[t]);
403 Expr firstBlock = parseBlock(false);
404 if (tok == IF && peekToken() == ELSE) {
406 return new Expr(curLine, tok, parenExpr, new Expr(curLine, ELSE, firstBlock, parseBlock(false)));
408 return new Expr(curLine, tok, parenExpr, firstBlock);
412 case IN: pushBackToken(); return prefix;
415 if (prefix != null) { pushBackToken(); return prefix; }
416 if (getToken() != LP) throw new ParserException("expected left paren");
417 e1 = parseMaximalExpr();
418 if (peekToken() == IN) {
420 e2 = parseMaximalExpr();
421 if (getToken() != RP) throw new ParserException("expected right paren");
422 return new Expr(curLine, FOR, new Expr(curLine, IN, e1.left, e2), parseBlock(false));
426 expect(SEMI); getToken();
427 Expr whileExpr = parseMaximalExpr();
428 expect(SEMI); getToken();
429 Expr incExpr = parseMaximalExpr();
430 expect(RP); getToken();
431 Expr body = parseBlock(false);
433 Expr loop = new Expr(curLine, WHILE, whileExpr, body);
434 if (initExpr == null) initExpr = loop; else initExpr.next = loop;
435 return new Expr(curLine, LC, initExpr);
439 if (prefix != null) { pushBackToken(); return prefix; }
440 Expr firstBlock = parseBlock(false);
441 if (getToken() != WHILE) throw new ParserException("expecting WHILE");
442 if (getToken() != LP) throw new ParserException("expected left paren");
443 Expr whileExpr = parseMaximalExpr();
444 if (getToken() != RP) throw new ParserException("expected right paren");
445 if (getToken() != SEMI) throw new ParserException("semicolon");
446 return new Expr(curLine, DO, firstBlock, whileExpr);
455 // Expr //////////////////////////////////////////////////////////////////////
457 /** sorta like gcc trees */
463 Expr next = null; // if this expr is part of a list
465 String sourceName = "unknown";
467 String string = null;
468 Number number = null;
470 public String toString() { return toString(0); }
471 public String toString(int indent) {
473 for(int i=0; i<indent; i++) ret += " ";
474 ret += Lexer.codeToString[code];
475 if (code == Lexer.NUMBER) ret += " " + number;
476 else if (string != null) ret += " \"" + string + "\"";
478 if (left != null) ret += left.toString(indent + 2);
479 if (right != null) ret += right.toString(indent + 2);
480 if (next != null) ret += next.toString(indent);
484 public Expr(int line, String s) { this(line, Lexer.STRING); this.string = s; } // an identifier or label
485 public Expr(int line, int code, String s) { this(line, code); this.string = s; }
486 public Expr(int line, Number n) { this(line, Lexer.NUMBER); this.number = n; } // an identifier or label
487 public Expr(int line, int code) { this(line, code, null, null); }
488 public Expr(int line, int code, Expr left) { this(line, code, left, null); }
489 public Expr(int line, int code, Expr left, Expr right) {
490 this.code = code; this.left = left; this.right = right;
492 this.sourceName = Parser.this.sourceName;
495 public double toDouble(Object o) { return toNumber(o).doubleValue(); }
496 public long toLong(Object o) { return toNumber(o).longValue(); }
497 public boolean toBoolean(Object o) {
498 if (o == null) return false;
499 if (o instanceof Boolean) return ((Boolean)o).booleanValue();
500 if (o instanceof Number) return o.equals(new Integer(0));
504 public Object eval(final JS.Scope s) throws ControlTransferException, JS.Exn {
507 case Lexer.BITOR: return new Long(toLong(left.eval(s)) | toLong(right.eval(s)));
508 case Lexer.BITXOR: return new Long(toLong(left.eval(s)) ^ toLong(right.eval(s)));
509 case Lexer.BITAND: return new Long(toLong(left.eval(s)) & toLong(right.eval(s)));
510 case Lexer.BITNOT: return new Long(~toLong(left.eval(s)));
513 Object l = left.eval(s);
514 Object r = right.eval(s);
515 if (l instanceof String || r instanceof String) {
516 if (l == null) l = "null";
517 if (r == null) r = "null";
518 if (l instanceof Number && ((Number)l).doubleValue() == ((Number)l).longValue())
519 l = new Long(((Number)l).longValue());
520 if (r instanceof Number && ((Number)r).doubleValue() == ((Number)r).longValue())
521 r = new Long(((Number)r).longValue());
522 return l.toString() + r.toString();
524 return new Double(toDouble(l) + toDouble(r));
527 case Lexer.SUB: return new Double(toDouble(left.eval(s)) - toDouble(right.eval(s)));
528 case Lexer.MUL: return new Double(toDouble(left.eval(s)) * toDouble(right.eval(s)));
529 case Lexer.DIV: return new Double(toDouble(left.eval(s)) / toDouble(right.eval(s)));
530 case Lexer.MOD: return new Double(toDouble(left.eval(s)) % toDouble(right.eval(s)));
532 case Lexer.LSH: return new Long(toLong(left.eval(s)) << toLong(right.eval(s)));
533 case Lexer.RSH: return new Long(toLong(left.eval(s)) >> toLong(right.eval(s)));
534 case Lexer.URSH: return new Long(toLong(left.eval(s)) >>> toLong(right.eval(s)));
536 // FIXME: these need to work on strings
537 case Lexer.LT: return toDouble(left.eval(s)) < toDouble(right.eval(s)) ? Boolean.TRUE : Boolean.FALSE;
538 case Lexer.LE: return toDouble(left.eval(s)) <= toDouble(right.eval(s)) ? Boolean.TRUE : Boolean.FALSE;
539 case Lexer.GT: return toDouble(left.eval(s)) > toDouble(right.eval(s)) ? Boolean.TRUE : Boolean.FALSE;
540 case Lexer.GE: return toDouble(left.eval(s)) >= toDouble(right.eval(s)) ? Boolean.TRUE : Boolean.FALSE;
543 boolean b1 = toBoolean(left.eval(s));
544 if (b1) return Boolean.TRUE;
545 return new Boolean(b1 || toBoolean(right.eval(s)));
549 boolean b1 = toBoolean(left.eval(s));
550 if (!b1) return Boolean.FALSE;
551 return new Boolean(b1 && toBoolean(right.eval(s)));
554 case Lexer.BANG: return new Boolean(!toBoolean(left.eval(s)));
558 // FIXME: should use Javascript coercion-equality rules
559 Object l = left.eval(s);
560 Object r = right.eval(s);
562 if (l == null) { Object t = r; r = l; l = t; }
563 if (l == null && r == null) ret = true;
564 else if (l instanceof Boolean) ret = new Boolean(toBoolean(r)).equals(l);
565 else if (l instanceof Number) ret = toNumber(r).doubleValue() == toNumber(l).doubleValue();
566 else if (l instanceof String) ret = r != null && l.equals(r.toString());
567 else ret = l.equals(r);
569 return new Boolean(code == Lexer.EQ ? ret : !ret);
575 Object v = (code == Lexer.ASSIGN) ? right.eval(s) : new Double(toDouble(left.eval(s)) + (code == Lexer.INC ? 1 : -1));
576 if (left.code == Lexer.DOT) {
577 Object o = left.left.eval(s);
578 if (o instanceof String) {
579 throw new EvaluatorException("can't set properties on a String");
580 } else if (o instanceof Number) {
581 throw new EvaluatorException("can't set properties on a Number");
582 } else if (o instanceof Boolean) {
583 throw new EvaluatorException("can't set properties on a Boolean");
586 if (target == null) throw new JS.Exn(new EvaluatorException("attempted to put to the null value"));
587 target.put(left.right.eval(s), v);
591 s.put(left.string, v);
597 Object o = left.eval(s);
598 if (o == null) return "null";
599 if (o.getClass() == String.class) return "string";
600 if (o.getClass() == Boolean.class) return "boolean";
601 if (o instanceof Number) return "number";
602 if (o instanceof JS.Array) return "array";
603 if (o instanceof JS) return "object";
604 throw new EvaluatorException("typeof " + o.getClass().getName() + " unknown");
607 case Lexer.NUMBER: return number;
608 case Lexer.STRING: return string;
610 case Lexer.NULL: return null;
611 case Lexer.FALSE: return Boolean.FALSE;
612 case Lexer.TRUE: return Boolean.TRUE;
613 case Lexer.ASSERT: if (!toBoolean(left.eval(s))) throw new EvaluatorException("assertion failed");
614 case Lexer.THROW: throw new JS.Exn(left.eval(s));
615 case Lexer.NAME: return s.get(string);
616 case Lexer.THIS: return s.isTransparent() ? s : this.eval(s.getParentScope());
618 final Object o = left.eval(s);
619 if (o == null) throw new EvaluatorException("tried to get a property from the null value");
620 Object v = (right.code == Lexer.STRING) ? right.string : right.eval(s);
621 if (o instanceof String) {
622 if (v.equals("length")) return new Integer(((String)o).length());
623 else if (v.equals("substring")) return new JS.Function() {
624 public Object _call(JS.Array args) {
625 if (args.length() == 1) return ((String)o).substring(toNumber(args.elementAt(0)).intValue());
626 else if (args.length() == 2) return ((String)o).substring(toNumber(args.elementAt(0)).intValue(),
627 toNumber(args.elementAt(1)).intValue());
628 else throw new EvaluatorException("String.substring() can only take one or two arguments");
631 else if (v.equals("toLowerCase")) return new JS.Function() {
632 public Object _call(JS.Array args) {
633 return ((String)o).toLowerCase();
635 else if (v.equals("toUpperCase")) return new JS.Function() {
636 public Object _call(JS.Array args) {
637 return ((String)o).toString().toUpperCase();
639 else if (v.equals("charAt")) return new JS.Function() {
640 public Object _call(JS.Array args) {
641 return ((String)o).charAt(toNumber(args.elementAt(0)).intValue()) + "";
643 else if (v.equals("lastIndexOf")) return new JS.Function() {
644 public Object _call(JS.Array args) {
645 if (args.length() != 1) return null;
646 return new Integer(((String)o).lastIndexOf(args.elementAt(0).toString()));
648 else if (v.equals("indexOf")) return new JS.Function() {
649 public Object _call(JS.Array args) {
650 if (args.length() != 1) return null;
651 return new Integer(((String)o).indexOf(args.elementAt(0).toString()));
653 throw new EvaluatorException("Not Implemented: properties on String objects");
654 } else if (o instanceof Boolean) {
655 throw new EvaluatorException("Not Implemented: properties on Boolean objects");
656 } else if (o instanceof Number) {
657 Log.log(this, "Not Implemented: properties on Number objects");
659 //throw new EvaluatorException("Not Implemented: properties on Number objects");
660 } else if (o instanceof JS) {
661 return ((JS)o).get(v);
666 boolean safeToExit = false;
668 Object ret = left.eval(s);
673 if (c.code == Lexer.CATCH) {
674 JS.Scope scope = new JS.Scope(s);
675 s.put(c.left.string, e);
679 if (c.code == Lexer.FINALLY) {
680 JS.Scope scope = new JS.Scope(s);
684 if (!safeToExit) Log.log(this, "WARNING: Java exception penetrated a JavaScript try{} block");
690 JS.Function f = (JS.Function)left.eval(s);
691 JS.Array arguments = new JS.Array();
692 for(Expr e = right; e != null; e = e.next) arguments.addElement(e.eval(s));
693 if (f == null) throw new JS.Exn(new EvaluatorException("attempted to call null"));
694 return f.call(arguments);
697 return new JS.Function() {
698 public String toString() { return right.sourceName + ":" + right.line; }
699 public String getSourceName() throws JS.Exn { return right.sourceName; }
700 public Object _call(final JS.Array args) throws JS.Exn {
701 Function save = JS.getCurrentFunction();
702 JS.currentFunction.put(Thread.currentThread(), this);
703 JS.Scope scope = new JS.Scope(s) {
705 public String getSourceName() { return sourceName; }
706 public Object get(Object key) throws JS.Exn {
707 if (key.equals("trapee")) return org.xwt.Trap.currentTrapee();
708 else if (key.equals("cascade")) return org.xwt.Trap.cascadeFunction;
709 else if (key.equals("arguments")) return args;
710 return super.get(key);
714 for(Expr e = left; e != null; e = e.next) {
715 scope.declare(e.string);
716 scope.put(e.string, args.get(new Integer(i++)));
719 return right.eval(scope);
720 } catch (ReturnException r) {
722 } catch (ControlTransferException c) {
723 throw new EvaluatorException("error, ControlTransferException tried to leave a function: "
726 if (save == null) JS.currentFunction.remove(Thread.currentThread());
727 else JS.currentFunction.put(Thread.currentThread(), save);
733 Object[] keys = ((JS)left.right.eval(s)).keys();
734 for(int i=0; i<keys.length; i++) {
735 JS.Scope scope = new JS.Scope(s);
736 scope.declare(left.string);
737 scope.put(left.string, keys[i]);
740 } catch (ContinueException c) {
741 if (c.label == null || c.label.equals(string)) continue;
742 } catch (BreakException b) {
743 if (b.label == null || b.label.equals(string)) return null;
744 throw (BreakException)b.fillInStackTrace();
750 Object switchVal = left.eval(s);
753 for(Expr e = right; e != null; e = e.next) {
754 if (go || e.code == Lexer.DEFAULT || e.left.eval(s).equals(switchVal)) go = true;
755 if (go) e.right.eval(s);
757 } catch (BreakException b) {
758 if (b.label == null || b.label.equals(string)) return null;
759 throw (BreakException)b.fillInStackTrace();
764 JS.Array ret = new JS.Array();
765 for(Expr e = left; e != null; e = e.next) ret.addElement(e.eval(s));
769 case Lexer.LC: // block
770 JS.Scope scope = new JS.Scope(s);
771 for(Expr e = left; e != null; e = e.next) e.eval(scope);
774 case Lexer.RC: { // Object ctor
775 JS.Obj ret = new JS.Obj();
776 for(Expr e = left; e != null; e = e.next) {
777 Object key = e.left.string;
778 Object val = e.right.eval(s);
785 for(Expr e = this.left; e != null; e = e.next) {
786 s.declare(e.left.string);
787 if (e.right != null) e.right.eval(s);
791 case Lexer.HOOK: return toBoolean(left.eval(s)) ? right.left.eval(s) : right.right.eval(s);
793 if (right.code == ELSE) {
794 if (toBoolean(left.eval(s))) right.left.eval(s);
795 else right.right.eval(s);
798 if (toBoolean(left.eval(s))) right.eval(s);
801 case Lexer.BREAK: throw new BreakException(string);
802 case Lexer.CONTINUE: throw new ContinueException(string);
803 case Lexer.RETURN: throw new ReturnException(left == null ? null : left.eval(s));
808 boolean first = true;
810 for(;(first && code == Lexer.DO) || toBoolean(left.eval(s)); bogus = (right.next == null ? null : right.next.eval(s))) {
813 } catch (ContinueException c) {
814 if (c.label == null || c.label.equals(string)) continue;
815 } catch (BreakException b) {
816 if (b.label == null || b.label.equals(string)) return null;
817 throw (BreakException)b.fillInStackTrace();
820 } catch (BreakException e) {
821 if (e.label != null && !e.label.equals(string)) throw e;
825 default: throw new EvaluatorException("don't know how to eval an Expr with code " + Lexer.codeToString[code] + "\n" + this);
829 class EvaluatorException extends RuntimeException {
830 public EvaluatorException(String s) { super(sourceName + ":" + line + " " + s); }
834 static abstract class ControlTransferException extends Exception { }
835 static class BreakException extends ControlTransferException {
837 BreakException(String label) { this.label = label; }
839 static class ContinueException extends ControlTransferException {
841 ContinueException(String label) { this.label = label; }
843 static class ReturnException extends ControlTransferException {
844 public Object retval;
845 ReturnException(Object retval) { this.retval = retval; }
848 class ParserException extends RuntimeException {
849 public ParserException(String s) { super(sourceName + ":" + line + " " + s); }
852 public static Number toNumber(Object o) {
853 if (o == null) return new Long(0);
854 if (o instanceof Number) return ((Number)o);
855 if (o instanceof String) try { return new Double((String)o); } catch (NumberFormatException e) { return new Double(0); }
856 if (o instanceof Boolean) return ((Boolean)o).booleanValue() ? new Long(1) : new Long(0);
857 if (o instanceof JS) return ((JS)o).coerceToNumber();
859 throw new Error("toNumber() got object of type " + o.getClass().getName());