Jak można napisać pierwszy kompilator C ++ w C ++?

48

Stroustrup twierdzi, że Cfront, pierwszy kompilator C ++, został napisany w C ++ ( FAQ Stroustrup ).

Ale jak to możliwe, że pierwszy kompilator C ++ zostanie napisany w C ++?

Kod, który składa się na kompilator, również musi zostać skompilowany, a zatem pierwszy kompilator C ++ nie mógł zostać napisany w C ++, prawda?

Pacerier
źródło

Odpowiedzi:

57

Klucz jest tutaj:

Pierwszy kompilator C ++ (Cfront) został napisany w C ++. Aby to zbudować, najpierw użyłem C do napisania preprocesora „C with Classes” –to-C. „C with Classes” był dialektem C, który stał się bezpośrednim przodkiem C ++. Ten preprocesor przetłumaczył konstrukcje „C z klasami” (takie jak klasy i konstruktory) na C. Był to tradycyjny preprocesor, który nie rozumiał całego języka, pozostawił większość sprawdzania typu dla kompilatora C do zrobienia i przetłumaczył pojedynczo konstruuje bez pełnej wiedzy. Potem napisałem pierwszą wersję Cfront w „C with Classes”.

Tak więc pierwsza wersja Cfront nie została napisana w C ++, a raczej w języku pośrednim. Możliwość tworzenia kompilatorów C i preprocesorów bezpośrednio w C doprowadziła do wielu innowacji (i ogromnych dziur bezpieczeństwa ) w C. Więc piszesz nowego preprosessora, który zamienia kod „C with Classes” w prosty C (ponieważ proste C może zrobić cokolwiek), a następnie używasz „C z klasami”, aby napisać kompilator C ++ (nie to, że nie możesz tego zrobić w C, tylko zajmie to chwilę), a następnie używasz tego kompilatora C ++, aby napisać bardziej wydajny / kompletny kompilator w C ++. Rozumiem?

Christopher Bibbs
źródło
5
+1 za dołączenie linku do jednej z moich ulubionych opowieści o rzeczach, które można zrobić (i nie należy).
jwernerny,
3
Kompilator został napisany prawidłowym kodem C ++, ale używał tylko kilku pełnych funkcji C ++, które były obsługiwane przez preprocesor „C with Classes”. Użył podzbioru pełnego języka, więc skompilował się również na wyniku (pierwsza działająca wersja Cfront). Po wykonaniu tego kroku „ładowania początkowego” prawdopodobnie nigdy nie musiał ponownie używać preprocesora.
joeytwiddle
2
@jwernerny - Zawsze uważałem ten artykuł za niezadowalający. Przechyla się nad najtrudniejszą i nietrywialną częścią: „Błąd pasowałby do kodu w poleceniu UNIX„ login ”. Kod zastępczy błędnie skompilowałby polecenie logowania, aby zaakceptować zamierzone zaszyfrowane hasło lub określone znane hasło. „ Ale jak by to zrobić? Czy kiedykolwiek faktycznie to wykazano?
detly
3
„doprowadziło do wielu innowacji (i ogromnych luk bezpieczeństwa) w C”: O ile mi wiadomo, tych sztuczek można używać w dowolnym języku, nie tylko w C. Tak więc każdy inny język może mieć takie same dziury w zabezpieczeniach.
Giorgio
2
@detly: Brzmi to teraz banalnie, ale w 1983 roku był to nowatorski atak, który stał się realny z powodu braku różnorodności implementacji. Bardziej ufaliśmy wówczas plikom binarnym, częściowo dlatego, że kompilowanie wszystkiego ze źródła było znacznie trudniejsze niż obecnie.
Blrfl
17

Został załadowany. Jak tylko funkcja C ++ została dodana do frontu, wtedy front mógł również użyć tej funkcji od tego momentu (ale nie w celu jej implementacji). Działało to, ponieważ cfront miał możliwość konwersji kodu C ++ na kod C. Jeśli więc pojawi się jakaś nowa platforma, możesz użyć cfront na innej platformie, aby przekonwertować cfront z C ++ na C, a następnie użyć kompilatora C nowej platformy, aby zakończyć kompilację z C do kodu obiektowego.

