2009-12-14 14 views
8

(Uwaga: te przykłady podano w kontekście budowania kompilatora, ale to pytanie dotyczy wzorca gościa i nie wymaga znajomości teorii kompilatorów.) Przechodzę przez stronę Andrew Appela Nowoczesna implementacja kompilatora w Javie, aby spróbować nauczyć się teorii kompilatora (więc nie, to nie jest praca domowa) i mam problem ze zrozumieniem, w jaki sposób chce wykorzystać wzorzec Odwiedzający, aby przekształcić AST w drzewo IR. (Uwaga: robię to w Pythonie, więc mogę również nauczyć się Pythona, dlatego nadchodzące przykłady nie są w Javie.) Jak rozumiem, metody odwiedzin i akceptacji w wzorzec Visitor są puste-typowane według projektu, więc jeśli mam coś podobnegoTransformacje drzewa przy użyciu wzorca odwiedzającego

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) 

następnie Chciałbym móc napisać metodę odwiedzających jak

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

co przełożyłoby dwóch wyrażeń dziecko w podczerwieni, a następnie połączyć je z BINOP reprezentujący wyrażenie plus. Oczywiście nie jest to możliwe, dopóki nie zmodyfikuję wszystkich funkcji akceptujących, aby zwrócić dodatkowe informacje, a to jest również kłopotliwe, ponieważ czasami po prostu chcesz drukowanego użytkownika, który niczego nie zwraca. Tekst ten twierdzi jednak, że odwiedzający to właściwa droga, a na tym w Javie, co oznacza, że ​​można to zrobić bez elastyczności Pythona. Nie mogę wymyślić żadnych rozwiązań, które nie są niewiarygodnie hackowe - czy ktoś może mnie oświecić, jeśli chodzi o projekt?

+0

hej Chcę również uczyć się kompilatorów ... czy ta książka to dobra książka do nauki kompilatorów ??? –

+2

Myślę, że to całkiem dobre wprowadzenie. Przeprowadza cię przez procesor (bez trzymania ręki) przez implementację kompilatora, który pobiera jako źródło podzbiór Javy i emituje natywny kod. Zawiera również szereg zaawansowanych tematów w teorii kompilacji. – danben

+0

Czy obejmuje część generowania kodu? –

Odpowiedz

9

Parser SAX jest rodzajem gościa. Aby uniknąć dodawania wartości zwracanej do metody, można użyć stosu:

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

Tak, to jest to, co ja Chciałam, dziękuję bardzo. – danben

0

Zastrzeżenie: nie czytałem tej książki.

Metoda może być void-typowana, ale w Javie (której książka została napisana) jest również częścią obiektu. Tak więc metoda odwiedzająca może budować strukturę w lokalnej zmiennej składowej, zachowując niezbędny kontekst między wywołaniami.

Na przykład użytkownik wydruku będzie dołączał do StringBuilder, który jest przechowywany jako zmienna składowa (lub jako ostateczna lokalna zmienna w metodzie, która utworzyła obiekt odwiedzający - jest to dość powszechne w Javie, gdzie tworzenie małych anonimowych obiektów klasy wewnętrznej jest powszechnym nawykiem).

W pythonie można w podobny sposób pozwolić metodzie odwiedzającej uzyskać dostęp do zmiennej lokalnej bez metody, aby zachować kontekst i zbudować strukturę. Np. Zamknięcie lub mały przedmiot.

Aktualizacja - mały fragment kodu dodaje się jako przykład, z komentarzem poniżej

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

lub

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

Pierwszy ładniejszy (choć nadal słaba komentarz przykład kod), natomiast druga pozwoliłoby ci zachować nieważny typ zwrotu, jeśli naprawdę tego potrzebujesz.

+0

Przepraszam, myślę, że źle zrozumiałeś - osoba odwiedzająca drukowanie byłaby bardzo łatwa do zbudowania, ponieważ reprezentacja Ciągów każdego węzła mogłaby zostać zapisana w strumieniu wyjściowym podczas odwiedzania węzła. Ale problem polega na tym, że jeśli chcemy zbudować drzewo, to kiedy skończymy odwiedzać dziecko, musimy wiedzieć, do którego węzła macierzystego należy go dołączyć, a także do której właściwości tego węzła nadrzędnego. – danben

+0

Możliwe, że nie rozumiem cię, ale wydaje mi się, że niezależnie od tego, czy zmieniasz typ zwrotny, czy zmieniasz odniesienie do miejsca docelowego "następna lokalizacja", kod odwiedzającego zawsze ustawia lokalizację dla wyniku następnego (głębszego) wywołania . (Patrz kod niskiej jakości dodany do odpowiedzi.) –

+0

Dobrze - nie wspomniałeś początkowo o lokalizacji, ale jest bliżej tego, co chciałem. Myślałem o tym również, ale uważam, że jest problematyczny (choć mógłbym się mylić), ponieważ nextLoc wskazuje tylko na lokalizację, którą chcę ustawić, więc przypisanie jej bezpośrednio nie wpłynie na lokalizację, którą wskazuje do. Raczej uważam, że (przynajmniej w Pythonie) można to osiągnąć przez skonstruowanie i przekazanie funkcji, która ustawiłaby następną lokalizację, ale w Javie nie byłoby to możliwe. – danben

1

Sprawdź kod źródłowy kompilatora THIS. Myślę, że facet użył wzorca Odwiedzającego.

+0

Tak, jego kod implementuje funkcje odwiedzin/akceptacji, aby zwracać wartości, gdy zajdzie taka potrzeba. Mógłbym to zrobić i byłoby to czystsze w Pythonie niż w Javie, ale zastanawiam się, czy nie ma jakiegoś zamierzonego sposobu, aby to zrobić, używając Gościa zgodnie z zaleceniami. – danben

+0

Niestety :(Nie wiem, czy mogę zrobić coś lepszego –

+0

Nie ma problemu, dziękuję za próbę. – danben

Powiązane problemy