Files @ c95fa8ab1067
Branch filter:

Location: Diana/src/diana/sgfParser/propValues.py

Laman
changed imports
import re

from . import ParserError, skipWhitespace


class Regexp:
	number=re.compile(r"(\+|-|)\d+")
	real=re.compile(r"(\+|-|)\d+(\.\d+)?")
	point=re.compile(r"[a-zA-Z]{2}|")
	text=re.compile(r"(?:.*?[^\\])??(?:\\\\)*(?=])", re.DOTALL)
	composedText=re.compile(r"(?:.*?[^\\])??(?:\\\\)*(?=]|:)", re.DOTALL)

	class Text:
		softBreaks=re.compile(r"(^|[^\\])((\\\\)*)\\((\n\r)|(\r\n)|\r|\n)")
		whitespace=re.compile(r"[\t\f\v]")
		simpleWhitespace=re.compile(r"[\t\f\v\n\r]")
		removeSlashes=re.compile(r"(^|[^\\])((\\\\)*)\\($|[^\\])")
		unescapeSlashes=re.compile(r"\\\\")


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) and s[i]=="["


def singletonEnds(s,i):
	return i<len(s) and s[i]=="]"


def singleton(vType):
	def f(s,start):
		if not singletonFits(s,start):
			raise ParserError("expected a property value starting with '['",s,start)
		i,x=vType(s,start+1)
		if not singletonEnds(s,i):
			raise ParserError("expected a property value ending with ']'",s,i)
		i=skipWhitespace(s,i+1)
		return (i,x)
	return f


def listOf(vType,allowEmpty=False):
	def f(s,start):
		i=start
		if not singletonFits(s,i):
			raise ParserError("expected a property value starting with '['",s,i)
		if singletonEnds(s,i+1) and allowEmpty:
			i=skipWhitespace(s,i+2)
			return (i,[])
		single=singleton(vType)
		i,x=single(s,i)
		res=[x]
		while singletonFits(s,i):
			i,x=single(s,i)
			res.append(x)
		return (i,res)
	return f


def compose(vTypeA,vTypeB):
	def f(s,start):
		i,a=vTypeA(s,start)
		if 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):
	m=Regexp.number.match(s,start)
	if m is None: raise ParserError("expected a number matching '{0}'".format(Regexp.number.pattern),s,start)
	res=int(m.group(0))
	return (m.end(),res)


def real(s,start):
	m=Regexp.real.match(s,start)
	if m is None: raise ParserError("expected a real number matching '{0}'".format(Regexp.real.pattern),s,start)
	res=float(m.group(0))
	return (m.end(),res)


def double(s,start):
	c=s[start]
	if c not in ("1", "2"):
		raise ParserError("expected a double value, either '1' or '2'",s,start)
	return (start+1,c)


def color(s,start):
	c=s[start]
	if c not in ("B", "W"):
		raise ParserError("expected a color value, either 'B' or 'W'",s,start)
	return (start+1,c)


def text(simple=True,composed=False):
	def f(s,start):
		regexps=Regexp.Text
		m=Regexp.composedText.match(s,start) if composed else Regexp.text.match(s,start)
		res=m.group(0)
		res=regexps.softBreaks.sub(r"\1\2",res) # remove soft line breaks
		if simple:
			res=regexps.simpleWhitespace.sub(" ",res) # convert whitespace to spaces, no escapes
		else:
			res=regexps.whitespace.sub(" ",res) # convert whitespace to spaces, no escapes
		res=regexps.removeSlashes.sub(r"\1\2\4",res)
		res=regexps.unescapeSlashes.sub(r"\\",res) # unescape slashes

		return (m.end(),res)
	return f


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


def anything(s,start):
	esc=False
	i=start
	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):
	m=Regexp.point.match(s,start) # !! limit to board size
	if m is None: raise ParserError("expected a point value matching '{0}'".format(Regexp.point.pattern),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