Changeset - cfcff53c74e6
[Not reviewed]
default
0 3 0
Laman - 8 years ago 2017-01-22 01:08:22

little property refactoring
3 files changed with 149 insertions and 213 deletions:
0 comments (0 inline, 0 general)
src/sgfParser/node.py
Show inline comments
 
from . import skipWhitespace, ParserError
 
from .property import Property
 
from .property import Property, GAME_INFO
 

	
 

	
 
class Node:
 
@@ -27,7 +27,7 @@ class Node:
 
		return (i,res)
 

	
 
	def isGINode(self):
 
		return any(prop.type==Property.GAME_INFO for prop in self.properties.values())
 
		return any(prop.type==GAME_INFO for prop in self.properties.values())
 

	
 
	def setProperty(self,name,value):
 
		self.properties[name]=value
src/sgfParser/property.py
Show inline comments
 
import re
 

	
 

	
 
GAME_INFO=1
 
UNKNOWN=99
 

	
 

	
 
class Property:
 
	GAME_INFO=1
 
	UNKNOWN=99
 

	
 
	def __init__(self):
 
		self.name=""
 
		self.value=""
 
@@ -33,8 +34,136 @@ class Property:
 
	@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 Property.GAME_INFO
 
		else: return Property.UNKNOWN
 
		if self.name in gameInfo: return GAME_INFO
 
		else: return UNKNOWN
 

	
 

	
 
def choose(*vTypes):
 
	def f(s,start):
 
		for vType in vTypes:
 
			i,x=vType(s,start)
 
			if x is not None: return (i,x)
 
		return (start,None)
 
	return f
 

	
 

	
 
def singleton(vType):
 
	def f(s,start):
 
		if s[start]!="[":
 
			return (start,None)
 
		i,x=vType(s,start+1)
 
		if x is None: return (start,None)
 
		if s[i]!="]":
 
			return (start,None)
 
		return (i+1,x)
 
	return f
 

	
 

	
 
def listOf(vType,allowEmpty=False):
 
	def f(s,start):
 
		res=[]
 
		single=singleton(vType)
 
		i,x=single(s,start)
 
		while x!=None:
 
			res.append(x)
 
			i,x=single(s,i)
 
		if len(res)==0 and not allowEmpty: return (start,None)
 
		return (i,res)
 
	return f
 

	
 

	
 
def compose(vTypeA,vTypeB):
 
	def f(s,start):
 
		i,a=vTypeA(s,start)
 
		if a==None or s[i]!=":": return (start,None)
 
		i,b=vTypeB(s,i+1)
 
		if b==None: return start,None
 
		return (i,(a,b))
 
	return f
 

	
 

	
 
def number(s,start):
 
	r=re.compile(r"(\+|-|)\d+")
 
	m=r.match(s,start)
 
	if m is None: return (start,None)
 
	res=int(m.group(0))
 
	return (m.end(),res)
 

	
 

	
 
def real(s,start):
 
	r=re.compile(r"(\+|-|)\d+(\.\d+)?")
 
	m=r.match(s,start)
 
	if m is None: return (start,None)
 
	res=float(m.group(0))
 
	return (m.end(),res)
 

	
 

	
 
def double(s,start):
 
	r=re.compile(r"1|2")
 
	m=r.match(s,start)
 
	if m is None: return (start,None)
 
	res=int(m.group(0))
 
	return (m.end(),res)
 

	
 

	
 
def color(s,start):
 
	r=re.compile(r"B|W")
 
	m=r.match(s,start)
 
	if m is None: return (start,None)
 
	return (m.end(),m.group(0))
 

	
 

	
 
def text(simple=True,composed=False):
 
	def f(s,start):
 
		res=""
 
		esc=False
 
		lastC=""
 
		for i,c in enumerate(s[start:],start):
 
			if esc:
 
				if c!="\n" and c!="\r": res+=c
 
				esc=False
 
			elif (c=="\n" and lastC=="\r") or (c=="\r" and lastC=="\n"): pass
 
			elif c=="\r" or c=="\n" and not simple:
 
				res+="\n"
 
			elif c.isspace():
 
				res+=" "
 
			elif c=="\\":
 
				esc=True
 
			elif c=="]" or (c==":" and composed):
 
				break
 
			else:
 
				res+=c
 
			lastC=c
 
		return (i,res)
 
	return f
 

	
 

	
 
