
water.rapids.Env Maven / Gradle / Ivy
package water.rapids;
import water.*;
import water.exceptions.H2OIllegalArgumentException;
import water.exceptions.H2OKeyNotFoundArgumentException;
import water.fvec.EnumWrappedVec;
import water.fvec.Frame;
import water.fvec.Vec;
import water.util.IcedHashMap;
import water.util.IcedInt;
import water.util.Log;
import java.util.*;
/** Execute a set of instructions in the context of an H2O cloud.
*
* An Env (environment) object is a classic stack of values used during walking of an AST. While walking the syntax tree
* new scopes may be encountered, and each new scope will inherit from the caller's scope. All scopes have a common
* ancestor as the global scope.
*
* For efficiency, reference counting is employed to recycle objects already in use rather than creating copies upon
* copies (a la R). When a Vec is `pushed` on to the stack, its reference count is incremented by 1. When a Vec is
* `popped` off of the stack, its reference count is decremented by 1. When the reference count is 0, the Env instance
* will dispose of the object. All objects live and die by the Env's that create them. That means that any object not
* created by an Env instance shalt not be DKV.removed.
*
* Therefore, the Env class is a stack of values + an API for reference counting.
*/
public class Env extends Iced {
final static int ID =0;
final static int ARY =1;
final static int STR =2;
final static int NUM =3;
final static int FUN =4;
final static int SPAN =5;
final static int SERIES=6;
final static int VEC =8;
final static int LIST =9;
final static int LARY =10;
final static int NULL =99999;
transient ExecStack _stack; // The stack
transient HashMap _refcnt; // Ref Counts for each vector
transient final public StringBuilder _sb; // Holder for print results
transient final HashSet _locked; // Vec keys, these shalt not be DKV.removed.
final SymbolTable _global;
SymbolTable _local;
final Env _parent;
final private boolean _isGlobal;
transient HashSet _trash;
transient HashSet _tmpFrames; // cleanup any tmp frames made by new ASTString
@Override public AutoBuffer write_impl(AutoBuffer ab) {
// write _refcnt
ab.put4(_refcnt.size());
for (Key k: _refcnt.keySet()) { ab.put(k); ab.put4(_refcnt.get(k)._val); }
return ab;
}
@Override public Env read_impl(AutoBuffer ab) {
_refcnt = new HashMap<>();
_stack = new ExecStack();
_trash = new HashSet<>();
int len = ab.get4();
for (int i = 0; i < len; ++i) {
_refcnt.put(ab.get(Key.class), new IcedInt(ab.get4()));
}
return this;
}
// Top-level Env object: This is the global Env object. To determine if we're in the global scope, _parent == null
// and _local == null will always be true. The parent of a scope is the calling scope. All scopes inherit from the
// global scope.
Env(HashSet locked) {
_stack = new ExecStack();
_refcnt = new HashMap<>();
_sb = new StringBuilder();
_locked = locked;
_global = new SymbolTable();
_local = null;
_parent = null;
_isGlobal = true;
_trash = new HashSet<>();
_tmpFrames = new HashSet<>();
}
// Capture the current environment & return it (for some closure's future execution).
// the only thing captured is the reference counts of live Vecs... everything else is new'd
Env capture() { return new Env(this); }
private Env(Env e) {
_stack = new ExecStack(); // nope want a new stack!
_refcnt = new HashMap<>(); // new ref counter... however, don't add vecs if they are tracked in some parent scope!
_sb = new StringBuilder(); // just for pretty printing in debug mode
_locked = new HashSet<>(); // brand new set of locked things -- must check up the nest for locked things
_global = null; // not a global ST
_local = new SymbolTable(); // new symbol table for me!
_parent = e; // ok i wanna know who my parent is, yes. parent has all of the goodies like TRUE, FALSE, E, NA, INF, PI, etc...
_isGlobal = false; // am not global, but multiple ways to check this!
_trash = new HashSet<>(); // don't want my parent scope's trash!
_tmpFrames = new HashSet<>();
}
// makes a new "global" context -- useful for one off invocations
static Env make(HashSet locked) {
Env env = new Env(locked);
// some default items in the symbol table
env.put("TRUE", Env.NUM, "1"); env.put("T", Env.NUM, "1");
env.put("FALSE", Env.NUM, "0"); env.put("F", Env.NUM, "0");
env.put("NA", Env.NUM, Double.toString(Double.NaN));
env.put("Inf", Env.NUM, Double.toString(Double.POSITIVE_INFINITY));
env.put("-Inf",Env.NUM, Double.toString(Double.NEGATIVE_INFINITY));
env.put("E",Env.NUM, Double.toString(Math.E));
env.put("PI",Env.NUM, Double.toString(Math.PI));
return env;
}
public boolean isGlobal() { return _isGlobal && _parent == null && _local == null; }
public static String typeToString(int type) {
switch(type) {
case ID: return "ID";
case ARY: return "ARY";
case STR: return "STR";
case NUM: return "NUM";
case FUN: return "FUN";
case SPAN: return "SPAN";
case SERIES: return "SERIES";
case VEC: return "VEC";
case NULL: return "NULL";
default: return "No type for number: " + type;
}
}
/**
* The stack API
*/
public int sp() { return _stack._head + 1; }
public void push(Val o) {
if (o instanceof ValFrame) {
ValFrame f = (ValFrame)o;
if( !_isGlobal ) {
assert f._key==null || DKV.get(f._key)!=null; // have a local Frame we're pushing -- not in the DKV yet!
_local.put(Key.make().toString(), f._fr, false /* any pushed Val _here_ is no longer isFrame */);
}
addRef(f);
}
_stack.push(o);
clean();
}
public void pushAry(Frame fr) { push(new ValFrame(fr)); }
public boolean isEmpty() { return _stack.isEmpty(); }
public Val peek() { return _stack.peek(); }
public Val peekAt(int i) { return _stack.peekAt(i); }
public int peekType() {return _stack.peekType(); }
public Frame peekAryAt(int i) {
try {
return ((ValFrame) _stack.peekAt(i))._fr;
} catch(ClassCastException e) {
e.printStackTrace();
throw new IllegalArgumentException("Bad input: Expected input to be a Frame.");
}
}
public int peekTypeAt(int i) { return _stack.peekTypeAt(i); }
public boolean isAry() { return peekType() == ARY; }
public boolean isNum() { return peekType() == NUM; }
public boolean isStr() { return peekType() == STR; }
public boolean isId () { return peekType() == ID; }
public boolean isFun() { return peekType() == FUN; }
public boolean isNul() { return peekType() == NULL;}
public boolean isSpan(){ return peekType() == SPAN;}
public boolean isSeries(){ return peekType() == SERIES;}
public Val pop() { return _stack.pop(); }
public void pop(int n) { for (int i = 0; i < n; ++i) pop(); }
public void poppush(int n, Val v) { pop(n); push(v);}
public Frame popAry () { return ((ValFrame)pop())._fr; }
public double popDbl() { return ((ValNum)pop())._d; }
public String popStr() {
Val v = pop();
if( v instanceof ValStr ) return ((ValStr)v)._s;
else if( v instanceof ValFrame ) return ((ValFrame)v)._fr._key.toString();
else throw new IllegalASTException("shouldn't be here.");
}
public ValSeries popSeries() {return (ValSeries)pop(); }
public ValSpan popSpan() { return (ValSpan)pop(); }
public Frame peekAry() {return ((ValFrame)peek())._fr; }
public double peekDbl() {return ((ValNum)peek())._d; }
public AST pop2AST() {
if (isAry()) return new ASTFrame(popAry());
if (isNum()) return new ASTNum(popDbl());
if (isStr()) return new ASTString('\"', popStr());
if (isNul()) {pop(); return new ASTNull(); }
throw new IllegalArgumentException("Invalid use of pop2AST. Got bad type: "+peekType());
}
public void toss(ValFrame f) { _trash.add(f); }
public synchronized void clean() {
if( _trash == null ) return;
for( ValFrame f : _trash )
if( !f._g ) cleanup(f._fr);
_trash.clear();
}
/**
* Reference Counting API
*/
public void addRef(ValFrame o) { addRef(o._fr); }
public void addRef(Frame f) { for (Vec v : f.vecs()) addRef(v); }
public void addRef(Vec v) {
if( inScope(v) ) {
IcedInt I = getRef(v);
assert I == null || I._val >= 0;
putRef(v, new IcedInt(I == null ? 1 : I._val + 1));
if (v instanceof EnumWrappedVec) {
Vec mv = ((EnumWrappedVec) v).masterVec();
IcedInt Imv = getRef(mv);
assert Imv == null || Imv._val >= 0;
putRef(mv, new IcedInt(Imv == null ? 1 : Imv._val + 1));
}
}
}
private IcedInt getRef(Vec v) { return v._key==null?null:_refcnt.get(v._key); }
private void putRef(Vec v, IcedInt i) { _refcnt.put(v._key,i); }
private void rmRef (Vec v) { if( v._key!=null ) _refcnt.remove(v._key); }
private boolean hasRef(Vec v) { return v._key != null && _refcnt.containsKey(v._key); }
// Vecs are only tracked in the scope that they are created in.
// thou shalt not kill vecs belonging to some parent scope
// => don't bother reference counting them here since the counts are always > 0
private boolean inScope(Vec v) {
// recurse up the parent scopes,
// if all null, then return true (means we have a new vec to reference count in THIS scope)
// if the vec is in some parent scope, return false -- will not be adding reference counts today!
Env e = _parent;
boolean in=false; // assume not in parent.
while( e!=null ) {
in |= e._refcnt.containsKey(v); // _refcnt is always non-null
e = e._parent;
}
return _refcnt.containsKey(v) || !in;
}
// check that this scope and no parent scope has a "lock" on this key.
// "locked" means shalt not be DKV removed.
public boolean hasLock(Key k) {
// recurse up the parent scopes,
// return true if any scope has a lock
// otherwise return false.
boolean locked=_locked!=null&&_locked.contains(k);
if( _parent != null ) locked |= _parent.hasLock(k);
return locked;
}
public void lock(Key k) { _locked.add(k); }
public void lock(Frame fr) { for (Vec v : fr.vecs()) lock(v); lock(fr._key); }
public void lock(Vec v) { lock(v._key); }
public void lock(String k) { lock(Key.make(k)); }
public Key lock() { Key k=Key.make(); _locked.add(k); return k; } // return a new key that is locked
private void subRef(Val o) {
assert o instanceof ValFrame;
boolean delete=true;
Frame f = ((ValFrame)o)._fr;
// subRef vecs that may (not) exist in DKV
for( Vec v : f.vecs() ) delete &= subRef(v);
// delete a Frame if delete is true
if( delete )
if( f._key != null && !hasLock(f._key) )
f.delete();
}
// subRef on a single vec, walk all Env objects to decide whether to kill the Vec or not.
//
// return false if the vec is not deleted via removeVec
// return true otherwise
public boolean subRef(Vec v) {
if( v == null ) return false;
boolean delete;
if( getRef(v) == null ) return false;
if( hasLock(v._key) ) return false;
int cnt = getRef(v)._val - 1;
if( cnt > 0 ) {
putRef(v, new IcedInt(cnt));
delete = false;
} else {
// safe to remove!;
extinguishCounts(v);
delete = true;
}
if (delete) removeVec(v, null);
return delete;
}
public void subRef(Frame f) { for (Vec v : f.vecs()) subRef(v); }
static Futures removeVec(Vec v, Futures fs) {
if (fs == null) {
fs = new Futures();
Keyed.remove(v._key, fs);
fs.blockForPending();
return null;
} else {
Keyed.remove(v._key, fs);
return fs;
}
}
private void extinguishCounts(Object o) {
if (o instanceof Vec) { rmRef((Vec)o);}
else for(Vec v: ((Frame) o).vecs()) rmRef(v);
}
/*
* Utility & Cleanup
*/
// Done writing into all things. Allow rollups.
public void postWrite() {
Futures fs = new Futures();
for( Key key : _refcnt.keySet() ) {
Vec v = DKV.getGet(key);
if( v!=null ) v.postWrite(fs);
}
fs.blockForPending();
}
public void remove_and_unlock() {
while(!_stack.isEmpty()) {
int type = peekType();
switch(type) {
case ARY: remove(peek(), false); break;
default : pop(); break;
}
}
Futures fs = new Futures();
for (Key k : _refcnt.keySet())
if (_refcnt.get(k)._val == 0)
removeVec(((Vec)DKV.getGet(k)), fs);
fs.blockForPending();
for(Frame f: _tmpFrames) {if(f._key!=null) DKV.remove(f._key); } // top level removal only (Vecs may be live still)
}
public void unlock() {
while(!_stack.isEmpty()) {
if (_stack.peekType() == ARY) {
Frame fr = ((ValFrame)_stack.pop())._fr;
if (fr._lockers != null && lockerKeysNotNull(fr)) fr.unlock_all();
} else _stack.pop();
}
}
void remove(Object o, boolean popped) {
assert o instanceof ValFrame || o instanceof Frame || o == null;
if (o == null) return;
if (o instanceof ValFrame) remove_and_unlock(((ValFrame)o)._fr);
else remove_and_unlock((Frame)o);
if(!popped) pop();
}
void cleanup(Frame f) {
if( f == null ) return;
if (f._lockers != null && lockerKeysNotNull(f)) f.unlock_all();
subRef(new ValFrame(f));
}
// nuking the current scope. Frames in the symbol table get nuked.
// reference counter also gets a once-over
//
// what happens when there's a result being returned to parent scope?
// must transfer reference counts for that result back to the parent.
// only ever have a SINGLE result on the stack ... multiple results is an error.
void popScope() {
if( _parent==null ) throw new IllegalArgumentException("Cannot pop the parent scope!");
Key k = isAry()?peekAry()._key:null; // shouldn't be null...
// kill any local frames...
Set local = _local._table.keySet();
for( String name : local ) {
Frame f;
if( _local.getType(name)==LARY ) {
f=_local.getFrame(name)._fr;
if( isAry() && f==peekAry() ) continue;
else cleanup(f);
}
}
_local.clear();
for( Key key: _locked ) {
if( k!=null && k==key ) continue;
if( isAry() && Arrays.asList(peekAry().keys()).contains(key)) continue;
// Keyed.remove(key);
}
_locked.clear();
for( Key ik: _refcnt.keySet()) {
if( _refcnt.get(ik)._val==0 && !hasLock(ik) ) Keyed.remove(ik); // no lock and zero counts, nuke it.
}
}
private void remove_and_unlock(Frame fr) {
extinguishCounts(fr);
if (fr._lockers != null && lockerKeysNotNull(fr)) fr.unlock_all();
if( anyLocked(fr) ) return;
fr.delete();
}
private boolean lockerKeysNotNull(Frame f) {
for (Key k : f._lockers)
if (k == null) return false;
return true;
}
private boolean anyLocked(Frame fr) {
if( hasLock(fr._key) ) return true;
for (Vec v : fr.vecs())
if( hasLock(v._key) ) return true;
return false;
}
public String toString(int i) {
int type = peekTypeAt(i);
Object o = peekAt(i);
switch(type) {
case ARY: return ((ValFrame)o)._fr.numRows()+"x"+((ValFrame)o)._fr.numCols();
case NUM: return Double.toString(((ValNum)o)._d);
case STR: return ((ValStr)o)._s;
case ID : return ((ValId)o)._id;
case SERIES: return o.toString();
case SPAN: return o.toString();
case NULL: return "null";
default: throw H2O.unimpl("Bad value on the stack: " + type);
}
}
@Override public String toString() {
int sp = sp();
String s="{";
for( int i=-sp+1; i <= 0; i++ ) s += toString(i)+",";
return s+"}";
}
/** Stack interface for the ExecStack
*
* Allowed Objects:
* -Strings
* -Frames
* -Vecs
* -doubles, ints, floats, etc.
*/
private interface Stack {
Val peek();
Val peekAt(int i);
Val pop();
void push(Val t);
boolean isEmpty();
int size();
int peekType();
int peekTypeAt(int i);
}
private class ExecStack implements Stack {
private final ArrayList _stack;
private int _head;
private ExecStack() {
_stack = new ArrayList<>();
_head = -1;
}
/**
* Peek the top of the stack
* @return the Object at the `_head` of the stack
*/
@Override public Val peek() {
if (isEmpty()) return null;
return _stack.get(_head);
}
/**
* Peek the stack at position passed in (does error checking on the position)
* @param i The position at which to peek the stack
* @return the Object at position `i`.
*/
@Override public Val peekAt(int i) {
// Another check just in case assertions aren't on.
if (i <= 0) {
i = _head + i;
if (i < 0) throw new IllegalArgumentException("Trying to peekAt a negative position in the stack: "+i);
}
// The stack may be empty
if (isEmpty()) return null;
// The requested index may be greater than _head (points to the top of the stack)
if (i > _head) {
Log.warn("peekAt("+i+"): i is greater than the top of the stack: "+_head+"<"+i);
return null;
}
// The requested index may be greater than the size of the stack (size() == _head if not empty),
// and it's good to check anyways for debugging and extra logging. This will also assert that the _head and
// stack sizes are aligned.
if (i > size()) {
Log.warn("peekAt("+i+"): i is greater than the size of the stack: "+size()+"<"+i);
return null;
}
// Return the Val at position i
return _stack.get(i);
}
/**
* Peek the type of the object at the top of the stack. Does not pop!
* @return an int representing the type of the object at the top of the stack
*/
@Override public int peekType() { return getType(peek()); }
/**
* Peek the tpe of the object at position `i` in the stack. Does not pop!
* @param i The position at which to peek the stack
* @return an int representing the type of the object at position `i` in the stack.
*/
@Override public int peekTypeAt(int i) { return getType(peekAt(i)); }
private int getType(Val o) {
if( o instanceof ValNull || o==null ) return NULL;
if( o instanceof ValId ) return ID;
if( o instanceof ValFrame ) return ARY;
if( o instanceof ValStr ) return STR;
if( o instanceof ValNum ) return NUM;
if( o instanceof ValSpan ) return SPAN;
if( o instanceof ValSeries ) return SERIES;
if( o instanceof ValLongList) return LIST;
if( o instanceof ValStringList) return LIST;
if( o instanceof ValDoubleList) return LIST;
throw H2O.unimpl("Got a bad type on the ExecStack: Object class: "+ o.getClass()+". Not a Frame, String, Double, Fun, Span, or Series");
}
/**
* Is the stack empty?
* @return true if empty, false otherwise
*/
@Override public boolean isEmpty() { return _head == -1; }
/**
* Get the size of the stack.
* @return the number of Objects sitting in the stack. Assert that the number of Objects is aligned with `_head`.
*/
@Override public int size() {
if (!isEmpty()) {
// Choice to add _head + 1, but when asserts stacksize - 1 because want to know where the _head is at!
assert _stack.size() == _head + 1 : "The stack size and the pointer to the top are out of alignment! Stack size: " + (_stack.size() - 1) + ", _head: " + _head;
return _stack.size();
}
return -1;
}
/**
* Pop one of the top of the stack.
* @return the Object sitting at the top of the stack
*/
@Override public Val pop() {
if (isEmpty()) return null;
Val o = peek();
if( o instanceof ValStr ) {
AST f = staticLookup( new ASTString('\"', ((ValStr)o)._s));
if( f instanceof ASTFrame) {
_stack.remove(_head--);
f.exec(Env.this);
return pop();
}
}
_stack.remove(_head--);
if (o instanceof ValFrame) toss((ValFrame)o);
return o;
}
/**
* Pop all of the values off the stack.
* @return void
*/
public void popAll() {
if (isEmpty()) return;
while(size() != -1) {
Val v = pop();
if (v instanceof ValFrame) ((ValFrame)v)._fr.unlock_all();
}
}
/**
* Push an Object onto the stack
* @param t is the Val to be pushed onto the stack.
*/
@Override public void push(Val t) {
_head++;
_stack.add(_head, t);
}
}
/**
* Map identifiers to types & values.
*
* > is the pair this class works with.
*/
class SymbolTable extends Iced {
IcedHashMap _table;
public SymbolTable() { _table = new IcedHashMap<>(); }
void clear() { _table.clear(); }
public void put(String name, int type, String value) {
if (_table.containsKey(name)) {
write(name, type);
write(name, value);
} else {
SymbolAttributes attributes = new SymbolAttributes(type, value);
_table.put(name, (T)attributes);
}
}
public void put(String name, Frame localFrame, boolean isFrame) {
ASTFrame fr = new ASTFrame(localFrame);
fr.isFrame = isFrame; // was it a Vec or a Frame?
_table.put(name, (T)fr);
}
public T get(String name) {
if( !_table.containsKey(name) ) return null;
return _table.get(name);
}
public int getType(String name) {
if (!_table.containsKey(name)) return NULL;
T V = get(name);
if( V instanceof ASTFrame ) return LARY; // special local array, all others come from the DKV!
else return ((SymbolAttributes)V)._type;
}
public String getValue(String name) { // caller knows exactly the type.
if( !_table.containsKey(name) ) return null;
T V = get(name);
try {
return ((SymbolAttributes) V)._value;
} catch( ClassCastException e ) {
throw new ClassCastException("API Error in Symbol Attributes. Caller expected SymbolAttributes but got " + V.getClass());
}
}
public ASTFrame getFrame(String name) {
if( !_table.containsKey(name) ) return null;
T V = get(name);
try {
return (ASTFrame)V;
} catch( ClassCastException e ) {
throw new ClassCastException("API Error in Symbol Attributes. Caller expected Frame but got " + V.getClass());
}
}
private void write(String name, int type) {
SymbolAttributes attrs = (SymbolAttributes)get(name);
attrs.write(type);
}
private void write(String name, String value) {
SymbolAttributes attrs = (SymbolAttributes)get(name);
attrs.write(value);
}
}
private class SymbolAttributes extends Iced {
private int _type;
private String _value;
SymbolAttributes(int type, String value) { _type = type; _value = value; }
public void write(int type) { this._type = type; }
public void write(String value) { this._value = value;}
}
SymbolTable newTable() { return new SymbolTable(); }
/**
* The symbol table interface.
*
* Overwrite existing values in writable tables.
*/
void put(String name, int type, String value) {
if( _isGlobal ) _global.put(name,type,value);
else _local.put(name,type,value);
}
void put(String name, Frame f, boolean isFrame) {
if( _isGlobal ) _global.put(name,f,isFrame);
else _local.put(name,f,isFrame);
}
void put(String name, Frame f) { put(name,f,true);}
int getType(String name, boolean search_global) {
if (name == null || name.equals("")) throw new IllegalArgumentException("Tried to lookup on a missing name. Are there free floating `%` in your AST?");
int res = NULL;
// Check the local_frames first
// then back out and look around symbol table for keys that MUST exist in DKV;
// frames in _local_frames do NOT exist in DKV.
if (_local != null) res = _local.getType(name);
// Check the local scope first if not null
if (res == NULL && _local != null) res = _local.getType(name);
// Didn't find it? Try the global scope next, if we haven't already
if (res == NULL && search_global && _global != null) res = _global.getType(name);
// Still didn't find it? Try the KV store next, if we haven't already
if (res == NULL && search_global) res = kvLookup(name);
// Still didn't find it? Start looking up the parent scopes.
if (res == NULL && _parent != null) res = _parent.getType(name, false); // false -> don't keep looking in the global env.
// Fail if the variable does not exist in any table!
if (res == NULL) throw new H2OIllegalArgumentException("Failed lookup of variable: " + name, "Failed lookup of variable: " + name + " in: " + this);
return res;
}
private static int kvLookup(String name) {
Value v = DKV.get(Key.make(name));
if (v == null) return NULL;
if (v.get() instanceof Frame) return ARY;
if (v.get() instanceof Vec) return ARY;
else throw new IllegalArgumentException("Unexpected type: " + v.getClass());
}
// if calling getValue, then already know that a Frame result isn't at hand!!!
String getValue(String name, boolean search_global) {
String res = null;
// Check the local scope first if not null
if (_local != null) res = _local.getValue(name);
// Didn't find it? Try the global scope next, if we haven't already
if (res == null && search_global) res = _global.getValue(name);
// Still didn't find it? Start looking up the parent scopes.
if (res == null && _parent!=null) res = _parent.getValue(name, false); // false -> don't keep looking in the global env.
// Fail if the variable does not exist in any table!
return res;
}
ASTFrame getFrame(String name, boolean search_global) {
ASTFrame res=null;
// check local scope first
if( _local != null ) res = _local.getFrame(name);
// if searching global, then look in DKV, _global may have transient Frames sitting inside the table, must check there next if not found here.
if( res==null && search_global /* search DKV in this case.*/ ) {
Value v = DKV.get(Key.make(name));
if( v==null || v.get()==null ) res=null;
else res=new ASTFrame(name);
}
// no dice in the DKV, try a transient Frame in the global symbol table (only if we're top level...
if( res==null && _global!=null ) { res = _global.getFrame(name); }
// still not found... search up parent scopes
if( res==null && _parent!=null ) res = _parent.getFrame(name, false);
// return null if failed to find and error out further up
return res;
}
AST lookup(water.rapids.ASTId id) {
switch(getType(id.value(), true)) {
case NUM: return new ASTNum(Double.valueOf(getValue(id.value(), true)));
case ARY: return new ASTFrame(id.value());
case LARY:return getFrame(id.value(), false); // pull the local frame out
case STR: return id.value().equals("()") ? new ASTNull() : new ASTString('\"', id.value());
default: throw H2O.unimpl("Could not find appropriate type for identifier "+id);
}
}
boolean tryLookup(water.rapids.ASTId id) {
try {
lookup(id);
} catch(Exception e) {
return false;
}
return true;
}
// Optimistically lookup strings in the K/V. On hit, return the found Key as a Frame.
// On a miss, return the String/Id.
static AST staticLookup(water.rapids.ASTId id) { return kvLookup(id.value())==ARY ? new ASTFrame(id.value()) : id; }
static AST staticLookup(water.rapids.ASTString str) { return kvLookup(str.value())==ARY ? new ASTFrame(str.value()) : str; }
}
abstract class Val extends Iced {
abstract String value();
abstract int type();
}
class ValFrame extends Val {
final String _key;
final Frame _fr;
boolean _isVec;
boolean _g; // are any vecs in the frame in the DKV ?
ValFrame(Frame fr) { _key = null; _fr = fr; }
ValFrame(Frame fr, boolean g) { this(fr); _g = g;}
ValFrame(Frame fr, boolean isVec, boolean g) { _key = null; _fr = fr; _isVec = isVec; _g = g; }
ValFrame(String key) {
Key k = Key.make(key);
if (DKV.get(k) == null) throw new H2OKeyNotFoundArgumentException(key);
_key = key;
_fr = k.get();
_g = true;
}
@Override public String toString() { return "Frame with key " + _key + ". Frame: :" +_fr.toString(); }
@Override int type () { return Env.ARY; }
@Override String value() { return _key; }
}
class ValNum extends Val {
final double _d;
ValNum(double d) { _d = d; }
@Override public String toString() { return ""+_d; }
@Override int type () { return Env.NUM; }
@Override String value() { return ""+_d; }
}
class ValStr extends Val {
final String _s;
ValStr(String s) { _s = s; }
@Override public String toString() { return _s; }
@Override int type () { return Env.STR; }
@Override String value() { return _s; }
}
class ValSpan extends Val {
final long _min; double _max;
final ASTNum _ast_min; final ASTNum _ast_max;
boolean _isCol; boolean _isRow;
ValSpan(ASTNum min, ASTNum max) { _ast_min = min; _ast_max = max; _min = (long)min._d; _max = max._d; }
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; }
void setSlice(boolean row, boolean col) { _isRow = row; _isCol = col; }
@Override String value() { return null; }
@Override int type() { return Env.SPAN; }
@Override public String toString() { return _min + ":" + _max; }
long[] toArray() {
long[] res = new long[Math.abs((int)_max) - Math.abs((int)_min) + 1];
long min = _min;
for (int i = 0; i < res.length; ++i) res[i] = min++;
Arrays.sort(res);
return res;
}
boolean isValid() { return (_min < 0 && _max < 0) || (_min >= 0 && _max >= 0); }
boolean all_neg() { return _min < 0; }
boolean all_pos() { return !all_neg(); }
}
//TODO: add in a boolean field for exclusion
class ValSeries extends Val {
final long[] _idxs;
final ASTSpan[] _spans;
final double[] _d;
boolean _isCol;
boolean _isRow;
int[] _order;
ValSeries(long[] idxs, ASTSpan[] spans) {
_idxs = idxs; _d=null;
if( _idxs!=null ) Arrays.sort(_idxs);
_spans = spans;
}
ValSeries(long[] idxs, double[] d, ASTSpan[] spans) {
_idxs = idxs; _d=d;
if( _idxs!=null ) Arrays.sort(_idxs);
_spans = spans;
}
boolean contains(final long a) {
if (_spans != null)
for (ASTSpan s : _spans)
if (s.contains(a))
return true;
return _idxs != null && Arrays.binarySearch(_idxs, a) >= 0;
}
boolean isColSelector() { return _isCol; }
boolean isRowSelector() { return _isRow; }
void setSlice(boolean row, boolean col) {
_isRow = row;
_isCol = col;
}
@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) {
if( _idxs.length > 20) res += "many ";
else {
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 += Math.abs((int) s._max) - Math.abs((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 (long aL : l) res[cur++] = aL;
}
}
if (_idxs != null) {
for (long _idx : _idxs) res[cur++] = _idx;
}
Arrays.sort(res);
if (all_neg()) reverse(res);
return res;
}
private static void reverse(long[] r) {
Long[] l = new Long[r.length];
for (int i = 0 ; i < l.length; ++i) l[i] = r[i];
List list = Arrays.asList(l);
Collections.reverse(list);
for (int i = 0; i < list.size(); ++i) r[i] = list.get(i);
}
boolean isValid() {
// long[] ary = toArray();
// boolean all_neg = false, all_pos = false, first=true;
// for (long l : ary) {
// if (first) { if (l < 0) all_neg = true; else all_pos = true; first = false; }
// if (all_neg && l >= 0) return false;
// if (all_pos && l < 0) return false;
// }
return true;
}
boolean isNum() {
boolean ret = false;
if (_idxs != null && _idxs.length > 0)
if (_idxs.length == 1) ret = true;
if (_spans != null && _spans.length > 0)
for (ASTSpan s : _spans) ret &= s.isNum();
return ret;
}
long toNum() {
if (_idxs != null && _idxs.length > 0) return _idxs[0];
if (_spans != null && _spans.length > 0) return _spans[0].toNum();
throw new IllegalArgumentException("Could not convert ASTSeries to a single number.");
}
boolean all_neg() { return (_idxs != null && _idxs.length > 0) ?_idxs[0] < 0 : _spans[0].all_neg(); }
boolean all_pos() { return !all_neg(); }
}
class ValNull extends Val {
ValNull() {}
@Override String value() { return null; }
@Override int type() { return Env.NULL; }
}
class ValId extends Val {
final String _id;
final char _type; // either '$' or '!'
ValId(char type, String id) { _type = type; _id = id; }
@Override public String toString() { return _type+_id; }
@Override int type() { return Env.ID; }
@Override String value() { return _id; }
boolean isSet() { return _type == '!'; }
boolean isLookup() { return _type == '$'; }
boolean isValid() { return isSet() || isLookup(); }
}
class ValDoubleList extends Val {
final double[] _d;
final ASTSpan[] _spans;
ValDoubleList(double[] d, ASTSpan[] spans) { _d=d; _spans=spans; }
@Override public String toString() { return null; }
@Override int type() { return Env.LIST; }
@Override String value() { return null; }
}
class ValLongList extends Val {
final long[] _l;
final ASTSpan[] _spans;
ValLongList(long[] l, ASTSpan[] spans) { _l=l; _spans=spans; }
@Override public String toString() { return null; }
@Override int type() { return Env.LIST; }
@Override String value() { return null; }
}
class ValStringList extends Val {
final String[] _s;
ValStringList(String[] s) { _s=s; }
@Override public String toString() { return null; }
@Override int type() { return Env.LIST; }
@Override String value() { return null; }
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy