All Downloads are FREE. Search and download functionalities are using the official Maven repository.

water.rapids.AST Maven / Gradle / Ivy

package water.rapids;

import water.*;
import water.exceptions.H2OIllegalArgumentException;
import water.exceptions.H2OKeyNotFoundArgumentException;
import water.fvec.Chunk;
import water.fvec.Frame;
import water.fvec.NewChunk;
import water.fvec.Vec;
import water.parser.ValueString;
import water.util.IcedInt;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;

/**
 *   Each node in the syntax tree knows how to parse a piece of text from the passed tree.
 */
abstract public class AST extends Iced {
  String[] _arg_names;
  AST[] _asts;
  AST parse_impl(Exec e) { throw H2O.fail("Missing parse_impl for "+this.getClass()); }
  abstract String opStr();
  abstract void exec(Env e);
  abstract String value();
  abstract int type();
  abstract AST make();
  public int numChildren() { return _asts.length; } // Must "apply" each arg, then put the results into ASTOp/UDF

  /**
   * Walk an AST and execute.
   */
  Env treeWalk(Env e) {

    // First check if we're a top-level node of type astop
    if (this instanceof ASTOp) {
      if (this instanceof ASTBinOp) {

        // Exec the right branch
        _asts[1].treeWalk(e);

        // Exec the left branch
        _asts[0].treeWalk(e);

        // Perform the binary operation
        ((ASTBinOp) this).apply(e);
      } else if (this instanceof ASTUniPrefixOp) {
        for (int i = 0; i < _asts.length; ++i) _asts[i].treeWalk(e);
        ((ASTUniPrefixOp) this).apply(e);
      } else if (this instanceof ASTReducerOp) {
        for (int i = 0; i < _asts.length; ++i) _asts[i].treeWalk(e);
        ((ASTReducerOp) this).apply(e);
      } else if (this instanceof ASTLs || this instanceof ASTSetTimeZone || this instanceof ASTListTimeZones || this instanceof ASTGetTimeZone || this instanceof ASTStoreSize) {
        ((ASTOp) this).apply(e);
      } else if (this instanceof ASTFunc) {
        ((ASTFunc) this).apply(e);
      } else if (this instanceof ASTApply) {
        _asts[0].treeWalk(e);  // push the frame we're `apply`ing over
        ((ASTApply) this).apply(e);
      } else if(this instanceof ASTMMult) {
        _asts[1].treeWalk(e);
        _asts[0].treeWalk(e);
        ((ASTMMult)this).apply(e);
      } else if(this instanceof ASTTranspose) {
        _asts[0].treeWalk(e);
        ((ASTTranspose)this).apply(e);
      } else if (this instanceof ASTddply) {
        _asts[0].treeWalk(e);
        ((ASTddply)this).apply(e);
      } else if (this instanceof ASTMerge) {
        _asts[1].treeWalk(e);
        _asts[0].treeWalk(e);
        ((ASTMerge)this).apply(e);
      } else {
        throw H2O.fail("Unknown AST in tree walk: " + this.getClass());
        // TODO: do the udf op thing: capture env...
      }

    // Check if there's an assignment
    } else if (this instanceof ASTAssign) {

      // Exec the right branch
      _asts[1].treeWalk(e);

      // Do the assignment
      this.exec(e);  // Special case exec => apply for assignment

    // Check if we have an ID node (can be an argument, or part of an assignment).
    } else if (this instanceof ASTId) {
      ASTId id = (ASTId) this;
      assert id.isValid();
      if (id.isLookup()) {
        // lookup the ID and return an AST
        AST ast = e.lookup(id);
//        e.put(id._id, ast.type(), id._id);
        ast.exec(e);
      } else if (id.isLocalSet() || id.isGlobalSet()) {
        e.put(((ASTId) this)._id, Env.ID, "");
        id.exec(e);
      } else {
        throw new H2OIllegalArgumentException("Got a bad identifier: '" + id.value() + "'. It has no type '!' or '$'.",
                "Got a bad identifier: '" + id.value() + "'. It has no type '!' or '$'." + " AST: " + this);
      }


    } else if(this instanceof ASTStatement) {

      if (this instanceof ASTIf) { this.exec(e); }
      else if (this instanceof ASTElse) { this.exec(e); }
      else if (this instanceof ASTFor) { throw H2O.unimpl("`for` loops are currently unsupported."); }
      else if (this instanceof ASTReturn) { this.exec(e); return e; }
      else this.exec(e);

    // Check if we have a slice.
    } else if(this instanceof ASTSlice) {
      _asts[0].treeWalk(e); // push hex
      _asts[1].treeWalk(e); // push rows
      _asts[2].treeWalk(e); // push cols
      this.exec(e);         // do the slice

    // Check if String, Num, Null, Series, Key, Span, Frame, or Raft
    } else if (this instanceof ASTString || this instanceof ASTNum || this instanceof ASTNull ||
            this instanceof ASTSeries || this instanceof ASTKey || this instanceof ASTSpan ||
            this instanceof ASTFrame || this._asts[0] instanceof ASTFrame ||
            this instanceof ASTDelete )
      { this.exec(e); }

    else { throw H2O.fail("Unknown AST class: " + this.getClass());}
    return e;
  }

  protected StringBuilder indent( StringBuilder sb, int d ) {
    for( int i=0; i _min)
        throw new IllegalArgumentException("max>min: All negative, incorrect order.");
    } else {
        if (!Double.isNaN(_max) && _min > _max) throw new IllegalArgumentException("min > max: min <= max for `:` operator.");
    }
  }
  ASTSpan(long min, long max) { _ast_min = new ASTNum(min); _ast_max = new ASTNum(max); _min = min; _max = max;
    if( _min < 0 && _max < 0) {
      if (_max > _min)
        throw new IllegalArgumentException("max>min: All negative, incorrect order.");
    } else {
      if (_min > _max) throw new IllegalArgumentException("min > max: min <= max for `:` operator.");
    }
  }
  ASTSpan parse_impl(Exec E) {
    AST l = E.parse();
    AST r = E.parse();
    E.eatEnd(); // eat ending ')'
    return new ASTSpan((ASTNum)l, (ASTNum)r);
  }
  boolean contains(long a) {
    if (all_neg()) return _max <= a && a <= _min;
    return _min <= a && a <= _max;
  }
  boolean isColSelector() { return _isCol; }
  boolean isRowSelector() { return _isRow; }
  ASTSpan setSlice(boolean row, boolean col) { _isRow = row; _isCol = col; return this; }
  @Override void exec(Env e) { ValSpan v = new ValSpan(_ast_min, _ast_max); v.setSlice(_isRow, _isCol); e.push(v); }
  @Override String value() { return null; }
  @Override int type() { return Env.SPAN; }
  @Override public String toString() { return _min + ":" + _max; }
  long length() { return (long)_max - _min + 1; }

  long[] toArray() {
    long[] res = new long[(int)_max - (int)_min + 1];
    long min = _min;
    for (int i = 0; i < res.length; ++i) res[i] = min++;
    return res;
  }
  boolean all_neg() { return _min<0||_max<0; }
  boolean all_pos() { return !all_neg(); }
  boolean isNum() { return _min == _max; }
  long toNum() { return _min; }
