diff --git a/src/sgfParser/property.py b/src/sgfParser/property.py new file mode 100644 --- /dev/null +++ b/src/sgfParser/property.py @@ -0,0 +1,331 @@ +import re + + +class Property: + GAME_INFO=1 + UNKNOWN=99 + + 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 Property.GAME_INFO + else: return Property.UNKNOWN + + +class PropValue: + def __init__(self): + 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 + + 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 +"""