// check if box being moved is currently target of a redirect
for(Box cur = newnode.getParent(); cur != null; cur = cur.getParent())
if (cur.redirect == newnode) {
- if (Log.on) Log.log(this, "attempt to move a box that is the target of a redirect at "+ JS.getCurrentFunctionSourceName());
+ if (Log.on) Log.log(this, "attempt to move a box that is the target of a redirect at "+
+ JS.getCurrentFunctionSourceName());
return;
}
// check for recursive ancestor violation
for(Box cur = this; cur != null; cur = cur.getParent())
if (cur == newnode) {
- if (Log.on) Log.log(this, "attempt to make a node a parent of its own ancestor at " + JS.getCurrentFunctionSourceName());
+ if (Log.on) Log.log(this, "attempt to make a node a parent of its own ancestor at " +
+ JS.getCurrentFunctionSourceName());
+ if (Log.on) Log.log(this, "box == " + this + " ancestor == " + newnode);
return;
}
public void put(Object name_, Object value, boolean ignoretraps, RootProxy rp) {
if (name_ instanceof Number) { put(((Number)name_).intValue(), value); return; }
String name = (String)name_;
+ if (name == null) return; // FIXME, shouldn't be necessary
if (name.startsWith("xwt_")) {
if (Log.on) Log.log(this, "attempt to set reserved property " + name + " at " + JS.getFileAndLine());
return;
/** Load a directory as if it were an archive */
public static synchronized void loadDirectory(File dir) throws IOException { loadDirectory(dir, ""); }
private static synchronized void loadDirectory(File dir, String prefix) throws IOException {
- new Static(prefix.replace(File.separatorChar, '.'));
+ String n = prefix.replace(File.separatorChar, '.');
+ if (n.endsWith(".")) n = n.substring(0, n.length() - 1);
+ new Static(n);
String[] subfiles = dir.list();
for(int i=0; i<subfiles.length; i++) {
if (subfiles[i].equals("CVS") || !validResourceName(subfiles[i])) continue;
String s = value == null ? null : value.toString();
if (value == null) newcolor = 0x00000000;
else if (s.length() > 0 && s.charAt(0) == '#')
- newcolor = 0xFF000000 |
- (Integer.parseInt(s.substring(1, 3), 16) << 16) |
- (Integer.parseInt(s.substring(3, 5), 16) << 8) |
- Integer.parseInt(s.substring(5, 7), 16);
+ try {
+ newcolor = 0xFF000000 |
+ (Integer.parseInt(s.substring(1, 3), 16) << 16) |
+ (Integer.parseInt(s.substring(3, 5), 16) << 8) |
+ Integer.parseInt(s.substring(5, 7), 16);
+ } catch (NumberFormatException e) {
+ Log.log(this, "invalid color " + s);
+ return;
+ }
else if (s.equals("black")) newcolor = black;
else if (s.equals("blue")) newcolor = blue;
else if (s.equals("green")) newcolor = green;
specialBoxProperties.put("static", new SpecialBoxProperty() {
public Object get(Box b) {
- String cfsn = JS.getCurrentFunction().getSourceName();
+ JS.Function cf = JS.getCurrentFunction();
+ String cfsn = cf.getSourceName();
for(int i=0; i<cfsn.length() - 1; i++)
if (cfsn.charAt(i) == '.' && (cfsn.charAt(i+1) == '_' || Character.isDigit(cfsn.charAt(i+1)))) {
cfsn = cfsn.substring(0, i);
/** creates a new static representing a package */
public Static(String resourcename) { this(resourcename, true); }
- public Object get(String name) {
+ public Object get(Object name_) {
+ String name = name_.toString();
if (!ispackage) return super.get(name);
return getStatic(resourcename + (resourcename.length() == 0 ? "" : ".") + name);
}
if (args.length() < 2) return args.elementAt(0);
return new Double(Math.max(((Number)args.elementAt(0)).doubleValue(),
((Number)args.elementAt(1)).doubleValue())); } };
+ else if ("cos".equals(name)) return new JS.Function() { public Object _call(JS.Array args) {
+ return new Double(Math.cos(((Number)args.elementAt(0)).doubleValue())); } };
+ else if ("sin".equals(name)) return new JS.Function() { public Object _call(JS.Array args) {
+ return new Double(Math.sin(((Number)args.elementAt(0)).doubleValue())); } };
+ else if ("tan".equals(name)) return new JS.Function() { public Object _call(JS.Array args) {
+ return new Double(Math.tan(((Number)args.elementAt(0)).doubleValue())); } };
+ else if ("acos".equals(name)) return new JS.Function() { public Object _call(JS.Array args) {
+ return new Double(Math.acos(((Number)args.elementAt(0)).doubleValue())); } };
+ else if ("asin".equals(name)) return new JS.Function() { public Object _call(JS.Array args) {
+ return new Double(Math.asin(((Number)args.elementAt(0)).doubleValue())); } };
+ else if ("atan".equals(name)) return new JS.Function() { public Object _call(JS.Array args) {
+ return new Double(Math.atan(((Number)args.elementAt(0)).doubleValue())); } };
return null;
}});
put("println", new JS.Function() { public Object _call(JS.Array args) throws JS.Exn {
if (args.length() != 1) return null;
if (Log.on) Log.log(this, JS.getFileAndLine() + " " +
- (args.elementAt(0) == null ? "null" : args.elementAt(0).toString()));
+ (args.elementAt(0) == null ? "**null**" : args.elementAt(0).toString()));
return null;
}});
return ret;
}});
- put("theme", new JS.Function() { public Object _call(JS.Array args) throws JS.Exn {
- Log.log(XWT.class, "xwt.theme() not implemented...");
- return null;
- }});
-
put("xmlrpc", new JS.Function() { public Object _call(JS.Array args) throws JS.Exn {
if (args.length() != 1 || args.elementAt(0) == null) return null;
return new XMLRPC(args.elementAt(0).toString(), "");
}});
put("loadArchive", new JS.Function() { public Object _call(JS.Array args) throws JS.Exn {
- if (!ThreadMessage.suspendThread()) return null;
-
- try {
- if (args == null || args.length() < 1 || args.elementAt(0) == null) return null;
- URL u = new URL(args.elementAt(0).toString());
-
- JS.Function callback = null;
- if (args.length() == 2 && args.elementAt(1) != null && args.elementAt(1) instanceof JS.Function)
- callback = (JS.Function)args.elementAt(1);
-
- if (!u.getFile().endsWith(".xwar")) {
- if (Log.on) Log.log(this, "Error: archive names must end with .xwar: " + u.getFile());
- throw new JS.Exn("Error: archive names must end with .xwar: " + u.getFile());
- }
-
- if (u.getProtocol().equals("http")) {
- HTTP http = new HTTP(u.toString());
- if (Main.originAddr == null) {
- try {
- Main.originAddr = InetAddress.getByName(u.getHost());
- } catch (UnknownHostException e) {
- if (Log.on) Log.log(this, "couldn't resolve " + u.getHost() + "; proceeding without permissions");
- Main.originAddr = InetAddress.getByName("0.0.0.0");
- }
- } else {
- Main.originAddr = InetAddress.getByName("0.0.0.0");
- }
- HTTP.HTTPInputStream in = http.GET();
- Resources.loadArchive(in, in.getContentLength(), callback);
-
- } else if (u.getProtocol().equals("file")) {
- if (Main.originAddr != null) {
- if (Log.on) Log.log(this, "scripts downloaded from the network may not load xwars from the local filesystem");
- throw new JS.Exn("scripts downloaded from the network may not load xwars from the local filesystem");
- }
- Resources.loadArchive(new FileInputStream(u.getFile()), (int)new File(u.getFile()).length(), callback);
-
- } else {
- if (Log.on) Log.log(this, "unknown protocol \"" + u.getProtocol() + "\"");
- throw new JS.Exn("unknown protocol \"" + u.getProtocol() + "\"");
- }
-
- } catch (MalformedURLException me) {
- if (Log.on) Log.log(this, "Malformed URL: " + args.elementAt(0));
- if (Log.on) Log.log(this, me);
- throw new JS.Exn(me.toString());
-
- } catch (IOException ioe) {
- if (Log.on) Log.log(this, "IOException while loading archive:");
- if (Log.on) Log.log(this, ioe);
- throw new JS.Exn(ioe.toString());
-
- } finally {
- ThreadMessage.resumeThread();
-
- }
- return null;
- }
- });
+ if (!ThreadMessage.suspendThread()) return null;
+ try {
+ if (args == null || args.length() < 1 || args.elementAt(0) == null) return null;
+ URL u = new URL(args.elementAt(0).toString());
+
+ JS.Function callback = null;
+ if (args.length() == 2 && args.elementAt(1) != null && args.elementAt(1) instanceof JS.Function)
+ callback = (JS.Function)args.elementAt(1);
+
+ if (!u.getFile().endsWith(".xwar")) {
+ if (Log.on) Log.log(this, "Error: archive names must end with .xwar: " + u.getFile());
+ throw new JS.Exn("Error: archive names must end with .xwar: " + u.getFile());
+ }
+
+ if (u.getProtocol().equals("http")) {
+ HTTP http = new HTTP(u.toString());
+ if (Main.originAddr == null) {
+ try {
+ Main.originAddr = InetAddress.getByName(u.getHost());
+ } catch (UnknownHostException e) {
+ if (Log.on) Log.log(this, "couldn't resolve " + u.getHost() + "; proceeding without permissions");
+ Main.originAddr = InetAddress.getByName("0.0.0.0");
+ }
+ } else {
+ Main.originAddr = InetAddress.getByName("0.0.0.0");
+ }
+ HTTP.HTTPInputStream in = http.GET();
+ Resources.loadArchive(in, in.getContentLength(), callback);
+
+ } else if (u.getProtocol().equals("file")) {
+ if (Main.originAddr != null) {
+ if (Log.on) Log.log(this, "scripts downloaded from the network may not load xwars from the local filesystem");
+ throw new JS.Exn("scripts downloaded from the network may not load xwars from the local filesystem");
+ }
+ Resources.loadArchive(new FileInputStream(u.getFile()), (int)new File(u.getFile()).length(), callback);
+
+ } else {
+ if (Log.on) Log.log(this, "unknown protocol \"" + u.getProtocol() + "\"");
+ throw new JS.Exn("unknown protocol \"" + u.getProtocol() + "\"");
+ }
+
+ } catch (MalformedURLException me) {
+ if (Log.on) Log.log(this, "Malformed URL: " + args.elementAt(0));
+ if (Log.on) Log.log(this, me);
+ throw new JS.Exn(me.toString());
+
+ } catch (IOException ioe) {
+ if (Log.on) Log.log(this, "IOException while loading archive:");
+ if (Log.on) Log.log(this, ioe);
+ throw new JS.Exn(ioe.toString());
+
+ } finally {
+ ThreadMessage.resumeThread();
+
+ }
+ return null;
+ }});
put("prefetchImage", new JS.Function() { public Object _call(JS.Array args) throws JS.Exn {
if (args == null || args.length() < 1 || args.elementAt(0) == null) return null;
for(int i=0; i<s.length(); i++) if (s.charAt(i) < '0' || s.charAt(i) > '9') return Integer.MIN_VALUE;
return Integer.parseInt(s);
}
- public Object get(Object key) {
+ public Object get(Object key) throws JS.Exn {
// FIXME: HACK!
if (key.equals("cascade")) return org.xwt.Trap.cascadeFunction;
+ if (key.equals("trapee")) return org.xwt.Trap.currentTrapee();
if (key.equals("length")) return new Long(vec.size());
int i = intVal(key);
if (i == Integer.MIN_VALUE) return super.get(key);
- return vec.elementAt(i);
+ try {
+ return vec.elementAt(i);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new JS.Exn(e.getMessage());
+ }
}
public void put(Object key, Object val) {
if (key.equals("length")) vec.setSize(Parser.toNumber(val).intValue());
int i = intVal(key);
if (i == Integer.MIN_VALUE) super.put(key, val);
- else vec.setElementAt(val, i);
+ else {
+ if (i >= vec.size()) vec.setSize(i+1);
+ vec.setElementAt(val, i);
+ }
}
public Object[] keys() {
Object[] sup = super.keys();
}
public static class Script extends Function {
- Parser.Expr e = null;
- private Script(Parser.Expr e) { this.e = e; }
+ Vector e = null;
+ private Script(Vector e) { this.e = e; }
+ public String getSourceName() throws JS.Exn { return ((Parser.Expr)e.elementAt(0)).sourceName; }
public Object _call(JS.Array args) throws JS.Exn {
Scope rootScope = (Scope)args.elementAt(0);
+ Function saved = (Function)currentFunction.get(Thread.currentThread());
+ currentFunction.put(Thread.currentThread(), this);
try {
- e.eval(rootScope);
+ for(int i=0; i<e.size(); i++)
+ ((Parser.Expr)e.elementAt(i)).eval(rootScope);
} catch (Parser.ReturnException e) {
// ignore
} catch (Parser.ControlTransferException e) {
throw new JS.Exn("block threw a ControlTransferException: " + e);
+ } finally {
+ if (saved == null) currentFunction.remove(Thread.currentThread());
+ else currentFunction.put(Thread.currentThread(), saved);
}
return null;
}
public static Script parse(Reader r, String sourceName, int line) throws IOException {
Parser p = new Parser(r, sourceName, line);
- p.pushBackToken(Lexer.LC); // FIXME: hack
try {
- return new Script(p.parseBlock(true));
+ Vector exprs = new Vector();
+ while(true) {
+ Parser.Expr ret = p.parseBlock(false);
+ if (ret == null || (ret.code == Parser.LC && ret.left == null)) break;
+ exprs.addElement(ret);
+ }
+ return new Script(exprs);
} catch (Exception e) {
if (Log.on) Log.log(Parser.class, e);
return null;
isRightAssociative[ASSIGN] = true;
precedence[HOOK] = 2;
precedence[COMMA] = 3;
- precedence[GT] = precedence[GE] = 4;
- precedence[OR] = precedence[AND] = 5;
+ precedence[OR] = precedence[AND] = 4;
+ precedence[GT] = precedence[GE] = 5;
precedence[BITOR] = 6;
precedence[BITXOR] = 7;
precedence[BITAND] = 8;
public Expr parseBlock(boolean requireBraces) throws IOException {
Expr ret = null;
int tok = peekToken();
+ if (tok == -1) return null;
boolean braced = tok == LC;
if (requireBraces && !braced) throw new ParserException("expected {");
if (braced) getToken();
case LC: smt = parseBlock(true); break;
case THROW: case RETURN: case ASSERT:
getToken();
+ if (peekToken() == SEMI) {
+ getToken();
+ smt = new Expr(curLine, tok);
+ break;
+ }
smt = new Expr(curLine, tok, parseMaximalExpr());
if (getToken() != SEMI) throw new ParserException("expected ;");
break;
default:
smt = parseMaximalExpr();
if (smt == null) {
- if (head == null) throw new ParserException("empty statement list; next token is " + codeToString[peekToken()]);
+ if (head == null) return null;
break OUTER;
}
+ if (peekToken() == SEMI) getToken();
break;
}
+ if (!braced) return smt;
if (head == null) head = tail = smt; else tail = (tail.next = smt);
}
return new Expr(curLine, LC, head);
if (prefix != null) { pushBackToken(); return prefix; }
while(true) {
if (getToken() != NAME) throw new ParserException("variable declarations must start with a variable name");
- Expr name = new Expr(curLine, NAME, string);
+ String name = string;
Expr initVal = null;
tok = peekToken();
Expr e = null;
getToken();
initVal = parseMaximalExpr();
tok = peekToken();
- e = new Expr(curLine, ASSIGN, name, initVal);
+ e = new Expr(curLine, VAR,
+ new Expr(curLine, name),
+ new Expr(curLine, ASSIGN,
+ new Expr(curLine, name),
+ initVal));
} else {
- e = new Expr(curLine, NAME, name);
+ e = new Expr(curLine, VAR, new Expr(curLine, name));
}
if (head == null) head = tail = e; else tail = tail.next = e;
if (tok != COMMA) break;
if (tok == IF && peekToken() == ELSE) {
getToken();
return new Expr(curLine, tok, parenExpr, new Expr(curLine, ELSE, firstBlock, parseBlock(false)));
- } else if (tok == IF) {
- return new Expr(curLine, tok, parenExpr, new Expr(curLine, ELSE, firstBlock, new Expr(curLine, LC)));
- } else if (tok == WHILE) {
+ } else {
return new Expr(curLine, tok, parenExpr, firstBlock);
}
}
case FOR:
if (prefix != null) { pushBackToken(); return prefix; }
if (getToken() != LP) throw new ParserException("expected left paren");
- e1 = parseMaximalExpr(null, -1);
+ e1 = parseMaximalExpr();
if (peekToken() == IN) {
getToken();
- e2 = parseMaximalExpr(null, -1);
+ e2 = parseMaximalExpr();
if (getToken() != RP) throw new ParserException("expected right paren");
- return new Expr(curLine, FOR, new Expr(curLine, IN, e1, e2), parseBlock(false));
+ return new Expr(curLine, FOR, new Expr(curLine, IN, e1.left, e2), parseBlock(false));
} else {
- if (getToken() != SEMI) throw new ParserException("expected ;");
- e2 = parseMaximalExpr();
- if (getToken() != SEMI) throw new ParserException("expected ;");
- e3 = parseMaximalExpr();
- if (getToken() != RP) throw new ParserException("expected right paren");
- return new Expr(curLine, LC, e1, new Expr(curLine, WHILE, e2, new Expr(curLine, LC, parseBlock(false), e3)));
+ Expr initExpr = e1;
+ expect(SEMI); getToken();
+ Expr whileExpr = parseMaximalExpr();
+ expect(SEMI); getToken();
+ Expr incExpr = parseMaximalExpr();
+ expect(RP); getToken();
+ Expr body = parseBlock(false);
+ body.next = incExpr;
+ Expr loop = new Expr(curLine, WHILE, whileExpr, body);
+ if (initExpr == null) initExpr = loop; else initExpr.next = loop;
+ return new Expr(curLine, LC, initExpr);
}
case DO: {
case Lexer.RSH: return new Long(toLong(left.eval(s)) >> toLong(right.eval(s)));
case Lexer.URSH: return new Long(toLong(left.eval(s)) >>> toLong(right.eval(s)));
+ // FIXME: these need to work on strings
case Lexer.LT: return toDouble(left.eval(s)) < toDouble(right.eval(s)) ? Boolean.TRUE : Boolean.FALSE;
case Lexer.LE: return toDouble(left.eval(s)) <= toDouble(right.eval(s)) ? Boolean.TRUE : Boolean.FALSE;
case Lexer.GT: return toDouble(left.eval(s)) > toDouble(right.eval(s)) ? Boolean.TRUE : Boolean.FALSE;
case Lexer.GE: return toDouble(left.eval(s)) >= toDouble(right.eval(s)) ? Boolean.TRUE : Boolean.FALSE;
- case Lexer.OR: return new Boolean(toBoolean(left.eval(s)) || toBoolean(right.eval(s)));
- case Lexer.AND: return new Boolean(toBoolean(left.eval(s)) && toBoolean(right.eval(s)));
+ case Lexer.OR: {
+ boolean b1 = toBoolean(left.eval(s));
+ if (b1) return Boolean.TRUE;
+ return new Boolean(b1 || toBoolean(right.eval(s)));
+ }
+
+ case Lexer.AND: {
+ boolean b1 = toBoolean(left.eval(s));
+ if (!b1) return Boolean.FALSE;
+ return new Boolean(b1 && toBoolean(right.eval(s)));
+ }
+
case Lexer.BANG: return new Boolean(!toBoolean(left.eval(s)));
case Lexer.EQ:
// FIXME: should use Javascript coercion-equality rules
Object l = left.eval(s);
Object r = right.eval(s);
- boolean ret = (l == null && r == null) || (l != null && l.equals(r));
+ boolean ret;
+ if (l == null) { Object t = r; r = l; l = t; }
+ if (l == null && r == null) ret = true;
+ else if (l instanceof Boolean) ret = new Boolean(toBoolean(r)).equals(l);
+ else if (l instanceof Number) ret = toNumber(r).doubleValue() == toNumber(l).doubleValue();
+ else if (l instanceof String) ret = r != null && l.equals(r.toString());
+ else ret = l.equals(r);
+
return new Boolean(code == Lexer.EQ ? ret : !ret);
}
} else if (o instanceof Boolean) {
throw new EvaluatorException("can't set properties on a Boolean");
} else {
- JS target = (JS)left.left.eval(s);
+ JS target = (JS)o;
if (target == null) throw new JS.Exn(new EvaluatorException("attempted to put to the null value"));
target.put(left.right.eval(s), v);
return v;
case Lexer.THROW: throw new JS.Exn(left.eval(s));
case Lexer.NAME: return s.get(string);
case Lexer.THIS: return s.isTransparent() ? s : this.eval(s.getParentScope());
-
case Lexer.DOT: {
final Object o = left.eval(s);
- Object v = ((right.code == Lexer.NAME) || (right.code == Lexer.STRING)) ? right.string : right.eval(s);
+ if (o == null) throw new EvaluatorException("tried to get a property from the null value");
+ Object v = (right.code == Lexer.STRING) ? right.string : right.eval(s);
if (o instanceof String) {
if (v.equals("length")) return new Integer(((String)o).length());
else if (v.equals("substring")) return new JS.Function() {
else throw new EvaluatorException("String.substring() can only take one or two arguments");
}
};
+ else if (v.equals("toLowerCase")) return new JS.Function() {
+ public Object _call(JS.Array args) {
+ return ((String)o).toLowerCase();
+ } };
+ else if (v.equals("toUpperCase")) return new JS.Function() {
+ public Object _call(JS.Array args) {
+ return ((String)o).toString().toUpperCase();
+ } };
+ else if (v.equals("charAt")) return new JS.Function() {
+ public Object _call(JS.Array args) {
+ return ((String)o).charAt(toNumber(args.elementAt(0)).intValue()) + "";
+ } };
+ else if (v.equals("lastIndexOf")) return new JS.Function() {
+ public Object _call(JS.Array args) {
+ if (args.length() != 1) return null;
+ return new Integer(((String)o).lastIndexOf(args.elementAt(0).toString()));
+ } };
else if (v.equals("indexOf")) return new JS.Function() {
public Object _call(JS.Array args) {
if (args.length() != 1) return null;
return new Integer(((String)o).indexOf(args.elementAt(0).toString()));
- }
- };
+ } };
throw new EvaluatorException("Not Implemented: properties on String objects");
} else if (o instanceof Boolean) {
throw new EvaluatorException("Not Implemented: properties on Boolean objects");
} else if (o instanceof Number) {
- throw new EvaluatorException("Not Implemented: properties on Number objects");
+ Log.log(this, "Not Implemented: properties on Number objects");
+ return null;
+ //throw new EvaluatorException("Not Implemented: properties on Number objects");
} else if (o instanceof JS) {
return ((JS)o).get(v);
}
JS.Scope scope = new JS.Scope(s) {
// FIXME
public String getSourceName() { return sourceName; }
- public boolean isTransparent() { return true; }
public Object get(Object key) throws JS.Exn {
if (key.equals("trapee")) return org.xwt.Trap.currentTrapee();
else if (key.equals("cascade")) return org.xwt.Trap.cascadeFunction;
}
};
int i = 0;
- for(Expr e = left; e != null; e = e.next) scope.put(e.string, args.get(new Integer(i++)));
+ for(Expr e = left; e != null; e = e.next) {
+ scope.declare(e.string);
+ scope.put(e.string, args.get(new Integer(i++)));
+ }
try {
return right.eval(scope);
} catch (ReturnException r) {
Object[] keys = ((JS)left.right.eval(s)).keys();
for(int i=0; i<keys.length; i++) {
JS.Scope scope = new JS.Scope(s);
- scope.put(left.left.string, keys[i]);
+ scope.declare(left.string);
+ scope.put(left.string, keys[i]);
try {
right.eval(scope);
} catch (ContinueException c) {
}
case Lexer.LC: // block
- for(Expr e = left; e != null; e = e.next) e.eval(s);
+ JS.Scope scope = new JS.Scope(s);
+ for(Expr e = left; e != null; e = e.next) e.eval(scope);
return null;
case Lexer.RC: { // Object ctor
JS.Obj ret = new JS.Obj();
- for(Expr e = left; e != null; e = e.next)
- ret.put(e.left.string, e.right.eval(s));
+ for(Expr e = left; e != null; e = e.next) {
+ Object key = e.left.string;
+ Object val = e.right.eval(s);
+ ret.put(key, val);
+ }
return ret;
}
case Lexer.VAR:
- for(Expr e = left; e != null; e = e.next)
- if (e.code == Lexer.NAME) {
- s.declare(e.string);
- } else {
- s.declare(e.left.string);
- e.eval(s);
- }
+ for(Expr e = this.left; e != null; e = e.next) {
+ s.declare(e.left.string);
+ if (e.right != null) e.right.eval(s);
+ }
return null;
case Lexer.HOOK: return toBoolean(left.eval(s)) ? right.left.eval(s) : right.right.eval(s);
- case Lexer.IF: return toBoolean(left.eval(s)) ? right.left.eval(s) : right.right.eval(s);
+ case Lexer.IF:
+ if (right.code == ELSE) {
+ if (toBoolean(left.eval(s))) right.left.eval(s);
+ else right.right.eval(s);
+ return null;
+ } else {
+ if (toBoolean(left.eval(s))) right.eval(s);
+ return null;
+ }
case Lexer.BREAK: throw new BreakException(string);
case Lexer.CONTINUE: throw new ContinueException(string);
case Lexer.RETURN: throw new ReturnException(left == null ? null : left.eval(s));
case Lexer.DO:
try {
boolean first = true;
- while((first && code == Lexer.DO) || toBoolean(left.eval(s))) {
+ Object bogus = null;
+ for(;(first && code == Lexer.DO) || toBoolean(left.eval(s)); bogus = (right.next == null ? null : right.next.eval(s))) {
first = false;
try { right.eval(s);
} catch (ContinueException c) {
public static Number toNumber(Object o) {
if (o == null) return new Long(0);
if (o instanceof Number) return ((Number)o);
- if (o instanceof String) return new Double((String)o);
+ if (o instanceof String) try { return new Double((String)o); } catch (NumberFormatException e) { return new Double(0); }
if (o instanceof Boolean) return ((Boolean)o).booleanValue() ? new Long(1) : new Long(0);
if (o instanceof JS) return ((JS)o).coerceToNumber();
// FIXME