6.13. Mettere tutto assieme

Abbiamo coperto una buona distanza. Facciamo un passo indietro e vediamo come si mettono assieme tutti i pezzi.

Per cominciare, questo è uno script che prende i suoi argomenti da riga di comando, usando il modulo getopt.


def main(argv):                         
...
    try:                                
        opts, args = getopt.getopt(argv, "hg:d", ["help", "grammar="])
    except getopt.GetoptError:          
...
    for opt, arg in opts:               
...

Creiamo una nuova istanza della classe KantGenerator e passiamole il file di grammatica ed il sorgente, che può, o meno, essere stato specificato nella riga di comando.

    k = KantGenerator(grammar, source)

L'istanza di KantGenerator carica automaticamente la grammatica, che è un file XML. Utilizziamo la nostra funzione personalizzata openAnything per aprire il file (che potrebbe essere in un file locale o in un web server remoto), quindi usiamo le funzioni di analisi built-in del modulo minidom per inserire l'XML in un albero di oggetti Python.

    def _load(self, source):
        sock = toolbox.openAnything(source)
        xmldoc = minidom.parse(sock).documentElement
        sock.close()

Già che ci siamo, avvantaggiamoci della nostra conoscenza della struttura di un documento XML per impostare una piccola cache di riferimenti, che sono semplicemente gli elementi nel documento XML.

    def loadGrammar(self, grammar):                         
        for ref in self.grammar.getElementsByTagName("ref"):
            self.refs[ref.attributes["id"].value] = ref     

Se avessimo specificato del sorgente nella riga di comando, avremmo usato questo; in caso contrario dovremmo cercare nella grammatica il riferimento al “top-level” (che non è puntato da nient'altro) ed usarlo come punto di partenza.

    def getDefaultSource(self):
        xrefs = {}
        for xref in self.grammar.getElementsByTagName("xref"):
            xrefs[xref.attributes["id"].value] = 1
        xrefs = xrefs.keys()
        standaloneXrefs = [e for e in self.refs.keys() if e not in xrefs]
        return '<xref id="%s"/>' % random.choice(standaloneXrefs)

Ora attraversiamo il sorgente. Anche il sorgente è in XML, e lo analizziamo un nodo per volta. Per mantenere il nostro codice separato e maggiormente manutenibile, usiamo diversi gestori per ogni tipo di nodo.

    def parse_Element(self, node): 
        handlerMethod = getattr(self, "do_%s" % node.tagName)
        handlerMethod(node)

Percorriamo tutta la grammatica, analizzando tutti i figli di ogni elemento p,

    def do_p(self, node):
...
        if doit:
            for child in node.childNodes: self.parse(child)

sostituendo gli elementi choice con un figlio a caso,

    def do_choice(self, node):
        self.parse(self.randomChildElement(node))

e sostituendo gli elementi xref con un figlio a caso del corrispondente elemento ref, che abbiamo precedentemente messo nella cache.

    def do_xref(self, node):
        id = node.attributes["id"].value
        self.parse(self.randomChildElement(self.refs[id]))

Nel caso, trasformiamo il tutto in testo semplice,

    def parse_Text(self, node):    
        text = node.data
...
            self.pieces.append(text)

che poi stampiamo.


def main(argv):                         
...
    k = KantGenerator(grammar, source)
    print k.output()