#!/usr/bin/env python
import StringIO
class ParseError(Exception):
    pass
IDchars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_"
ID2chars = IDchars + "0123456789"
class Scanner(object):
    reservedWords = set(["SELECT", 
                         "FROM", 
                         "GROUP", 
                         "BY", 
                         "WHERE", 
                         "AND", 
                         "OR", 
                         "NOT", 
                         "INTO", 
                         "CURSOR", 
                         "FOR", 
                         "WHILE", 
                         "CASE", 
                         "END", 
                         "BEGIN", 
                         "COMMIT", 
                         "WORK", 
                         "ROLLBACK", 
                         "INSERT", 
                         "UPDATE", 
                         "DELETE"])
    operatorChars = "!=<>.\"',()*+-/"
    def __init__(self):
        self.stream = None
        self.position = 0
        self.inputDetail = None
        self.input = None
        self.bCaseSensitive = False
        self.matchText = StringIO.StringIO()
    def push(self, inputStream, inputPosition = 0):
        self.stream = inputStream
        self.position = inputPosition
    def err(self, expectedInput, gotInput):
        raise ParseError("position %d: expected %r but got %r" % (self.position, expectedInput, gotInput))
    def consumeDetail(self, expectedInput = None):
        if not hasattr(expectedInput, "__len__"):
            expectedInput = [expectedInput]
        for expectedPart in expectedInput:
            oldInput = self.inputDetail
            if expectedPart is not None and (oldInput if self.bCaseSensitive else (oldInput.upper() if oldInput else oldInput)) != expectedPart:
                self.err(expectedInput, oldInput)
            self.position += 1
            self.inputDetail = self.stream.read(1)
        #if(oldInput == "" and self.input == ""):
        #    self.err("<end-of-parsing>", "<EOF>")
        return(oldInput)
    def parseWhitespace(self):
        assert(self.inputDetail and self.inputDetail in [" ", "\t", "\n", "\r"])
        self.parseOptionalWhitespace()
    def parseOptionalWhitespace(self):
        while self.inputDetail and self.inputDetail in [" ", "\t", "\n", "\r"]:
            self.consumeDetail()
    def parseStringLiteral(self):
        assert(self.inputDetail == "'")
        while self.inputDetail and self.inputDetail != "'":
            # FIXME escaping
            self.matchText.write(self.consumeDetail())
        self.consumeDetail()
        return("STRING")
    def parseQuotedSymbol(self):
        assert(self.inputDetail == '"')
        while self.inputDetail and self.inputDetail != '"':
            # FIXME escaping
            self.matchText.write(self.consumeDetail())
        self.consumeDetail()
        return("ID")
    def consume(self, expectedInput = None):
        oldInput = self.input
        self.parseOptionalWhitespace()
        self.matchText = StringIO.StringIO()
        if self.inputDetail:
            if self.inputDetail in IDchars:
                while(self.inputDetail and self.inputDetail in ID2chars):
                    self.matchText.write(self.consumeDetail())
                self.input = "ID"
                v = (self.matchText.getvalue() if self.bCaseSensitive else self.matchText.getvalue().upper())
                if v in self.__class__.reservedWords:
                    self.input = v
            elif self.inputDetail in self.__class__.operatorChars:
                if self.inputDetail == '"':
                    self.input = self.parseQuotedSymbol() # FIXME
                elif self.inputDetail == "'":
                    self.input = self.parseStringLiteral()
                else:
                    self.input = self.inputDetail
            else:
                self.err("<something>", self.inputDetail)
        else:
            self.err("<something>", "<nothing>")
        return(oldInput)
if __name__ == "__main__":
    scanner = Scanner()
    import StringIO
    scanner.push(StringIO.StringIO("test"))
    scanner.consumeDetail()
    scanner.consume()
    assert(scanner.input == "ID")
    try:
        scanner.consume()
        assert(False)
    except ParseError:
        assert(True)
        