# HG changeset patch # User Laman # Date 2017-01-29 18:48:06 # Node ID 7a91946966484554cb43e31c29d8195bc7ed0e1c # Parent c6e20613189df94f2520408adcb242f35a7a6bdc split propValues parsing into a separate module 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 diff --git a/src/sgfParser/property.py b/src/sgfParser/property.py --- a/src/sgfParser/property.py +++ b/src/sgfParser/property.py @@ -1,182 +1,12 @@ import re + +from .propValues import choose, singleton, listOf, compose, number, real, double, color, text, empty, anything, point, move, stone from . import skipWhitespace, ParserError - GAME_INFO=1 UNKNOWN=99 -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 - - class Property: identRegexp=re.compile(r"[A-Z]+") @@ -212,7 +42,7 @@ class Property: else: # !! raise or log or ignore # print('warning, unknown property "{0}" at position {1}'.format(name,start)) - return choose(listOf(anything)(s,start), singleton(anything)(s,start)) + return choose(listOf(anything)(s, start), singleton(anything)(s, start)) @property def type(self):