Dlaczego kompilatory samoobsługowe są uważane za rytuał przejścia dla nowych języków?


30

Słyszałem już w wielu miejscach, w których ludzie oczekują, że języki będą korzystać z kompilatora samoobsługowego lub przynajmniej będą go używać, aby zasłużyć na szacunek.

Jestem ciekawy, dlaczego tak jest. Kompilator wydaje się być bardzo znaczącym oprogramowaniem do napisania i wyobrażam sobie, że nie wszystkie języki są odpowiednie do ich tworzenia. Czy nie ma większego sensu poświęcanie wysiłku na pracę nad czymś, co da lepsze wyniki?


17
„Kompilator wydaje się być bardzo znaczącym oprogramowaniem do napisania i wyobrażam sobie, że nie wszystkie języki nadają się do jego tworzenia.”: Uznałbym to za bardzo dobry powód, aby napisać kompilator w nowym języku, a mianowicie aby udowodnić, że język spełnia swoje zadanie.
Giorgio

13
O ile nie jest to język specjalnego przeznaczenia, język, który nie nadaje się do pisania kompilatora, prawdopodobnie nie nadaje się do tego, co chcę robić.
CodesInChaos

3
AFAIK, nie zawsze tak jest w przypadku Fortran. Kilka kompilatorów Fortran (np. gfortranZ GCC ...) nie jest kodowanych w Fortran.
Basile Starynkevitch

Odpowiedzi:


29

Czy nie ma większego sensu poświęcanie wysiłku na pracę nad czymś, co da lepsze wyniki?

Jak co?

Zaletą kompilatorów jest to, że nie mają wielu zależności. To czyni ich dobrymi kandydatami do nowego języka, który prawdopodobnie nie ma jeszcze bardzo dużej lub zróżnicowanej standardowej biblioteki.

Co więcej, wymagają różnych rzeczy, a jednocześnie są dobrze zbadane. Różnorodność pomaga upewnić się, że Twój przykład testuje różne części języka. Bycie dobrze zbadanym oznacza, że ​​masz inne kompilatory do porównania - a także dajesz większą wiarygodność akademickim, którzy wiedzą, co robisz.

I chociaż kompilatory wydają się mnóstwo pracy, są dość małe w wielkim schemacie rzeczy. Jeśli osoby wdrażające język nie mogą nawet zrobić czegoś, co wcześniej zrobiły w nowym języku, to w jaki sposób zamierzają robić nowe rzeczy? Jak zamierzają poradzić sobie z naprawdę dużymi rzeczami, takimi jak biblioteki standardowe lub IDE?


Na marginesie, chciałbym wspomnieć, że pomimo tego, że jest miły, wciąż istnieje wiele różnych powodów, dla których kompilator może być napisany w innym języku. Na przykład większość silników javascript nie jest napisana w javascript. Powodów jest wiele: integracja z innym oprogramowaniem, łączenie z istniejącymi bibliotekami / zależnościami, lepsze narzędzia, wydajność, starszy kod ... Czasami samokompilacja języka jest przyjemna, ale nadal warto utrzymywać główny kompilator w inne. Jednak sam język ma sens. Po prostu zwykle nie możesz sobie pozwolić na przebudowę całego ekosystemu.
Dagnelies

2
@arnaud Oraz fakt, że kompilator Javascript wymagałby środowiska Javascript, którego nie można zapisać w Javascript, ponieważ JavaScript wymaga środowiska Javascript, <powtarzaj paradoksalnie> , ponieważ środowisko Javascript nie jest dostarczane przez system operacyjny (i jeśli to nie byłoby napisane w JavaScript).
Qix

3
@Qix en.wikipedia.org/wiki/Bootstrapping_%28compilers%29 Ale głównie nie ma powodu, aby z niego korzystać. Jest powszechnie znany jako słaby język, przeglądarki nie używają go do kompilacji, ponieważ mają kontrolę nad sytuacją :), podczas gdy reszta z nas nie ma wyboru w Internecie.
Den

