@@ -21,11 +21,11 @@ class IntValue : Value { BigInt data; - this(bool n) { this.data = n?1:0; } - this(int n) { this.data = n; } - this(long n) { this.data = n; } + this(bool n) { this.data = n?1:0; } + this(int n) { this.data = n; } + this(long n) { this.data = n; } this(BigInt n) { this.data = n; } this(string n) { this.data = BigInt(n); } override string toString() const { return toDecimalString(cast(BigInt)data); } override int opCmp(Object rhs) { @@ -82,15 +82,17 @@ this( Table proto=null, Kind k = Kind.PropagateSet ) { this.prototype = proto; this.kind = k; } + /// Set the value v to the index i of layer lay void set(string i, Layer lay, Value v) { if( setIfExist(i, lay, v) ) return; data[i][lay] = v; } + /// True if index i has value in layer lay bool has(string i, Layer lay) const { if( i in data ) return !!(lay in data[i]); @@ -98,8 +100,9 @@ return false; return prototype.has(i, lay); } + /// Return the value of index i at layer lay. Throws if it is not set Value get(string i, Layer lay, LexPosition pos=null) { if( i in data ) { // [TODO] consider forwarding to proto also in this case @@ -111,8 +114,10 @@ throw genex!RuntimeException(pos, sprintf!"'%s' not found in %s layer"(i,lay)); return prototype.get(i, lay, pos); } + /// t.access!T(lay,a,b,...) returns t.get(a,lay).get(b,lay).... if exists + /// and has type T. Returns null otherwise T access(T,S...)( Layer lay, string path, S rest ) { static if( rest.length == 0 ) { @@ -126,40 +131,8 @@ } return null; } - string toStringWithoutParen() const - { - string result; - bool first = true; - foreach(k, l2d; data) - foreach(l,d; l2d) - { - if(first) first=false; else result~=", "; - result ~= k; - if( l.empty ) - result ~= "(emptylayer)"; - else if( l != ValueLayer ) - result ~= l; - result ~= ":"; - result ~= text(cast(Value)d); - } - if( prototype !is null ) - { - result ~= " / "; - result ~= prototype.toStringWithoutParen(); - } - return result; - } - - string toString() - { - if( isList() ) - return text(toList()); - return "{" ~ toStringWithoutParen() ~ "}"; - } - -public: /// Is this an empty table? bool empty() { return data.length==0 && (prototype is null || prototype.empty); @@ -193,13 +166,105 @@ if( t.empty ) return result; throw genex!RuntimeException("this table is not a cons-list"); } + + /// Get the list of direct entries ignoring prototypes in sorted order + Tuple!(string,Layer,Value)[] direct_entries() + { + Tuple!(string,Layer,Value)[] arr; + foreach(k, l2d; data) + foreach(l,d; l2d) + arr ~= tuple(k,l,d); + arr.sort(); + return arr; + } + + /// Get the whole list of observable entries in unspecified order + Tuple!(string,Layer,Value)[] entries() + { + bool[string] hidden; + Tuple!(string,Layer,Value)[] arr; + enumerateEntries(hidden, arr); + return arr; + } + + private void enumerateEntries( ref bool[string] hidden, ref Tuple!(string,Layer,Value)[] arr ) + { + foreach(k, l2d; data) + if( k !in hidden ) + { + foreach(l,d; l2d) + arr ~= tuple(k,l,d); + hidden[k] = true; + } + if(prototype !is null) + prototype.enumerateEntries(hidden, arr); + } + + override string toString() + { + if( isList() ) + return text(toList()); + return "{" ~ toStringWithoutParen() ~ "}"; + } + + override int opCmp(Object rhs) + { + if(auto r = cast(Table)rhs) { + Tuple!(string,Layer,Value)[] ls = this.entries(); + Tuple!(string,Layer,Value)[] rs = r.entries(); + if( ls.length != rs.length ) + return (ls.length < rs.length ? -1 : +1); + ls.sort(); + rs.sort(); + foreach(i,_; ls) + if(auto c = ls[i].opCmp(rs[i])) + return c; + return 0; + } + if(auto r = cast(Value)rhs) return typeid(this).opCmp(typeid(r)); + throw genex!RuntimeException("comparison with value and somithing other"); + } + + override hash_t toHash() + { + Tuple!(string,Layer,Value)[] ls = this.entries(); + ls.sort(); + hash_t h; + foreach(e; ls) + h += structuralHash(e[0])+structuralHash(e[1])+structuralHash(e[2]); + return h; + } private: Table prototype; Kind kind; Value[Layer][string] data; + + string toStringWithoutParen() const + { + string result; + bool first = true; + foreach(k, l2d; data) + foreach(l,d; l2d) + { + if(first) first=false; else result~=", "; + result ~= k; + if( l.empty ) + result ~= "(emptylayer)"; + else if( l != ValueLayer ) + result ~= l; + result ~= ":"; + result ~= text(cast(Value)d); + } + if( prototype !is null ) + { + result ~= " / "; + result ~= prototype.toStringWithoutParen(); + } + return result; + } bool setIfExist(string i, Layer lay, Value v) { if( i in data )