
"""BraceConverter.py

	Contributed 2000-09-04 by Dave Wallace (dwallace@delanet.com)

	Converts Brace-blocked Python into normal indented Python.
	Brace-blocked Python is non-indentation aware and blocks are delimited by ':{' and '}' pairs.

	Thus:
		for x in range(10) :{
			if x%2 :{ print x } else :{ print z }
		}

	Becomes (roughly, barring some spurious newlines):
		for x in range(10) :
			if x%2 :
				print x
			else :
				print z

	This implementation is fed a line at a time via parseLine(), outputs to
	a PSPServletWriter, and tracks the current quotation and block levels internally.

"""

import re
import sys


class BraceConverter:

	CSKIP = re.compile("(^[^\"'{}:#]+)")
	COLONBRACE = re.compile(":\s*{\s*([^\s].*)?$")

	def __init__(self):
		self.inquote = 0
		self.dictlevel = 0

	def parseLine(self,line,writer):
		"""Parse a line.

		The only public method of this class, call with subsequent lines
		and an instance of PSPServletWriter.

		"""
		self.line = line
		if self.inquote and self.line:
			self.skipquote(writer)
		self.line = self.line.lstrip()
		if not self.line:
			writer.printChars('\n')
			return
		writer.printIndent()
		while self.line:
			while self.inquote and self.line:
				self.skipquote(writer)
			match = self.CSKIP.search(self.line)
			if match:
				writer.printChars(self.line[:match.end(1)])
				self.line = self.line[match.end(1):]
			else:
				ch = self.line[0]
				if ch == "'":
					self.handleQuote("'",writer)
					self.skipquote(writer)
				elif ch == '"':
					self.handleQuote('"',writer)
					self.skipquote(writer)
				elif ch == '{':
					self.openBrace(writer)
				elif ch == '}':
					self.closeBrace(writer)
				elif ch == ':':
					self.openBlock(writer)
				elif ch == '#':
					writer.printChars(self.line)
					self.line = ""
				else:
					# should never get here
					raise Exception()
		else:
			writer.printChars('\n')

	def openBlock(self,writer):
		"""Open a new block."""
		match = self.COLONBRACE.match(self.line)
		if match and not self.dictlevel:
			writer.printChars(":")
			writer.pushIndent()
			if match.group(1):
				# text follows :{, if its a comment leave it on the same line
				# else start a new line and leave the text for processing
				if match.group(1)[0] == '#':
					writer.printChars(" " + match.group(1))
					self.line = ""
				else:
					writer.printChars('\n')
					writer.printIndent()
					self.line = match.group(1)
			else:
				self.line = ""
		else:
			writer.printChars(":")
			self.line = self.line[1:]

	def openBrace(self,writer):
		"""Open brace encountered."""
		writer.printChars("{")
		self.line = self.line[1:]
		self.dictlevel += 1

	def closeBrace(self,writer):
		"""Close brace encountered."""
		if self.dictlevel:
			writer.printChars("}")
			self.line = self.line[1:]
			self.dictlevel -= 1
		else:
			writer.popIndent()
			self.line = self.line[1:].lstrip()
			if (self.line):
				writer.printChars('\n')
				writer.printIndent()

	def skipquote(self,writer):
		"""Skip to end of quote.

		Skip over all chars until the line is exhausted
		or the current non-escaped quote sequence is encountered.

		"""
		pos = self.line.find(self.quotechars)
		if pos == -1:
			writer.printChars(self.line)
			self.line = ""
		elif (pos > 0) and self.line[pos-1] == '\\':
			pos += 1
			writer.printChars(self.line[:pos])
			self.line = self.line[pos:]
			self.skipquote(writer)
		else:
			pos += len(self.quotechars)
			writer.printChars(self.line[:pos])
			self.line = self.line[pos:]
			self.inquote = 0

	def handleQuote(self,quote,writer):
		"Check and handle if current pos is a single or triple quote."""
		self.inquote = 1
		triple = quote*3
		if self.line[0:3] == triple:
			self.quotechars = triple
			writer.printChars(triple)
			self.line = self.line[3:]
		else:
			self.quotechars = quote
			writer.printChars(quote)
			self.line = self.line[1:]

### testing

if __name__ == "__main__":
	from ServletWriter import ServletWriter
	# For stand alone testing:
	class DummyWriter(ServletWriter):
		def __init__(self):
			self._filehandle = sys.stdout
			self._tabcnt = 0
			self._blockcount = 0 # a hack to handle nested blocks of python code
			self._indentSpaces = ServletWriter.SPACES
			self._useTabs = 1
			self._useBraces = 0
			self._indent = '\t'
			self._userIndent = ServletWriter.EMPTY_STRING
	test = r'''
	for x in range(10): { q = {
	'test': x
	}
	print x
	}

	for x in range(10): { q= {'test': x}; print x} else: { print "\"done\"" #""}{
	x = { 'test1': {'sub2': {'subsub1': 2}} # yee ha
	}
	} print "all done"

	'''
	p = BraceConverter()
	dw = DummyWriter()
	for line in test.split('\n'):
		p.parseLine(line, dw)
