2009-12-14 7 views
8

(Disclaimer: questi esempi vengono forniti nel contesto della creazione di un compilatore, ma questa domanda riguarda esclusivamente il pattern Visitor e non richiede alcuna conoscenza della teoria del compilatore.) Sto esaminando Andrew Appel's Modern Compiler Implementazione in Java per cercare di insegnare a me stesso la teoria del compilatore (quindi no, questo non è compito a casa) e ho difficoltà a capire come vuole usare il pattern Visitor per trasformare un AST in un albero IR. (Nota: lo sto facendo in Python, quindi posso imparare anche Python, ed è per questo che gli esempi imminenti non sono in Java.) A quanto ho capito, i metodi visita e accetta nel pattern Visitor sono vidimati dalla progettazione, quindi se ho qualcosa di simileTrasformazioni albero utilizzando il pattern visitatore

class PlusExp(Exp): 
    def __init__(self, exp_left, exp_right): 
     self.exp_left = exp_left 
     self.exp_right = exp_right 

    def accept(self, v): 
     v.visit_plus_exp(self) 

quindi mi piacerebbe essere in grado di scrivere un metodo come

def visit_plus_exp(self, plus_exp): 
    return BINOP(BinOp.PLUS, 
       plus_exp.exp_left.accept(self), 
       plus_exp.exp_right.accept(self)) 

visitatore che si tradurrebbe le due espressioni bambino in IR e poi collegarsi con la BINOP che rappresenta l'espressione positiva. Naturalmente, questo non è possibile a meno che non modifico tutte le funzioni di accettazione per restituire informazioni extra, e questo è anche disordinato perché a volte vuoi solo un visitatore di stampa che non restituisca nulla. Tuttavia, questo testo insiste sul fatto che un visitatore è la strada giusta da percorrere, e in Java a ciò, il che significa che può essere fatto senza la flessibilità di Python. Non riesco a pensare ad alcuna soluzione che non sia incredibilmente hacky: qualcuno può illuminarmi sul design previsto?

+0

hey voglio anche studiare compilatori ... è quel libro un buon libro per studiare compilatori ??? –

+2

Penso che sia abbastanza buono come introduzione. Ti guida attraverso (senza tenere la mano) l'implementazione di un compilatore che prende come sorgente un sottoinsieme di Java ed emette codice nativo. Contiene anche una serie di argomenti avanzati nella teoria del compilatore. – danben

+0

Riguarda la parte di generazione del codice? –

risposta

9

Un parser SAX è un tipo di visitatore. Per evitare di aggiungere un valore di ritorno al metodo, è possibile utilizzare una pila:

class Visitor { 
    Stack<Node> stack = new Stack<Node>(); 

// . . . 

    void visitPlus(PlusExp pe) { 
     pe.left.accept(this); 
     pe.right.accept(this); 
     Node b = stack.pop(); 
     Node a = stack.pop(); 
     stack.push(new BinOp(BinOp.PLUS, a, b)); 
    } 
+0

Sì, questo è quello che ho voluto. Grazie mille. – danben

0

Avvertenza: non ho letto quel libro.

Il metodo può essere immatricolato, ma in Java (per il quale è stato scritto il libro) fa anche parte di un oggetto. Quindi, il metodo visitor può creare la struttura in una variabile membro locale, mantenendo così il contesto necessario tra le chiamate.

Quindi, ad esempio, il visitatore di stampa si accoderebbe a un oggetto StringBuilder contenuto come variabile membro (o come variabile locale finale in un metodo che ha creato l'oggetto visitatore), questo è abbastanza comune in Java, dove creare piccoli oggetti di classe interna anonima è un'abitudine comune).

In python, è possibile consentire al metodo visitatore di accedere a una variabile locale non del metodo per mantenere il contesto e creare la struttura. Ad esempio, chiusura, o un piccolo oggetto.

Update - piccola porzione di codice aggiunto come ad esempio dal commento qui sotto

result = new Node(); 
result.left.add(n1.accept(this)); 
result.right.add(n2.accept(this)); 
return result; 

o

result = new Node(); 
this.nextLoc.add(result); 
this.nextLoc = result.left; 
n1.accept(this); 
this.nextLoc = result.right; 
n2.accept(this); 

Il primo è più bella (anche se ancora scadente commento codice di esempio), ma il secondo ti permetterebbe di mantenere il tipo di reso vuoto se davvero ne avessi bisogno.

+0

Mi spiace, penso che tu abbia frainteso: il visitatore di stampa sarebbe stato molto facile da costruire perché la rappresentazione String di ogni nodo poteva essere scritta su un flusso di output mentre il nodo veniva visitato. Ma il problema qui è che se vogliamo costruire un albero, quando abbiamo finito di visitare un bambino, dobbiamo sapere a quale nodo genitore collegarlo, e anche quale proprietà di quel nodo genitore. – danben

+0

È possibile che ti stia fraintendendo, ma mi sembra che se stai modificando il tipo di ritorno o modificando un riferimento di destinazione "posizione successiva", il codice visitatore imposta sempre la posizione per il risultato della successiva (più profonda) chiamata . (Vedi codice di bassa qualità aggiunto per rispondere). –

+0

Giusto: non hai menzionato inizialmente il riferimento alla posizione, ma è più vicino a ciò che volevo. Ci avevo pensato anche io, ma credo che sia problematico (anche se potrei sbagliarmi) perché nextLoc punta solo alla posizione che voglio impostare, e quindi assegnandole direttamente non inciderà sulla posizione che punta a. Piuttosto, penso che (almeno in Python) potrebbe essere realizzato costruendo e passando una funzione che stabilisca la posizione successiva, ma in Java ciò non sarebbe possibile. – danben

1

Controllare il codice sorgente del compilatore THIS. Penso che il ragazzo abbia usato il pattern Visitor.

+0

Sì, il suo codice implementa semplicemente le funzioni di visita/accettazione per restituire i valori quando necessario. Potrei farlo, e sarebbe più pulito in Python che in Java, ma mi chiedo se non ci sia un modo per farlo usando Visitor come prescritto. – danben

+0

Scusa :(Non so se posso fare di meglio –

+0

Nessun problema, grazie per aver provato. – danben