//  @Override public AutoBuffer write_impl(AutoBuffer ab) {
//    ab.put8(_min);
//    ab.put8(_max);
//    return ab;
//  }
//  @Override public ASTSpan read_impl(AutoBuffer ab) {
//    return new ASTSpan(ab.get8(), ab.get8());
//  }
  @Override ASTSpan make() {return new ASTSpan(new ASTNum(0),new ASTNum(0)); }
}

class ASTSeries extends AST {
  String opStr() { return "{";}
  final long[] _idxs;
  final ASTSpan[] _spans;
  final double[] _d;
  boolean _isCol;
  boolean _isRow;
  int[] _order; // a sequence of 0s and 1s. 0 -> span; 1 -> index

  ASTSeries(long[] idxs, double[] d, ASTSpan[] spans) {
    _idxs=idxs; _d=d; _spans=spans;
  }
  ASTSeries(long[] idxs, ASTSpan[] spans) {
    _idxs = idxs; _d=null;
    _spans = spans;
  }

  ASTSeries parse_impl(Exec E) {
    ArrayList l_idxs = new ArrayList<>();
    ArrayList s_spans = new ArrayList<>();
    if (!E.hasNext()) throw new IllegalArgumentException("End of input unexpected. Badly formed AST.");
    String[] strs = E.parseString('}').split(";");
    _order = new int[strs.length];
    int o = 0;
    for (String s : strs) {
      if (s.charAt(0) == '(') {
        _order[o] = 0;
        // get a non ASTSpan as next elt
        try {
          s_spans.add((ASTSpan) (new Exec(s, null)).parse());
        } catch (ClassCastException e) {
          try {
            ASTOp anum = (ASTOp) (new Exec(s, null)).parse();
            long n = (long)anum.treeWalk(E._env).popDbl();
            _order[o] = 1;
            l_idxs.add(n);
          } catch (ClassCastException e2) {
            throw new IllegalArgumentException("AST in sequence did not evaluate to a range or number.\n Only (: min max), #, and ASTs that evaluate to # are valid.");
          }
        }
        o++;
      } else {
        _order[o++] = 1;
        if (s.charAt(0) == '#') s = s.substring(1, s.length());
        try {
          Long.valueOf(s);
        } catch (Exception e) {
          throw new IllegalArgumentException("Invalid input. Value was not long or int: "+s);
        }
        l_idxs.add(Long.valueOf(s));
      }
    }
    long[] idxs = new long[l_idxs.size()];
    ASTSpan[] spans = new ASTSpan[s_spans.size()];
    for (int i = 0; i < idxs.length; ++i) idxs[i] = l_idxs.get(i);
    for (int i = 0; i < spans.length; ++i) spans[i] = s_spans.get(i);
    ASTSeries aa = new ASTSeries(idxs, spans);
    aa._order = _order;
    return aa;
  }

  boolean contains(long a) {
    if (_spans != null)
      for (ASTSpan s : _spans) if (s.contains(a)) return true;
    if (_idxs != null)
      for (long l : _idxs) if (l == a) return true;
    return false;
  }

  boolean isColSelector() { return _isCol; }
  boolean isRowSelector() { return _isRow; }

  ASTSeries setSlice(boolean row, boolean col) { _isRow = row; _isCol = col; return this; }

  @Override
  void exec(Env e) {
    ValSeries v = new ValSeries(_idxs, _d, _spans);
    v._order = _order;
    v.setSlice(_isRow, _isCol);
    e.push(v);
  }

  @Override String value() { return null; }
  @Override int type() { return Env.SERIES; }

  @Override
  public String toString() {
    String res = "c(";
    if (_spans != null) {
      for (ASTSpan s : _spans) {
        res += s.toString();
        res += ",";
      }
      if (_idxs == null) res = res.substring(0, res.length() - 1); // remove last comma?
    }
    if (_idxs != null) {
      for (long l : _idxs) {
        res += l;
        res += ",";
      }
      res = res.substring(0, res.length() - 1); // remove last comma.
    }
    res += ")";
    return res;
  }

  long[] toArray() {
    int res_length = 0;
    if (_spans != null) for (ASTSpan s : _spans) res_length += (int) s._max - (int) s._min + 1;
    if (_idxs != null) res_length += _idxs.length;
    long[] res = new long[res_length];
    int cur = 0;
    if (_spans != null) {
      for (ASTSpan s : _spans) {
        long[] l = s.toArray();
        for (int i = 0; i < l.length; ++i) res[cur++] = l[i];
      }
    }
    if (_idxs != null) {
      for (int i = 0; i < _idxs.length; ++i) res[cur++] = _idxs[i];
    }
    return res;
  }
//  @Override public AutoBuffer write_impl(AutoBuffer ab) {
//    ab.putA8(_idxs);
//    ab.put1(_spans.length);
//    for (int i = 0; i < _spans.length; ++i) {
//      ab = _spans[i].write_impl(ab);
//    }
//    ab.putZ(_isCol);
//    ab.putA4(_order);
//    return ab;
//  }
//  @Override public ASTSeries read_impl(AutoBuffer ab) {
//    long[] idxs = ab.getA8();
//    int nspans = ab.get1();
//    ASTSpan[] spans = new ASTSpan[nspans];
//    for (int i = 0; i < nspans; ++i) {
//      spans[i] = ab.get(ASTSpan.class);
//    }
//    boolean isCol = ab.getZ();
//    int[] order = ab.getA4();
//    ASTSeries series = new ASTSeries(idxs, spans);
//    series._order = order;
//    series._isCol = isCol;
//    series._isRow = !isCol;
//    return series;
//  }
  @Override ASTSeries make() { return new ASTSeries(null, null); }
}

class ASTStatement extends AST {
  @Override ASTStatement make() { return new ASTStatement(); }
  String opStr() { return ","; }
  // must parse all statements: {(ast);(ast);(ast);...;(ast)}
  @Override ASTStatement parse_impl( Exec E ) {
    ArrayList ast_ary = new ArrayList();

    // an ASTStatement is an array of ASTs. May have ASTStatements within ASTStatements.
    while( !E.isEnd() )
      ast_ary.add(E.parse());

    E.eatEnd(); // eat the ending ')'

    ASTStatement res = (ASTStatement) clone();
    res._asts = ast_ary.toArray(new AST[ast_ary.size()]);
    return res;
  }
  @Override void exec(Env env) {
    ArrayList cleanup = new ArrayList<>();
    if( _asts.length==0 ) { env.push(new ValNull()); return; }
    for( int i=0; i<_asts.length-1; i++ ) {
      if (_asts[i] instanceof ASTReturn) {
       _asts[i].treeWalk(env);
        return;
      }
      _asts[i].treeWalk(env);  // Execute the statements by walking the ast
      if( !(_asts[i+1] instanceof ASTDelete || _asts[i+1] instanceof ASTRemoveFrame) ) env.pop();
    }
    _asts[_asts.length-1].treeWalk(env); // Return final statement as result
    for (Frame f : cleanup) f.delete();
  }