3
Nie jestem pewien co do twierdzenia „nie mam wielu zależności”. To może być prawdą dla kompilatora frontonu . Ale gdy tylko masz AST, uruchomienie własnego optymalizatora i generatora kodu nie wygląda na obiecującą trasę. Poza faktem, że nowoczesne techniki optymalizacji wymagają wyrafinowanych mechanizmów logiki formalnej, do których można by użyć biblioteki innej firmy, nie ma powodu, by wymyślać koło dla każdego nowego języka zamiast budować silną pozycję w branży, taką jak GCC lub LLVM.
5gon12eder

30

Cel posiadania kompilatora w kompilowanym języku jest często częścią praktyki „ jedzenia własnego jedzenia dla psów ”. To pokazuje światu, że uważasz, że język, kompilator i ekosystem modułów pomocniczych i narzędzi są „wystarczająco dobre do poważnej pracy” lub „gotowe do produkcji”.

Ma również pozytywny efekt, zmuszając osoby najbliższe projektowaniu języka, kompilatora i środowiska wykonawczego do bezpośredniego stawienia czoła skutkom wszystkich podjętych przez nich decyzji i wybranych przez nich priorytetów rozwojowych - brodawek i wszystkich innych. Często prowadzi to do powstania grupy podstawowej, która nie tylko rozumie środowisko językowe w teorii, ale ma duże praktyczne doświadczenie w posługiwaniu się językiem / narzędziami w tyglu trudnych warunków rzeczywistych.


1
dla kompletności: spożywanie własnej karmy dla psów ; patrz dog fed (przym.) lub dogfooding (czasownik)
Qix

17

Ludzie tworzą nowe języki ogólnego przeznaczenia z jednego głównego powodu: nienawidzą przynajmniej jednej rzeczy w każdym innym języku. Właśnie dlatego tak wiele języków nie powstaje. Masz świetny pomysł na język, który poprawiłby Twoje życie programistyczne, ale musisz wykonać pierwszą implementację w języku, który irytuje Cię przynajmniej na jeden sposób. Hosting własny oznacza, że ​​nie musisz już pracować w tym starym irytującym języku. Dlatego twórcy języka pracują nad tym krokiem i postrzegają go jako ważny kamień milowy.

Wiele funkcji językowych wygląda dobrze na papierze, ale kiedy zaczynasz używać ich w prawdziwym projekcie, zaczynasz dostrzegać ich ograniczenia. Na przykład, wiele języków początkowo nie ma przyzwoitej obsługi Unicode. Ukończenie dużego projektu pomaga upewnić się, że napotkano wiele takich sytuacji, a kompilator samoobsługowy jest tak dobrym projektem, jak każdy inny. Dlatego ludzie inni niż twórcy języka postrzegają to jako kamień milowy.

To nie znaczy, że jest to jedyny kamień milowy warty odnotowania. Istnieją funkcje, które nie są wykonywane przez kompilator, takie jak integracja bazy danych, interfejsy graficzne, praca w sieci itp.


Wydaje mi się, że (rodzimy) język jest językiem, w którym może się skompilować, a jądro Linuksa można do niego przenieść (ponieważ obejmuje większość / wszystkie zadania niezbędne do funkcjonowania większości współczesnych systemów operacyjnych).
Qix

Jednak przyzwoita obsługa Unicode nie jest tak naprawdę potrzebna do napisania kompilatora.
Paŭlo Ebermann

11

Steve Yegge napisał świetny post na blogu, który w pewien pośredni sposób rozwiązuje ten problem.

Wielki punkt nr 1: kompilatory obejmują prawie każdy aspekt informatyki. Są to kursy wyższego poziomu, ponieważ na początek musisz znać wszystkie inne rzeczy, których uczysz się w programie nauczania informatyki. Struktury danych, wyszukiwanie i sortowanie, wydajność asymptotyczna, kolorowanie wykresów? Wszystko tam jest.

Jest powód, dla którego Knuth pracował nad swoją monumentalną (i niekończącą się) „sztuką programowania komputerowego” od kilku dziesięcioleci, chociaż początkowo był (tylko) podręcznikiem kompilatora. W ten sam sposób, w jaki powiedział Carl Sagan „Jeśli chcesz zrobić szarlotkę od zera, musisz najpierw wynaleźć wszechświat”, jeśli chcesz napisać kompilator, musisz najpierw poradzić sobie z prawie każdym aspektem informatyki.

