"""\
ParseString(s) -> LispList  is the function you want to use.
The rest?  Merely players."""

from __future__ import generators


#### Huge terminology contradiction!!! ###
# THe string-checking functions use the term LispList to mean a pre-pared
# string like (1 2 3)  or '(a 3 42).
# BUT often functions in all.py take in a 'lispList' which is a _parsed_
# list:  NOT a string.

# Another note:  If the parsing functions seem hacky,
#  that's because python strings are immutable, so you need to return them
#  all the time.  It would probably clean stuff up in some places to just
#  implement something like java's StringBuffer.  ah well.

##print 'Loading Parser.py' #I use this because the python ide IDLE has.. issues

DELIMS = ' \r\t\n'
SYMBOL_STARTERS = [chr(x) for x in range(32,127)] 
for not_a_starter in DELIMS + "()'":
    if not_a_starter in SYMBOL_STARTERS:
        SYMBOL_STARTERS.remove(not_a_starter)
        
print """Parser.py: Legitimate characters to begin a lisp atom/symbol:
""", SYMBOL_STARTERS

def StripFront(s, delims):
    "Strips delimiters off the front, returns the new string"
    i=0
    while(s[i] in delims): i += 1
    return s[:i]

def ParenCount(s):
    "Returns how many right parens are needed to finish off the expr"
    parenLvl = 0
    for c in s:
        if   c == '(': parenLvl += 1
        elif c == ')': parenLvl -= 1
        elif c == ']': return 0
    return parenLvl

def ReplaceSuperBracket(s):
    if s[-1] != ']': return s
    return s[:-1] + ')' * ParenCount(s[:-1])

def WellformedLispList(s):
    "DOes parenthesis checking, and runs IsLiteral and Quoted on it"
    s = ReplaceSuperBracket(s)
    if not IsLiteralLispList(s) and not QuotedLispList(s): return 0
    return ParenCount(s) == 0

def IsLiteralLispList(s):
    "Something like (1 2 3).  No quote at beginning."
    return len(s) >=2 and s[0] == '(' and  s[-1] in ')]'

def QuotedLispList(s):
    return len(s) >= 3 and s[0:2] == "'(" and s[-1] in ')]'

def ParseElemsGen(lispLiteral):
    """Give this sucker an UNQUOTED lisp-string.
    Generates strings and only strings."""
    def _GotoMatchingParen(i, curParenDepth):
        assert 'curListStr' in locals()
        while(i < len(curListStr) and curParenDepth > 0):
            if   curListStr[i] == '(': curParenDepth += 1
            elif curListStr[i] == ')': curParenDepth -= 1
            else: assert curListStr[i] != ']' #shouldn't hit those in this function
            i += 1
        assert not (i == len(curListStr) and curParenDepth > 0), "Parenthesis Mismatch error!  (Big surprise forLISP). For %s" %lispLiteral
        return i
        
#STart actual function body
    assert IsLiteralLispList(lispLiteral)
    curListStr = lispLiteral[1:-1] #Now strip off the outermost parentheses
##    print "ParseElemsGen: parsing '%s'" %curListStr

    elem = ""
    while(curListStr): #Progress through, chopping off what's been processed
        #We should now be at beginning of a new element
        if curListStr[0] in DELIMS: StripFront(curListStr, DELIMS)
##        print "ParseElemsGen: \"%s\"" %curListStr
        if curListStr[0] in SYMBOL_STARTERS:
            i = 0
            while(i < len(curListStr) and curListStr[i] not in DELIMS):
                i += 1
            yield curListStr[:i]
            curListStr = curListStr[i:]                
        elif curListStr[0] == '(':
            matchingPos = _GotoMatchingParen(1,1)  #Start off after the first open-paren
            yield curListStr[:matchingPos]
            curListStr = curListStr[matchingPos:]
        elif curListStr[0:2] == "'(":
            matchingPos = _GotoMatchingParen(2,1)
            yield curListStr[:matchingPos]
            curListStr = curListStr[matchingPos:]
            
        else: raise Exception, "Dunno what to do with '%s'" %curListStr
        #Now, clear past delimiters to the next expr
        while curListStr and curListStr[0] in DELIMS:
            curListStr = curListStr[1:]

def ParseString( lispListStr):
    """lispListStr can either be quoted or unquoted.  Prepends the quote macro and all."""
    def HelperGen(s):
        for elem in ParseElemsGen(s):
##            print "ParseString: '%s'  " %elem ,
##            print "Wellformed? %s  " % WellformedLispList(elem),
##            print "Quoted? %s" % QuotedLispList(elem)
            if WellformedLispList(elem):    yield ParseString(elem)
            else:                           yield elem

    assert WellformedLispList(lispListStr)
#Begin actual function body
    lispListStr = ReplaceSuperBracket(lispListStr)
    if QuotedLispList(lispListStr):
        notQuotedPart = lispListStr[1:]
        ret = ['quote', list(HelperGen(notQuotedPart))]
    else:
        ret = list(HelperGen(lispListStr))
    return ret

if __name__=='__main__':
    pass

