# HG changeset patch # User Laman # Date 2017-01-21 21:39:14 # Node ID b66f5379b832147ac26676430ebbb754af6361d5 # Parent 5781a5a0b2fb315b7b0050a12edd024734581ba2 split SgfParser into multiple files diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 diff --git a/src/sgfParser/__init__.py b/src/sgfParser/__init__.py new file mode 100644 --- /dev/null +++ b/src/sgfParser/__init__.py @@ -0,0 +1,11 @@ +def skipWhitespace(s,start): + i=start + while i",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 - - patterns={ - "B":singleton(move), - "KO":singleton(empty), - "MN":singleton(number), - "W":singleton(move), - "AB":listOf(stone), # - "AE":listOf(point), # - "AW":listOf(stone), # - "PL":singleton(color), - "C":singleton(text(simple=False)), - "DM":singleton(double), - "GB":singleton(double), - "GW":singleton(double), - "HO":singleton(double), - "N":singleton(text()), - "UC":singleton(double), - "V":singleton(real), - "BM":singleton(double), - "DO":singleton(empty), - "IT":singleton(empty), - "TE":singleton(double), - "AR":listOf(compose(point,point)), # - "CR":listOf(point), # - "DD":listOf(point,allowEmpty=True), # - "LB":listOf(compose(point,text())), # - "LN":listOf(compose(point,point)), # - "MA":listOf(point), # - "SL":listOf(point), # - "SQ":listOf(point), # - "TR":listOf(point), # - "AP":singleton(compose(text(composed=True),text())), # - "CA":singleton(text()), - "FF":singleton(number), - "GM":singleton(number), - "ST":singleton(number), - "SZ":choose(singleton(number),singleton(compose(number,number))), # - "AN":singleton(text()), - "BR":singleton(text()), - "BT":singleton(text()), - "CP":singleton(text()), - "DT":singleton(text()), - "EV":singleton(text()), - "GN":singleton(text()), - "GC":singleton(text(simple=False)), - "ON":singleton(text()), - "OT":singleton(text()), - "PB":singleton(text()), - "PC":singleton(text()), - "PW":singleton(text()), - "RE":singleton(text()), - "RO":singleton(text()), - "RU":singleton(text()), - "SO":singleton(text()), - "TM":singleton(real), - "US":singleton(text()), - "WR":singleton(text()), - "WT":singleton(text()), - "BL":singleton(real), - "OB":singleton(number), - "OW":singleton(number), - "WL":singleton(real), - "FG":choose(singleton(empty),singleton(compose(number,text()))), # - "PM":singleton(number), - "VW":listOf(point,allowEmpty=True), # - - # go specific - "HA":singleton(number), - "KM":singleton(real), - "TB":listOf(point,allowEmpty=True), - "TW":listOf(point,allowEmpty=True) - } - -class ParserError(Exception): - def __init__(self,line,col,message): - self.line=line - self.col=col - self.message=message - -# 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 -""" \ No newline at end of file diff --git a/src/sgfParser/node.py b/src/sgfParser/node.py new file mode 100644 --- /dev/null +++ b/src/sgfParser/node.py @@ -0,0 +1,59 @@ +from . import skipWhitespace, ParserError +from .property import Property + + +class Node: + def __init__(self): + self.properties=dict() + self._parent=None + self._children=[] + + @staticmethod + def create(s,start): + res=Node() + if s[start]!=";": + # print("error when parsing Node") + return (start,None) + i=skipWhitespace(s,start+1) + i,x=Property.create(s,start+1) + while x is not None: + if x.name in res.properties: + print(res.properties) + raise ParserError(0,0,'duplicate "{0}" property in node at position {1}. second value ignored'.format(x.name,start)) + else: + res.properties[x.name]=x + i=skipWhitespace(s,i) + i,x=Property.create(s,i) + return (i,res) + + def isGINode(self): + return any(prop.type==Property.GAME_INFO for prop in self.properties.values()) + + def setProperty(self,name,value): + self.properties[name]=value + # zkontrolovat typ value + + def setParent(self,node): + self._parent=node + + def addChild(self,node): + if node in self._children: return node + 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)] + return node + + def removeChildAt(self,i): + if -len(self._children)",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 + + patterns={ + "B":singleton(move), + "KO":singleton(empty), + "MN":singleton(number), + "W":singleton(move), + "AB":listOf(stone), # + "AE":listOf(point), # + "AW":listOf(stone), # + "PL":singleton(color), + "C":singleton(text(simple=False)), + "DM":singleton(double), + "GB":singleton(double), + "GW":singleton(double), + "HO":singleton(double), + "N":singleton(text()), + "UC":singleton(double), + "V":singleton(real), + "BM":singleton(double), + "DO":singleton(empty), + "IT":singleton(empty), + "TE":singleton(double), + "AR":listOf(compose(point,point)), # + "CR":listOf(point), # + "DD":listOf(point,allowEmpty=True), # + "LB":listOf(compose(point,text())), # + "LN":listOf(compose(point,point)), # + "MA":listOf(point), # + "SL":listOf(point), # + "SQ":listOf(point), # + "TR":listOf(point), # + "AP":singleton(compose(text(composed=True),text())), # + "CA":singleton(text()), + "FF":singleton(number), + "GM":singleton(number), + "ST":singleton(number), + "SZ":choose(singleton(number),singleton(compose(number,number))), # + "AN":singleton(text()), + "BR":singleton(text()), + "BT":singleton(text()), + "CP":singleton(text()), + "DT":singleton(text()), + "EV":singleton(text()), + "GN":singleton(text()), + "GC":singleton(text(simple=False)), + "ON":singleton(text()), + "OT":singleton(text()), + "PB":singleton(text()), + "PC":singleton(text()), + "PW":singleton(text()), + "RE":singleton(text()), + "RO":singleton(text()), + "RU":singleton(text()), + "SO":singleton(text()), + "TM":singleton(real), + "US":singleton(text()), + "WR":singleton(text()), + "WT":singleton(text()), + "BL":singleton(real), + "OB":singleton(number), + "OW":singleton(number), + "WL":singleton(real), + "FG":choose(singleton(empty),singleton(compose(number,text()))), # + "PM":singleton(number), + "VW":listOf(point,allowEmpty=True), # + + # go specific + "HA":singleton(number), + "KM":singleton(real), + "TB":listOf(point,allowEmpty=True), + "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 +""" diff --git a/src/tests/testSgfParser.py b/src/tests/testSgfParser.py --- a/src/tests/testSgfParser.py +++ b/src/tests/testSgfParser.py @@ -2,11 +2,12 @@ import unittest from unittest import TestCase import os -from sgfParser import Collection +from sgfParser.collection import Collection dataDir=os.path.join(os.path.dirname(__file__), "data") + class TestCollection(TestCase): def testEmptySgf(self): Collection("(;)")