Oznacza to, że jeśli kompilator jest hostowany samodzielnie, to z pewnością będzie w stanie zrobić to, czego potrzebuję, bez względu na to, co robię. I odwrotnie, jeśli nie napisałeś kompilatora w swoim języku, istnieje duża szansa, że ​​ominie coś, co jest naprawdę ważne dla kogoś, ponieważ implementatorzy języka nigdy nie musieli pisać programu, który wymagałby od nich myślenia o tych wszystkich problemach.

Wielki punkt # 2: z 30 000 stóp zaskakująca liczba problemów wygląda jak kompilatory.

Kompilatory pobierają strumień symboli, ustalają ich strukturę zgodnie z pewnymi predefiniowanymi regułami specyficznymi dla domeny i przekształcają je w inny strumień symboli. Brzmi dość ogólnie, prawda? No tak.

Niezależnie od tego, czy jesteś w zespole Visual C ++, czy nie, bardzo często będziesz musiał zrobić coś, co wygląda jak część kompilatora. Robię to dosłownie każdego dnia.

W przeciwieństwie do większości innych zawodów, programiści nie tylko używają narzędzi, ale również budują własne narzędzia. Programista, który nie może (z powodu braku umiejętności lub braku przydatnych narzędzi do budowania innych narzędzi) pisać na zawsze będzie upośledzony, ograniczony do narzędzi, które zapewnia ktoś inny.

Jeśli język „nie nadaje się do tworzenia” programów, które mogą pobierać strumień symboli, stosować do nich reguły i przekształcać je w inny strumień symboli, co brzmi jak dość ograniczony język, a nie taki, który byłby przydatny Dla mnie.

(Na szczęście nie sądzę, aby istniało wiele języków programowania, które nie nadają się do przekształcania symboli. C jest prawdopodobnie jednym z najgorszych obecnie używanych takich języków, jednak kompilatory C są zazwyczaj hostowane przez siebie, więc nigdy nikogo nie zatrzymał.)

Trzeci powód, dla którego skończę, z własnego doświadczenia, o którym nie wspomina Yegge (ponieważ nie pisał o tym „dlaczego sam się hostuje”): to usuwa błędy. Kiedy piszesz kompilator, co oznacza, że ​​za każdym razem, gdy go budujesz (nie tylko za każdym razem, gdy go uruchamiasz ), zależy od niego, aby działał i działał poprawnie w oparciu o bazę kodową przyzwoitych rozmiarów (sam kompilator).

W tym miesiącu korzystałem ze stosunkowo nowego i znanego kompilatora nieobsługiwanego przez hosta (prawdopodobnie można się domyślić, który z nich) i nie mogę przejść 2 dni bez segregacji. Zastanawiam się, ile projektanci musieli z niego skorzystać.


8

Jeśli chcesz, aby kompilator dla języka X był samowystarczalny, najpierw musisz go zaimplementować w innym języku, powiedzmy Y, tak, że pobiera dane wejściowe dla języka X i wydziela kod asemblera lub kod pośredni, a nawet kod obiektu dla komputera, na którym działa kompilator. Chcesz wybrać język Y tak, aby był jak najbardziej podobny do języka X, ponieważ w pewnym momencie będziesz tłumaczyć kod napisany w Y na X.

Ale nie chcesz pisać więcej o kompilatorze w języku Y niż to konieczne, więc na początek implementujesz tylko podzbiór języka - eliminując zbędne konstrukcje. W przypadku języka typu „C”, natomiast ale nie za lub zrobić, gdy . jeśli ale nie ma przypadku ani trzeciorzędu op. Brak struktur, związków lub wyliczeń. Itd. Pozostało ci tylko tyle języka, aby napisać parser i podstawowy generator kodu dla języka X. Następnie sprawdź dane wyjściowe. Jeszcze raz.

Gdy już to zadziała, możesz przepisać źródło kompilatora, które zostało napisane w języku Y na język X, i skompilować źródło języka X za pomocą kompilatora napisanego w języku Y. Wyjście będzie nowym kompilatorem napisanym w nowym języku X, który kompiluje język X, tzn. jest teraz samowystarczalny. Nie jest to jednak kompletne, ponieważ zaimplementowałeś tylko podzbiór języka w języku Y.