  @Override String value() { return null; }
  @Override int type() {return 0; }

  @Override public String toString() { return toString(new StringBuilder(""), 0).toString() + ";;;"; }
  @Override public StringBuilder toString( StringBuilder sb, int d ) {
    for (int i = 0; i < _asts.length - 1; i++)
      _asts[i].toString(sb, d + 1).append(";\n");
    return _asts[_asts.length - 1].toString(sb, d + 1);
  }
}

class ASTReturn extends ASTStatement {
  protected AST _stmnt;
  ASTReturn() {}

  @Override ASTReturn parse_impl(Exec E) {
    if (!E.hasNext()) throw new IllegalArgumentException("End of input unexpected. Badly formed AST.");
    AST stmnt = E.parse();
    E.eatEnd(); // eat the ending ')'
    ASTReturn res = (ASTReturn) clone();
    res._stmnt = stmnt;
    return res;
  }

  @Override void exec(Env e) { _stmnt.treeWalk(e); }
  @Override String value() { return null; }
  @Override int type() { return 0; }
}

class ASTIf extends ASTStatement {
  protected AST _pred;
  protected ASTElse _else = null;
  ASTIf() {}

  // (if pred body)
  @Override ASTIf parse_impl(Exec E) {
    // parse the predicate
    AST pred = E.parse();
    ASTStatement statement = super.parse_impl(E);
    ArrayList ast_list = new ArrayList<>();
    for (int i = 0; i < statement._asts.length; ++i) {
      if (statement._asts[i] instanceof ASTElse) _else = (ASTElse)statement._asts[i];
      else ast_list.add(statement._asts[i]);
    }
    E.eatEnd(); // eat the ending ')'
    ASTIf res = (ASTIf) clone();
    res._pred = pred;
    res._asts = ast_list.toArray(new AST[ast_list.size()]);
    return res;
  }

  @Override void exec(Env e) {
    Env captured = e.capture();
    captured = _pred.treeWalk(captured);
    if (captured.isAry()) throw new IllegalArgumentException("Frames not supported in the if's condition.");
    double v = captured.popDbl();
    captured.popScope();
    if (v == 0) if (_else == null) return; else _else.exec(e);
    else super.exec(e);  // run the statements
  }
  @Override String value() { return null; }
  @Override int type() { return 0; }
}

class ASTElse extends ASTStatement {
  // (else body)
  ASTElse() {}
  @Override ASTElse parse_impl(Exec E) {
    if (!E.hasNext()) throw new IllegalArgumentException("End of input unexpected. Badly formed AST.");
    ASTStatement statements = super.parse_impl(E);
    E.eatEnd(); // eat the ending ')'
    ASTElse res = (ASTElse)clone();
    res._asts = statements._asts;
    return res;
  }
  @Override void exec(Env e) { super.exec(e); }
  @Override String value() { return null; }
  @Override int type() { return 0; }
}

class ASTFor extends ASTStatement {
  protected int _start;
  protected int _end;
//  protected Object[] confusion_matrix;

  // (for #start #end body)
  @Override ASTFor parse_impl(Exec E) {
    if (!E.hasNext()) throw new IllegalArgumentException("End of input unexpected. Badly formed AST.");
    int s = (int)((ASTNum)E.skipWS().parse())._d;
    if (!E.hasNext()) throw new IllegalArgumentException("End of input unexpected. Badly formed AST.");
    int e = (int)((ASTNum)E.skipWS().parse())._d;
    ASTStatement stmts = super.parse_impl(E);
    ASTFor res = (ASTFor)clone();
    res._asts = stmts._asts;
    res._start = s;
    res._end = e;
    return res;
  }

  @Override void exec(Env e) { for (int i = _start; i < _end; ++i) super.exec(e); }
  @Override String value() { return null; }
  @Override int type() { return 0; }
}

class ASTWhile extends ASTStatement {
//  protected AST _pred;

  // (while pred body)
  @Override
  ASTWhile parse_impl(Exec E) {
    throw H2O.unimpl("while loops are not supported.");
  }
}

//    AST pred = E.parse();
//    ASTStatement statement = super.parse_impl(E);
//    ASTWhile res = (ASTWhile) clone();
//    res._pred = pred;
//    res._asts = statement._asts;
//    return res;
//  }
//
//  @Override void exec(Env e) {
//    while(checkPred(e)) {
//
//    }
//  }
//
//  private boolean checkPred(Env e) {
//    Env captured = e.capture();
//    captured = _pred.treeWalk(captured);
//    double v = captured.popDbl();
//    captured.popScope();
//    if (v == 0) return false;
//    return true;
//  }
//
//  @Override String value() { return null; }
//  @Override int type() { return 0; }
//}

class ASTString extends AST {
  @Override ASTString make() { return new ASTString(_eq,""); }
  String opStr() { return String.valueOf(_eq); }
  final String _s;
  final char _eq;
  ASTString(char eq, String s) { _eq = eq; _s = s; }
  AST parse_impl(Exec E) {
    if (!E.hasNext()) throw new IllegalArgumentException("End of input unexpected. Badly formed AST.");
    ASTString as = new ASTString(_eq, E.parseString(_eq));
    return Env.staticLookup(as);
  }
  @Override public String toString() { return _s; }
  @Override void exec(Env e) { e.push(new ValStr(_s)); }
  @Override int type () { return Env.STR; }
  @Override String value() { return _s; }
}

class ASTNull extends AST {
  @Override ASTNull make() { return new ASTNull(); }
  String opStr() { throw H2O.unimpl();}
  ASTNull() {}
  ASTNull parse_impl(Exec E) { return this; }
  @Override void exec(Env e) { e.push(new ValNull());}
  @Override String value() { return null; }
  @Override int type() { return Env.NULL; }
}

/**
 *  The ASTAssign Class
 *
 *  Handle the four cases of assignment:
 *
 *     1. Whole frame assignment:  hexA = RHS
 *     2. Three flavors of slot assignment:
 *        A. hex[,col(s)]       = RHS       // column(s) re-assign
 *        B. hex[row(s),]       = RHS       // row(s) re-assign
 *        C. hex[row(s),col(s)] = RHS       // row(s) AND col(s) re-assign
 *
 *     NB: RHS is any arbitrary (but valid) AST
 *
 *     This also supports adding a new column.
 *     (e.g., hex$new_column <- 10, creates "new_column" vector in hex with values set to 10)
 *
 *     The RHS is already on the stack, the LHS is not yet on the stack.
 *
 *     Note about Categorical/Non-Categorical Vecs:
 *
 *       If the vec is enum, then the RHS must also be enum (if enum is not in domain of LHS produce NAs).
 *       If the vec is numeric, then the RHS must also be numeric (if enum, then produce NAs or throw IAE).
 */
