#!/usr/bin/env python #================================================================ # litsource: Extract code from literate-programming source files. # For documentation, see: # http://www.nmt.edu/tcc/help/lang/python/examples/litsource/ #---------------------------------------------------------------- # Overall intended function: # [ output files named in input files given on the command line # := code fragments designated for those files # sys.stderr +:= error messages if any ] #---------------------------------------------------------------- import sys from xml.sax.handler import ContentHandler from xml.sax import make_parser #================================================================ # Manifest constants #---------------------------------------------------------------- PROG_ELT = "programlisting" ROLE_ATTR = "role" ROLE_PREFIX = "outFile:" # - - - - - m a i n - - - - - def main(): """Main program for litsource.""" #-- 1 -- for inFileName in sys.argv[1:]: #-- 1 body -- # [ if inFileName names a readable, valid DocBook XML file -> # output files named in that file := code fragments # designated for those files # sys.stderr +:= error messages from processing that file, # if any # else -> # sys.stderr +:= error message ] processFile ( inFileName ) # - - - p r o c e s s F i l e - - - def processFile ( inFileName ): """Process one input file. [ inFileName is a string -> if inFileName names a readable, valid DocBook XML file -> output files named in that file := code fragments designated for those files sys.stderr +:= error messages from processing that file, if any else -> sys.stderr +:= error message ] """ #-- 1 -- # [ if inFileName names a readable file -> # inFile := that file opened for reading # else -> # sys.stderr +:= error message # return ] try: inFile = open ( inFileName ) except IOError, detail: sys.stderr.write ( "*** Can't open file '%s' for reading: %s\n" % (inFileName, detail) ) return #-- 2 -- # [ ch := an ArticleHandler instance ] ch = ArticleHandler() #-- 3 -- # [ ch is an ArticleHandler object -> # if inFile contains a readable, well-formed XML file -> # output files named in inFile := code fragments # designated for those files # sys.stderr +:= error message(s), if any ] saxparser = make_parser() saxparser.setContentHandler ( ch ) saxparser.parse ( inFile ) # - - - - - c l a s s A r t i c l e H a n d l e r - - - - - class ArticleHandler(ContentHandler): """Content handler object. Exports: ArticleHandler(): [ return a new ArticleHandler ] State/Invariants: .fileMap: [ a dictionary whose keys are the names of files in fragments seen so far; each value is a writeable file handle for that file ] .outFileName: [ if currently within a fragment -> the output file name for that fragment else -> None ] .outFile: [ if currently within a fragment -> the output file handle for that fragment else -> None ] """ # - - - A r t i c l e H a n d l e r . _ _ i n i t _ _ - - - def __init__ ( self ): """Constructor for ArticleHandler. """ #-- 1 -- # [ self := a new ContentHandler instance ] ContentHandler.__init__ ( self ) #-- 2 -- self.fileMap = {} self.outFileName = self.outFile = None # - - - A r t i c l e H a n d l e r . s t a r t E l e m e n t - - - def startElement ( self, name, attrs ): """Handle a start tag. [ (name is the element name) and (attrs is a dictionary containing the attribute names |-> attribute values) -> if this tag starts a fragment -> self.outFileName := the fragment's file name self.outFile := the fragment's output file self.fileMap := self.fileMap with an entry mapping the fragment's file name |-> the fragment's output file else -> I ] """ #-- 1 -- if name != PROG_ELT: return #-- 2 -- # [ if attrs has a key ROLE_ATTR -> # role := that attribute's value # else -> return ] try: role = attrs [ ROLE_ATTR ] except KeyError: return #-- 3 -- # [ if role starts with ROLE_PREFIX -> # self.outFileName := the rest of role # else -> return ] if role.startswith ( ROLE_PREFIX ): self.outFileName = role [ len ( ROLE_PREFIX ) : ] else: return #-- 4 -- # [ if self.fileMap has no key self.outFileName -> # self.fileMap := self.fileMap with an entry mapping # self.outFileName |-> a writeable file handle for # self.outFileName # self.outfile := that same file handle # else -> # self.outFile := the corresponding value from # self.fileMap ] try: self.outFile = self.fileMap [ self.outFileName ] except KeyError: try: self.outFile = open ( self.outFileName, "w" ) self.fileMap[self.outFileName] = self.outFile except IOError, detail: print >> sys.stderr, ( "*** Can't open file " "'%s' for writing." % self.outFileName ) sys.exit(1) # - - - A r t i c l e H a n d l e r . c h a r a c t e r s - - - def characters ( self, text ): """Handle text within an element. [ text is a string -> if self.outFile is not None -> self.outFile +:= text else -> I ] """ #-- 1 -- if self.outFile is not None: self.outFile.write ( text ) # - - - A r t i c l e H a n d l e r . e n d E l e m e n t - - - def endElement ( self, name ): """Handle the end of an element. [ name is an element name -> if name==PROG_ELT -> self.outFile := None self.outFileName := None else -> I ] """ if name == PROG_ELT: self.outFile = self.outFileName = None # - - - - - e p i l o g u e - - - - - if __name__ == '__main__': main()