Dlaczego C ++ pisać kompilator?

14

Zastanawiałem się, dlaczego C ++ jest dobrym wyborem do napisania kompilatora. Oczywiście, C jest również dobry do tego celu, ponieważ wiele kompilatorów jest napisanych w C lub C ++, ale tym razem bardziej interesuję się C ++. Jakieś dobre powody? Szukałem tego w Internecie, ale nie mogę znaleźć dobrych powodów.

Kobra
źródło
3
„Wiele kompilatorów jest napisanych [...] w C ++” - jakieś odniesienia? Które? Co sprawia, że ​​myślisz, że C ++ jest częściej używany do budowy kompilatora niż inne popularne języki?
Doc Brown
6
@DocBrown Cóż, Clang i MSVC są napisane głównie w C ++, gcc mają teraz trochę C ++, Java JVM są napisane w C ++ stackoverflow.com/questions/410320/what-is-java-written-in, a także superużytkownik. com / pytania / 136136 /…
Klaim
@DocBrown DMD kompilator referencyjny dla D jest napisany w C ++
maniak ratchet
3
Kto powiedział, że to dobry wybór?
Phil
1
@Phil Czy myślisz, że dokonali tego wyboru bez rozważania alternatyw? To nie jest „dobry” wybór, to „efektywny” wybór.
Klaim

Odpowiedzi:

24

C ++ ma dwie strony. Ma niskopoziomową stronę programistyczną, co sprawia, że ​​wydaje się naturalnym językiem do robienia rzeczy na niskim poziomie, takich jak generowanie kodu. Ma także stronę wysokiego poziomu (której nie ma C), która pozwala na tworzenie złożonej aplikacji (takiej jak kompilator) w logiczny, obiektowy sposób, przy jednoczesnym zachowaniu wydajności. Ponieważ ma zarówno niski, jak i wysoki poziom, jest dobrym wyborem dla dużych aplikacji, które wymagają funkcji niskiego poziomu lub wydajności.

Oleksi
źródło
11
O ile wiem, wiele logiki wewnątrz kompilatora ma charakter funkcjonalny (przekształcanie złożonych struktur danych w inne struktury danych), więc nie jestem pewien, czy obiekty obiektowe (które są bardziej ukierunkowane na programowanie w dużych , aspekty architektoniczne) przynoszą prawdziwą przewagę konstrukcji kompilatora w stylu programowania proceduralnego. Tylko moje 2 centy.
Giorgio
5
@Giorgio Posiadanie obiektów pomaga w wielu innych aspektach pisania kompilatora. Na przykład, istnieje wiele stanów, z którymi kompilator musi sobie radzić podczas optymalizacji i tego rodzaju rzeczy dobrze nadają się do OOP. Ponadto OOP i programowanie funkcjonalne mogą być dość komplementarne, więc to, że algorytmy mogą być w większości funkcjonalne, nie oznacza, że ​​obiekty nie pomogą.
Oleksi
3
@Giorgio i Oleksi: Mogę potwierdzić was oboje. Napisałem kompilator z Haskell dla języka świata rzeczywistego. To było naprawdę dobre dopasowanie. Ale czasami tęskniłem za jakimś OO. Gdybym musiał napisać inny kompilator, zdecydowanie wybrałbym Haskell, ale to naprawdę wyjątkowy przypadek. Nie wahałbym się wybrać Haskell bez wahania w przypadku innych rodzajów projektów.
scarfridge
27
Dlaczego do generowania kodu potrzebny jest język „niskopoziomowy”? Nie widzę, w jaki sposób te dwa elementy są w jakikolwiek sposób połączone.
phant0m
5
Nie potrzebujesz „strony niskiego poziomu” do generowania kodu tak samo, jak potrzebujesz identyfikatorów Unicode, aby móc pisać japoński tekst do pliku.
dan04
17

Moje doświadczenie nie zgadza się z twoją przesłanką tutaj. W rzeczywistości, w przypadku języków ogólnego przeznaczenia wysokiego poziomu, bardzo powszechną praktyką jest pisanie kompilatora w tym samym języku co język źródłowy (język kompilowany). Na przykład:

  • Kompilator Java firmy Sun jest napisany w Javie
  • Kompilator Scala jest napisany w języku Scala
  • Kompilator C # Mono jest napisany w C #
  • Kompilator Squick's Smalltalk został napisany w języku Smalltalk
  • ... i wiele więcej

