"""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


    class DummyWriter(ServletWriter):
        """For stand alone testing."""

        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._emptyString

    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)