Source code for pyrser.parsing.node

""" Basic classes for AST manipulation """

import weakref


[docs]class Node(dict): """Base class for node manipulation."""
[docs] def __bool__(self): return True
[docs] def __repr__(self): #TODO: use to_yml items = [] if len(self) > 0: items.append(repr(self.items())) for k, v in vars(self).items(): items.append("{} = {}".format(k, repr(v))) return "{}({})".format(self.__class__.__name__, ', '.join(items))
[docs] def check(self, ndict: dict, info="") -> bool: """ Debug method, help detect cycle and/or other incoherence in a tree of Node """ def iscycle(thing, ndict: dict, info: str) -> bool: # check if not already here idthing = id(thing) ndict[info] = idthing if idthing not in ndict: # add myself ndict[idthing] = "%s:%s no cycle" % (type(thing), info) return False else: ndict[idthing] += "\n%s:%s cycle" % (type(thing), info) return True def recurs(thing, ndict: dict, info: str) -> bool: if not iscycle(thing, ndict, info): res = False if isinstance(thing, list): idx = 0 for i in thing: res |= recurs(i, ndict, "%s[%d]" % (info, idx)) idx += 1 elif isinstance(thing, Node): res |= thing.check(ndict, info) elif isinstance(thing, dict): for k, v in thing.items(): res |= recurs(v, ndict, "%s[%s]" % (info, k)) return res return True # add ME FIRST if len(ndict) == 0: ndict['self'] = id(self) info = 'self' if not iscycle(self, ndict, info): res = False if len(self) > 0: keys = list(self.keys()) for k in keys: ndict["['" + k + "']"] = id(self[k]) res |= recurs(self[k], ndict, "%s[%s]" % (info, k)) keys = list(vars(self).keys()) for k in keys: ndict["." + k] = id(getattr(self, k)) res |= recurs(getattr(self, k), ndict, "%s.%s" % (info, k)) return res return True
[docs] def clean(self): if len(self) > 0: keys = list(self.keys()) for k in keys: del self[k] keys = list(vars(self).keys()) for k in keys: delattr(self, k)
[docs] def set(self, othernode): """allow to completly mutate the node into any subclasses of Node""" self.__class__ = othernode.__class__ self.clean() if len(othernode) > 0: for k, v in othernode.items(): self[k] = v for k, v in vars(othernode).items(): setattr(self, k, v)
class ListNodeItem: pass class ListNodeItemIterator: pass
[docs]class ListNode:
[docs] def __init__(self, it: []=None): self.cache = None self.must_update = False self.begin = None self.end = None if it is not None: head = None for data in it: self.append(data)
[docs] def append(self, d): if self.end is None: self.begin = ListNodeItem(d) self.end = self.begin self.must_update = True else: self.end = self.end.append(d)
[docs] def prepend(self, d): if self.begin is None: self.begin = ListNodeItem(d) self.end = self.begin self.must_update = True else: self.begin = self.begin.prepend(d)
[docs] def __str__(self) -> str: return str(self.begin)
[docs] def __repr__(self) -> str: txt = "" for it in self.begin._fwd(): txt += repr(it) return txt
[docs] def __len__(self) -> int: self._update() return len(self.cache)
[docs] def __iter__(self) -> ListNodeItemIterator: return ListNodeItemIterator(self.begin)
[docs] def __reversed__(self) -> ListNodeItemIterator: return ListNodeItemIterator(self.end, True)
[docs] def _trueindex(self, k) -> int: if k >= 0: return k if k < 0: return len(self.cache) + k
[docs] def _cache(self) -> str: txt = "{\n" txt += "begin= %d\n" % id(self.begin) txt += "end= %d\n" % id(self.end) txt += "}\n" return txt
[docs] def _update(self): if self.cache is None or self.must_update: self.cache = {} idx = 0 for it in self.begin._fwd(): it.thelist = self self.cache[idx] = it idx += 1 self.must_update = False
[docs] def index(self, v) -> int: self._update() for k, v in self.cache.items(): if v.data == v: return k raise ValueError("%d is not in list" % v)
[docs] def count(self, v) -> int: self._update() cnt = 0 for v in self.cache.values(): if v.data == v: cnt += 1 return cnt
[docs] def get(self, k) -> ListNodeItem: if type(k) is not int: raise ValueError('Key must be an int') k = self._trueindex(k) self._update() if k not in self.cache: raise IndexError("list index out of range") return self.cache[k] # []
[docs] def __getitem__(self, k) -> object: if type(k) is not int: raise ValueError('Key must be an int') k = self._trueindex(k) self._update() if k not in self.cache: raise IndexError("list index out of range") return self.cache[k].data # [] =
[docs] def __setitem__(self, k, d): if type(k) is not int: raise ValueError('Key must be an int') k = self._trueindex(k) self._update() if k not in self.cache: raise IndexError("list index out of range") self.cache[k].data = d # del []
[docs] def __delitem__(self, k): if type(k) is not int: raise ValueError('Key must be an int') k = self._trueindex(k) self._update() if k not in self.cache: raise IndexError("list index out of range") self.cache[k].popitem() self.must_update = True
[docs]class ListNodeItem:
[docs] def __init__(self, data): self.data = data self.prev = None self.next = None self.thelist = None
[docs] def __str__(self) -> str: ls = [] for it in self._fwd(): ls.append(repr(it.data)) return '[' + ", ".join(ls) + ']'
[docs] def __repr__(self) -> str: txt = "{\n" txt += "prev= %d\n" % id(self.prev) txt += "self= %d\n" % id(self) txt += "data= %d - %s\n" % (id(self.data), repr(self.data)) txt += "thelist= %d\n" % id(self.thelist) txt += "next= %d\n" % id(self.next) txt += "}\n" return txt
[docs] def index(self, v) -> int: idx = 0 for it in self._fwd(): if it.data == v: return idx idx += 1 raise ValueError("%d is not in list" % v)
[docs] def count(self, v) -> int: cnt = 0 for it in self._fwd(): if it.data == v: cnt += 1 return cnt
[docs] def _get_slice(self, sl) -> [object]: # TODO: slice getter begin, end, step = sl # []
[docs] def __getitem__(self, k) -> object: return self.thelist[k] # [] =
[docs] def __setitem__(self, k, d): self.thelist[k] = d # del []
[docs] def __delitem__(self, k): if type(k) is not int: raise ValueError('Key must be an int') gen = self._fwd if k < 0: gen = self._bwd idx = 0 for it in gen(): if idx == k: it.popitem() return idx += 1 raise IndexError("list index out of range")
[docs] def popitem(self) -> object: if self.prev is not None: self.prev.next = self.next if self.next is not None: self.next.prev = self.prev thelist = self.thelist if thelist is not None: if thelist.begin is self: thelist.begin = self.next if thelist.end is self: thelist.end = self.prev thelist.must_update = True return self.data
[docs] def append(self, data) -> ListNodeItem: if self.thelist is None: self.thelist = ListNode() thelist = self.thelist thelist.must_update = True new = ListNodeItem(data) new.thelist = thelist new.next = self.next self.next = new new.prev = self if new.next is not None: new.next.prev = new # update thelist if thelist.end is self or thelist.end is None: thelist.end = new if thelist.begin is None: thelist.begin = self return new
[docs] def prepend(self, data) -> ListNodeItem: if self.thelist is None: self.thelist = ListNode() thelist = self.thelist thelist.must_update = True new = ListNodeItem(data) new.thelist = thelist new.prev = self.prev self.prev = new new.next = self if new.prev is not None: new.prev.next = new # update thelist if thelist.begin is self or thelist.begin is None: thelist.begin = new if thelist.end is None: thelist.end = self return new
[docs] def values(self): tmp = self while tmp is not None: yield tmp.data tmp = tmp.next
[docs] def rvalues(self): tmp = self while tmp is not None: yield tmp.data tmp = tmp.prev
[docs] def _fwd(self): tmp = self while tmp is not None: yield tmp tmp = tmp.next
[docs] def _bwd(self): tmp = self while tmp is not None: yield tmp tmp = tmp.prev
[docs]class ListNodeItemIterator:
[docs] def __init__(self, current: ListNodeItem, reverse=False): self.current = current self.attr = 'next' if reverse: self.attr = 'prev'
[docs] def __iter__(self): return self
[docs] def __next__(self): if self.current is None: raise StopIteration data = self.current.data self.current = getattr(self.current, self.attr) return data