#!/usr/bin/env python #================================================================ # pyrang: Writes Python declarations for RNC schema names. # For documentation and code, see: # http://www.nmt.edu/tcc/help/lang/python/examples/pyrang/ #---------------------------------------------------------------- #================================================================ # Imports #---------------------------------------------------------------- import sys from sysargs import * from Ft.Xml import Parse ELEMENT_N = 'element' ATTRIBUTE_N = 'attribute' NAME_A = 'name' def main(): """Main program """ #-- 1 -- # [ if sys.argv contains valid command line arguments -> # args := an Args instance representing those arguments # else -> # sys.stderr +:= error message(s) # stop execution ] args = Args() #-- 2 -- # [ args is an Args object -> # if args.inFileName names a readable file containing a # valid RNG schema -> # nameTable := a dictionary whose keys are the Python # manifest constant names for the element and # attribute names in that schema (using args.prefix # as a prefix), and each corresponding value is the # XML name # else -> # sys.stderr +:= error message(s) # stop execution ] nameTable = processInput ( args ) #-- 3 -- # [ args is an Args object -> # if args.outFileName is None -> # outFile := sys.stdout # else if args.outFileName names a writeable file -> # outFile := that file opened new for writing # else -> # sys.stderr +:= error message(s) # stop execution ] if args.outFileName is None: outFile = sys.stdout else: try: outFile = open ( args.outFileName, "w" ) except IOError, detail: fatal ( "Can't open output file '%s' for writing." % args.outFileName ) #-- 4 -- # [ (outFile is a writeable file) and # (nameTable is a dictionary whose keys are Python names # and whose values are XML names -> # outFile := Python statements of the form 'n = v' # for n in the set of keys of nameTable and each v # is the corresponding nameTable value ] writeOutput ( args, outFile, nameTable ) def fatal ( *L ): """Write a message and terminate. [ L is a list of strings -> sys.stderr +:= (concatenated elements of L) stop execution ] """ print >>sys.stderr, "*** Error: %s" % "".join(L) sys.exit(1) def processInput ( args ): """Process the input schema. [ args is an Args object -> if args.inFileName names a readable file containing a valid RNG schema -> nameTable := a dictionary whose keys are the Python manifest constant names for the element and attribute names in that schema (using args.prefix as a prefix), and each corresponding value is the XML name else -> sys.stderr +:= error message(s) stop execution ] """ #-- 1 -- # [ nameTable := a new, empty dictionary ] nameTable = {} #-- 2 -- # [ if args.inFileName can be opened for reading -> # inFile := that file, so opened # else -> # sys.stderr +:= error message # stop execution ] try: inFile = open ( args.inFileName ) except IOError, detail: fatal ( "Can't open '%s' for reading: %s" % (args.inFileName, detail) ) #-- 3 -- # [ if inFile contains a valid XML document -> # doc := that document as a DOM tree # else -> # sys.stderr +:= error message(s) # stop execution ] try: doc = Parse ( inFile ) except Exception, detail: fatal ( "Can't parse the schema file: %s" % detail ) #-- 4 -- # [ (nameTable is a dictionary) and # (args is an Args object) -> # nameTable := nameTable with Python/XML name pairs added # from the subtree rooted at doc.documentElement, # with the names prefixed by args.prefix ] findNames ( doc.documentElement, args, nameTable ) #-- 5 -- return nameTable def findNames ( node, args, nameTable ): """Find element and attribute names in a subtree. [ (node is a DOM Element node) and (args is an Args object) and (nameTable is a dictionary whose keys are the Python names for elements and attributes, and each corresponding value is the XML name) -> nameTable := nameTable with Python/XML name pairs added from the subtree rooted at node, with the names prefixed by args.prefix ] """ #-- 1 -- # [ if node.nodeName is ELEMENT_N or ATTRIBUTE_N -> # nameTable := nameTable with an entry added with a # the Python equivalent of node's "name" attribute as # the key, and node's "name" attribute as the value # else -> I ] if node.nodeName == ELEMENT_N: eltName = node.getAttributeNS ( None, NAME_A ) addName ( nameTable, args, eltName, "N" ) elif node.nodeName == ATTRIBUTE_N: attrname = node.getAttributeNS ( None, NAME_A ) addName ( nameTable, args, attrname, "A" ) #-- 2 -- # [ nameTable := nameTable with new element and attribute # names added from children of node ] for child in node.childNodes: findNames ( child, args, nameTable ) def addName ( nameTable, args, name, suffix ): """Add one element or attribute name to nameTable. [ (nameTable is a dictionary) and (args is an Args object) and (name is an XML element or attribute name as a string) and (suffix is a string) -> nameTable := nameTable with an entry added with (args.prefix + (name, uppercased and with each "-" replaced by "_") + "_" + suffix) as the key, and name as the corresponding value ] """ #-- 1 -- # [ pyName := name, convert to str, uppercased, and with # hyphens converted to underbars, and "_" inserted # at each lowercase->uppercase transition ] pyName = pythonizeName ( name ) #-- 2 -- # [ key := args.prefix + pyName + "_" + suffix ] key = "%s%s_%s" % (args.prefix, pyName, suffix) #-- 3 -- # [ nameTable := nameTable with an entry whose key=key and # whose value=name ] nameTable[key] = name def pythonizeName ( s ): """Convert an XML name to its Python equivalent. """ #-- 1 -- xlated = [] #-- 2 -- # [ xlated +:= characters from s, convert to string type, # uppercasing lowercase characters, adding "_" at each # lowercase->uppercase transition, and converting "-" # to "-" ] for i in range(len(s)): #-- 2 body -- # [ if s[i] == '-' -> # xlated +:= ['_'] # else if s[i] is lowercase -> # xlated +:= [str(s[i]), uppercased] # else if (s[i] is uppercase) and (i>0) and # (s[i-1] is lowercase) -> # xlated +:= ["-", str(s[i])] # else -> # xlated +:= [str(s[i])] ] #-- 2.1 -- c = str(s[i]) #-- 2.2 -- if c == '-': xlated.append ( '_' ) elif c.islower(): xlated.append ( c.upper() ) elif c.isupper(): if ( ( i > 0 ) and ( s[i-1].islower() ) ): xlated.append ( '_' ) xlated.append ( c ) else: xlated.append ( c ) #-- 3 -- return "".join ( xlated ) def writeOutput ( args, outFile, nameTable ): """Output the Python assignment statements. [ (args is an Args object) and (outFile is a writeable file) and (nameTable is a dictionary whose keys are Python names and each value is a string) -> outFile +:= (opening comment) + (lines of the form 'name = value', one for each entry in nameTable) ] """ #-- 1 -- print >>outFile, ( "'''Do not edit this file. It was produced automatically from\n" " the %s schema by the %s script.\n" "'''" % (args.inFileName, sys.argv[0]) ) #-- 2 -- # [ outFile +: (lines of the form 'name = value', one for # each entry in nameTable) ] keyList = nameTable.keys() keyList.sort() for key in keyList: print >>outFile, "%s = '%s'" % (key, nameTable[key] ) outFile.close() class Args: """Represents the command line arguments. Exports: Args(): [ if sys.argv contains valid command line arguments -> return a new Args object representing those arguments else -> sys.stderr +:= (usage message) + (error message) stop execution ] .inFileName: [ the input file name argument from sys.argv ] .prefix: [ if sys.argv specifies a prefix argument -> that argument as a string else -> "" ] .outFileName: [ if sys.argv specifies an output file name -> that file name as a string else -> None ] """ PREFIX_SWITCH = 'p' OUTFILE_SWITCH = 'o' INFILE_ARG = 'inFile' SWITCH_SPECS = [ SwitchArg ( PREFIX_SWITCH, [ "Prefix for each generated name" ], takesValue=1 ), SwitchArg ( OUTFILE_SWITCH, [ "Optional output file name" ], takesValue=1 ) ] POS_SPECS = [ PosArg ( INFILE_ARG, [ "Name of input .rng file" ] ) ] def __init__ ( self ): """Check and process command line arguments. """ #-- 1 -- # [ if sys.argv contains valid command line arguments -> # sysArgs := a SysArgs object representing those # arguments # else -> # sys.stderr +:= (usage message) + (error message) # stop execution ] sysArgs = SysArgs ( self.SWITCH_SPECS, self.POS_SPECS ) #-- 2 -- # [ if sysArgs.switchMap has a key self.PREFIX_SWITCH -> # self.prefix := the corresponding value # else -> # self.prefix := "" ] self.prefix = sysArgs.switchMap[self.PREFIX_SWITCH] if self.prefix is None: self.prefix = "" #-- 3 -- # [ if sysArgs.switchMap has a key self.OUTFILE_SWITCH -> # self.outFileName := the corresponding value # else -> # self.outFileName := None ] try: self.outFileName = sysArgs.switchMap[self.OUTFILE_SWITCH] except KeyError: self.outFileName = None #-- 4 -- self.inFileName = sysArgs.posMap[self.INFILE_ARG] #================================================================ # Epilogue #---------------------------------------------------------------- if __name__ == '__main__': main()