class ASTAssign extends AST {
  @Override ASTAssign make() { return new ASTAssign(); }
  String opStr() { return "="; }
  ASTAssign parse_impl(Exec E) {
    AST l;

    // LHS parsing
    // LHS can be one of six things:
    //   !ID, &ID, "ID" / 'ID', %ID, ID, or (...)
    // ! means do a dkv put -- essentially keep the result global for all the world
    // & means do a local put -- do not keep result around
    // " and ' mean parse and do local put (semantically an '&')
    // nothing on the ID implies & semantics
    // % on the LHS is semantically equivalent to & -- does not overwrite the global with the same ID
    // ( implies that the LHS is a new AST -- result locality depends on the AST
    if (E.isSpecial(E.peek())) {  // LHS is NOT an AST... no slice assignment
      boolean putkv = E.peek() == '!';
      E._x++; // skip the special char...
      boolean skip1 = (E.peek() == '\'' || E.peek() == '\"');  // skip one more at the end if quoted ID...
      if( skip1 ) E._x++; // skip beginning quote
      l = new ASTId(putkv ? '!' : '&', E.parseID()); // parse the ID on the left, or could be a column, or entire frame, or a row
      if( skip1 ) E._x++; // skip ending quote
    } else {
      if( E.peek() == '(' ) l = E.parse();  // thing on the LHS is an AST => slot assign [<-
      else l = new ASTId('&', E.parseID()); // else got plain old ID, assume local put
    }

    // RHS parsing
    if (!E.hasNext()) throw new IllegalArgumentException("Missing RHS in ASTAssign.");
    AST r = E.parse();   // parse double, String, or Frame on the right

    E.eatEnd(); // eat ending ')'

    // clone and return
    ASTAssign res = (ASTAssign)clone();
    res._asts = new AST[]{l,r};
    return res;
  }

  @Override int type () { return -1; }
  @Override String value() { throw H2O.unimpl("No value() for ASTAssign."); }
  private static boolean in(String s, String[] matches) { return Arrays.asList(matches).contains(s); }

  private static void replaceRow(Chunk[] chks, int row, double d0, String s0, long[] cols) {
    for (int c = 0; c < cols.length; ++c) {
      int col = (int)cols[c];
      // have an enum column
      if (chks[col].vec().isEnum()) {
        if (s0 == null) { chks[col].setNA(row); continue; }
        String[] dom = chks[col].vec().domain();
        if (in(s0, dom)) { chks[col].set(row, Arrays.asList(dom).indexOf(s0)); continue; }
        chks[col].setNA(row); continue;

        // have a numeric column
      } else if (chks[col].vec().isNumeric()) {
        if (Double.isNaN(d0) || s0 != null) { chks[col].setNA(row); continue; }
        chks[col].set(row, d0); continue;
      }
    }
  }

  // replace thing on the RHS -> LHS. set from cs to ary
  private static void replaceRow(Chunk[] cs, int row0, long row_id, long[] cols, Frame ary) {
    for (int c = 0; c < cols.length; ++c) {
      int col = (int)cols[c];
      // got an enum trying to set into enum
      // got an enum trying to set into something else -> NA
      if (cs[c].vec().isEnum()) {
        if (ary.vecs()[col].isEnum()) {
          ary.vecs()[col].set(row_id, cs[c].atd(row0));
        } else {
          ary.vecs()[col].set(row_id, Double.NaN);
        }
      }

      // got numeric trying to set into something non-numeric -> NA
      // got numeric trying to set into numeric
      if (cs[c].vec().isNumeric()) {
        if (ary.vecs()[col].isNumeric()) {
          ary.vecs()[col].set(row_id, cs[c].atd(row0));
        } else {
          ary.vecs()[col].set(row_id, Double.NaN);
        }
      }
    }
  }

