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.
14
Odpowiedzi:
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.
źródło
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:
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.
źródło
javac
wiersz 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.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.
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.
źródło
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).
źródło
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).
źródło
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 ...
źródło
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.
źródło
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 .
źródło