# Grammar generation
# for lisp lists with strings, ints, vars, print, and setq

# set this variable to regenerate the grammar on each load
REGENERATEONLOAD = 1

import string
GRAMMARSTRING ="""
       Value ::  ## indicates Value is the root nonterminal for the grammar
         @R SetqRule :: Value >> ( setq var Value )
         @R ListRule :: Value >> ( ListTail
         @R TailFull :: ListTail >> Value ListTail
         @R TailEmpty :: ListTail >> )
         @R Varrule :: Value >> var
         @R Intrule :: Value >> int
         @R Strrule :: Value >> str
         @R PrintRule :: Value >> ( print Value )
"""
COMPILEDFILENAME = "TESTLispG.py"
MARSHALLEDFILENAME = "TESTLispG.mar"
LISPCOMMENTREGEX = ";.*"
INTREGEX = "["+string.digits+"]+"
STRREGEX = '"[^\n"]*"'
VARREGEX = "["+string.letters+"]["+string.letters+string.digits+"]*"

### declare interpretation functions and regex's for terminals
def intInterp( str ):
    return string.atoi(str)
def stripQuotes( str ):
    return str[1:len(str)-1]
def echo(string):
    return string
def DeclareTerminals(Grammar):
    Grammar.Addterm("int", INTREGEX, intInterp)
    Grammar.Addterm("str", STRREGEX, stripQuotes)
    Grammar.Addterm("var", VARREGEX, echo)

### declare the rule reduction interpretation functions.
def EchoValue( list, Context ):
    return list[0]
def VarValue( list, Context ):
    varName = list[0]
    if Context.has_key(varName):
        return Context[varName]
    else:
        raise NameError, "no such lisp variable in context "+varName
def NilTail( list, Context ):
    return []
def AddToList( list, Context ):
    return [ list[0] ] + list[1]
def MakeList( list, Context ):
    return list[1]
def DoSetq( list, Context):
    Context[ list[2] ] = list[3]
    return list[3]
def DoPrint( list, Context ):
    print list[2]
    return list[2]
def BindRules(Grammar):
    Grammar.Bind( "Intrule", EchoValue )
    Grammar.Bind( "Strrule", EchoValue )
    Grammar.Bind( "Varrule", VarValue )
    Grammar.Bind( "TailEmpty", NilTail )
    Grammar.Bind( "TailFull", AddToList )
    Grammar.Bind( "ListRule", MakeList )
    Grammar.Bind( "SetqRule", DoSetq )
    Grammar.Bind( "PrintRule", DoPrint )

# This function generates the grammar and dumps it to a file.
def GrammarBuild():
    import kjParseBuild
    LispG = kjParseBuild.NullCGrammar()
    LispG.SetCaseSensitivity(0) # grammar is not case sensitive for keywords
    DeclareTerminals(LispG)
    LispG.Keywords("setq print")
    LispG.punct("().")
    LispG.Nonterms("Value ListTail")
    LispG.comments([LISPCOMMENTREGEX])
    LispG.Declarerules(GRAMMARSTRING)
    LispG.Compile()

    print "dumping as python to "+COMPILEDFILENAME
    outfile = open(COMPILEDFILENAME, "w")
    LispG.Reconstruct("LispG",outfile,"GRAMMAR")
    outfile.close()

    print "dumping as binary to "+MARSHALLEDFILENAME
    outfile = open(MARSHALLEDFILENAME, "w")
    LispG.MarshalDump(outfile)
    outfile.close()

    BindRules(LispG)
    return LispG

# this function initializes the compiled grammar from the generated file.
def LoadLispG():
    import TESTLispG
    # reload to make sure we get the most recent version!
    # (only needed when debugging the grammar).
    reload(TESTLispG)
    LispG = TESTLispG.GRAMMAR()
    DeclareTerminals(LispG)
    BindRules(LispG)
    return LispG

def unMarshalLispG():
    import kjParser
    infile = open(MARSHALLEDFILENAME, "r")
    LispG = kjParser.UnMarshalGram(infile)
    infile.close()
    DeclareTerminals(LispG)
    BindRules(LispG)
    return LispG

########## test the grammar generation
if REGENERATEONLOAD:
    print "(re)generating the LispG grammar in file TESTLispG.py"
    Dummy = GrammarBuild()
    print "(re)generation done."

print "loading grammar as python"
LispG = LoadLispG()
### declare an initial context, and do some tests.
Context = { 'x':3 }
test1 = LispG.DoParse1( '()', Context)
test2 = LispG.DoParse1( '(123)', Context)
test3 = LispG.DoParse1( '(x)', Context)
test4 = LispG.DoParse1( '" a string "', Context)
test5 = LispG.DoParse1( '(setq y (1 2 3) )', Context )
test6 = LispG.DoParse1( '(SeTq x ("a string" "another" 0))', Context )
test7str = """
  ; this is a lisp comment
  (setq abc (("a" x)
             ("b" (setq d 12))
             ("c" y) ) ; another lisp comment
  )
"""
test7 = LispG.DoParse1( test7str, Context)
test8 = LispG.DoParse1( '(print (1 x d))', Context)

print "unmarshalling the grammar"
LispG2 = unMarshalLispG()
### declare an initial context, and do some tests.
Context = { 'x':3 }
test1 = LispG2.DoParse1( '()', Context)
test2 = LispG2.DoParse1( '(123)', Context)
test3 = LispG2.DoParse1( '(x)', Context)
test4 = LispG2.DoParse1( '" a string "', Context)
test5 = LispG2.DoParse1( '(setq y (1 2 3) )', Context )
test6 = LispG2.DoParse1( '(SeTq x ("a string" "another" 0))', Context )
test7str = """
  ; this is a lisp comment
  (setq abc (("a" x)
             ("b" (setq d 12))
             ("c" y) ) ; another lisp comment
  )
"""
test7 = LispG2.DoParse1( test7str, Context)
test8 = LispG2.DoParse1( '(print (1 x d))', Context)