Wyjątek stanowią nakładki kompilatora napisane dla istniejących platform kompilatora, takich jak GCC, LLVM lub Polyglot, które są następnie pisane w języku frameworka, lub kompilatorów opartych na istniejących generatorach analizatorów składni, takich jak Yacc. Ponieważ GCC, LLVM i Yacc są powszechnymi narzędziami napisanymi w C i C ++, zachęca pisarzy kompilatorów do korzystania z nich, co może prowadzić do uzyskania przez C i C ++ dużego udziału w dystrybucji języka implementacji kompilatora.

Dąb
źródło
2
Myślę, że ma to dużo więcej wspólnego z ludźmi, którzy piszą kompilator, znając dobrze i podoba się język, w którym piszą kompilator, niż z obiektywnych przyczyn technicznych.
Thomas Bonini
1
@Krelp Zgadzam się, że nie chodzi tu o obiektywny powód techniczny, ale też nie jest to tak naprawdę „lubienie” - jest to po prostu pewien rytuał przejścia dla języka - „czy jest wystarczająco dojrzały, aby móc służyć jako własny język implementacji kompilator".
Oak
1
Kompilator Java firmy Sun został napisany w języku C ++: stackoverflow.com/questions/410320/what-is-java-written-in
Klaim
12
@ Oświadcz, że mylisz tutaj dwa produkty. Jednym z nich jest kompilator Java firmy Sun ( javacwiersz poleceń), który kompiluje kod Java Bytecode. Jest napisany w Javie - sam wiele razy go modyfikowałem i możesz przeglądać jego źródła Java online . Drugim jest kompilator just-in-time osadzony w JVM Hotspot, który kompiluje kod Java Bytecode do natywnego kodu maszynowego. Jak większość JVM, jest napisany w C ++, ale nie jest to kompilator Java - w rzeczywistości nic nie wie o języku Java.
Dąb
@Oak, całkowicie poprawne! Innymi słowy, JVM! = Javac
Paul Draper
6

Aby skompilować co do czego? Kompilator przekształca kod źródłowy z jednego języka ( języka źródłowego) na inny (język docelowy), co nie wskazuje nic na temat niskiego poziomu języka docelowego.

  • CoffeeScript kompiluje się w JavaScript, kompilator jest napisany w CoffeeScript.
  • Skrypt # kompiluje C # w JavaScript, kompilator jest napisany, jeśli dobrze pamiętam, C #.
  • itp.

Język, który wybierasz do pisania kompilatora, zależy od kontekstu. Na przykład, pracując nad projektem, który kompiluje język pochodzący z PHP do natywnego kodu PHP, użyłem mieszanki PHP i C # do napisania kompilatora, ponieważ dla mnie było to najbardziej sensowne, biorąc pod uwagę moje umiejętności. Inna osoba wybrałaby Python, Java i PHP lub C ++ z odrobiną JavaScript, czy cokolwiek innego.

C lub C ++ jest popularnym wyborem ze względu na obsługę narzędzi związanych z kompilatorem (patrz odpowiedź Telastyn) oraz ponieważ te dwa języki pozwalają na naprawdę natywną pracę. Ale wybór innego języka nie jest niczym złym.

Zauważ, że aby być bardziej naukowym , możesz wybrać język źródłowy, aby napisać sam kompilator. Tak stało się w przypadku kompilatora CoffeeScript i wielu innych kompilatorów. Jest również popularny wśród IDE: jeden z pierwszych Visual Studio został zbudowany przy użyciu tego samego Visual Studio.

Arseni Mourzenko
źródło
5
Samo hosting nie jest geekowaty, jest ważną właściwością do przenoszenia kompilatora.
5
Powodem jest to, że natychmiast umożliwia on samemu kompilatorowi program testowy. Najprawdopodobniej będzie to również największy program dla tego kompilatora przez dłuższy czas.
6

Mam tendencję do kwestionowania tutaj podstawowej przesłanki. Podczas gdy C i C ++ działają doskonale do pisania kompilatorów, wydaje się, że całkiem sporo innych języków działa doskonale również do tego zadania.

