from collections import deque import logging as log from . import skipWhitespace, ParserWarning from .property import Property, GAME_INFO class Node: def __init__(self): self.properties = dict() self.parent = None self.children = [] @staticmethod def fits(s, i): return i < len(s) and s[i] == ";" @staticmethod def create(s, start): assert Node.fits(s, start) res = Node() i = skipWhitespace(s, start+1) while Property.fits(s, i): (i, x) = Property.create(s, i) if x.name in res.properties: log.warning(ParserWarning('duplicate "{0}" property in a node. second value ignored'.format(x.name), s, i)) else: res.properties[x.name] = x i = skipWhitespace(s, i) return (i, res) def listGINodes(self): if self.isGINode(): yield self empty = not self.isGINode() node = self while node.parent: node = node.parent if node.isGINode(): empty = False yield node queue = deque(self.children) while len(queue) > 0: node = queue.popleft() if node.isGINode(): empty = False yield node queue.extend(node.children) if empty: yield self # always yield at least self, can work as GINode as well as any other def isGINode(self): return any(prop.type == GAME_INFO for prop in self.properties.values()) def setProp(self, name, value): self.properties[name] = value # check value type def setChildren(self, children): self.children = children for child in children: child.parent = self def addChild(self, node): if node in self.children: return node node.parent = self self.children.append(node) return node def removeChild(self, node): if node not in self.children: return None del self.children[self.children.index(node)] node.parent = None return node def removeChildAt(self, i): if -len(self.children) < i < len(self.children): res = self.children[i] del self.children[i] res.parent = None return res return None ## Create a copy of the Node, with the same parent and deep copied properties, no copied children. def copy(self): res = Node() res.properties = {k: v.copy() for (k, v) in self.properties.items()} res.parent = self.parent return res def getProp(self, name, default=None): if name in self.properties: return self.properties[name].value else: return default ## Returns textual representation of the Node itself, but disregards its children. def __str__(self): return ";" + "".join(str(p) for p in self.properties.values()) def export(self): # there is a beautiful recursive solution, which this stack is too narrow to contain stack = [(self, 1, 1)] output = [] while len(stack) > 0: (node, left, right) = stack.pop() if left > 0: output.append("("*left) output.append(str(node)+"\n") childCount = len(node.children) if childCount == 0: # a leaf output.append(")"*right+"\n") elif childCount == 1: # a line stack.append((node.children[0], 0, right)) else: # a branching node # first child pops first, last child closes parent's parentheses children = zip(node.children, [1]*childCount, [1]*(childCount-1)+[1+right]) stack.extend(reversed(list(children))) return "".join(output)