+ class ExprList extends Expr {
+ Vec v = new Vec();
+ public ExprList(int curLine, int code) { super(curLine, code); }
+ public void add(Expr e) { v.addElement(e); }
+ public int numExprs() { return v.size(); }
+ public int size() { return v.size(); }
+ public Expr elementAt(int i) { return (Expr)v.elementAt(i); }
+ public Object eval(final JS.Scope s) throws ControlTransferException, JS.Exn {
+ switch(code) {
+ case LC: {
+ // Block
+ JS.Scope scope = new JS.Scope(s);
+ for(int i=0; i<v.size(); i++) ((Expr)v.elementAt(i)).eval(scope);
+ return null;
+ }
+ case Lexer.LB: {
+ // Array ctor
+ JS.Array ret = new JS.Array();
+ for(int i=0; i<numExprs(); i++) ret.addElement(elementAt(i).eval(s));
+ return ret;
+ }
+ case Lexer.RC: {
+ // Object ctor
+ JS.Obj ret = new JS.Obj();
+ for(int i=0; i<numExprs(); i++) {
+ Expr e = elementAt(i);
+ Object key = e.left.string;
+ Object val = e.right.eval(s);
+ ret.put(key, val);
+ }
+ return ret;
+ }
+ case Lexer.WHILE:
+ case Lexer.DO:
+ try {
+ boolean first = true;
+ Object bogus = null;
+ ExprList list = this;
+ Expr whileExpr = list.elementAt(0);
+ Expr bodyExpr = list.elementAt(1);
+ Expr incExpr = list.elementAt(2);
+ for(;(first && code == Lexer.DO) || toBoolean(whileExpr.eval(s)); bogus = incExpr.eval(s)) {
+ first = false;
+ try { bodyExpr.eval(s);
+ } catch (ContinueException c) {
+ if (c.label == null || c.label.equals(string)) continue;
+ } catch (BreakException b) {
+ if (b.label == null || b.label.equals(string)) return null;
+ throw (BreakException)b.fillInStackTrace();
+ }
+ }
+ } catch (BreakException e) {
+ if (e.label != null && !e.label.equals(string)) throw e;
+ }
+ return null;
+ case Lexer.VAR:
+ for(int i=0; i<size(); i++) {
+ Expr e = elementAt(i);
+ s.declare(e.left.string);
+ if (e.right != null) e.right.eval(s);
+ }
+ return null;
+ default: throw new Error("augh!");
+ }
+ }
+ }
+
+ /** 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 {
+ Expr smt = null;
+ int tok = peekToken();
+ if (tok == -1) return null;
+ boolean braced = tok == LC;
+ if (requireBraces && !braced) throw new ParserException("expected {, got " + codeToString[tok]);
+ if (braced) getToken();
+ int curLine = line;
+ ExprList block = new ExprList(curLine, LC);
+ while(true) {
+ switch(tok = peekToken()) {
+
+ case -1:
+ return block;
+
+ case LC:
+ smt = parseBlock(true); break;
+
+ 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);
+ }
+ break;
+
+ case GOTO: 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));
+ consume(SEMI);
+ break;
+ }
+
+ case RC:
+ if (braced) consume(RC);
+ return block;
+
+ case SEMI:
+ consume(SEMI);
+ if (!braced) return block;
+ continue;
+
+/* FIXME: LL(2)
+ case NAME: {
+ String name = string;
+ getToken();
+ if (peekToken() == COLON) {
+ smt = new Expr(curLine, COLON, new Expr(curLine, name), parseBlock(false));
+ break;
+ } else {
+ pushBackToken(tok);
+ string = name;
+ // fall through to default case
+ }
+ }
+*/
+ default:
+ smt = parseMaximalExpr();
+ if (smt == null) {
+ if (block.numExprs() == 0) return null;
+ return block;
+ }
+ if (peekToken() == SEMI) getToken();
+ break;
+ }
+
+ if (!braced) return smt;
+ block.add(smt);
+ }
+ }
+
+