diff --git a/src/sgfParser/propValues.py b/src/sgfParser/propValues.py new file mode 100644 --- /dev/null +++ b/src/sgfParser/propValues.py @@ -0,0 +1,175 @@ +import re + +from . import ParserError, skipWhitespace + + +class Composed: + def __init__(self,a=None,b=None): + self.a=a + self.b=b + + def __str__(self): + return "{0}:{1}".format(self.a,self.b) + + +class Point: + def __init__(self,c,r): + self.r=r + self.c=c + + def __iter__(self): + yield self.c + yield self.r + + def __str__(self): + a=ord("a") + return chr(a+self.c)+chr(a+self.r) + + +## Metatype matching one of the provided types. +# +# Returns the first match, so the order is important. +def choose(*vTypes): + def f(s,start): + for vType in vTypes: + try: + i,x=vType(s,start) + return (i,x) + except ParserError: pass + raise ParserError("no variant of a 'choose' property value matched",s,start) + return f + + +def singletonFits(s,i): + return i=len(s) or s[i]!=":": + raise ParserError("expected a composed property value separated by ':'",s,i) + i,b=vTypeB(s,i+1) + return (i,Composed(a,b)) + return f + + +def number(s,start): + r=re.compile(r"(\+|-|)\d+") + m=r.match(s,start) + if m is None: raise ParserError("expected a number matching '(\+|-|)\d+'",s,start) + 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: raise ParserError("expected a real number matching '(\+|-|)\d+(\.\d+)?'",s,start) + 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: raise ParserError("expected a double value, either '1' or '2'",s,start) + 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: raise ParserError("expected a color value, either 'B' or 'W'",s,start) + return (m.end(),m.group(0)) + + +def text(simple=True,composed=False): + def f(s,start): + res="" + esc=False + lastC="" + i=start + 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: raise ParserError("expected a point value matching '[a-zA-Z]{2}|'",s,start) + 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(),Point(col,row)) + + +move=point +stone=point