# # test for kjParseBuild module automatic parser generation # # lisp lists with strings, ints, vars, and setq import string ### The string representation for the grammar. ### Since this is used only by GrammarBuild() ### it could be put in a separate file with GrammarBuild() ### to save space/load time after Grammar compilation. ### 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 """ ### the name of the file in which to create the compiled ### grammar declarations COMPILEDFILENAME = "TESTLispG2.py" ### declare comment form(s) as regular expressions LISPCOMMENTREGEX = ";.*" ### declare regular expression string constants for terminals #integer terminal::::::: INTREGEX = "["+string.digits+"]+" #string terminal:::::::: STRREGEX = '"[^\n"]*"' #var terminal:::::::: VARREGEX = "["+string.letters+"]["+string.letters+string.digits+"]*" ### declare interpretation functions for terminals # int interpretation function: translates string to int: # Could use string.atoi without the extra level of indirection # but for demo purposes here it is. # def intInterp( str ): return string.atoi(str) # interpretation function for strings strips off the surrounding quotes. def stripQuotes( str ): if len(str)<2: TypeError, "string too short?" return str[1:len(str)-1] # interpretation function for vars just returns the recognized string def echo(string): return string # This function declares the nonterminals both in the # "grammar generation phase" and in loading the compiled # grammar after generation # def DeclareTerminals(Grammar): Grammar.Addterm("int", INTREGEX, intInterp) Grammar.Addterm("str", STRREGEX, stripQuotes) Grammar.Addterm("var", VARREGEX, echo) ### declare the rule reduction interpretation functions. # EchoValue() serves for Intrule and Strrule, since # we just want to echo the value returned by the # respective terminal interpretation functions. # # Parser delivers list of form [ interpreted_value ] def EchoValue( list, Context ): if len(list)!=1: raise TypeError, "this shouldn't happen! (1)" return list[0] # for Varrule interpreter must try to look up the value # in the Context dictionary # # Parser delivers list of form [ var_name ] def VarValue( list, Context ): if len(list)!=1: raise TypeError, "Huh? (2)" varName = list[0] if Context.has_key(varName): return Context[varName] else: raise NameError, "no such lisp variable in context "+varName # for an empty tail, return the empty list # # Parser delivers list of form [")"] def NilTail( list, Context ): if len(list) != 1 or list[0] != ")": return TypeError, "Bad reduction?" return [] # For a full tail, add the new element to the front of the list # # Parser delivers list of form [Value, TailValue] def AddToList( list, Context ): if len(list) !=2: return TypeError, "Bad reduction?" return [ list[0] ] + list[1] # For a list, simply return the list determined by the tail # # Parser delivers list of form ["(", TailValue ] def MakeList( list, Context ): if len(list)!=2 or list[0]!="(": raise TypeError, "Bad reduction? (3)" return list[1] # For a setq, declare a new variable in the Context dictionary # # Parser delivers list of form # ["(", "setq", varName, Value, ")"] def DoSetq( list, Context): if len(list) != 5\ or list[0] != "("\ or list[1] != "setq"\ or list[4] != ")": print list raise TypeError, "Bad reduction? (4)" VarName = list[2] if type(VarName) != type(''): raise TypeError, "Bad var name? (5)" Value = list[3] # add or set the variable in the Context dictionary Context[ VarName ] = Value return Value # This function Binds the named rules of the Grammar string to their # interpretation functions in a Grammar. # 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 ) # This function generates the grammar and dumps it to a file. # Since it will be used only once (after debugging), # it probably should be put in another file save memory/load-time. # # the result returned is a Grammar Object that can be used # for testing/debugging purposes. # # (maybe this should be made into a generic function?) def GrammarBuild(): import kjParseBuild # initialize a Null compilable grammar to define LispG = kjParseBuild.NullCGrammar() # declare terminals for the grammar DeclareTerminals(LispG) # declare the keywords for the grammar # defun is not used, included here for demo purposes only LispG.Keywords("setq defun") # Declare punctuations # dot is not used here LispG.punct("().") # Declare Nonterms LispG.Nonterms("Value ListTail") # Declare comment forms LispG.comments([LISPCOMMENTREGEX]) # Declare rules LispG.Declarerules(GRAMMARSTRING) # Compile the grammar LispG.Compile() # Write the grammar to a file except for # the function bindings (which must be rebound) outfile = open(COMPILEDFILENAME, "w") LispG.Reconstruct("LispG",outfile,"GRAMMAR") outfile.close() # for debugging purposes only, bind the rules # in the generated grammar BindRules(LispG) # return the generated Grammar return LispG # this function initializes the compiled grammar from # generated file. def LoadLispG(): import TESTLispG2 # make sure we have most recent version (during debugging) reload(TESTLispG2) # evaluate the grammar function from generated file LispG = TESTLispG2.GRAMMAR() # bind the semantics functions DeclareTerminals(LispG) BindRules(LispG) return LispG ########## test grammar generation # do generation Dummy = GrammarBuild() # load the grammar from the file as LispG 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)