Trochę zależy jednak od języka, który kompilujesz. W przypadku małych, prostych języków C i Pascal działają całkiem nieźle. Jeśli zamierzasz skompilować coś dużego i złożonego, twój kompilator również staje się duży i złożony - w takim przypadku dodatkowe funkcje C ++ do organizowania i pracy z większymi programami są oczywiście przydatne. Nie jest to jednak bardzo specyficzne dla kompilacji, a jedynie funkcje przydatne dla większych programów w ogóle.

Myślę, że warto również wspomnieć o jeszcze jednej kwestii. Początkujący (zdają się) myślą o kompilatorach jako głównie wykonujących manipulacje tekstem, więc myślą, że coś takiego jak Perl będzie ogromną pomocą w pisaniu kompilatorów. W rzeczywistości większość interesujących części kompilacji zaczyna się dopiero po zbudowaniu AST. Chociaż jestem pewien, że Perl może wykonać to zadanie doskonale, jego możliwości manipulacji tekstem tak naprawdę nie dają mu ogromnej przewagi (manipulowanie tekstem odbywa się głównie w leksyrze, a generatory leksykonów dla rzeczy takich jak C i tak obsługują RE).

Jerry Coffin
źródło
2
AST = abstrakcyjne drzewo składni, RE = wyrażenia regularne
chaotyczna równowaga
5

Kompilatory mogą być implementowane w dowolnym nowoczesnym języku. Jednak jednym z najważniejszych wymagań kompilatora jest szybkość.

C ++ ma tutaj wyraźną przewagę. Optymalizacja w C ++ nie jest tania. Jednak ze względu na niskopoziomową naturę tego języka możliwe jest ręczne zoptymalizowanie kodu C ++ bardziej niż w jakimkolwiek innym języku (z wyjątkiem asemblera, który nie jest przenośny).

Lior Kogan
źródło
11
Kolejnym ważnym wymaganiem jest poprawność generowanego kodu - wolę mieć powolny kompilator, któremu mogę zaufać, niż szybki, który generuje niepoprawny kod.
2
Chociaż na pewno można bardzo mocno zoptymalizować C ++, istnieje wiele raczej… no cóż… mniej niż optymalny kod C ++.
Donal Fellows
2
@DonalFellows Odwróć to na odwrót: można napisać mniej niż optymalny kod w dowolnym języku, ale istnieją optymalizacje, których nie można włączyć w innych językach niż C ++ (innych niż Asembler. Nie dołączam C z powodu braku wysokopoziomowych struktur pozwalających na mocniejsze wstawianie).
Klaim
@ user1249 - Nie ma powodu, dla którego szybkość kodu C ++ spowodowałaby, że byłby to błąd. Wolę mieć szybki, poprawny kompilator niż wolny, poprawny kompilator.
gnasher729
3

Podejrzewam, że głównym czynnikiem motywującym do ich zastosowania jest to, że dane wyjściowe Lex / Yacc / Bison są (przede wszystkim) w C. Ponieważ jest to standard od tak dawna, ma on pęd.

Nie dlatego, że są to szczególnie dobre powody ...

Telastyn
źródło
Właściwie to mnie nie satysfakcjonuje, ale dziękuję za próbę.
Kobra
To nie odpowiada na pytanie „dlaczego wybrać C ++ zamiast C do budowy kompilatora”.
Doc Brown
3
To wcale nie jest dobry powód. Na wielu platformach istnieją analogiczne narzędzia do Lexa i Yacca. Na przykład PLY i ANTLR.
user16764
Co więcej, najbardziej popularne kompilatory w świecie rzeczywistym (na przykład abuot Clang i GCC) używają ręcznie napisanych parserów.
@delnan: Tak, ale prawdopodobnie zaczęli korzystać z wygenerowanego, aby uzyskać wszystko od podstaw. Ręczne generowanie parsera to krok optymalizacji, którego tak naprawdę nie chcesz robić, dopóki nie udowodnisz, że działają inne rzeczy.
Martin York
1