Teraz dodajesz brakujące funkcje, testując każdą (lub grupę funkcji), czy generują poprawny kod. tzn. gdy funkcja zostanie zaimplementowana w kompilatorze, możesz pisać programy testowe przy użyciu nowych funkcji, kompilować je i testować, ale nie powinieneś ich jeszcze używać w źródle kompilatora. Po zweryfikowaniu nowych funkcji można następnie użyć tych nowych funkcji w samym źródle kompilatora - być może zastępując część oryginalnego kodu napisanego w podzbiorze językowym - ponownie skompilować źródło kompilatora przy użyciu wersji z nowymi funkcjami.

Masz teraz mechanizm dodawania nowych funkcji do języka - a po poprawnym wygenerowaniu kodu dla funkcji można ich użyć w następnej generacji samego kompilatora.

Jakieś 60 lat temu, kiedy komputery pojawiły się na scenie (a później, kiedy pojawiły się mikroprocesory), nie było innych języków Y odpowiednich do implementacji początkowego kompilatora. Tak więc pierwsze kompilatory musiały zostać napisane w kodzie asemblera, a następnie, gdy wystarczająca ilość kompilatora działała, kod asemblera zostałby zastąpiony wersją napisaną w nowym języku. Nie masz też asemblera? Cały procesor spadł o kolejny poziom, a asembler został początkowo zapisany w kodzie maszynowym .


2

Czy można stworzyć język programowania, który nie jest dobrze zaprojektowany do pisania kompilatora, ale jest dobrze zaprojektowany do innych celów?

Patrząc na język taki jak SQL, myślę, że odpowiedź brzmi tak. Ale języki tego rodzaju nie mają ogólnego zastosowania.


1
Zakwestionowane zaakceptowano: Napisz kompilator C w SQL.
Qix

2

Kto to mówi? ... w każdym razie to tylko opinia. Niektórzy mogą się zgodzić, inni nie, nie ma tu ani dobra, ani zła. Niektóre języki mają kompilatory napisane same w sobie, inne nie. Cokolwiek.

Niemniej jednak uważam, że to fajne ćwiczenie / weryfikacja koncepcji, jeśli język jest w stanie „skompilować się”… jest po prostu… fajny… i udowadnia, że ​​język nadaje się do wykonywania skomplikowanych czynności.

Chciałbym również wspomnieć, że pomimo tego, że jest miły, istnieje wiele różnych powodów, dla których kompilator może być napisany w innym języku. Na przykład większość silników javascript nie jest napisana w javascript. Powodów jest wiele: integracja z innym oprogramowaniem, łączenie z istniejącymi bibliotekami / zależnościami, lepsze narzędzia, wydajność, starszy kod ... Czasami samokompilacja języka jest przyjemna, ale nadal warto utrzymywać główny kompilator w inne. Jednak sam język ma sens. Po prostu zwykle nie możesz sobie pozwolić na przebudowę całego ekosystemu.


2

Clang jest napisany w C ++. Przepisanie kompilatora Clang Objective-C w Objective-C nie byłoby trudne, ale byłoby to zupełnie bezużyteczne. Wszelkie zmiany w kompilatorze C ++ musiałyby być przerobione w Objective-C i odwrotnie. Więc dlaczego?

Jest teraz kompilator Clang Swift. Z pewnością ten kompilator można przepisać w Swift. Ale jaki by to był cel? Aby wykazać, że język jest wystarczająco silny, aby napisać w nim kompilator? Nikt nie dba o to, czy potrafisz pisać kompilatory w Swift. Ludzi nie obchodzi, czy można napisać interfejsów użytkownika w Swift, i wyraźnie można.

Jeśli masz dobrze przetestowany kompilator, który można łatwo dostosować do kompilacji różnych języków, przepisywanie go na różne języki jest zupełnie bezcelowe, chyba że przepisanie w innym języku ułatwiłoby pracę z kompilatorem. A jeśli byłoby sensu pisać dzyń w Swift, na przykład wtedy Clang C, C ++ i Objective-C kompilatory to wszystko być napisane w Swift.

Są ważniejsze rzeczy do zrobienia niż udowodnienie, że możesz napisać kompilator w jakimś języku programowania.


1

Pokazuje, że język jest w stanie przetwarzać złożone przetwarzanie ciągów i tłumaczyć na inny język / samemu się tłumaczyć.

Podczas tworzenia kompilatora (pierwszego dużego projektu) na pierwszy plan wysuną się problemy.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.