yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
624770a1e
master
1#ifndef SLANG_CORE_DICTIONARY_H 2#define SLANG_CORE_DICTIONARY_H 3 4#include "slang-common.h" 5#include "slang-exception.h" 6#include "slang-hash.h" 7#include "slang-linked-list.h" 8#include "slang-list.h" 9#include "slang-math.h" 10#include "slang-uint-set.h" 11 12#include <ankerl/unordered_dense.h> 13#include <initializer_list> 14 15namespace Slang 16{ 17template < typename TKey ,typename TValue > 18class KeyValuePair 19{ 20public : 21TKey key ; 22TValue value ; 23KeyValuePair () {} 24KeyValuePair (const TKey & inKey ,const TValue & inValue ) 25 { 26key = inKey ; 27value = inValue ; 28 } 29KeyValuePair (TKey && inKey ,TValue && inValue ) 30 { 31key = _Move (inKey ); 32value = _Move (inValue ); 33 } 34KeyValuePair (TKey && inKey ,const TValue & inValue ) 35 { 36key = _Move (inKey ); 37value = inValue ; 38 } 39KeyValuePair (const KeyValuePair < TKey ,TValue >& that ) 40 { 41key = that .key ; 42value = that .value ; 43 } 44KeyValuePair (KeyValuePair < TKey ,TValue >&& that ) {operator = (_Move (that )); } 45KeyValuePair & operator = (KeyValuePair < TKey ,TValue >&& that ) 46 { 47key = _Move (that .key ); 48value = _Move (that .value ); 49return * this ; 50 } 51KeyValuePair & operator = (const KeyValuePair < TKey ,TValue >& that ) 52 { 53key = that .key ; 54value = that .value ; 55return * this ; 56 } 57HashCode getHashCode ()const 58 { 59return combineHash (Slang ::getHashCode (key ),Slang ::getHashCode (value )); 60 } 61bool operator == (const KeyValuePair < TKey ,TValue >& that )const 62 { 63return (key == that .key )&& (value == that .value ); 64 } 65}; 66 67template < typename TKey ,typename TValue > 68inline KeyValuePair < TKey ,TValue > KVPair (const TKey & k ,const TValue & v ) 69{ 70return KeyValuePair < TKey ,TValue > (k ,v ); 71} 72 73namespace KeyValueDetail 74{ 75 76template < typename KEY ,typename VALUE > 77SLANG_FORCE_INLINE const KEY * getKey (const std ::pair < KEY ,VALUE >* in ) 78{ 79return & in -> first ; 80} 81template < typename KEY ,typename VALUE > 82SLANG_FORCE_INLINE const KEY * getKey (const KeyValuePair < KEY ,VALUE >* in ) 83{ 84return & in -> key ; 85} 86 87template < typename KEY ,typename VALUE > 88SLANG_FORCE_INLINE const VALUE * getValue (const std ::pair < KEY ,VALUE >* in ) 89{ 90return & in -> second ; 91} 92template < typename KEY ,typename VALUE > 93SLANG_FORCE_INLINE const VALUE * getValue (const KeyValuePair < KEY ,VALUE >* in ) 94{ 95return & in -> value ; 96} 97 98}// namespace KeyValueDetail 99 100const float kMaxLoadFactor = 0.7f ; 101 102template < 103typename TKey , 104typename TValue , 105typename Hash = Slang ::Hash < TKey > , 106typename KeyEqual = std ::equal_to < TKey >> 107class Dictionary 108{ 109using InnerMap = ankerl ::unordered_dense ::map < TKey ,TValue ,Hash ,KeyEqual > ; 110using ThisType = Dictionary < TKey ,TValue ,Hash ,KeyEqual > ; 111InnerMap map ; 112 113public : 114Dictionary ()= default ; 115Dictionary (const Dictionary & )= default; 116Dictionary (Dictionary && ) = default; 117ThisType & operator = ( const ThisType & ) = default ; 118ThisType & operator = (ThisType && ) = default; 119Dictionary ( std ::initializer_list < typename InnerMap ::value_type > inits) 120: map( std :: move ( inits )) 121{ 122} 123 124// 125// Types 126// 127using Iterator = typename InnerMap ::iterator; 128using ConstIterator = typename InnerMap ::const_iterator; 129using KeyType = TKey; 130using ValueType = TValue; 131 132// 133// Iterators 134// 135 136auto begin() { return map. begin (); } 137auto begin() const { return map. begin (); } 138auto end() { return map. end (); } 139auto end() const { return map. end (); } 140 141// 142// Modifiers 143// 144 145// Removes all values from the map 146void clear () { map. clear (); } 147 148// Erases the value at the specified key if it exists 149void remove ( const TKey & key) { map. erase (key); } 150 151// Removes all values satifying the predicate: 152// bool predicate(pair<Key, Value>) 153template < typename Predicate > 154void removeIf (Predicate && predicate) 155{ 156auto it = begin (); 157while (it != end ()) 158{ 159if ( predicate ( * it)) 160{ 161it = map. erase (it); 162} 163else 164{ 165++ it; 166} 167} 168} 169 170// Reserves enough space for the specified number of values 171void reserve ( Index size) { map. reserve (std:: size_t (size)); }; 172 173// Swap with another map 174void swapWith ( ThisType & rhs) { std :: swap ( * this, rhs); } 175 176// 177// Query capacity 178// 179 180std :: size_t getCount () const { return map. size (); } 181 182// 183// Lookup 184// 185 186// Returns true if the map contains an equivalent key 187template < typename K > 188bool containsKey( const K & k) const 189{ 190return map. contains (k); 191} 192 193// Returns a valid pointer to the requested element, or nullptr if it 194// doesn't exist 195template < typename K > 196const TValue * tryGetValue ( const K & key) const 197{ 198auto i = map. find (key); 199return i == map. end () ? nullptr : & (i -> second ); 200} 201// Returns a valid pointer to the requested element, or nullptr if it 202// doesn't exist 203template < typename K > 204TValue * tryGetValue (const K & key) 205{ 206auto i = map. find (key); 207return i == map. end () ? nullptr : std:: addressof (i -> second ); 208} 209 210// Returns true and copies the element into 'value' if present. 211// Otherwise returns false and value unmodified. 212template < typename K > 213bool tryGetValue( const K & key, TValue & value) const 214{ 215auto i = map. find (key); 216if (i == map. end ()) 217return false; 218value = i -> second ; 219return true; 220} 221 222// Returns a const reference to the value at the given key. Asserts if 223// the value doesn't exist 224const TValue & getValue ( const TKey & key) const 225{ 226if (const auto x = tryGetValue (key)) 227return * x; 228SLANG_ASSERT_FAILURE ( "The key does not exist in dictionary." ); 229} 230 231// Returns a reference to the value at the given key. Asserts if the 232// value doesn't exist 233TValue & getValue (const TKey & key) 234{ 235if (const auto x = tryGetValue (key)) 236return * x; 237SLANG_ASSERT_FAILURE ( "The key does not exist in dictionary." ); 238} 239 240// 241// Combined Lookup and Insertion 242// 243 244// Tries to insert the given element, if a value was already present at 245// the given key then returns a pointer to that element instead. 246// Returns nullptr if insertion was successful. 247TValue * tryGetValueOrAdd ( const typename InnerMap::value_type & kvPair) 248{ 249const auto & [ iterator , inserted] = map. insert (kvPair); 250return inserted ? nullptr : std:: addressof (iterator -> second ); 251} 252// Tries to insert the given element, if a value was already present at 253// the given key then returns a pointer to that element instead. 254// Returns nullptr if insertion was successful. 255TValue * tryGetValueOrAdd ( typename InnerMap::value_type && kvPair) 256{ 257const auto & [ iterator , inserted] = map. insert (std:: move (kvPair)); 258return inserted ? nullptr : std:: addressof (iterator -> second ); 259} 260// Tries to insert the given element, if a value was already present at 261// the given key then returns a pointer to that element instead. 262// Returns nullptr if insertion was successful. 263TValue * tryGetValueOrAdd ( const TKey & key, const TValue & value) 264{ 265return tryGetValueOrAdd ({key, value}); 266} 267 268// Inserts the given value if it doesn't exist already 269// Return a reference to the (possibly new) value in the map 270TValue & getOrAddValue (const TKey & key, const TValue & defaultValue) 271{ 272auto [ iterator , inserted] = map. insert ({key, defaultValue}); 273return iterator -> second ; 274} 275 276// Returns a reference to the value at the specified key, default 277// initializing it if it doesn't already exist 278TValue & operator[](const TKey & key) { return map[key]; } 279// Returns a reference to the value at the specified key, default 280// initializing it if it doesn't already exist 281TValue & operator[](TKey && key) { return map[std:: move (key)]; } 282 283// 284// Insertion 285// 286 287// Returns true if the value was inserted, returns false if the map 288// already has a value associated with this key 289bool addIfNotExists ( typename InnerMap::value_type && kvPair) 290{ 291return ! tryGetValueOrAdd (std:: move (kvPair)); 292} 293// Returns true if the value was inserted, returns false if the map 294// already has a value associated with this key 295bool addIfNotExists ( const typename InnerMap::value_type & kvPair) 296{ 297return ! tryGetValueOrAdd (kvPair); 298} 299// Returns true if the value was inserted, returns false if the map 300// already has a value associated with this key 301bool addIfNotExists ( const TKey & k, const TValue & v) { return addIfNotExists ({k, v}); } 302// Returns true if the value was inserted, returns false if the map 303// already has a value associated with this key 304bool addIfNotExists ( TKey && k, TValue && v) 305{ 306return addIfNotExists ({std:: move (k), std::move( v )}); 307} 308 309// Asserts if the key already exists in the dictionary 310void add ( typename InnerMap::value_type && kvPair) 311{ 312if (! addIfNotExists (std:: move (kvPair))) 313SLANG_ASSERT_FAILURE ( "The key already exists in Dictionary." ); 314} 315// Asserts if the key already exists in the dictionary 316void add ( const typename InnerMap::value_type & kvPair) 317{ 318if (! addIfNotExists (kvPair)) 319SLANG_ASSERT_FAILURE ( "The key already exists in Dictionary." ); 320} 321// Asserts if the key already exists in the dictionary 322void add ( const TKey & key, const TValue & value) { add ({key, value}); } 323// Asserts if the key already exists in the dictionary 324void add ( TKey && key, TValue && value) { add ({std:: move (key), std::move( value )}); } 325 326// Inserts into the dictionary or assigns if the key already exists 327void set ( const TKey & key, const TValue & value) { map. insert_or_assign (key, value); } 328}; 329 330/* We may want to rename this, as strictly speaking _Caps names are reserved */ 331class _DummyClass 332{ 333}; 334 335template < typename T , typename DictionaryType > 336class HashSetBase 337{ 338protected : 339DictionaryType dict; 340 341private : 342void init () {} // Base case for recursion 343template < typename... Args > 344void init( const T & v, Args... args) 345{ 346add (v); 347init( args ...); 348} 349 350public : 351HashSetBase () {} 352template < typename Arg, typename... Args > 353HashSetBase (Arg arg, Args... args) 354{ 355init (arg, args...); 356} 357HashSetBase( const HashSetBase & set) { operator = (set); } 358HashSetBase (HashSetBase && set) { operator = ( _Move (set)); } 359HashSetBase & operator = ( const HashSetBase & set) 360{ 361dict = set. dict ; 362return * this; 363} 364HashSetBase & operator = (HashSetBase && set) 365{ 366dict = _Move (set. dict ); 367return * this; 368} 369 370public : 371class Iterator 372{ 373private : 374typename DictionaryType::ConstIterator iter; 375 376public : 377Iterator () = default; 378const T & operator * () const { return * KeyValueDetail:: getKey (std:: addressof ( * iter)); } 379const T * operator -> () const { return KeyValueDetail ::getKey( std ::addressof( * iter )); } 380 381Iterator & operator ++ () 382{ 383++ iter ; 384return * this ; 385} 386Iterator operator ++ ( int ) 387{ 388Iterator rs = * this ; 389operator ++ (); 390return rs ; 391} 392bool operator != ( const Iterator & that ) const { return iter != that . iter ; } 393bool operator == ( const Iterator & that ) const { return iter == that . iter ; } 394Iterator ( const typename DictionaryType :: ConstIterator & _iter ) { this -> iter = _iter ; } 395}; 396Iterator begin() const { return Iterator( dict .begin()); } 397Iterator end() const { return Iterator( dict .end()); } 398 399public : 400auto getCount () const { return dict .getCount(); } 401void clear() { dict .clear(); } 402bool add( const T & obj ) { return dict .addIfNotExists( obj , _DummyClass()); } 403bool add( T && obj ) { return dict .addIfNotExists(_Move( obj ), _DummyClass()); } 404void remove( const T & obj ) { dict .remove( obj ); } 405bool contains( const T & obj ) const { return dict .containsKey( obj ); } 406}; 407template < typename T > 408class HashSet : public HashSetBase < T , Dictionary < T , _DummyClass >> 409{ 410public : 411using HashSetBase < T , Dictionary < T , _DummyClass >>:: HashSetBase ; 412}; 413 414template < typename TKey , typename TValue > 415class OrderedDictionary 416{ 417friend class Iterator ; 418friend class ItemProxy ; 419 420private : 421inline int getProbeOffset( int /*probeIdx*/ ) const 422{ 423// quadratic probing 424return 1 ; 425} 426 427private : 428int m_bucketCountMinusOne ; 429int m_count ; 430UIntSet m_marks ; 431 432LinkedList < KeyValuePair < TKey , TValue >> m_kvPairs ; 433LinkedNode < KeyValuePair < TKey , TValue >> ** m_hashMap ; 434void deallocateAll() 435{ 436if ( m_hashMap ) 437delete [] m_hashMap ; 438m_hashMap = nullptr ; 439m_kvPairs .clear(); 440} 441inline bool isDeleted( int pos ) const { return m_marks .contains(( pos << 1 ) + 1 ); } 442inline bool isEmpty( int pos ) const { return ! m_marks .contains(( pos << 1 )); } 443inline void setDeleted( int pos , bool val ) 444{ 445if ( val ) 446m_marks .add(( pos << 1 ) + 1 ); 447else 448m_marks .remove(( pos << 1 ) + 1 ); 449} 450inline void setEmpty( int pos , bool val ) 451{ 452if ( val ) 453m_marks .remove(( pos << 1 )); 454else 455m_marks .add(( pos << 1 )); 456} 457struct FindPositionResult 458{ 459int objectPosition ; 460int insertionPosition ; 461FindPositionResult () 462{ 463objectPosition = -1 ; 464insertionPosition = -1 ; 465} 466FindPositionResult ( int objPos , int insertPos ) 467{ 468objectPosition = objPos ; 469insertionPosition = insertPos ; 470} 471}; 472template < typename T > 473inline int getHashPos( T & key ) const 474{ 475const unsigned int hash = ( unsigned int )getHashCode( key ); 476return (( unsigned int )( hash * 2654435761 )) % m_bucketCountMinusOne ; 477} 478template < typename T > 479FindPositionResult findPosition ( const T & key ) const 480{ 481int hashPos = getHashPos (( T & )key); 482int insertPos = -1 ; 483int numProbes = 0 ; 484while (numProbes <= m_bucketCountMinusOne) 485{ 486if ( isEmpty (hashPos)) 487{ 488if (insertPos == -1 ) 489return FindPositionResult ( -1 , hashPos); 490else 491return FindPositionResult ( -1 , insertPos); 492} 493else if ( isDeleted (hashPos)) 494{ 495if (insertPos == -1 ) 496insertPos = hashPos; 497} 498else if (m_hashMap[hashPos] -> value . key == key) 499{ 500return FindPositionResult (hashPos, -1 ); 501} 502numProbes ++ ; 503hashPos = (hashPos + getProbeOffset (numProbes)) & m_bucketCountMinusOne; 504} 505if (insertPos != -1 ) 506return FindPositionResult ( -1 , insertPos); 507SLANG_ASSERT_FAILURE ( 508"Hash map is full. This indicates an error in Key::Equal or Key::GetHashCode." ); 509} 510TValue & _insert (KeyValuePair < TKey, TValue >&& kvPair, int pos) 511{ 512auto node = m_kvPairs. addLast (); 513node -> value = _Move (kvPair); 514m_hashMap[pos] = node; 515setEmpty (pos, false); 516setDeleted (pos, false); 517return node -> value . value ; 518} 519void maybeRehash () 520{ 521if (m_bucketCountMinusOne == -1 || m_count / ( float )m_bucketCountMinusOne >= kMaxLoadFactor) 522{ 523int newSize = (m_bucketCountMinusOne + 1 ) * 2 ; 524if (newSize == 0 ) 525{ 526newSize = 128 ; 527} 528OrderedDictionary < TKey, TValue > newDict; 529newDict. m_bucketCountMinusOne = newSize - 1 ; 530newDict. m_hashMap = new LinkedNode < KeyValuePair < TKey, TValue>> * [newSize]; 531newDict. m_marks . resizeAndClear (newSize * 2 ); 532if (m_hashMap) 533{ 534for (auto & kvPair : * this) 535{ 536newDict. add ( _Move ( kvPair )); 537} 538} 539* this = _Move (newDict); 540} 541} 542 543bool addIfNotExists (KeyValuePair < TKey, TValue >&& kvPair) 544{ 545maybeRehash (); 546auto pos = findPosition (kvPair. key ); 547if (pos. objectPosition != -1 ) 548return false; 549else if (pos. insertionPosition != -1 ) 550{ 551m_count ++ ; 552_insert ( _Move (kvPair), pos. insertionPosition ); 553return true; 554} 555else 556SLANG_ASSERT_FAILURE ( 557"Inconsistent find result returned. This is a bug in Dictionary implementation." ); 558} 559void add (KeyValuePair < TKey, TValue >&& kvPair) 560{ 561if (! addIfNotExists ( _Move (kvPair))) 562SLANG_ASSERT_FAILURE ( "The key already exists in Dictionary." ); 563} 564TValue & set (KeyValuePair < TKey, TValue >&& kvPair) 565{ 566maybeRehash (); 567auto pos = findPosition (kvPair. key ); 568if (pos. objectPosition != -1 ) 569{ 570m_hashMap[pos. objectPosition ] -> removeAndDelete (); 571return _insert ( _Move (kvPair), pos. objectPosition ); 572} 573else if (pos. insertionPosition != -1 ) 574{ 575m_count ++ ; 576return _insert ( _Move (kvPair), pos. insertionPosition ); 577} 578else 579SLANG_ASSERT_FAILURE ( 580"Inconsistent find result returned. This is a bug in Dictionary implementation." ); 581} 582 583public: 584using Iterator = typename LinkedList < KeyValuePair < TKey, TValue>>::Iterator; 585using ConstIterator = typename LinkedList < KeyValuePair < TKey, TValue>>::ConstIterator; 586 587Iterator begin() { return m_kvPairs. begin (); } 588Iterator end () { return m_kvPairs. end (); } 589ConstIterator begin () const { return m_kvPairs. begin (); } 590ConstIterator end () const { return m_kvPairs. end (); } 591 592public : 593void add ( const TKey & key, const TValue & value) { add (KeyValuePair < TKey, TValue > (key, value)); } 594void add ( TKey && key, TValue && value) 595{ 596add (KeyValuePair < TKey, TValue > ( _Move (key), _Move (value))); 597} 598bool addIfNotExists ( const TKey & key, const TValue & value) 599{ 600return addIfNotExists (KeyValuePair < TKey, TValue > (key, value)); 601} 602bool addIfNotExists ( TKey && key, TValue && value) 603{ 604return addIfNotExists (KeyValuePair < TKey, TValue > ( _Move (key), _Move (value))); 605} 606void remove ( const TKey & key) 607{ 608if (m_count > 0 ) 609{ 610auto pos = findPosition ( key ); 611if (pos. objectPosition != -1 ) 612{ 613m_kvPairs. removeAndDelete (m_hashMap[pos. objectPosition ]); 614m_hashMap[pos. objectPosition ] = 0 ; 615setDeleted (pos. objectPosition , true); 616m_count -- ; 617} 618} 619} 620void clear () 621{ 622m_count = 0 ; 623m_kvPairs. clear (); 624m_marks. clear (); 625} 626template < typename T > 627bool containsKey( const T & key) const 628{ 629if (m_bucketCountMinusOne == -1 ) 630return false; 631auto pos = findPosition ( key ); 632return pos. objectPosition != -1 ; 633} 634template < typename T > 635TValue * tryGetValue (const T & key) const 636{ 637if (m_bucketCountMinusOne == -1 ) 638return nullptr ; 639auto pos = findPosition ( key ); 640if (pos. objectPosition != -1 ) 641{ 642return & (m_hashMap[pos. objectPosition ] -> value . value ); 643} 644return nullptr ; 645} 646template < typename T > 647bool tryGetValue( const T & key, TValue & value) const 648{ 649if (m_bucketCountMinusOne == -1 ) 650return false; 651auto pos = findPosition ( key ); 652if (pos. objectPosition != -1 ) 653{ 654value = m_hashMap[pos. objectPosition ] -> value . value ; 655return true; 656} 657return false; 658} 659class ItemProxy 660{ 661private: 662const OrderedDictionary < TKey, TValue >* dict; 663TKey key; 664 665public : 666ItemProxy( const TKey & _key, const OrderedDictionary < TKey, TValue >* _dict) 667{ 668this -> dict = _dict; 669this -> key = _key; 670} 671ItemProxy (TKey && _key, const OrderedDictionary < TKey, TValue >* _dict) 672{ 673this -> dict = _dict; 674this -> key = _Move (_key); 675} 676TValue & getValue () const 677{ 678auto pos = dict -> findPosition (key); 679if (pos. objectPosition != -1 ) 680{ 681return dict -> m_hashMap [pos. objectPosition ] -> value . value ; 682} 683else 684{ 685SLANG_ASSERT_FAILURE ( "The key does not exists in dictionary." ); 686} 687} 688inline TValue & operator ()() const { return getValue (); } 689operator TValue & () const { return getValue (); } 690TValue & operator = ( const TValue & val) 691{ 692return ((OrderedDictionary < TKey, TValue >* )dict) 693-> set (KeyValuePair < TKey, TValue > ( _Move (key), val)); 694} 695TValue & operator = (TValue && val) 696{ 697return ((OrderedDictionary < TKey, TValue >* )dict) 698-> set (KeyValuePair < TKey, TValue > ( _Move (key), _Move (val))); 699} 700}; 701ItemProxy operator []( const TKey & key) const { return ItemProxy(key, this); } 702ItemProxy operator[]( TKey && key) const { return ItemProxy ( _Move (key), this); } 703 704int getCount () const { return m_count; } 705KeyValuePair < TKey, TValue >& getFirst () const { return m_kvPairs. getFirst (); } 706KeyValuePair < TKey, TValue >& getLast () const { return m_kvPairs. getLast (); } 707 708private : 709template < typename... Args > 710void init( const KeyValuePair < TKey, TValue >& kvPair, Args... args) 711{ 712add (kvPair); 713init( args ...); 714} 715 716public : 717OrderedDictionary () 718{ 719m_bucketCountMinusOne = -1 ; 720m_count = 0 ; 721m_hashMap = 0 ; 722} 723template < typename Arg, typename... Args > 724OrderedDictionary (Arg arg, Args... args) 725{ 726init (arg, args...); 727} 728OrderedDictionary( const OrderedDictionary < TKey, TValue >& other) 729: m_bucketCountMinusOne ( -1 ), m_count ( 0 ), m_hashMap ( 0 ) 730{ 731* this = other; 732} 733OrderedDictionary (OrderedDictionary < TKey, TValue >&& other) 734: m_bucketCountMinusOne ( -1 ), m_count ( 0 ), m_hashMap ( 0 ) 735{ 736* this = ( _Move (other)); 737} 738OrderedDictionary < TKey, TValue >& operator = ( const OrderedDictionary < TKey, TValue >& other) 739{ 740if (this == & other) 741return * this; 742clear (); 743for (auto & item : other) 744add ( item .key, item .value); 745return * this; 746} 747OrderedDictionary < TKey, TValue >& operator = (OrderedDictionary < TKey, TValue >&& other) 748{ 749if (this == & other) 750return * this; 751deallocateAll(); 752m_bucketCountMinusOne = other. m_bucketCountMinusOne ; 753m_count = other. m_count ; 754m_hashMap = other. m_hashMap ; 755m_marks = _Move (other. m_marks ); 756other. m_hashMap = 0 ; 757other. m_count = 0 ; 758other. m_bucketCountMinusOne = -1 ; 759m_kvPairs = _Move (other. m_kvPairs ); 760return * this; 761} 762~ OrderedDictionary () { deallocateAll (); } 763}; 764 765template < typename T > 766class OrderedHashSet : public HashSetBase < T , OrderedDictionary < T , _DummyClass>> 767{ 768public: 769T & getLast () { return this -> dict . getLast (). key ; } 770void removeLast () { this -> remove ( getLast ()); } 771}; 772} // namespace Slang 773 774#endif