package water.exec;
import java.util.ArrayList;
import water.H2O;
import water.Key;
import water.fvec.AppendableVec;
import water.fvec.Frame;
import water.fvec.NewChunk;
import water.fvec.Vec;
/** Parse a generic R string and build an AST, in the context of an H2O Cloud
* @author cliffc@0xdata.com
*/
// --------------------------------------------------------------------------
public class ASTFunc extends ASTOp {
final AST _body;
final int _tmps;
Env _env; // Captured environment at each apply point
ASTFunc( String vars[], Type vtypes[], AST body, int tmps ) {
super(vars,vtypes,OPF_PREFIX,OPP_PREFIX,OPA_RIGHT); _body = body; _tmps=tmps;
}
ASTFunc( String vars[], Type t, AST body, int tmps ){
super(vars,t,OPF_PREFIX,OPP_PREFIX,OPA_RIGHT); _body = body; _tmps=tmps;
}
@Override String opStr() { return "fun"; }
//@Override ASTOp make() { throw H2O.fail();}
@Override ASTOp make() { return new ASTFunc(_vars, _t.copy(), _body, _tmps); }
static ASTOp parseFcn(Exec2 E ) {
int x = E._x;
String var = E.isID();
if( var == null ) return null;
if( !"function".equals(var) ) { E._x = x; return null; }
E.xpeek('(',E._x,null);
ArrayList<ASTId> vars = new ArrayList<ASTId>();
if( !E.peek(')',false) ) {
while( true ) {
x = E._x;
var = E.isID();
if( var == null ) E.throwErr("Invalid var",x);
for( ASTId id : vars ) if( var.equals(id._id) ) E.throwErr("Repeated argument",x);
// Add unknown-type variable to new vars list
vars.add(new ASTId(Type.unbound(),var,0,vars.size()));
if( E.peek(')') ) break;
E.xpeek(',',E._x,null);
E.skipWS();
}
}
int argcnt = vars.size(); // Record current size, as body may extend
// Parse the body
E._env.push(vars);
AST body = E.peek('{',false) ? E.xpeek('}',E._x,ASTStatement.parse(E)) : parseCXExpr(E,true);
if( body == null ) E.throwErr("Missing function body",x);
E._env.pop();
// The body should force the types. Build a type signature.
String xvars[] = new String[argcnt+1];
Type types[] = new Type [argcnt+1];
xvars[0] = "fun";
types[0] = body._t; // Return type of body
for( int i=0; i<argcnt; i++ ) {
ASTId id = vars.get(i);
xvars[i+1] = id._id;
types[i+1] = id._t;
}
return new ASTFunc(xvars,types,body,vars.size()-argcnt);
}
@Override void exec(Env env) {
// We need to push a Closure: the ASTFunc plus captured environment.
// Make a shallow copy (the body remains shared across all ASTFuncs).
// Then fill in the current environment.
ASTFunc fun = (ASTFunc)clone();
fun._env = env.capture(false);
env.push(fun);
}
@Override void apply(Env env, int argcnt, ASTApply apply) {
int res_idx = env.pushScope(argcnt-1);
env.push(_tmps);
_body.exec(env);
env.tos_into_slot(res_idx-1,null);
env.popScope();
}
@Override double[] map(Env env, double[] in, double[] out) {
final int sp = env._sp;
Key key = Vec.VectorGroup.VG_LEN1.addVecs(1)[0];
AppendableVec av = new AppendableVec(key);
NewChunk nc = new NewChunk(av,0);
for (double v : in) nc.addNum(v);
nc.close(0,null);
Frame fr = new Frame(new String[]{"row"},new Vec[]{av.close(null)});
env.push(this);
env.push(fr);
this.apply(env,2,null);
if (env.isDbl()) {
if (out==null || out.length<1) out= new double[1];
out[0] = env.popDbl();
} else if (env.isAry()) {
fr = env.peekAry();
if (fr.vecs().length > 1) H2O.unimpl();
Vec vec = fr.anyVec();
if (vec.length() > 1<<8) H2O.unimpl();
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);
env.pop();
} else {
H2O.unimpl();
}
assert sp == env._sp;
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("}");
}
}