Files @ 686166c7d5bc
Branch filter:

Location: Diana/src/diana/sgfParser/node.py

Laman
added more whitespace
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)