"""Definition of the ParserSyntaxError raised on parse failure"""
import string
from simpleparse.stt.TextTools.TextTools import countlines

class ParserSyntaxError( SyntaxError ):
	"""Sub-class of SyntaxError for use by SimpleParse parsers

	Every instance will have the following attributes:
		buffer -- pointer to the source buffer
		position -- integer position in buffer where error occured or -1
		production -- the production which failed
		expected -- string (currently taken from grammar) describing
			what production/element token failed to match
	the following will be calculated in order to display
	human-friendly error messages:
		line -- ~ text line-number or -1
		lineChar -- ~ character on line where parsing failed or -1
	
	"""
	buffer = ""
	position = -1
	line = -1
	production = ""
	expected = ""
	error_message = None
	DEFAULTTEMPLATE = """Failed parsing production "%(production)s" @pos %(position)s (~line %(line)s:%(lineChar)s).\nExpected syntax: %(expected)s\nGot text: %(text)s"""
	def __str__( self ):
		"""Create a string representation of the error"""
		if self.error_message:
			return '%s: %s'%( self.__class__.__name__, self.messageFormat(self.error_message) )
		else:
			return '%s: %s'%( self.__class__.__name__, self.messageFormat() )
	def messageFormat( self, template=None):
		"""Create a default message for this syntax error"""
		if template is None:
			template = self.DEFAULTTEMPLATE
		line, lineChar = self.getLineCoordinate()
		variables = {
			"production": self.production,
			"position": self.position,
			"line": line,
			"lineChar": lineChar,
			"expected": self.expected or "UNKNOWN",
			"text": repr(self.buffer[ self.position:self.position+50 ]),
		}
		return template % variables
	def getLineCoordinate( self ):
		"""Get (line number, line character) for the error"""
		lineChar = string.rfind( self.buffer, '\n', 0, self.position)
		if lineChar == -1: # was no \n before the current position
			lineChar = self.position
			line = 1
		else:
			line = countlines( self.buffer[:lineChar] )
			lineChar = self.position-lineChar
		return line, lineChar