Mam doświadczenie w tej sprawie. Napisałem kompilatory w C i C ++. Główną różnicą między C i C ++ jest to, że C nie ma dynamicznego zarządzania pamięcią w sposób automatyczny. Całe zarządzanie pamięcią w C musi być wykonane jawnie. Pisanie kompilatora zajmuje się głównie przetwarzaniem ciągów i zarządzaniem tablicą. W C musisz myśleć o rozmiarze każdego łańcucha i każdej deklarowanej tablicy, a także sprawdzać indeksy podczas uzyskiwania dostępu do tych obiektów (jeśli chcesz, aby kod był bezpieczny i stabilny). W C możesz oczywiście mieć dynamiczne zarządzanie pamięcią, ale nic nie jest automatyczne. Musisz jawnie przydzielić i zwolnić pamięć za pomocą malloc () i free (), utrzymywać rozmiar obiektów dynamicznych w osobnych zmiennych, aby mieć pewność, że nie uzyskasz do nich dostępu poza granicami.

W C ++ możesz mieć te same mechanizmy, ale jest to naprawdę czas efektywny, ponieważ całe twoje zarządzanie pamięcią może być zawarte w konstruktorach i destuktorach, których nie musisz jawnie wywoływać. Kompilator przydziela i zwalnia zasoby. Rozmiar twoich obiektów dynamicznych może być również enkapsulowany, jeśli tworzysz własne klasy, a indeksy można sprawdzać pod kątem dostępu do granic przez przeciążenie operatora []. Te abstrakcje pomagają uczynić kod czystszym, łatwiejszym do zrozumienia i debugowania oraz zdecydowanie przyspieszają programowanie.

Jeśli utworzysz kompilator w C, na pewno zajmie to więcej czasu. C ++ sprawi, że ukończysz projekt w krótszym czasie. C i C ++ mają tę samą wydajność, ale C ++ ma wiele zalet, których C nie ma.

Diego Marin
źródło
0

Projekt CompCert jest kompilatorem badawczym C, który nie jest napisany w C lub C ++, ale bardziej w Ocaml i Coq.

Zauważ, że C ++ było tłumaczone na C (w Cfront ). Teraz możesz użyć interfejsu GCC do Gimple , następnie zrzucić Gimple do jakiejś bazy danych, a następnie napisać Gimple do tłumacza asemblera. Jednak względy prawne ( wyjątek biblioteki wykonawczej GCC ) wymagają, aby taki kompilator był oprogramowaniem typu open source. Zapytaj swojego prawnika o szczegóły, nie jestem prawnikiem. Stare warianty GCC zostały napisane w C (+ kilka języków specyficznych dla domeny) z interfejsem dla niektórych wariantów C ++. OpenWatcom może być kompilatorem C ++ napisanym w C (zostawiam to, aby to sprawdzić).

Źródło Compcert jest bezpłatnie dostępne do celów akademickich i badawczych. Jeśli chcesz korzystać z niego przemysłowo (i legalnie), musisz uzyskać licencję od Absinta.

Zobacz także to i tamto odpowiedzi na dwa powiązane pytania.

Gdyby w 2020 r. Powierzono mi zadanie napisania od zera kompilatora C (lub C ++) (działającego w systemie Linux, być może kompilatora krzyżowego ), prawdopodobnie nie napisałbym go w C ++. Zastanowiłbym się nad napisaniem go za pomocą Ocaml , Go lub Rust . I mógłbym oprzeć to na Frama-C, jeśli pozwolę . Jeśli jest to wymagane do kodowania w C lub C ++, najpierw kodowałbym dla niego bibliotekę śmieciarza , prawdopodobnie pewną warstwę trwałości - bardzo przydatną do optymalizacji całego programu - a następnie rozważyłbym podejście do metaprogramowania (generowanie większości kodu C lub C ++ z kompilator z moimi narzędziami ad-hoc, może jeśli jest to dozwolone). Bismon lub RefPerSys

Możesz znaleźć niektóre (mniej lub bardziej otwarte) kompilatory C zakodowane we Common Lisp lub Python (np. ShivyC lub nqcc ). Zobacz także ZetaC .

Zauważ, że najnowsze wersje GCC technicznie nie są kodowane w czystym C ++, są to tuziny języków specyficznych dla domeny zaangażowanych w GCC (niektóre z nich są w pełni Turinga ). Zobacz także mój stary GCC MELT projekt .

Nie zdziwię się, jeśli w przyszłych wersjach GCC pojawi się jakiś Python lub Guile interpreter byłby w nich osadzony (na przykład jako zamiennik menedżera przepustek GCC).

Zobacz także projekt MILEPOST GCC .

Basile Starynkevitch
źródło