  private static void assignRows(Env e, Object rows, final Frame lhs_ary, Object cols) {
    // For every col at the range of indexes, set the value to be the rhs, which is expected to be a scalar (or possibly a string).
    // If the rhs is a double or str, then fill with doubles or NA when type is Categorical.
    final long[] cols0 = cols == null ? new long[lhs_ary.numCols()] : (long[])cols;
    if (cols == null) for (int i = 0; i < lhs_ary.numCols(); ++i) cols0[i] = i;

    if (!e.isAry()) {

      String s = null;
      double d = Double.NaN;
      if (e.isStr()) s = e.popStr();
      else if (e.isNum()) d = e.popDbl();
      else if (e.isNul()) d = Double.NaN;
      else throw new IllegalArgumentException("Did not get a single number or factor level on the RHS of the assignment. Got type #:" + Env.typeToString(e.peekType()));
      final double d0 = d;
      final String s0 = s;

      // Case: Have a long[] of rows
      if (rows instanceof long[]) {
        final long[] rows0 = (long[]) rows;

        // MRTask over the lhs array
        new MRTask() {
          @Override public void map(Chunk[] chks) {
            for (int row = 0; row < chks[0]._len; ++row)
              if (Arrays.asList(rows0).contains(row)) replaceRow(chks, row, d0, s0, cols0);
          }
        }.doAll(lhs_ary);
        e.push(new ValFrame(lhs_ary));
        return;

        // Case: rows is a Frame -- in this case it's expected to be a predicate vec
      } else if (rows instanceof Frame) {
        Frame rr = new Frame(lhs_ary).add((Frame) rows);
        if (rr.numCols() != lhs_ary.numCols() + 1)
          throw new IllegalArgumentException("Got multiple columns for row predicate.");

        // treat rows as a bit vec, nonzeros mean rows should be replaced with s0 or d0, 0s mean continue
        new MRTask() {
          @Override public void map(Chunk[] cs, NewChunk[] ncs) {
            Chunk pred = cs[cs.length - 1];
            int rows = cs[0]._len;
            for (int r = 0; r < rows; ++r) if (pred.atd(r) != 0) replaceRow(cs, r, d0, s0, cols0);
          }
        }.doAll(rr);
        e.push(new ValFrame(lhs_ary));
        return;
      } else throw new IllegalArgumentException("Invalid row selection. (note: RHS was a constant)");

      // If the rhs is an array, then fail if `height` of the rhs != rows.length. Otherwise, fetch-n-fill! (expensive)
    } else {
      // RHS shape must match LHS shape...
      // Example: hex[1:50,] <- hex2[90:100,] will fail, but swap out 90:100 w/ 101:150 should pass
      // LHS.numCols() == RHS.numCols()
      // mismatch in types results in NA

      final Frame rhs_ary = e.popAry();
      if ( ( cols == null && rhs_ary.numCols() != lhs_ary.numCols()) || (cols != null && rhs_ary.numCols() != ((long[]) cols).length ) )
        throw new IllegalArgumentException("Right-hand frame has does not match the number of columns required in the assignment to the left-hand side." );
      if (rhs_ary.numRows() > lhs_ary.numRows()) throw new IllegalArgumentException("Right-hand side frame has more rows than the left-hand side.");

      // case where rows is a long[]
      if (rows instanceof long[]) {
        final long[] rows0 = (long[])rows;
        if (rows0.length != rhs_ary.numRows()) throw new IllegalArgumentException("Right-hand side array does not match the number of rows selected in the left-hand side.");
        // rows0 will have access to the index of the row to grab, use this to get the correct row from the rhs ary

        // MRTask over the lhs array
        new MRTask() {
          @Override public void map(Chunk[] chks) {
            for (int row = 0; row < chks[0]._len; ++row) {
              if (Arrays.asList(rows0).contains(row)) {
                long row_id = (long)Arrays.asList(rows0).indexOf(row);
                for (int c = 0; c < cols0.length; ++c) {
                  int col = (int)cols0[c];
                  // vec is enum
                  if (chks[col].vec().isEnum())
                    if (!rhs_ary.vecs()[col].isEnum()) { chks[col].setNA(row); continue; }
                    // else vec is numeric
                  else if (chks[col].vec().isNumeric())
                    if (!rhs_ary.vecs()[col].isNumeric()) { chks[col].setNA(row); continue; }
                  chks[col].set(row, rhs_ary.vecs()[col].at(row_id));
                }
              }
            }
          }
        }.doAll(lhs_ary);
        e.push(new ValFrame(lhs_ary));
        return;

        // case where rows is a Frame
      } else if (rows instanceof Frame) {

        // MRTask over the lhs frame + rows predicate vec. the value of the predicate vec will be 1 + the row index
        // in the corresponding rhs frame

        // MRTask over the pred vec to collapse to a dense set of row IDs
        if (((Frame)rows).numCols() != 1) throw new IllegalArgumentException("Got multiple columns for row predicate.");
        Frame pred = new MRTask() {
          @Override public void map(Chunk c, NewChunk nc) {
            for (int r = 0; r < c._len; ++r) {
              double d = c.atd(r);
              if (d != 0) nc.addNum(d);
            }
          }
        }.doAll(1, (Frame)rows).outputFrame(null, null);

        if (pred.numRows() != rhs_ary.numRows())
          throw new IllegalArgumentException("Right-hand side array does not match the number of rows selected in the left-hand side.");

        Frame rr = new Frame(rhs_ary).add(pred);

        // MRTask over the RHS ary, pushing data out to the LHS ary based on the pred vec
        new MRTask() {
          @Override public void map(Chunk[] cs) {
            Chunk pred = cs[cs.length-1];
            int rows = cs[0]._len;
            for (int r=0; r AST[]{hex, rows, cols}

      // push the slice onto the stack
      lhs_slice._asts[0].treeWalk(e);   // push hex
      lhs_slice._asts[1].treeWalk(e);   // push rows
      lhs_slice._asts[2].treeWalk(e);   // push cols

      // Case C: Simple case where we have a single row and a single column
      if (e.isNum() && e.peekTypeAt(-1) == Env.NUM) {
        int col = (int) e.popDbl();
        long row = (long) e.popDbl();
        Frame ary = e.popAry();
        if (Math.abs(row) > ary.numRows())
          throw new IllegalArgumentException("New rows would leave holes after existing rows.");
        if (Math.abs(col) > ary.numCols())
          throw new IllegalArgumentException("New columns would leave holes after existing columns.");
        if (row < 0 && Math.abs(row) > ary.numRows()) throw new IllegalArgumentException("Cannot extend rows.");
        if (col < 0 && Math.abs(col) > ary.numCols()) throw new IllegalArgumentException("Cannot extend columns.");
        if (e.isNum()) {
          double d = e.popDbl();
          if (ary.vecs()[col].isEnum()) ary.vecs()[col].set(row, Double.NaN);
          else ary.vecs()[col].set(row, d);
          if (ary._key != null && DKV.get(ary._key) != null) DKV.put(ary);
          e.push(new ValFrame(ary));
          return;

        } else if (e.isAry()) {
          Frame one_by_one_ary = e.popAry();
          if (one_by_one_ary.numCols() != 1 && one_by_one_ary.numRows() != 1)
            throw new IllegalArgumentException("Expected RHS to be a 1x1 (one row, one column). Got: " + one_by_one_ary.numRows() + " rows " + one_by_one_ary.numCols() + " cols.");
          Vec theVec = one_by_one_ary.anyVec();

          // RHS is enum
          if (theVec.isEnum()) {
            String s = theVec.domain()[(int)theVec.at(0)];
            String[] dom = ary.vecs()[col].domain();
            if (in(s, dom)) ary.vecs()[col].set(row, Arrays.asList(dom).indexOf(s));
            else ary.vecs()[col].set(row, Double.NaN);
            if (ary._key != null && DKV.get(ary._key) != null) DKV.put(ary);
            e.push(new ValFrame(ary));
            return;

            // LHS is enum but RHS is not
          } else if (ary.vecs()[col].isEnum()) {
            ary.vecs()[col].set(row, Double.NaN);
            if (ary._key != null && DKV.get(ary._key) != null) DKV.put(ary);
            e.push(new ValFrame(ary));
            return;

          // LHS and RHS are both numeric
          } else {
            double d = theVec.at(0);
            ary.vecs()[col].set(row, d);
            if (ary._key != null && DKV.get(ary._key) != null) DKV.put(ary);
            e.push(new ValFrame(ary));
            return;
          }
        } else if (e.isStr()) {
          if (!ary.vecs()[col].isEnum())
            throw new IllegalArgumentException("Currently can only set categorical columns.");
          String s = e.popStr();
          String[] dom = ary.vecs()[col].domain();
          if (in(s, dom)) ary.vecs()[col].set(row, Arrays.asList(dom).indexOf(s));
          else ary.vecs()[col].set(row, Double.NaN);
          if (ary._key != null && DKV.get(ary._key) != null) DKV.put(ary);
          e.push(new ValFrame(ary));
          return;
        } else
          throw new IllegalArgumentException("Did not get a single number or factor level on the RHS of the assignment.");
      }

      // Get the LHS slicing rows/cols. This is a more complex case than the simple 1x1 re-assign
      Val colSelect = e.pop();
      Val rowSelect = e.pop();

      Frame lhs_ary = e.peekAry(); // Now the stack looks like [ ..., RHS, LHS_FRAME]
      Object cols = ASTSlice.select(lhs_ary.numCols(), colSelect, e, true);
      Object rows = ASTSlice.select(lhs_ary.numRows(), rowSelect, e, false);
      lhs_ary = e.popAry(); // Now the stack looks like [ ..., RHS]

      long[] cs1;
      long[] rs1;

      // Repeat of case C with a single col and row specified, but possibly packaged into ASTSeries objects.
      if (cols != null && rows != null
              && (cols instanceof long[]) && (rows instanceof long[])
              && (cs1 = (long[]) cols).length == 1 && (rs1 = (long[]) rows).length == 1) {
        long row = rs1[0];
        int col = (int) cs1[0];
        if (Math.abs(row) > lhs_ary.numRows())
          throw new IllegalArgumentException("New rows would leave holes after existing rows.");
        if (Math.abs(col) > lhs_ary.numCols())
          throw new IllegalArgumentException("New columnss would leave holes after existing columns.");
        if (row < 0 && Math.abs(row) > lhs_ary.numRows()) throw new IllegalArgumentException("Cannot extend rows.");
        if (col < 0 && Math.abs(col) > lhs_ary.numCols()) throw new IllegalArgumentException("Cannot extend columns.");
        if (e.isNum()) {
          if (lhs_ary.vecs()[col].isEnum())
            throw new IllegalArgumentException("Currently can only set numeric columns");
          lhs_ary.vecs()[col].set(row, e.popDbl());
          e.pushAry(lhs_ary);
          if (lhs_ary._key != null && DKV.get(lhs_ary._key) != null) DKV.put(lhs_ary);
          return;
        } else if (e.isStr()) {
          if (!lhs_ary.vecs()[col].isEnum()) throw new IllegalArgumentException("Currently can only set categorical columns.");
          String s = e.popStr();
          String[] dom = lhs_ary.vecs()[col].domain();
          if (in(s, dom)) lhs_ary.vecs()[col].set(row, Arrays.asList(dom).indexOf(s));
          else lhs_ary.vecs()[col].set(row, Double.NaN);
          if (lhs_ary._key != null && DKV.get(lhs_ary._key) != null) DKV.put(lhs_ary);
          e.pushAry(lhs_ary);
          return;
        } else throw new IllegalArgumentException("Did not get a single number or factor level on the RHS of the assignment.");
      }

      // Partial row assignment? Cases B and C
      if (rows != null) {

        // Only have partial row assignment, Case B
        if (cols == null) {
          assignRows(e, rows, lhs_ary, null);

          // Have partial row and col assignment? Case C
        } else {
          assignRows(e, rows, lhs_ary, cols);
        }

      // Case A, just cols
      } else if (cols != null) {
        Frame rhs_ary;
        Key tKey=null;
        // convert constant into a whole vec
        if (e.isNum()) rhs_ary = new Frame(lhs_ary.anyVec().makeCon(e.popDbl()));
        else if (e.isStr()) rhs_ary = new Frame(lhs_ary.anyVec().makeZero(new String[]{e.popStr()}));
        else if (e.isAry()) rhs_ary = e.popAry(); //.deepCopy(null);
        else throw new IllegalArgumentException("Bad RHS on the stack: " + e.peekType() + " : " + e.toString());

        long[] cs = (long[]) cols;
        if (rhs_ary.numCols() != 1 && rhs_ary.numCols() != cs.length)
          throw new IllegalArgumentException("Can only assign to a matching set of columns; trying to assign " + rhs_ary.numCols() + " cols over " + cs.length + " cols");

        // Replace the LHS cols with the RHS cols
        Vec rvecs[] = rhs_ary.deepCopy((tKey=Key.make()).toString()).vecs();
        Futures fs = new Futures();
        for (int i = 0; i < cs.length; i++) {
          int cidx = (int) cs[i];
          Vec rv = rvecs[i];
          e.addRef(rv);
          if (cidx == lhs_ary.numCols()) {
            if (!rv.group().equals(lhs_ary.anyVec().group())) {
              Vec rvOld = rv;
              rv = lhs_ary.anyVec().align(rv);
              e.subRef(rvOld);
              e.addRef(rv);
            }
            lhs_ary.add("C" + String.valueOf(cidx + 1), rv);     // New column name created with 1-based index
          } else {
            if (!(rv.group().equals(lhs_ary.anyVec().group())) && rv.length() == lhs_ary.anyVec().length()) {
              Vec rvOld = rv;
              rv = lhs_ary.anyVec().align(rv); // creates a new vec
              e.subRef(rvOld); // should delete it now...
              e.addRef(rv);
            }
            Vec vv = lhs_ary.replace(cidx, rv);
            e._locked.remove(vv._key);
            e.subRef(vv);
          }
        }
        fs.blockForPending();
        if (lhs_ary._key != null && DKV.get(lhs_ary._key) != null) DKV.put(lhs_ary);
        e.pushAry(lhs_ary);
        if( tKey!=null ) DKV.remove(tKey);  // shallow remove of tKey
        return;
      } else throw new IllegalArgumentException("Invalid row/col selections.");
    }
  }

//  String argName() { return this._asts[0] instanceof ASTId ? ((ASTId)this._asts[0])._id : null; }
  @Override public String toString() { return "="; }
//  @Override public StringBuilder toString( StringBuilder sb, int d ) {
//    indent(sb,d).append(this).append('\n');
//    _lhs.toString(sb,d+1).append('\n');
//    _eval.toString(sb,d+1);
//    return sb;
//  }
}

// AST Slice
class ASTSlice extends AST {
  @Override ASTSlice make() { return new ASTSlice(); }
  String opStr() { return "["; }
  ASTSlice() {}

  ASTSlice parse_impl(Exec E) {
    // ([ %fr rows cols)

    // parse the frame, could be an AST...
    AST fr = E.parse();

    // parse the rows
    // Five possibilties: AST, Vec/Frame, String, Span, Series
    // AST and Vec/Frame must produce single column of booleans.
    // String -- Automatically assume it's null (could also be "wakka wakka", or any string, but mapped automatically to null)
    //           null means "all"
    // Span   -- A span is a contiguous range of whole numbers specified like this (: #lo #hi)
    // List   -- A list...
    AST rows = E.parse();
    switch( rows.type() ) {
      case Env.STR:    rows = new ASTNull();                              break;
      case Env.SPAN:   rows = ((ASTSpan) rows).setSlice(true, false);     break;
      case Env.SERIES: rows = ((ASTSeries) rows).setSlice(true, false);   break;
      case Env.LIST:   rows = new ASTSeries(((ASTLongList)rows)._l,null,((ASTLongList)rows)._spans); ((ASTSeries)rows).setSlice(true,false); break;
      case Env.NULL:   rows = new ASTNull(); break;
      default: //pass thru
    }

    if (!E.hasNext())
      throw new IllegalArgumentException("Slice expected 3 arguments (frame, rows, cols), but got 2");

    // parse the cols
    AST cols = E.parse();
    if( !(cols instanceof ASTStringList) ) {
      switch( cols.type() ) {
        case Env.STR:    cols = cols.value().equals("null") ? new ASTNull() : cols; break;
        case Env.SPAN:   cols = ((ASTSpan) cols).setSlice(false, true);             break;
        case Env.SERIES: cols = ((ASTSeries) cols).setSlice(false, true);           break;
        case Env.LIST:
          if( cols instanceof ASTLongList ) cols = new ASTSeries(((ASTLongList)cols)._l,null,((ASTLongList)cols)._spans);
          else {
            double[] d = ((ASTDoubleList)cols)._d;
            long  [] l = new long[d.length];
            int i=0;
            for(double dd:d) l[i++]=(long)dd;
            cols = new ASTSeries(l,null,((ASTDoubleList) cols)._spans);
          }
          ((ASTSeries)cols).setSlice(false,true);
          break;
        case Env.NULL:   cols = new ASTNull(); break;
        default: // pass thru
      }
    }

    E.eatEnd(); // eat ending ')'

    ASTSlice res = (ASTSlice) clone();
    res._asts = new AST[]{fr,rows,cols};
    return res;
  }

  @Override String value() { return null; }
  @Override int type() { return 0; }

  // Read-only execution.  Assignment to the left-hand-side is handled by
  // ASTAssign.  Here we do copy-on-write when possible.
  @Override void exec(Env env) {

    // stack looks like:  [....,hex,rows,cols], so pop, pop !
    int cols_type = env.peekType();
    Val cols = env.pop();    int rows_type = env.peekType();
    Val rows = env.pop();

    if( cols_type == Env.LIST ) {
      assert cols instanceof ValStringList : "Expected ValStringList. Got: " + cols.getClass();
      String[] colnames = ((ValStringList)cols)._s;
      long[] colz = new long[colnames.length];
      for( int i=0;i 0 idx...
          // really want to do all columns BUT this one... so not a single scalar result => recurse
          long[] columns = new long[ary.numCols()-1];
          int v=0;
          for(int i=0;i= ary.vecs()[col].length()) throw new IllegalArgumentException("Row index out of bounds: tried to select row 0<="+row+"<="+(ary.vecs()[col].length()-1)+".");
      }
    } else {
      // Else It's A Big Copy.  Some Day look at proper memory sharing,
      // disallowing unless an active-temp is available, etc.
      // Eval cols before rows (R's eval order).
      Frame fr2,ary = env.peekAry(); // Get without popping
      if (rows_type == Env.ARY) env.addRef(((ValFrame)rows)._fr);
      Object colSelect = select(ary.numCols(),cols, env, true);
      Object rowSelect = select(ary.numRows(),rows,env, false);
      if( rowSelect == null && (colSelect==null || colSelect instanceof long[]) ) {
        if( colSelect == null ) {
          fr2 = ary;            // All of it
        } else {
          long[] cols2 = (long[])colSelect;
          if( cols2.length > 0 && cols2[0] < 0 ) { // Dropping cols
            int[] idxs = new int[cols2.length];
            fr2 = new Frame(ary);
            for( int i=0; i len) a._max=Math.max(0,len-1);
//      else a._max = Math.min(len,a._max-1);
      if (!a.isValid()) throw new IllegalArgumentException("Cannot mix negative and positive array selection.");
      // if selecting out columns, build a long[] cols and return that.
      if (a.isColSelector()) return a.toArray();

      // Otherwise, we have rows selected: Construct a compatible "predicate" vec
      Frame ary = env.peekAry();
      final ValSpan a0 = a;
      Vec v0 = a.all_neg() ? ary.anyVec().makeCon(1) : ary.anyVec().makeZero();
      Frame fr = a0.all_neg()
        ? new MRTask() {
            @Override public void map(Chunk cs) {
              for (long i = cs.start(); i < cs._len + cs.start(); ++i)
                if (a0.contains(-i)) cs.set((int) (i - cs.start() - 1), 0); // -1 for indexing
            }
          }.doAll(v0).getResult()._fr
        : new MRTask() {
            @Override public void map(Chunk cs) {
              for (long i = cs.start(); i < cs._len + cs.start(); ++i)
                if (a0.contains(i)) cs.set((int) (i - cs.start()), i + 1);
              }
          }.doAll(v0).getResult()._fr;
      return fr;
    }
    // Got a frame/list of results.
    // Decide if we're a toss-out or toss-in list
    Frame ary = env.popAry();
    if( ary.numCols() != 1 ) throw new IllegalArgumentException("Selector must be a single column: "+AtoS(ary.names()));
    Vec vec = ary.anyVec();

    // got a frame as a column selector... it must be a boolean selector.
    if( isCol ) {
      if( vec.min() != 0 && vec.max() != 1 && !vec.isInt() )
        throw new IllegalArgumentException("Vec selector must be a single columns of 1s and 0s.");
      // passed in len is ncols of the frame to slice
      final ASTGroupBy.IcedNBHS hs = new ASTGroupBy.IcedNBHS();
      // MRTask to fill cols in parallel
      new MRTask() {
        @Override public void map(Chunk c) {
          int start = (int)c.start();
          for( int i=0;i(i+start) ) hs.add(new IcedInt(start+i));
          }
        }
      }.doAll(ary);
      cols = new long[(int)Math.min(hs.size(), len)];
      Iterator it = hs.iterator();
      int j=0;
      while( j= 0 && vec.max() <= 1 && vec.isInt())
        return ary;    // Boolean vector selection.
      // Convert single vector to a list of longs selecting rows
      if (ary.numRows() > 10000000)
        throw H2O.fail("Unimplemented: Cannot explicitly select > 10000000 rows in slice.");
      cols = MemoryManager.malloc8((int) ary.numRows());
      for (int i = 0; i < cols.length; ++i) {
        if (vec.isNA(i)) throw new IllegalArgumentException("Can not use NA as index!");
        cols[i] = vec.at8(i);
      }
    }
    return cols;
  }

  private static String AtoS(String[] s) {
    StringBuilder sb = new StringBuilder();
    for (String ss : s) sb.append(ss).append(',');
    return sb.toString();
  }

  @Override public String toString() { return "[,]"; }
  @Override public StringBuilder toString( StringBuilder sb, int d ) {
    indent(sb,d).append(this).append('\n');
    _asts[0].toString(sb,d+1).append("\n");
    if(  _asts[2]==null ) indent(sb,d+1).append("all\n");
    else _asts[2].toString(sb,d+1).append("\n");
    if(  _asts[1]==null ) indent(sb,d+1).append("all");
    else _asts[1].toString(sb,d+1);
    return sb;
  }
}

//-----------------------------------------------------------------------------
class ASTDelete extends AST {
  @Override ASTDelete make() { return new ASTDelete(); }
  String opStr() { return "del"; }
  ASTDelete parse_impl(Exec E) {
    AST ary = E.parse();
    AST cols = null;
    if( !E.isEnd() ) cols = E.parse();
    E.eatEnd(); // eat ending ')'
    ASTDelete res = (ASTDelete) clone();
    res._asts = new AST[]{ary,cols};
    return res;
  }
  @Override String value() { return null; }
  @Override int type() { return 0; }
  @Override public String toString() { return "(del)"; }
  @Override void exec(Env env) {
    // stack looks like:  [....,hex,cols]
    _asts[0].exec(env);
    // Hard/deep delete of a Frame
    if( env.isAry() )  env.popAry().remove();
    else if( env.isStr() ) Keyed.remove(Key.make(env.popStr()));
    else throw H2O.unimpl(env.pop().getClass().toString());
  }
}


// typed lists
// lists look like this
// (list a1 a2 a3 ...)

abstract class ASTList extends AST {
  @Override void exec(Env e) { throw H2O.unimpl("Illegal: cannot do anything with " + getClass()); }
  @Override String value() { return null; }
  @Override int type() { return Env.LIST; }
  ASTSpan[] _spans;
  ArrayList spans;
  ASTList() { spans=new ArrayList<>(); }
}

class ASTAry extends ASTList {
  AST[] _a;
  @Override String opStr() { return "list"; }
  @Override ASTDoubleList make() { return new ASTDoubleList(); }
  ASTAry parse_impl(Exec E) {
    ArrayList asts = new ArrayList<>();
    while( !E.isEnd() ) asts.add(E.parse());
    E.eatEnd();
    _a = asts.toArray(new AST[asts.size()]);
    ASTAry res = (ASTAry) clone();
    res._a = _a;
    return res;
  }
}

class ASTDoubleList extends ASTList {
  ASTDoubleList() {super();}
  double[] _d;
  @Override String opStr() { return "dlist"; }
  @Override ASTDoubleList make() { return new ASTDoubleList(); }
  ASTDoubleList parse_impl(Exec E) {
    ArrayList dbls = new ArrayList<>();
    while( !E.isEnd() ) {
      AST a = E.parse();
      if( a instanceof ASTNum ) dbls.add(((ASTNum)a)._d);
      else if( a instanceof ASTSpan ) spans.add((ASTSpan)a);
    }
    E.eatEnd();
    _d = new double[dbls.size()];
    int i=0;
    for( double d:dbls ) _d[i++] = d;
    if( spans.size()!=0 ) _spans = spans.toArray(new ASTSpan[spans.size()]);

    ASTDoubleList res = (ASTDoubleList) clone();
    res._d = _d; //probably useless
    res._spans = _spans;
    return res;
  }
  @Override public Env treeWalk(Env e) { e.push(new ValDoubleList(_d,_spans)); return e; }
}

class ASTLongList extends ASTList {
  ASTLongList() {super();}
  long[] _l;
  @Override String opStr() { return "llist"; }
  @Override ASTLongList make() { return new ASTLongList(); }
  ASTLongList parse_impl(Exec E) {
    ArrayList longs = new ArrayList<>();
    while( !E.isEnd() ) {
      AST a = E.parse();
      if( a instanceof ASTNum ) longs.add((long)((ASTNum)a)._d);
      else if( a instanceof ASTSpan ) spans.add((ASTSpan)a);
    }
    E.eatEnd();
    _l = new long[longs.size()];
    int i=0;
    for( long l:longs ) _l[i++] = l;
    if( spans.size()!=0 ) _spans = spans.toArray(new ASTSpan[spans.size()]);

    ASTLongList res = (ASTLongList) clone();
    res._l = _l; //probably useless
    res._spans = _spans;
    return res;
  }
  @Override public Env treeWalk(Env e) { e.push(new ValLongList(_l,_spans)); return e; }
}

class ASTStringList extends ASTList {
  String[] _s;
  @Override String opStr() { return "slist"; }
  @Override ASTStringList make() { return new ASTStringList(); }
  ASTStringList parse_impl(Exec E) {
    ArrayList strs = new ArrayList<>();
    while( !E.isEnd() ) {// read until we hit a ")"
      AST a = E.parse();
      if( a instanceof ASTNull ) strs.add(null);
      else if( a instanceof ASTString ) strs.add(((ASTString) a)._s);
      else if( a instanceof ASTFrame  ) strs.add(((ASTFrame)a)._key);  // got screwed by the st00pid aststring hack for keys w/ spaces
    }
    E.eatEnd();
    _s = new String[strs.size()];
    int i=0;
    for( String s:strs ) _s[i++] = s;

    ASTStringList res = (ASTStringList) clone();
    res._s = _s; //probably useless
    return res;
  }
  @Override public Env treeWalk(Env e) { e.push(new ValStringList(_s)); return e; }
}

class ASTShortList extends ASTList {
  short[] _s;
  @Override String opStr() { return "shortlist"; }
  @Override ASTShortList make() { return new ASTShortList(); }
  ASTShortList parse_impl(Exec E) {
    ArrayList shorts = new ArrayList<>();
    while( !E.isEnd() ) // read until we hit a ")"
      shorts.add((short)E.nextDbl());
    E.eatEnd();
    _s = new short[shorts.size()];
    int i=0;
    for( short s:shorts ) _s[i++] = s;

    ASTShortList res = (ASTShortList) clone();
    res._s = _s; //probably useless
    return res;
  }
}

class ASTFloatList extends ASTList {
  float[] _f;
  @Override String opStr() { return "flist"; }
  @Override ASTFloatList make() { return new ASTFloatList(); }
  ASTFloatList parse_impl(Exec E) {
    ArrayList flts = new ArrayList<>();
    while( !E.isEnd() ) // read until we hit a ")"
      flts.add((float)E.nextDbl());
    E.eatEnd();
    _f = new float[flts.size()];
    int i=0;
    for( float f:flts ) _f[i++] = f;

    ASTFloatList res = (ASTFloatList) clone();
    res._f = _f; //probably useless
    return res;
  }
}

class ASTIntList extends ASTList {
  int[] _i;
  @Override String opStr() { return "ilist"; }
  @Override ASTIntList make() { return new ASTIntList(); }
  ASTIntList parse_impl(Exec E) {
    ArrayList ints = new ArrayList<>();
    while( !E.isEnd() ) // read until we hit a ")"
      ints.add((int)E.nextDbl());
    E.eatEnd();
    _i = new int[ints.size()];
    int j=0;
    for( int i:ints ) _i[j++] = i;

    ASTIntList res = (ASTIntList) clone();
    res._i = _i; //probably useless
    return res;
  }
}

class ASTBoolList extends ASTList {
  boolean[] _b;
  @Override String opStr() { return "blist"; }
  @Override ASTBoolList make() { return new ASTBoolList(); }
  ASTBoolList parse_impl(Exec E) {
    ArrayList bools = new ArrayList<>();
    while( !E.isEnd() ) // read until we hit a ")"
      bools.add((int)E.nextDbl()==1?true:false);
    E.eatEnd();
    _b = new boolean[bools.size()];
    int j=0;
    for( boolean b:bools ) _b[j++] = b;

    ASTBoolList res = (ASTBoolList) clone();
    res._b = _b; //probably useless
    return res;
  }
}

class ASTByteList extends ASTList {
  byte[] _b;
  @Override String opStr() { return "bytelist"; }
  @Override ASTByteList make() { return new ASTByteList(); }
  ASTByteList parse_impl(Exec E) {
    ArrayList bytes = new ArrayList<>();
    while( !E.isEnd() ) // read until we hit a ")"
      bytes.add((byte)E.nextDbl());
    E.eatEnd();
    _b = new byte[bytes.size()];
    int j=0;
    for( byte b:bytes ) _b[j++] = b;

    ASTByteList res = (ASTByteList) clone();
    res._b = _b; //probably useless
    return res;
  }
}

class ASTCharList extends ASTList {
  char[] _c;
  @Override String opStr() { return "clist"; }
  @Override ASTCharList make() { return new ASTCharList(); }
  ASTCharList parse_impl(Exec E) {
    ArrayList chars = new ArrayList<>();
    while( !E.isEnd() ) // read until we hit a ")"
      chars.add(E.nextStr().charAt(0));
    E.eatEnd();
    _c = new char[chars.size()];
    int j=0;
    for( char c:chars ) _c[j++] = c;

    ASTCharList res = (ASTCharList) clone();
    res._c = _c; //probably useless
    return res;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy