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

water.rapids.ASTFunc Maven / Gradle / Ivy

package water.rapids;

import water.Futures;
import water.H2O;
import water.Key;
import water.fvec.AppendableVec;
import water.fvec.Frame;
import water.fvec.NewChunk;
import water.fvec.Vec;

import java.util.Arrays;

/**
 *  The ASTFunc Object
 *
 *  An ASTFunc pulls the function ast produced by the front-end and creates a reference to this function.
 *
 *  A function has a body (which may be empty), and a body is a list of statements.
 *
 *  Statements that are possible:
 *
 *  if statements
 *  else statements
 *  for statements
 *  while statements
 *  switch statement
 *  declarative statements
 *  operative statements
 *  return statements
 *
 *  The last statement of a function will return the result of that statement.
 *
 *  Some Rules:
 *  -----------
 *
 *  Every function defines a new Environment that inherits from the current one. Put another way, the calling scope
 *  provides the context for the function to be executed in. Environments can be captured with the `capture` call.
 *
 *  No function shall modify state in any of its parent environments (which includes the DKV store). A function may only
 *  return values to a parent scope.
 */


/**
 * Functions will always have all of their arguments fully specified (even if that means they are filled with null).
 * This means `nargs` arguments are always parsed.
 */
public class ASTFunc extends ASTFuncDef {
  public ASTFunc() { super(); }
  public ASTFunc(String name, String[] arg_names, Env.SymbolTable table, ASTStatement body) {
    _name = name; _arg_names = arg_names; _table = table; _body = body;
  }
  AST[] _args;

  // (name args)
  @Override ASTFunc parse_impl(Exec E) {
    int nargs = _arg_names.length;
    AST[] args = new AST[nargs];
    for (int i = 0; i < nargs; ++i) args[i] = E.parse();
    E.eatEnd();
    ASTFunc res = (ASTFunc)clone();
    res._args = args;
    res._asts = _asts;
    return res;
  }

  @Override String opStr() { return _name; }
  @Override ASTOp make() {
    ASTFunc res = (ASTFunc)clone();
    res._name = _name;
    res._arg_names = _arg_names==null?null:Arrays.copyOf(_arg_names, _arg_names.length);
    res._args = _args==null?null:Arrays.copyOf(_args, _args.length);
    res._body = new ASTStatement();
    res._body._asts = _body==null?null:Arrays.copyOf(_body._asts, _body._asts.length);
    return res;
  }
  @Override void apply(Env e) {
    Env captured = e.capture();

    // for each arg in _args, want to fill in the appropriate value into the captured Env's symbol table
    for (int i = 0; i < _args.length; ++i) {
      if( _args[i] instanceof ASTId     ) _args[i] = e.lookup((ASTId)_args[i]);   // may have to do an initial lookup...

      // arg is an AST, must eval it...
      if( !(_args[i] instanceof ASTNum || _args[i] instanceof ASTString || _args[i] instanceof ASTFrame || _args[i] instanceof ASTNull) ) {
        _args[i].treeWalk(e);
        _args[i] = e.pop2AST();
      }

      if( _args[i] instanceof ASTNum         ) captured.put(_arg_names[i], Env.NUM, _args[i].value()); // put a #
      else if( _args[i] instanceof ASTString ) captured.put(_arg_names[i], Env.STR, _args[i].value()); // put a string
      else if( _args[i] instanceof ASTNull   ) captured.put(_arg_names[i], Env.NULL,"()");             // put a null
      else if( _args[i] instanceof ASTFrame  ) captured.put(_arg_names[i], ((ASTFrame)_args[i])._fr);  // put a frame
      else throw new IllegalArgumentException("Argument of type "+ _args[i].getClass()+" unsupported. Argument must be a String, number, Frame, or null.");
    }
    _body.exec(captured);    // execute the fcn body
    e.push(captured.peek()); // get the result from the captured scope without popping, sticks references into parent scope.
    captured.popScope();     // pop the captured scope
  }

  // used by methods that pass their args to FUN (e.g. apply, sapply, ddply); i.e. args are not parsed here.
  @Override void exec(Env e, AST... args) {
    _args = args;
    apply(e);
  }

  double[] map(Env env, double[] in, double[] out, AST[] args) {
    Futures fs = new Futures();
    Vec[] vecs = new Vec[in.length];
    Key keys[] = Vec.VectorGroup.VG_LEN1.addVecs(vecs.length);
    for( int c = 0; c < vecs.length; c++ ) {
      AppendableVec vec = new AppendableVec(keys[c]);
      NewChunk chunk = new NewChunk(vec, 0);
      chunk.addNum(in[c]);
      chunk.close(0, fs);
      vecs[c] = vec.close(fs);
    }
    fs.blockForPending();
    Key local_key = Key.make();
    Frame fr = new Frame(local_key, null, vecs);
    env.addRef(fr);
//    if( !env.isGlobal() ) {
//      if( env._local==null ) env._local = env.newTable();
//    }
//    env.put(local_key.toString(), fr); // push fr, since not in DKV, into the _local_frames -> must trash this frame at some point ... during popScope()
    AST[] as = new AST[args==null?1:args.length+1];
    as[0]=new ASTFrame(fr);
    if( args!=null )
      System.arraycopy(args, 0, as, 1, args.length);
    // execute the function on the row
    exec(env, as);

    // cleanup results and return
    if (env.isNum()) {
      if (out==null || out.length<1) out= new double[1];
      out[0] = env.popDbl();
    } else if (env.isAry()) {
      fr = env.popAry();
      if (fr.numCols() > 1 && fr.numRows() != 1) throw H2O.unimpl("Number of rows returned is > 1");
//      if (fr.numRows() > 1<<8) throw H2O.unimpl("Too many rows!");
      if (fr.numCols() > 1) {
        out = new double[fr.numCols()];
        for (int v = 0; v < fr.vecs().length; ++v) out[v] = fr.vecs()[v].at(0);
      } else {
        Vec vec = fr.anyVec();
        if (out == null || out.length < vec.length()) out = new double[(int) vec.length()];
        for (long i = 0; i < vec.length(); i++) out[(int) i] = vec.at(i);
      }
    } else {
      throw H2O.unimpl();
    }
//    env.cleanup(fr);
    return out;
  }

  @Override public StringBuilder toString( StringBuilder sb, int d ) {
    indent(sb,d).append(this).append(") {\n");
    _body.toString(sb,d+1).append("\n");
    return indent(sb,d).append("}");
  }
}

class ASTFuncDef extends ASTOp {
  String _name;
  String[] _arg_names;
  Env.SymbolTable _table;
  ASTStatement _body;
  public ASTFuncDef() { super(null); }   // super(null) => _vars[] = null

  void parse_func(Exec E) {
    String l;
    if( E.isSpecial(E.peek()) ) l = E.nextStr();
    else                        l = E.parseID();
    _name=l;

    // parse the function args: these are just arg names -> will do _local.put(name, Env.NULL, null) (local ST put)
    Env.SymbolTable table = E._env.newTable(); // grab a new SymbolTable
    AST arggs = E.parse();
    if( arggs instanceof ASTStringList ) _arg_names = ((ASTStringList)arggs)._s;
    else if( arggs instanceof ASTString) _arg_names = new String[]{((ASTString)arggs)._s};
    else throw new IllegalArgumentException("Expected args to be either a slist or a string (for a single argument). Got: " + arggs.getClass());

    if (_arg_names == null) table.put(null, Env.NULL, null);
    else for (String arg : _arg_names) table.put(arg, Env.NULL, null);
    _table = table;

    // parse the function body
    _body = new ASTStatement().parse_impl(E);
    E.eatEnd();
    ASTFunc res = new ASTFunc(_name, _arg_names, _table, _body);
    res._asts = null;
    putUDF(res, _name);  // not all nodes get this...
  }

  @Override String opStr() { return "def"; }
  @Override ASTOp make() { return new ASTFuncDef(); }
  @Override void apply(Env e) { throw H2O.unimpl("No `apply` call for ASTFuncDef"); }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy