@@ -178,12 +178,15 @@ def __init__(self, name: str, fname: Optional[str] = None, line: int = 0) -> Non
178178 # Give all nonterminals a unique, negative sequence number for hashing purposes
179179 self ._index = Nonterminal ._index
180180 Nonterminal ._index -= 1
181- self ._hash = id (self ).__hash__ ()
181+ # Use the creation-order sequence number as the hash. It is
182+ # deterministic between runs (unlike id()), so that iteration
183+ # order over sets and dicts of nonterminals is stable, and it
184+ # never changes during the object's lifetime (unlike self._index,
185+ # which may be renumbered after the grammar has been processed).
186+ self ._hash = self ._index
182187
183188 def __hash__ (self ) -> int :
184- """Use the id of this nonterminal as a basis for the hash"""
185- # The index may change after the entire grammar has been
186- # read and processed; therefore it is not suitable for hashing
189+ """Return the cached, deterministic hash of this nonterminal"""
187190 return self ._hash
188191
189192 def __eq__ (self , o : Any ) -> bool :
@@ -274,8 +277,13 @@ def __init__(self, name: str) -> None:
274277 self ._name = name
275278 self ._index = Terminal ._index
276279 Terminal ._index += 1
277- # The hash is used quite often so it is worth caching
278- self ._hash = id (self ).__hash__ ()
280+ # The hash is used quite often so it is worth caching.
281+ # Use the creation-order sequence number: it is deterministic
282+ # between runs (unlike id()), so that iteration order over sets
283+ # and dicts of terminals is stable, and it never changes during
284+ # the object's lifetime (unlike self._index, which may be
285+ # renumbered after the grammar has been processed).
286+ self ._hash = self ._index
279287
280288 def __hash__ (self ) -> int :
281289 return self ._hash
0 commit comments