David Schwartz
źródło
9

Myślę, że BS odpowiada na to pytanie:

Pierwszy kompilator C ++ (Cfront) został napisany w C ++. Aby to zbudować, najpierw użyłem C do napisania preprocesora „C with Classes” –to-C. „C with Classes” był dialektem C, który stał się bezpośrednim przodkiem C ++. Ten preprocesor przetłumaczył konstrukcje „C z klasami” (takie jak klasy i konstruktory) na C. Był to tradycyjny preprocesor, który nie rozumiał całego języka, pozostawił większość sprawdzania typu dla kompilatora C do zrobienia i przetłumaczył pojedynczo konstruuje bez pełnej wiedzy.

Potem napisałem pierwszą wersję Cfront w „C with Classes”. Cfront był tradycyjnym kompilatorem, który dokonał pełnej kontroli składni i semantycznej źródła C ++. W tym celu miał kompletny parser, zbudował tabele symboli i zbudował kompletną wewnętrzną reprezentację drzewa każdej klasy, funkcji itp. Przeprowadził również pewną optymalizację na poziomie źródła w swojej wewnętrznej reprezentacji drzewa konstrukcji C ++ przed wypuszczeniem C. Wersja, która wygenerowany C, nie polegał na C do sprawdzania typu. Po prostu użył C jako asemblera. Powstały kod był bezkompromisowo szybki.

Najpierw stworzył coś, co nazwał „C with Classes” zaimplementowanym przez prosty preprocesor w C. To był w zasadzie C ++, ale preprocesor nie sprawdzał wcale lub wcale. Następnie użył go do napisania Cfront, potężniejszej wersji translatora C ++ na C, wraz ze sprawdzaniem typu, tablicami symboli itp.

Mike Dunlavey
źródło
1
więc w zasadzie, kiedy kompilujemy program C ++, zostaje on przekonwertowany na C, a następnie po przekonwertowaniu na C, jest ponownie kompilowany do kodu maszynowego?
Pacerier
@Pacerier: Początkowo tak, ale nie teraz myślę.
Mike Dunlavey,
nie do końca rozumiem twój komentarz. masz na myśli teraz, że istnieją kompilatory, które pomijają drugi krok i po prostu biorą źródło C ++ i kompilują do kodu maszynowego?
Pacerier
7
@Pacerier: Cóż, nie przechodzą bezpośrednio do języka asemblera lub kodu maszynowego. Zwykle najpierw przechodzą do niezależnej od maszyny reprezentacji pośredniej (trzy lub czterokąty) i analizują ją w celu optymalizacji. Z tego generują kod zestawu lub maszyny. Jeśli wybierzesz książkę o projektowaniu kompilatora (Aho i Ullman) , jestem pewien, że okaże się interesująca.
Mike Dunlavey,
1
Należy zauważyć, że budowany przez niego C ++ był również ułamkiem języka, który teraz istnieje. Nie miał szablonów, żadnych nowych bibliotek, używał tylko rzutowania C i jeśli dobrze pamiętam, nie miał wyjątków.
Gort the Robot
2

Dodam tę odpowiedź, ponieważ żadna odpowiedź nie obejmowała tego aspektu.

Technicznie nie potrzebujesz oprogramowania do kompilacji kodu. Tak długo, jak dysponujesz niezbędnymi specyfikacjami kompilatora, możesz ręcznie dokonać rzeczywistej kompilacji. Nie w ten sposób skompilowano pierwszy kompilator C ++. Mówię tylko, że to możliwe.

Porównaj z językiem asemblera. Kiedy były używane we wczesnych dniach, nie było oprogramowania asemblera do konwersji kodu asemblera na kod maszynowy. Dokonano tego ręcznie, ale język asemblera dał programistom lepszy przegląd.

klutt
źródło