public void consume(int code) throws IOException {
if (getToken() != code)
- throw new ParserException("expected " + codeToString[op] + ", got " + (op == -1 ? "EOL" : codeToString[op]));
+ throw new ParserException("expected " + codeToString[code] + ", got " + (op == -1 ? "EOL" : codeToString[op]));
}
/** parses the largest possible expression */
return r;
}
- // Needs break //
-
- case FOR:
- if (prefix != null) { pushBackToken(); return prefix; }
- if (getToken() != LP) throw new ParserException("expected left paren");
- e1 = parseMaximalExpr();
- if (peekToken() == IN) {
- getToken();
- e2 = parseMaximalExpr();
- if (getToken() != RP) throw new ParserException("expected right paren");
- return new Expr(curLine, FOR, new Expr(curLine, IN, e1.left, e2), parseBlock(false));
-
- } else {
- Expr initExpr = e1;
- if (initExpr == null) initExpr = new Expr(curLine, NULL);
- consume(SEMI);
- Expr whileExpr = parseMaximalExpr();
- consume(SEMI);
- Expr incExpr = parseMaximalExpr();
- consume(RP);
- Expr body = parseBlock(false);
- Expr loop = new Expr(curLine, WHILE, whileExpr, body);
- ExprList list = new ExprList(curLine, LC);
- list.add(initExpr);
- ExprList list2 = new ExprList(curLine, WHILE);
- list.add(list2);
- list2.add(whileExpr);
- list2.add(body);
- list2.add(incExpr);
- return list;
- }
-
case TRY: {
+ // FIXME: don't just ignore this!
// We deliberately allow you to omit braces in catch{}/finally{} if they are single statements...
if (prefix != null) { pushBackToken(); return prefix; }
Expr tryBlock = parseBlock(true);
tok = peekToken();
- ExprList list = new ExprList(curLine, TRY);
if (tok == CATCH) {
getToken();
if (getToken() != LP) throw new ParserException("expected (");
if (getToken() != NAME) throw new ParserException("expected name");
- Expr name = new Expr(curLine, NAME, string);
if (getToken() != RP) throw new ParserException("expected )");
- list.add(new Expr(curLine, CATCH, name, parseBlock(false)));
tok = peekToken();
}
- if (tok == FINALLY) {
- getToken();
- list.add(new Expr(curLine, FINALLY, parseBlock(false)));
- }
+ if (tok == FINALLY) getToken();
- if (list.size() == 0) throw new ParserException("try without catch or finally");
- return new Expr(curLine, TRY, tryBlock, list);
+ return tryBlock;
}
+ case FOR: {
+ if (prefix != null) { pushBackToken(); return prefix; }
+ if (getToken() != LP) throw new ParserException("expected left paren");
+
+ tok = getToken();
+ if (tok == VAR) tok = getToken();
+ String varName = string;
+ boolean forIn = peekToken() == IN;
+ pushBackToken(tok, varName);
+
+ ByteCode b = new ByteCode(curLine);
+ if (forIn) {
+ consume(NAME);
+ consume(IN);
+ b.add(b.EXPR, parseMaximalExpr());
+ b.add(b.PUSHKEYS, NO_ARG);
+ b.add(b.LITERAL, "length");
+ b.add(b.GET, NO_ARG);
+ consume(RP);
+ ByteCode b2 = new ByteCode(curLine);
+ b.add(b.SCOPE, b2);
+ b2.add(b.LITERAL, new Integer(1));
+ b2.add(SUB, NO_ARG);
+ b2.add(b.DUP, NO_ARG);
+ b2.add(b.LITERAL, new Integer(0));
+ b2.add(LT, NO_ARG);
+ b2.add(b.JT, new Integer(7));
+ b2.add(b.GET_PRESERVE, NO_ARG);
+ b2.add(b.LITERAL, varName);
+ b2.add(b.LITERAL, varName);
+ b2.add(b.DECLARE, NO_ARG);
+ b2.add(b.PUT, NO_ARG);
+ b2.add(b.EXPR, parseBlock(false));
+ b2.add(b.LITERAL, null);
+ return b;
+
+ } else {
+ ByteCode b2 = new ByteCode(curLine);
+ b.add(b.SCOPE, b2);
+ b2.add(b.EXPR, parseMaximalExpr());
+ b2.add(b.POP, NO_ARG);
+ consume(SEMI);
+ ByteCode b3 = new ByteCode(curLine);
+ b2.add(b.LOOP, b3);
+ b2.add(b.LITERAL, null);
+ b3.add(b.EXPR, parseMaximalExpr());
+ consume(SEMI);
+ b3.add(b.JT, new Integer(2));
+ b3.add(BREAK, NO_ARG);
+ b3.add(b.JT, new Integer(2));
+ b3.add(b.EXPR, parseMaximalExpr());
+ consume(RP);
+ parseBlock(false, b3);
+ return b;
+ }
+ }
+
default:
pushBackToken();
return prefix;
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 static final byte ASSERT = -12; // -- fail if topmost element is not true
public static final byte JT = -13; // < relative_address > -- pop the stack; if true, jump to <relative_address>
public static final byte JF = -21; // < relative_address > -- pop the stack; if false, jump to <relative_address>
public static final byte JMP = -22; // < relative_address > -- jump to <relative_address>
static public final byte POP = -14; // -- discard the top element on the stack
public static final byte CALL = -15; // < numargs > -- call stack[0] with the topmost <numargs> values as arguments
- public static final byte TRY = -16; // < bytecode_block > -- run the block pointed to; returns with thrown exn on top of stack
-
- public static final byte INSTANCEOF = -17; // -- ??
- public static final byte TYPEOF = -18; // --
- public static final byte FOR__IN = -19; // -- ??
+ public static final byte PUSHKEYS = -19; // -- ??
public static final byte EXPR = -20; // -- transitional
public static final byte SWAP = -23; // -- transitional
public static final byte SCOPE = -30; // -- transitional
public static final byte LOOP = -40; // -- transitional
public static final byte DUP = -50; // -- transitional
+ public static final byte LABEL = -60; // -- transitional
int[] op = new int[10];
Object[] arg = new Object[10];
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;
- case LITERAL:
- t.push(arg[i]);
- break;
+ case LABEL: break; // FIXME
+ case LITERAL: t.push(arg[i]); break;
case OBJECT: t.push(new JS.Obj()); break;
case ARRAY: t.push(new JS.Array(toNumber(arg[i]).intValue())); break;
case DECLARE: s.declare(arg[i] == NO_ARG ? (String)t.pop() : (String)arg[i]); break;
case THIS: t.push(s); break; // FIXME: transparents
- case ASSERT: if (!toBoolean(t.pop())) throw new EvaluatorException("assertion failed"); break;
- case THROW: throw new JS.Exn(t.pop());
case JT: if (toBoolean(t.pop())) i += toNumber(arg[i]).intValue() - 1; break;
case JF: if (!toBoolean(t.pop())) i += toNumber(arg[i]).intValue() - 1; break;
case JMP: i += toNumber(arg[i]).intValue() - 1; break;
case POP: t.pop(); break;
case SWAP: t.swap(); break;
case DUP: t.push(t.peek()); break;
+ case NOP: break;
+ case EXPR: t.push(((Expr)arg[i]).eval(s)); break;
+ case SCOPE: t.push(((ByteCode)arg[i]).eval(new JS.Scope(s), t)); break;
+
+ case ASSERT: if (!toBoolean(t.pop())) throw new EvaluatorException("assertion failed"); break;
+ case RETURN: throw new ReturnException(t.pop());
+ case THROW: throw new JS.Exn(t.pop());
+
+ case TRY: break;
+ case INSTANCEOF: break;
+ case TYPEOF: break;
+ case PUSHKEYS: {
+ Object o = t.peek();
+ Object[] keys = ((JS)o).keys();
+ JS.Array a = new JS.Array();
+ a.setSize(keys.length);
+ for(int j=0; j<keys.length; j++) a.setElementAt(keys[j], j);
+ t.push(a);
+ break;
+ }
+
+ case Lexer.BITNOT: t.push(new Long(~toLong(t.pop()))); break;
+ case Lexer.BANG: t.push(new Boolean(!toBoolean(t.pop()))); break;
case Lexer.BREAK: {
// FIXME: make sure this can only appear in proper places
t.push(doGet(o, v));
break;
}
+
case GET_PRESERVE: {
Object v = t.pop();
Object o = t.peek();
t.push(doGet(o, v));
break;
}
- case NOP: break;
- case EXPR: t.push(((Expr)arg[i]).eval(s)); break;
- case SCOPE: t.push(((Expr)arg[i]).eval(new JS.Scope(s))); break;
-
- case CALL:
+ case CALL: {
JS.Array arguments = new JS.Array();
int numArgs = toNumber(arg[i]).intValue();
arguments.setSize(numArgs);
if (f == null) throw new JS.Exn(new EvaluatorException("attempted to call null"));
t.push(f.call(arguments));
break;
+ }
case FUNCTION: {
final ByteCode myBytes = (ByteCode)arg[i];
break;
}
- case RETURN: break;
- case TRY: break;
- case INSTANCEOF: break;
- case TYPEOF: break;
- case FOR__IN: break;
-
- case Lexer.BITNOT: t.push(new Long(~toLong(t.pop()))); break;
- case Lexer.BANG: t.push(new Boolean(!toBoolean(t.pop()))); break;
-
case Lexer.INC: case Lexer.DEC: {
boolean isPrefix = toBoolean(arg[i]);
Object key = t.pop();
public void push(Object o) {
os[size++] = o;
}
- public Object pop() {
- return os[--size];
- }
+ public Object pop() { return os[--size]; }
public Object peek() { return os[size - 1]; }
public void swap() { Object temp = os[size - 1]; os[size - 1] = os[size - 2]; os[size - 2] = temp; }
public int size() { return size; }
while(true) {
switch(tok = peekToken()) {
- case LC:
- smt = parseBlock(true); break;
+ case LC: smt = parseBlock(true); break;
+ case GOTO: throw new ParserException("goto not supported");
- case THROW: case RETURN: case ASSERT:
+ case THROW: case RETURN: case ASSERT: {
getToken();
- if (peekToken() == SEMI) {
- if (tok == THROW || tok == ASSERT) throw new ParserException(codeToString[tok] + " requires an argument");
- consume(SEMI);
- smt = new Expr(curLine, tok);
- } else {
- smt = new Expr(curLine, tok, parseMaximalExpr());
- consume(SEMI);
- }
+ ByteCode r = new ByteCode(curLine);
+ if (tok == RETURN && peekToken() == SEMI) r.add(b.LITERAL, null);
+ else r.add(b.EXPR, parseMaximalExpr());
+ consume(SEMI);
+ r.add(tok, NO_ARG);
+ smt = r;
break;
+ }
- case GOTO: case BREAK: case CONTINUE: {
+ case BREAK: case CONTINUE: {
getToken();
- int t = peekToken();
- if (t == NAME) {
- getToken();
- smt = new Expr(curLine, tok, new Expr(curLine, string));
- } else if (tok == GOTO) {
- throw new ParserException("goto must be followed by a label");
- }
- smt = new Expr(curLine, tok, new Expr(curLine, string));
+ if (peekToken() == NAME) consume(NAME);
+ smt = new ByteCode(curLine, tok, string);
consume(SEMI);
break;
}
consume(NAME);
if (peekToken() == COLON) {
consume(COLON);
- smt = new Expr(curLine, COLON, new Expr(curLine, name), parseBlock(false));
+ smt = new ByteCode(curLine, ByteCode.LABEL, string);
break;
} else {
pushBackToken(NAME, name);
throw new EvaluatorException("typeof " + o.getClass().getName() + " unknown");
}
- case Lexer.THROW: throw new JS.Exn(left.eval(s));
-
case Lexer.TRY: {
boolean safeToExit = false;
try {
}
return null;
- case Lexer.SWITCH:
- Object switchVal = left.eval(s);
- boolean go = false;
- try {
- ExprList list = (ExprList)right;
- for(int i=0; i<list.size(); i++) {
- Expr e = list.elementAt(i);
- if (go || e.code == Lexer.DEFAULT || e.left.eval(s).equals(switchVal)) go = true;
- if (go) e.right.eval(s);
- }
- } catch (BreakException b) {
- if (b.label == null || b.label.equals(string)) return null;
- throw (BreakException)b.fillInStackTrace();
- }
- 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));
-
default: throw new EvaluatorException("don't know how to eval an Expr with code " + Lexer.codeToString[code] + "\n" + this);
}
}