def empty(s,start): return (start,"")
 

	
 

	
 
def anything(s,start):
 
	esc=False
 
	for i,c in enumerate(s[start:],start):
 
		if esc: esc=False
 
		elif c=="\\": esc=True
 
		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
 
	m=r.match(s,start)
 
	if m is None: return (start,None)
 
	if m.group(0)=="": # pass, !! tt
 
		return (m.end(),tuple())
 
	col=m.group(0)[0]
 
	row=m.group(0)[1]
 
	col=ord(col) - (ord("a") if "a"<=col<="z" else ord("A")-26)
 
	row=ord(row) - (ord("a") if "a"<=row<="z" else ord("A")-26)
 
	return (m.end(),(col,row))
 

	
 
move=point
 
stone=point
 

	
 

	
 
class PropValue:
 
@@ -42,134 +171,13 @@ class PropValue:
 
		self.type=""
 
		self.value=None
 

	
 
	patterns=dict()
 

	
 
	@staticmethod
 
	def create(s,start,name):
 
		if name in PropValue.patterns:
 
			return PropValue.patterns[name](s,start)
 
		else:
 
			print('warning, unknown property "{0}" at position {1}'.format(name,start))
 
			return PropValue.singleton(PropValue.anything)(s,start)
 

	
 
	def choose(*vTypes):
 
		def f(s,start):
 
			for vType in vTypes:
 
				i,x=vType(s,start)
 
				if x is not None: return (i,x)
 
			return (start,None)
 
		return f
 

	
 
	def singleton(vType):
 
		def f(s,start):
 
			if s[start]!="[":
 
				return (start,None)
 
			i,x=vType(s,start+1)
 
			if x is None: return (start,None)
 
			if s[i]!="]":
 
				return (start,None)
 
			return (i+1,x)
 
		return f
 

	
 
	def listOf(vType,allowEmpty=False):
 
		def f(s,start):
 
			res=[]
 
			single=PropValue.singleton(vType)
 
			i,x=single(s,start)
 
			while x!=None:
 
				res.append(x)
 
				i,x=single(s,i)
 
			if len(res)==0 and not allowEmpty: return (start,None)
 
			return (i,res)
 
		return f
 

	
 
	def compose(vTypeA,vTypeB):
 
		def f(s,start):
 
			i,a=vTypeA(s,start)
 
			# print(">",i,a)
 
			if a==None or s[i]!=":": return (start,None)
 
			i,b=vTypeB(s,i+1)
 
			# print(">",i,b)
 
			if b==None: return start,None
 
			return (i,(a,b))
 
		return f
 

	
 
	def number(s,start):
 
		r=re.compile(r"(\+|-|)\d+")
 
		m=r.match(s,start)
 
		if m is None: return (start,None)
 
		res=int(m.group(0))
 
		return (m.end(),res)
 

	
 
	def real(s,start):
 
		r=re.compile(r"(\+|-|)\d+(\.\d+)?")
 
		m=r.match(s,start)
 
		if m is None: return (start,None)
 
		res=float(m.group(0))
 
		return (m.end(),res)
 

	
 
	def double(s,start):
 
		r=re.compile(r"1|2")
 
		m=r.match(s,start)
 
		if m is None: return (start,None)
 
		res=int(m.group(0))
 
		return (m.end(),res)
 

	
 
	def color(s,start):
 
		r=re.compile(r"B|W")
 
		m=r.match(s,start)
 
		if m is None: return (start,None)
 
		return (m.end(),m.group(0))
 

	
 
	def text(simple=True,composed=False):
 
		def f(s,start):
 
			res=""
 
			esc=False
 
			lastC=""
 
			for i,c in enumerate(s[start:],start):
 
				if esc:
 
					if c!="\n" and c!="\r": res+=c
 
					esc=False
 
				elif (c=="\n" and lastC=="\r") or (c=="\r" and lastC=="\n"): pass
 
				elif c=="\r" or c=="\n" and not simple:
 
					res+="\n"
 
				elif c.isspace():
 
					res+=" "
 
				elif c=="\\":
 
					esc=True
 
				elif c=="]" or (c==":" and composed):
 
					break
 
				else:
 
					res+=c
 
				lastC=c
 
			return (i,res)
 
		return f
 

	
 
	def empty(s,start): return (start,"")
 

	
 
	def anything(s,start):
 
		esc=False
 
		for i,c in enumerate(s[start:],start):
 
			if esc: esc=False
 
			elif c=="\\": esc=True
 
			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
 
		m=r.match(s,start)
 
		if m is None: return (start,None)
 
		if m.group(0)=="": # pass, !! tt
 
			return (m.end(),tuple())
 
		col=m.group(0)[0]
 
		row=m.group(0)[1]
 
		col=ord(col) - (ord("a") if "a"<=col<="z" else ord("A")-26)
 
		row=ord(row) - (ord("a") if "a"<=row<="z" else ord("A")-26)
 
		return (m.end(),(col,row))
 

	
 
	move=point
 
	stone=point
 
			return singleton(anything)(s,start)
 

	
 
	patterns={
 
		"B":singleton(move),
 
@@ -243,89 +251,6 @@ class PropValue:
 
		"TW":listOf(point,allowEmpty=True)
 
	}
 

	
 

	
 
