Files @ 966ee650fabf
Branch filter:

Location: Diana/src/diana/sgfparser/property.py - annotation

Laman
updated comments
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
afabea7d0e61
import re
from datetime import date
import logging as log

from .prop_values import choose, singleton, list_of, compose, number, real, double, color, text, empty, anything, point, move, stone
from . import skip_whitespace, ParserError

GAME_INFO = 1
UNKNOWN = 99


class DateException(Exception):
	pass


class Property:
	ident_regexp = re.compile(r"[A-Z]+")

	def __init__(self):
		self.name = ""
		self.value = ""

	@staticmethod
	def fits(s, i):
		return i < len(s) and s[i].isupper()

	@staticmethod
	def create(s, start):
		assert Property.fits(s, start)
		res = Property()
		(i, res.name) = Property.ident(s, start)
		i = skip_whitespace(s, i)
		try:
			(i, x) = Property.create_value(s, i, res.name)
		except ParserError as e:  # a malformed value
			log.warning(e)
			(i, x) = choose(list_of(anything), singleton(anything))(s, i)
			res.name = "_"+res.name
		res.value = x
		if res.name == "DT":
			res = DateProperty(x)
		i = skip_whitespace(s, i)
		return (i, res)

	@staticmethod
	def ident(s, start):
		m = Property.ident_regexp.match(s, start)
		if m is None:
			raise ParserError("expected a property identifier matching '[A-Z]+'", s, start)
		return (m.end(), m.group())

	@staticmethod
	def create_value(s, start, name):
		if name in Property.patterns:
			return Property.patterns[name](s, start)
		else:
			log.info("unknown property %s at position %d", name, start)
			return choose(list_of(anything), singleton(anything))(s, start)

	@property
	def type(self):
		game_info = {"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 game_info:
			return GAME_INFO
		else:
			return UNKNOWN

	def copy(self):
		res = Property()
		res.name = self.name
		res.value = self.value if not isinstance(self.value, list) else self.value[:]
		return res

	def __str__(self):
		name = self.name.lstrip("_")
		val = "[{0}]".format(self.value) \
			if not isinstance(self.value, list) \
			else "".join("[{0}]".format(x) for x in self.value)
		return "{0}{1}".format(name, val)

	patterns = {
		"B": singleton(move),
		"KO": singleton(empty),
		"MN": singleton(number),
		"W": singleton(move),
		"AB": list_of(stone),  #
		"AE": list_of(point),  #
		"AW": list_of(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": list_of(compose(point, point)),  #
		"CR": list_of(point),  #
		"DD": list_of(point, allow_empty=True),  #
		"LB": list_of(compose(point, text())),  #
		"LN": list_of(compose(point, point)),  #
		"MA": list_of(point),  #
		"SL": list_of(point),  #
		"SQ": list_of(point),  #
		"TR": list_of(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": list_of(point, allow_empty=True),  #

		# go specific
		"HA": singleton(number),
		"KM": singleton(real),
		"TB": list_of(point, allow_empty=True),
		"TW": list_of(point, allow_empty=True)
	}


class DateProperty(Property):
	def __init__(self, value):
		super().__init__()
		self.name = "DT"
		self.value = []
		self.raw_value = value
		self.parse(value)

	def parse(self, s):
		regexp = re.compile(r"\d{4}(-\d\d){0,2}(,(\d{4}(-\d\d){0,2}|\d\d(-\d\d)?))*")
		match = re.search(regexp, s)
		if not match:
			raise DateException('Could not parse a DT value: "{0}"'.format(s))
		substr = match.group(0)
		date_strs = substr.split(",")
		dates = []
		prev_format = None

		for s in date_strs:
			try:
				(prev_format, d) = DateProperty.parse_single(s, prev_format, dates[-1] if dates else None)
			except ValueError:
				raise DateException('Could not parse a DT value: "{0}"'.format(s))
			dates.append(d)
		self.value = dates

	@staticmethod
	def parse_single(date_str, prev_format, prev=None):
		tokens = date_str.split("-")
		num_tokens = list(map(int, tokens))
		if len(tokens) == 3:
			return ("YMD", date(*num_tokens))
		elif len(tokens) == 2:
			if len(tokens[0]) == 4:
				return ("YM", date(*num_tokens, 1))
			else:
				return ("MD", date(prev.year, *num_tokens))
		else:
			if len(tokens[0]) == 4:
				return ("Y", date(*num_tokens, 1, 1))
			elif prev_format in ("YM", "M"):
				return ("M", date(prev.year, *num_tokens, 1))
			else:
				return ("D", date(prev.year, prev.month, *num_tokens))