1 // Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
7 /** sorta like gcc trees */
13 Expr next = null; // if this expr is part of a list
18 public String toString() { return toString(0); }
19 public String toString(int indent) {
21 for(int i=0; i<indent; i++) ret += " ";
22 ret += Lexer.codeToString[code];
23 if (code == Lexer.NUMBER) ret += " " + number;
24 else if (string != null) ret += " \"" + string + "\"";
26 if (left != null) ret += left.toString(indent + 2);
27 if (right != null) ret += right.toString(indent + 2);
28 if (next != null) ret += next.toString(indent);
32 public Expr(String s) { this(Lexer.STRING); this.string = s; } // an identifier or label
33 public Expr(int code, String s) { this(code); this.string = s; }
34 public Expr(Number n) { this(Lexer.NUMBER); this.number = n; } // an identifier or label
35 public Expr(int code) { this(code, null, null); }
36 public Expr(int code, Expr left) { this(code, left, null); }
37 public Expr(int code, Expr left, Expr right) { this.code = code; this.left = left; this.right = right; }
39 public static Number toNumber(Object o) {
40 if (o == null) return new Long(0);
41 if (o instanceof Number) return ((Number)o);
42 if (o instanceof String) return new Double((String)o);
43 if (o instanceof Boolean) return ((Boolean)o).booleanValue() ? new Long(1) : new Long(0);
44 if (o instanceof JS) return ((JS)o).coerceToNumber();
45 throw new Error("toNumber() got object of type " + o.getClass().getName());
48 public static double toDouble(Object o) { return toNumber(o).doubleValue(); }
49 public static long toLong(Object o) { return toNumber(o).longValue(); }
50 public static boolean toBoolean(Object o) {
51 if (o == null) return false;
52 if (o instanceof Boolean) return ((Boolean)o).booleanValue();
53 if (o instanceof Number) return o.equals(new Integer(0));
57 public Object eval(final JS.Scope s) throws ControlTransferException, JS.Exn {
60 case Lexer.BITOR: return new Long(toLong(left.eval(s)) | toLong(right.eval(s)));
61 case Lexer.BITXOR: return new Long(toLong(left.eval(s)) ^ toLong(right.eval(s)));
62 case Lexer.BITAND: return new Long(toLong(left.eval(s)) & toLong(right.eval(s)));
63 case Lexer.BITNOT: return new Long(~toLong(left.eval(s)));
66 Object l = left.eval(s);
67 Object r = right.eval(s);
68 if (l instanceof String || r instanceof String) return l.toString() + r.toString();
69 return new Double(toDouble(l) + toDouble(r));
72 case Lexer.SUB: return new Double(toDouble(left.eval(s)) - toDouble(right.eval(s)));
73 case Lexer.MUL: return new Double(toDouble(left.eval(s)) * toDouble(right.eval(s)));
74 case Lexer.DIV: return new Double(toDouble(left.eval(s)) / toDouble(right.eval(s)));
75 case Lexer.MOD: return new Double(toDouble(left.eval(s)) % toDouble(right.eval(s)));
77 case Lexer.LSH: return new Long(toLong(left.eval(s)) << toLong(right.eval(s)));
78 case Lexer.RSH: return new Long(toLong(left.eval(s)) >> toLong(right.eval(s)));
79 case Lexer.URSH: return new Long(toLong(left.eval(s)) >>> toLong(right.eval(s)));
81 case Lexer.LT: return toDouble(left.eval(s)) < toDouble(right.eval(s)) ? Boolean.TRUE : Boolean.FALSE;
82 case Lexer.LE: return toDouble(left.eval(s)) <= toDouble(right.eval(s)) ? Boolean.TRUE : Boolean.FALSE;
83 case Lexer.GT: return toDouble(left.eval(s)) > toDouble(right.eval(s)) ? Boolean.TRUE : Boolean.FALSE;
84 case Lexer.GE: return toDouble(left.eval(s)) >= toDouble(right.eval(s)) ? Boolean.TRUE : Boolean.FALSE;
86 case Lexer.OR: return new Boolean(toBoolean(left.eval(s)) || toBoolean(right.eval(s)));
87 case Lexer.AND: return new Boolean(toBoolean(left.eval(s)) && toBoolean(right.eval(s)));
91 // FIXME: should use Javascript coercion-equality rules
92 boolean ret = left.eval(s).equals(right.eval(s));
93 return new Boolean(code == Lexer.EQ ? ret : !ret);
99 Object v = (code == Lexer.ASSIGN) ? right.eval(s) : new Double(toDouble(left.eval(s)) + (code == Lexer.INC ? 1 : -1));
100 if (left.code == Lexer.DOT) {
101 Object o = left.left.eval(s);
102 if (o instanceof String) {
103 throw new Error("can't set properties on a String");
104 } else if (o instanceof Number) {
105 throw new Error("can't set properties on a Number");
106 } else if (o instanceof Boolean) {
107 throw new Error("can't set properties on a Boolean");
109 ((JS)left.left.eval(s)).put(left.right.eval(s), v);
113 s.put(left.string, v);
119 Object o = left.eval(s);
120 if (o == null) return "null";
121 if (o.getClass() == String.class) return "string";
122 if (o.getClass() == Boolean.class) return "boolean";
123 if (o instanceof Number) return "number";
124 if (o instanceof JS.Array) return "array";
125 if (o instanceof JS) return "object";
126 throw new Error("typeof " + o.getClass().getName() + " unknown");
129 case Lexer.NUMBER: return number;
130 case Lexer.STRING: return string;
132 case Lexer.NULL: return null;
133 case Lexer.THIS: return s;
134 case Lexer.FALSE: return Boolean.FALSE;
135 case Lexer.TRUE: return Boolean.TRUE;
136 case Lexer.ASSERT: if (!toBoolean(left.eval(s))) throw new Error("assertion failed");
137 case Lexer.THROW: throw new JS.Exn(left.eval(s));
139 case Lexer.NAME: return s.get(string);
141 Object o = left.eval(s);
142 Object v = right.eval(s);
143 if (o instanceof String) {
144 if (v.equals("length")) return new Integer(((String)o).length());
145 throw new Error("Not Implemented: properties on String objects");
146 } else if (o instanceof Boolean) {
147 throw new Error("Not Implemented: properties on Boolean objects");
148 } else if (o instanceof Number) {
149 throw new Error("Not Implemented: properties on Number objects");
150 } else if (o instanceof JS) {
151 return ((JS)o).get(v);
156 boolean safeToExit = false;
158 Object ret = left.eval(s);
163 if (c.code == Lexer.CATCH) {
164 JS.Scope scope = new JS.Scope(s);
165 s.put(c.left.string, e);
169 if (c.code == Lexer.FINALLY) {
170 JS.Scope scope = new JS.Scope(s);
174 if (!safeToExit) Log.log(this, "WARNING: Java exception penetrated a JavaScript try{} block");
180 JS.Function f = (JS.Function)left.eval(s);
181 JS.Array arguments = new JS.Array();
182 for(Expr e = right; e != null; e = e.next) arguments.addElement(e.eval(s));
183 return f.call(arguments);
186 return new JS.ObjFunction() {
187 public JS.Scope getParentScopeOfDeclaration() { return s; }
188 public Object call(JS.Array args) throws JS.Exn {
189 JS.Scope scope = new JS.Scope(getParentScopeOfDeclaration()) {
190 public Object get(Object key) throws JS.Exn {
191 if (key.equals("trapee")) return org.xwt.Trap.currentTrapee();
192 return super.get(key);
196 args.put("cascade", org.xwt.Trap.cascadeFunction);
197 scope.put("arguments", args);
199 for(Expr e = left; e != null; e = e.next) scope.put(e.string, args.get(new Integer(i++)));
201 return right.eval(scope);
202 } catch (ReturnException r) {
204 } catch (ControlTransferException c) {
205 throw new Error("error, ControlTransferException tried to leave a function: " + c);
211 Object[] keys = ((JS)left.right.eval(s)).enumerateProperties();
213 for(int i=0; i<keys.length; i++) {
214 JS.Scope scope = new JS.Scope(s);
215 scope.put(left.left.string, keys[i]);
218 } catch (ContinueException c) { /* FIXME: handle labels */ }
220 } catch (BreakException b) { /* FIXME: handle labels */ }
224 Object switchVal = left.eval(s);
227 for(Expr e = right; e != null; e = e.next) {
228 if (go || e.code == Lexer.DEFAULT || e.left.eval(s).equals(switchVal)) go = true;
229 if (go) e.right.eval(s);
231 } catch (BreakException b) { /* FIXME: handle labels */ }
235 JS.Array ret = new JS.Array();
236 for(Expr e = left; e != null; e = e.next) ret.addElement(e.eval(s));
240 case Lexer.LC: // block
241 for(Expr e = left; e != null; e = e.next) e.eval(s);
244 case Lexer.RC: { // Object ctor
245 JS.Obj ret = new JS.Obj();
246 for(Expr e = left; e != null; e = e.next)
247 ret.put(e.left.string, e.right.eval(s));
252 for(Expr e = left; e != null; e = e.next)
253 if (e.code == Lexer.NAME) {
256 s.declare(e.left.string);
261 case Lexer.HOOK: return toBoolean(left.eval(s)) ? right.left.eval(s) : right.right.eval(s);
262 case Lexer.IF: return toBoolean(left.eval(s)) ? right.left.eval(s) : right.right.eval(s);
264 case Lexer.BREAK: throw new BreakException(string);
265 case Lexer.CONTINUE: throw new ContinueException(string);
266 case Lexer.RETURN: throw new ReturnException(left == null ? null : left.eval(s));
271 boolean first = true;
272 while((first && code == Lexer.DO) || toBoolean(left.eval(s))) {
274 try { right.eval(s); }
275 catch (ContinueException e) {
276 if (e.label != null && !e.label.equals(string)) throw e;
279 } catch (BreakException e) {
280 if (e.label != null && !e.label.equals(string)) throw e;
284 default: throw new Error("don't know how to eval an Expr with code " + Lexer.codeToString[code]);
288 private static abstract class ControlTransferException extends Exception { }
289 private static class BreakException extends ControlTransferException {
291 BreakException(String label) { this.label = label; }
293 private static class ContinueException extends ControlTransferException {
295 ContinueException(String label) { this.label = label; }
297 private static class ReturnException extends ControlTransferException {
298 public Object retval;
299 ReturnException(Object retval) { this.retval = retval; }