# TODO:
 
# date
 

	
 

	
 
"""
 
# move
 
B	move
 
KO	none
 
MN	number
 
W	move
 

	
 
# setup
 
AB	list of stone
 
AE	list of point
 
AW	list of stone
 
PL	color
 

	
 
# node annotation
 
C	text
 
DM	double
 
GB	double
 
GW	double
 
HO	double
 
N	simpleText
 
UC	double
 
V	real
 

	
 
# move annotation
 
BM	double
 
DO	none
 
IT	none
 
TE	double
 

	
 
# markup
 
AR	list of composed point:point
 
CR	list of point
 
DD	elist of point
 
LB	list of composed point:simpleText
 
LN	list of composed point:point
 
MA	list of point
 
SL	list of point
 
SQ	list of point
 
TR	list of point
 

	
 
# root
 
AP	composed simpleText:simpleText
 
CA	simpleText
 
FF	number
 
GM	number
 
ST	number
 
SZ	number | composed number:number
 

	
 
# game info
 
AN	simpleText
 
BR	simpleText
 
BT	simpleText
 
CP	simpleText
 
DT	simpleText
 
EV	simpleText
 
GN	simpleText
 
GC	text
 
ON	simpleText
 
OT	simpleText
 
PB	simpleText
 
PC	simpleText
 
PW	simpleText
 
RE	simpleText
 
RO	simpleText
 
RU	simpleText
 
SO	simpleText
 
TM	real
 
US	simpleText
 
WR	simpleText
 
WT	simpleText
 

	
 
# timing
 
BL	real
 
OB	number
 
OW	number
 
WL	real
 

	
 
# misc
 
FG	none | composition of number:simpleText
 
PM	number
 
VW	elist of point
 
"""
src/tests/testSgfParser.py
Show inline comments
 
@@ -3,11 +3,22 @@ from unittest import TestCase
 
import os
 

	
 
from sgfParser.collection import Collection
 
from sgfParser.property import Property
 

	
 

	
 
dataDir=os.path.join(os.path.dirname(__file__), "data")
 

	
 

	
 
class TestProperty(TestCase):
 
	def testName(self):
 
		self.assertEqual(Property.create("[99]",0), (0,None))
 
		self.assertEqual(Property.create("99[99]",0), (0,None))
 

	
 
		i,prop=Property.create("MN[99]",0)
 
		self.assertNotEqual((i,prop), (0,None))
 
		self.assertEqual((i,prop.name), (6,"MN"))
 

	
 

	
 
class TestCollection(TestCase):
 
	def testEmptySgf(self):
 
		Collection("(;)")
0 comments (0 inline, 0 general)