# HG changeset patch
# User Laman
# Date 2017-01-22 22:40:14
# Node ID 2ed7f0dab5efdcf0255854535d9a9fc0ee145662
# Parent  cfcff53c74e67597eb294de0ae2dcff5806f127a

expanding compressed game trees

diff --git a/src/sgfParser/collection.py b/src/sgfParser/collection.py
--- a/src/sgfParser/collection.py
+++ b/src/sgfParser/collection.py
@@ -30,7 +30,7 @@ class GameTree:
 		if i>=len(s) or s[i]!="(":
 			# print("error when parsing GameTree")
 			return (start,None)
-		i,x=Node.create(s,start+1)
+		i,x=Node.create(s,i+1)
 		if x is None:
 			# print("error when parsing GameTree")
 			return (i,None)
@@ -67,14 +67,19 @@ class GameTree:
 			return self.nodes[i]
 		return None
 
-	## Create and return a new GameTree containing the provided Node.
+	## Create and return a new game tree containing the provided Node.
 	#
-	# The Node objects are shared between the trees, not copied.
-	def _buildSubtree(self,node):
-		res=GameTree()
+	# Ancestor nodes are copied, descendants are taken directly.
+	def _buildSubtree(self,seedNode):
+		node=seedNode.copy()
 
-		return res
-		# chci celou cestu ke kořeni a všechny podstromy nodu
+		while node.parent:
+			newNode=node.parent.copy()
+			node.parent=newNode
+			newNode.setChildren([node])
+			node=newNode
+
+		return node
 
 	## Find and yield Game Info nodes.
 	def _listGINodes(self):
diff --git a/src/sgfParser/node.py b/src/sgfParser/node.py
--- a/src/sgfParser/node.py
+++ b/src/sgfParser/node.py
@@ -5,8 +5,8 @@ from .property import Property, GAME_INF
 class Node:
 	def __init__(self):
 		self.properties=dict()
-		self._parent=None
-		self._children=[]
+		self.parent=None
+		self.children=[]
 
 	@staticmethod
 	def create(s,start):
@@ -34,26 +34,37 @@ class Node:
 		# zkontrolovat typ value
 
 	def setParent(self,node):
-		self._parent=node
+		self.parent=node
+
+	def setChildren(self,children):
+		self.children=children
 
 	def addChild(self,node):
-		if node in self._children: return node
-		self._children.append(node)
+		if node in self.children: return node
+		self.children.append(node)
 		return node
 
 	def removeChild(self,node):
-		if node not in self._children:
+		if node not in self.children:
 			return None
-		del self._children[self._children.index(node)]
+		del self.children[self.children.index(node)]
 		return node
 
 	def removeChildAt(self,i):
-		if -len(self._children)<i<len(self._children):
-			res=self._children[i]
-			del self._children[i]
+		if -len(self.children)<i<len(self.children):
+			res=self.children[i]
+			del self.children[i]
 			return res
 		return None
 
+	## Create a copy of the Node, with deep copied propeties and shallow copied parent and children.
+	def copy(self):
+		res=Node()
+		res.properties={k: v.copy() for (k,v) in self.properties.items()}
+		res.parent=self.parent
+		res.setChildren(self.children[:])
+		return res
+
 	def getProperty(self,name):
 		if name in self.properties: return self.properties[name]
 		else: return None
diff --git a/src/sgfParser/property.py b/src/sgfParser/property.py
--- a/src/sgfParser/property.py
+++ b/src/sgfParser/property.py
@@ -5,39 +5,6 @@ GAME_INFO=1
 UNKNOWN=99
 
 
-class Property:
-	def __init__(self):
-		self.name=""
-		self.value=""
-
-	@staticmethod
-	def create(s,start):
-		res=Property()
-		i,x=Property.ident(s,start)
-		if x is None:
-			return (start,None)
-		res.name=x
-		i,x=PropValue.create(s,i,res.name)
-		if x is None:
-			print('error when parsing property "{0}" at position {1}'.format(res.name,i))
-			return (start,None)
-		res.value=x
-		return (i,res)
-
-	@staticmethod
-	def ident(s,start):
-		r=re.compile(r"[A-Z]+")
-		m=r.match(s,start)
-		if m is None: return (start,None)
-		return (m.end(),m.group())
-
-	@property
-	def type(self):
-		gameInfo={"AN","BR","BT","CP","DT","EV","GN","GC","ON","OT","PB","PC","PW","RE","RO","RU","SO","TM","US","WR","WT"}
-		if self.name in gameInfo: return GAME_INFO
-		else: return UNKNOWN
-
-
 def choose(*vTypes):
 	def f(s,start):
 		for vType in vTypes:
@@ -149,6 +116,7 @@ def anything(s,start):
 		elif c=="]": break
 	return (i,s[start:i])
 
+
 # go specific
 def point(s,start):
 	r=re.compile(r"[a-zA-Z]{2}|") # !! limit to board size
@@ -166,19 +134,53 @@ move=point
 stone=point
 
 
-class PropValue:
+class Property:
 	def __init__(self):
-		self.type=""
-		self.value=None
+		self.name=""
+		self.value=""
 
 	@staticmethod
-	def create(s,start,name):
-		if name in PropValue.patterns:
-			return PropValue.patterns[name](s,start)
+	def create(s,start):
+		res=Property()
+		i,x=Property.ident(s,start)
+		if x is None:
+			return (start,None)
+		res.name=x
+		i,x=Property.createValue(s,i,res.name)
+		if x is None:
+			print('error when parsing property "{0}" at position {1}'.format(res.name,i))
+			return (start,None)
+		res.value=x
+		return (i,res)
+
+	@staticmethod
+	def ident(s,start):
+		r=re.compile(r"[A-Z]+")
+		m=r.match(s,start)
+		if m is None: return (start,None)
+		return (m.end(),m.group())
+
+	@staticmethod
+	def createValue(s,start,name):
+		if name in Property.patterns:
+			return Property.patterns[name](s,start)
 		else:
 			print('warning, unknown property "{0}" at position {1}'.format(name,start))
 			return singleton(anything)(s,start)
 
+	@property
+	def type(self):
+		gameInfo={"AN","BR","BT","CP","DT","EV","GN","GC","ON","OT","PB","PC","PW","RE","RO","RU","SO","TM","US","WR","WT"}
+		if self.name in gameInfo: return GAME_INFO
+		else: return UNKNOWN
+
+	def copy(self):
+		res=Property()
+		res.name=self.name
+		# !! wouldn't work for listOf(listOf())
+		res.value=self.value if not isinstance(self.value,list) else self.value[:]
+		return res
+
 	patterns={
 		"B":singleton(move),
 		"KO":singleton(empty),
@@ -252,5 +254,4 @@ class PropValue:
 	}
 
 
-# TODO:
-# date
+# !! TODO: date
diff --git a/src/tests/testSgfParser.py b/src/tests/testSgfParser.py
--- a/src/tests/testSgfParser.py
+++ b/src/tests/testSgfParser.py
@@ -20,6 +20,16 @@ class TestProperty(TestCase):
 
 
 class TestCollection(TestCase):
+	def testSubtrees(self):
+		c=Collection("""
+(;B[aa]
+	(;W[ab]PB[Some Black]PW[Some White];B[ac])
+	(;W[bb]PB[Other Black]PW[Other White])
+)""")
+		games=list(c.listGames())
+
+		self.assertEqual(len(games),2)
+
 	def testEmptySgf(self):
 		Collection("(;)")