from collections import defaultdict class DuplicatedNameError(NameError): pass class NameScope(object): def __init__(self): self._useset = set(['']) self._basenamemap = defaultdict(int) def is_used(self, name): return name in self._useset def register(self, name, deduplicate=False): if deduplicate: name = self.deduplicate(name) elif self.is_used(name): raise DuplicatedNameError(name) self._useset.add(name) return name def deduplicate(self, name): basename = name while self.is_used(name): ident = self._basenamemap[basename] + 1 self._basenamemap[basename] = ident name = "{0}.{1}".format(basename, ident) return name def get_child(self): return type(self)(parent=self) class _StrCaching(object): def _clear_string_cache(self): try: del self.__cached_str except AttributeError: pass def __str__(self): try: return self.__cached_str except AttributeError: s = self.__cached_str = self._to_string() return s class _StringReferenceCaching(object): def get_reference(self): try: return self.__cached_refstr except AttributeError: s = self.__cached_refstr = self._get_reference() return s class _HasMetadata(object): def set_metadata(self, name, node): """ Attach unnamed metadata *node* to the metadata slot *name* of this value. """ self.metadata[name] = node def _stringify_metadata(self, leading_comma=False): if self.metadata: buf = [] if leading_comma: buf.append("") buf += ["!{0} {1}".format(k, v.get_reference()) for k, v in self.metadata.items()] return ', '.join(buf) else: return ''