// these case arms match the precedence of operators; each arm is a precedence level.
switch (tok) {
- case ASSIGN_BITOR: case ASSIGN_BITXOR: case ASSIGN_BITAND: case ASSIGN_LSH:
- case ASSIGN_RSH: case ASSIGN_URSH: case ASSIGN_ADD: case ASSIGN_SUB: case ASSIGN_MUL: case ASSIGN_DIV: case ASSIGN_MOD:
- return new Expr(curLine, ASSIGN, prefix, new Expr(curLine, tok - 1, prefix, parseMaximalExpr(null, precedence[ASSIGN])));
+ case VAR: {
+ if (prefix != null) { pushBackToken(); return prefix; }
+ ByteCode b = new ByteCode(curLine);
+ b.add(b.THIS, NO_ARG);
+ while(true) {
+ consume(NAME);
+ String name = string;
+ b.add(b.DECLARE, name);
+ if (peekToken() == ASSIGN) {
+ b.add(b.LITERAL, name);
+ consume(ASSIGN);
+ b.add(b.EXPR, parseMaximalExpr());
+ b.add(b.PUT, NO_ARG);
+ b.add(b.POP, NO_ARG);
+ }
+ if (peekToken() != COMMA) break;
+ consume(COMMA);
+ }
+ return b;
+ }
- // DONE //
+ case IN: pushBackToken(); return prefix;
+
+ case IF: {
+ if (prefix != null) { pushBackToken(); return prefix; }
+ ByteCode b = new ByteCode(curLine);
+ consume(LP);
+ b.add(b.EXPR, parseMaximalExpr());
+ consume(RP);
+ b.add(b.JF, new Integer(3));
+ b.add(b.EXPR, parseBlock(false));
+ b.add(b.JMP, new Integer(2));
+ if (peekToken() != ELSE) return b.add(b.LITERAL, null);
+ consume(ELSE);
+ b.add(b.EXPR, parseBlock(false));
+ return b;
+ }
// FIXME: ugly hack!!
+ case ASSIGN_BITOR: if (tok == ASSIGN_BITOR) tok = BITOR;
+ case ASSIGN_BITXOR: if (tok == ASSIGN_BITXOR) tok = BITXOR;
+ case ASSIGN_BITAND: if (tok == ASSIGN_BITAND) tok = BITAND;
+ case ASSIGN_LSH: if (tok == ASSIGN_LSH) tok = LSH;
+ case ASSIGN_RSH: if (tok == ASSIGN_RSH) tok = RSH;
+ case ASSIGN_URSH: if (tok == ASSIGN_URSH) tok = URSH;
+ case ASSIGN_ADD: if (tok == ASSIGN_ADD) tok = ADD;
+ case ASSIGN_SUB: if (tok == ASSIGN_SUB) tok = SUB;
+ case ASSIGN_MUL: if (tok == ASSIGN_MUL) tok = MUL;
+ case ASSIGN_DIV: if (tok == ASSIGN_DIV) tok = DIV;
+ case ASSIGN_MOD: if (tok == ASSIGN_MOD) tok = MOD;
+ {
+ ByteCode b = (ByteCode)prefix;
+ b.set(b.size() - 1, b.GET_PRESERVE, new Boolean(true));
+ b.add(b.EXPR, parseMaximalExpr(null, precedence[tok]));
+ b.add(tok, NO_ARG);
+ b.add(b.PUT, NO_ARG);
+ b.add(b.SWAP, NO_ARG);
+ b.add(b.POP, NO_ARG);
+ return b;
+ }
+
case INC: case DEC:
if (prefix == null) {
// prefix
while(true) {
Expr e = parseMaximalExpr();
if (e == null && peekToken() == RB) { consume(RB); return b; }
- if (e == null) e = new Expr(curLine, NULL);
b.add(b.LITERAL, new Integer(i++));
- b.add(b.EXPR, e);
+ if (e == null) b.add(b.LITERAL, null);
+ else b.add(b.EXPR, e);
b.add(b.PUT, NO_ARG);
b.add(b.POP, NO_ARG);
if (peekToken() == RB) { consume(RB); return b; }
case FUNCTION: {
if (prefix != null) { pushBackToken(); return prefix; }
consume(LP);
- ExprList list = new ExprList(curLine, LC);
+ ByteCode b = new ByteCode(curLine);
+ int numArgs = 0;
+ b.add(b.THIS, NO_ARG);
+ b.add(b.SWAP, NO_ARG);
+ b.add(b.LITERAL, "arguments");
+ b.add(b.LITERAL, "arguments");
+ b.add(b.DECLARE, NO_ARG);
+ b.add(b.SWAP, NO_ARG);
+ b.add(b.PUT, NO_ARG);
+ b.add(b.SWAP, NO_ARG);
+ b.add(b.POP, NO_ARG);
if (peekToken() == RP) consume(RP);
else while(true) {
- tok = peekToken();
- if (tok == COMMA) {
+ if (peekToken() == COMMA) {
consume(COMMA);
- list.add(new Expr(curLine, NULL));
} else {
consume(NAME);
- list.add(new Expr(curLine, NAME, string));
+
+ // declare the name
+ b.add(b.LITERAL, string);
+ b.add(b.DECLARE, NO_ARG);
+
+ // retrieve it from the arguments array
+ b.add(b.LITERAL, new Integer(numArgs));
+ b.add(b.GET_PRESERVE, NO_ARG);
+ b.add(b.SWAP, NO_ARG);
+ b.add(b.POP, NO_ARG);
+
+ // put it to the current scope
+ b.add(b.THIS, NO_ARG);
+ b.add(b.SWAP, NO_ARG);
+ b.add(b.LITERAL, string);
+ b.add(b.SWAP, NO_ARG);
+ b.add(b.PUT, NO_ARG);
+
+ // clean the stack
+ b.add(b.POP, NO_ARG);
+ b.add(b.POP, NO_ARG);
+
if (peekToken() == RP) { consume(RP); break; }
consume(COMMA);
}
+ numArgs++;
}
- return new Expr(curLine, FUNCTION, list, parseBlock(true));
+ // pop off the arguments array
+ b.add(b.POP, NO_ARG);
+ parseBlock(true, b);
+ return new ByteCode(curLine, b.FUNCTION, b);
}
- case VAR: {
- if (prefix != null) { pushBackToken(); return prefix; }
- ByteCode b = new ByteCode(curLine);
- b.add(b.THIS, NO_ARG);
- while(true) {
- consume(NAME);
- String name = string;
- b.add(b.DECLARE, name);
- if (peekToken() == ASSIGN) {
- b.add(b.LITERAL, name);
- consume(ASSIGN);
- b.add(b.EXPR, parseMaximalExpr());
- b.add(b.PUT, NO_ARG);
- b.add(b.POP, NO_ARG);
- }
- if (peekToken() != COMMA) break;
- consume(COMMA);
- }
- return b;
- }
-
- case IN: pushBackToken(); return prefix;
-
- case IF: {
- if (prefix != null) { pushBackToken(); return prefix; }
- ByteCode b = new ByteCode(curLine);
- consume(LP);
- b.add(b.EXPR, parseMaximalExpr());
- consume(RP);
- b.add(b.JF, new Integer(3));
- b.add(b.EXPR, parseBlock(false));
- b.add(b.JMP, new Integer(2));
- if (peekToken() != ELSE) return b.add(b.LITERAL, null);
- consume(ELSE);
- b.add(b.EXPR, parseBlock(false));
- return b;
- }
-
// Needs break //
case SWITCH: {
if (prefix != null) { pushBackToken(); return prefix; }
- if (getToken() != LP) throw new ParserException("expected left paren");
+ consume(LP);
Expr switchExpr = parseMaximalExpr();
- if (getToken() != RP) throw new ParserException("expected left paren");
- if (getToken() != LC) throw new ParserException("expected left brace");
+ consume(RP);
+ consume(LC);
ExprList toplevel = new ExprList(curLine, LC);
while(true) {
tok = getToken();
public static final byte DECLARE = -6; // < name > -- declare <name> in the current scope
public static final byte THIS = -7; // -- push the topmost non-transparent scope onto the stack
public static final byte GET = -8; // -- get stack[0] from stack[1]
+ public static final byte GET_PRESERVE = -80; // -- get stack[0] from stack[1]
public static final byte PUT = -9; // -- put stack[1] to key stack[0] on stack[2]; leaves object on the stack
public static final byte THROW = -10; // -- throw topmost element
public static final byte RETURN = -11; // -- return the topmost value on the stack
public Object eval(final JS.Scope s) throws ControlTransferException, JS.Exn {
return eval(s, new Parser.Thread());
}
- public Object eval(JS.Scope s, Parser.Thread t) throws ControlTransferException {
+ public Object eval(final JS.Scope s, Parser.Thread t) throws ControlTransferException {
for(int i=0; i<size; i++)
switch(op[i]) {
case arithmetic: break;
t.push(doGet(o, v));
break;
}
+ case GET_PRESERVE: {
+ Object v = t.pop();
+ Object o = t.peek();
+ t.push(v);
+ t.push(doGet(o, v));
+ break;
+ }
case NOP: break;
case EXPR: t.push(((Expr)arg[i]).eval(s)); break;
t.push(f.call(arguments));
break;
- case FUNCTION: break;
+ case FUNCTION: {
+ final ByteCode myBytes = (ByteCode)arg[i];
+ t.push(new JS.Function() {
+ public String toString() { return sourceName + ":" + line; }
+ public String getSourceName() throws JS.Exn { return sourceName; }
+ public Object _call(final JS.Array args) throws JS.Exn {
+ Function save = JS.getCurrentFunction();
+ JS.currentFunction.put(java.lang.Thread.currentThread(), this);
+ JS.Scope scope = new JS.Scope(s) {
+ // FIXME
+ public String getSourceName() { return sourceName; }
+ 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;
+ return super.get(key);
+ }
+ };
+ Parser.Thread t0 = new Parser.Thread();
+ t0.push(args);
+ try {
+ return myBytes.eval(scope, t0);
+ } catch (ReturnException r) {
+ return r.retval;
+ } catch (ControlTransferException c) {
+ throw new EvaluatorException("error, ControlTransferException tried to leave a function: " + c);
+ } finally {
+ if (save == null) JS.currentFunction.remove(java.lang.Thread.currentThread());
+ else JS.currentFunction.put(java.lang.Thread.currentThread(), save);
+ }
+ }
+ });
+ break;
+ }
+
case RETURN: break;
case TRY: break;
case INSTANCEOF: break;
if (args.length() != 1) return null;
return new Integer(((String)o).indexOf(args.elementAt(0).toString()));
} };
- throw new Error("Not Implemented: properties on String objects");
+ throw new Error("Not Implemented: propery " + v + " on String objects");
} else if (o instanceof Boolean) {
throw new Error("Not Implemented: properties on Boolean objects");
} else if (o instanceof Number) {
}
/** a block is either a single statement or a list of statements surrounded by curly braces; all expressions are also statements */
- public Expr parseBlock(boolean requireBraces) throws IOException {
+ public Expr parseBlock(boolean requireBraces) throws IOException { return parseBlock(requireBraces, null); }
+ public Expr parseBlock(boolean requireBraces, ByteCode b) throws IOException {
Expr smt = null;
int tok = peekToken();
if (tok == -1) return null;
if (braced) consume(LC);
int curLine = line;
ByteCode ret = new ByteCode(curLine);
- ByteCode block = new ByteCode(curLine);
+ ByteCode block = b == null ? new ByteCode(curLine) : b;
block.add(ret.LITERAL, Boolean.TRUE);
ret.add(block.SCOPE, block);
while(true) {
public Object eval(final JS.Scope s) throws ControlTransferException, JS.Exn {
switch(code) {
+ case Lexer.NULL: return null;
case Lexer.TYPEOF: {
Object o = left.eval(s);
if (o == null) return "null";
throw new EvaluatorException("typeof " + o.getClass().getName() + " unknown");
}
- case Lexer.NUMBER: return number;
- case Lexer.STRING: return string;
-
- case Lexer.ASSIGN: {
- Object v = (code == Lexer.ASSIGN) ? right.eval(s) : new Double(toDouble(left.eval(s)) + (code == Lexer.INC ? 1 : -1));
- if (left.code == Lexer.DOT) {
- Object o = left.left.eval(s);
- if (o instanceof String) {
- throw new EvaluatorException("can't set properties on a String");
- } else if (o instanceof Number) {
- throw new EvaluatorException("can't set properties on a Number");
- } else if (o instanceof Boolean) {
- throw new EvaluatorException("can't set properties on a Boolean");
- } else {
- 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;
- }
- } else {
- s.put(left.string, v);
- return v;
- }
- }
-
- case Lexer.NULL: return null;
- case Lexer.FALSE: return Boolean.FALSE;
- case Lexer.TRUE: return Boolean.TRUE;
- case Lexer.ASSERT: if (!toBoolean(left.eval(s))) throw new EvaluatorException("assertion failed");
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.TRY: {
boolean safeToExit = false;
return null;
}
- case Lexer.FUNCTION:
- return new JS.Function() {
- public String toString() { return right.sourceName + ":" + right.line; }
- public String getSourceName() throws JS.Exn { return right.sourceName; }
- public Object _call(final JS.Array args) throws JS.Exn {
- Function save = JS.getCurrentFunction();
- JS.currentFunction.put(java.lang.Thread.currentThread(), this);
- JS.Scope scope = new JS.Scope(s) {
- // FIXME
- public String getSourceName() { return sourceName; }
- 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;
- else if (key.equals("arguments")) return args;
- return super.get(key);
- }
- };
- int i = 0;
- ExprList list = (ExprList)left;
- for(i=0; i<list.size(); i++) {
- scope.declare(list.elementAt(i).string);
- scope.put(list.elementAt(i).string, args.get(new Integer(i)));
- }
- try {
- return right.eval(scope);
- } catch (ReturnException r) {
- return r.retval;
- } catch (ControlTransferException c) {
- throw new EvaluatorException("error, ControlTransferException tried to leave a function: "
- + c);
- } finally {
- if (save == null) JS.currentFunction.remove(java.lang.Thread.currentThread());
- else JS.currentFunction.put(java.lang.Thread.currentThread(), save);
- }
- }
- };
-
case Lexer.FOR:
Object[] keys = ((JS)left.right.eval(s)).keys();
for(int i=0; i<keys.length; i++) {