Jakie rodzaje wzorców mogę zastosować w kodzie, aby ułatwić tłumaczenie na inny język programowania? [Zamknięte]

95

Mam zamiar zrobić poboczny projekt, którego celem jest przetłumaczenie kodu z jednego języka programowania na inny. Języki, od których zaczynam to PHP i Python (Python do PHP powinno być łatwiejsze na początku), ale idealnie byłoby, gdyby był w stanie dodać inne języki z (względną) łatwością. Plan jest następujący:

  • Jest to ukierunkowane na tworzenie stron internetowych. Oryginalny i docelowy kod będzie siedział na szczycie frameworka (który też będę musiał napisać). Te ramy będą obejmować wzorzec projektowy MVC i będą przestrzegać ścisłych konwencji kodowania. Powinno to nieco ułatwić tłumaczenie.

  • Patrzę również na IOC i wstrzyknięcie zależności, ponieważ mogą one ułatwić proces tłumaczenia i zmniejszyć podatność na błędy.

  • Skorzystam z modułu parsera Pythona , który pozwala mi bawić się drzewem składni abstrakcyjnych. Najwyraźniej najbliżej, co mogę uzyskać z PHP, jest token_get_all () , który jest początkiem.

  • Od tego momentu mogę budować AST, tablice symboli i przepływ sterowania.

Wtedy wierzę, że mogę rozpocząć tworzenie kodu. Nie potrzebuję idealnego tłumaczenia . Nadal będę musiał przejrzeć wygenerowany kod i rozwiązać problemy. W idealnym przypadku tłumacz powinien zgłosić problematyczne tłumaczenia.

Zanim zapytasz: „Jaki jest w tym do cholery sens?” Odpowiedź brzmi ... To będzie ciekawe doświadczenie edukacyjne. Jeśli masz jakieś spostrzeżenia, jak to zrobić, daj mi znać.


EDYTOWAĆ:

Bardziej interesuje mnie wiedza, jakie rodzaje wzorców mogę zastosować w kodzie, aby ułatwić tłumaczenie (np. IoC, SOA?), Niż sposób wykonania tłumaczenia.

NullUserException
źródło
6
Czy spojrzałeś na systemy takie jak .NET CLR lub Perl6's Parrot? Kompilują zestaw języków aż do reprezentacji pośredniej, którą może uruchomić wspólny interpreter. Jeśli możesz wrócić z reprezentacji pośredniej do języka, masz tłumacza.
Borealid
1
@Borealid AFAIK .NET CIL jest (stosunkowo) łatwo skompilować pod , ale powodzenia uzyskanie czytelnego kodu z powrotem z tego. Patrzę teraz na Parrot.
NullUserException,
Istnieją podobne projekty dla innych języków; Nie jestem pewien, jak bogaci są ich autorzy. I tak naprawdę bardzo się tutaj ograniczam, potrzebując ram i przestrzegania ścisłych konwencji kodowania.
NullUserException,
2
Nie mogę dodać żadnej konkretnej wiedzy, ale czy spojrzałeś na piżamę ( pyjs.org ), a konkretnie na translator.py? To jest kompilator python do javascript.
Stephan
3
Re EDIT: Jeśli masz kontrolę nad kodem, który zostanie przetłumaczony, najbardziej oczywistą rzeczą jest unikanie konstrukcji, które są trudne do przetłumaczenia! Na przykład C jest znacznie łatwiejsze do przetłumaczenia na Javę, jeśli nie ma żadnej arytmetyki wskaźnikowej. W przypadku Pythona prawdopodobnie trzymałbym się z daleka od zamknięć. Inną rzeczą, którą możesz zrobić, jest napisanie kodu źródłowego w taki sposób, że trudniejsze do przetłumaczenia części są zawsze kodowane idiomatycznie, co ułatwia ich rozpoznanie i obsługę specjalnych przypadków.
Ira Baxter

Odpowiedzi:

122

Tworzę narzędzia (DMS Software Reengineering Toolkit) do manipulacji programami ogólnego przeznaczenia (ze szczególnym uwzględnieniem tłumaczeń językowych) od 1995 roku, wspierane przez silny zespół informatyków. DMS zapewnia generyczne analizowanie, budowanie AST, tablice symboli, kontrolę i analizę przepływu danych, stosowanie reguł tłumaczenia, regenerację tekstu źródłowego z komentarzami itp., Wszystko sparametryzowane przez wyraźne definicje języków komputerowych.

Ilość maszyn, których potrzebujesz, aby to zrobić dobrze, jest ogromna (szczególnie jeśli chcesz móc to zrobić dla wielu języków w sposób ogólny), a następnie potrzebujesz niezawodnych parserów dla języków z niewiarygodnymi definicjami (PHP jest tego doskonałym przykładem ).

Nie ma nic złego w tym, że myślisz o stworzeniu translatora z języka na język lub o jego próbie, ale myślę, że dla prawdziwych języków będzie to o wiele większe zadanie, niż się spodziewasz. Zainwestowaliśmy około 100 osobolat w sam DMS i kolejne 6-12 miesięcy w każdą „niezawodną” definicję języka (w tym tę, którą boleśnie zbudowaliśmy dla PHP), znacznie więcej w przypadku nieprzyjemnych języków, takich jak C ++. To będzie „piekielna nauka”; to było dla nas. (Sekcja artykułów technicznych na powyższej stronie może być interesująca, aby szybko rozpocząć naukę).

Ludzie często próbują zbudować jakiś rodzaj uogólnionej maszyny, zaczynając od jakiejś technologii, którą znają, a która wykonuje część pracy. (Świetnym przykładem są Python AST). Dobra wiadomość jest taka, że ​​część pracy została wykonana. Zła wiadomość jest taka, że ​​maszyneria ma wbudowane miliony założeń, których większości nie odkryjesz, dopóki nie spróbujesz zmusić ich do zrobienia czegoś innego. W tym momencie dowiadujesz się, że maszyneria jest zaprogramowana tak, aby robić to, co pierwotnie robi, i naprawdę naprawdę oprze się twojej próbie zmuszenia go do zrobienia czegoś innego. (Podejrzewam, że próba zmuszenia Pythona AST do modelowania PHP będzie świetną zabawą).

Powodem, dla którego zacząłem budować DMS, było zbudowanie fundamentów, które miały bardzo niewiele wbudowanych takich założeń. Niektóre przyprawiają nas o ból głowy. Jak dotąd żadnych czarnych dziur. (Najtrudniejszą częścią mojej pracy w ciągu ostatnich 15 lat jest próba zapobieżenia wkradaniu się takich założeń).

Wielu ludzi popełnia również błąd, zakładając, że jeśli potrafią przeanalizować (i być może uzyskać AST), są na najlepszej drodze do zrobienia czegoś skomplikowanego. Jedną z trudnych lekcji jest to, że potrzebujesz tabel symboli i analizy przepływu, aby przeprowadzić dobrą analizę lub transformację programu. AST są konieczne, ale niewystarczające. To jest powód, dla którego książka kompilatora Aho & Ullmana nie kończy się na rozdziale 2. (OP ma do tego prawo, planując budowę dodatkowych maszyn poza AST). Aby uzyskać więcej informacji na ten temat, zobacz Life After Parsing .

Uwaga „Nie potrzebuję doskonałego tłumaczenia” jest kłopotliwa. To, co robią słabi tłumacze, to konwertowanie „łatwych” 80% kodu, pozostawiając trudne 20% do zrobienia ręcznie. Jeśli aplikacja, którą zamierzasz przekonwertować, jest dość mała i zamierzasz ją przekonwertować tylko raz, wtedy te 20% jest w porządku. Jeśli chcesz przekonwertować wiele aplikacji (lub nawet tę samą z niewielkimi zmianami w czasie), nie jest to miłe. Jeśli spróbujesz przekonwertować 100 000 SLOC, 20% to 20 000 oryginalnych linii kodu, które są trudne do przetłumaczenia, zrozumienia i modyfikacji w kontekście kolejnych 80 000 przetłumaczonych linii programu, którego już nie rozumiesz. To wymaga ogromnego wysiłku. Na poziomie miliona linii w praktyce jest to po prostu niemożliwe.trudniejsze i zwykle boleśnie się o tym przekonują, z dużymi opóźnieniami, wysokimi kosztami i często wręcz porażką).

To, do czego musisz dążyć, aby tłumaczyć systemy na dużą skalę, to wysoki procentowy współczynnik konwersji z lat dziewięćdziesiątych, albo jest prawdopodobne, że nie możesz wykonać ręcznej części czynności tłumaczeniowej.

Inną kluczową kwestią jest rozmiar kodu do przetłumaczenia. Zbudowanie działającego, solidnego tłumacza wymaga dużo energii, nawet przy użyciu dobrych narzędzi. Chociaż wydaje się seksowne i fajne, aby zbudować tłumacza zamiast po prostu wykonywać ręczną konwersję, dla małych baz kodu (np. Do około 100 000 SLOC z naszego doświadczenia) ekonomia po prostu tego nie usprawiedliwia. Nikt nie lubi tej odpowiedzi, ale jeśli naprawdę musisz przetłumaczyć tylko 10 000 SLOC kodu, prawdopodobnie lepiej będzie po prostu ugryźć kulę i zrobić to. I tak, to bolesne.

Uważam, że nasze narzędzia są wyjątkowo dobre (ale z drugiej strony jestem dość stronniczy). I nadal bardzo trudno jest zbudować dobrego tłumacza; zajmuje nam to około 1,5-2 osobolat i wiemy, jak korzystać z naszych narzędzi. Różnica polega na tym, że przy tak dużej liczbie maszyn znacznie częściej odnosimy sukcesy niż porażki.

Ira Baxter
źródło
8
Czy kiedykolwiek zastanawiałeś się nad przekazaniem swojej „boleśnie zbudowanej” definicji PHP z powrotem do całej społeczności PHP, czy też jest to zbyt blisko związane z własnym strumieniem dochodów, aby było to wykonalne?
TML
53
Poproszono mnie, aby wszystko, co robimy, było „open source” przez wiele osób, które nie chciały wnosić wkładu w strumień przychodów i nie miały energii, aby samodzielnie wykonać tę pracę i otworzyć oprogramowanie. Jeśli wnosisz tylko niewielką część do bardzo dużego projektu i / lub masz inne źródło dochodu, „open source” wydaje się w porządku. Jeśli sam wykonałeś całą pracę i jest to Twoje jedyne źródło dochodu, jest to o wiele mniej atrakcyjne. [Nie chcę wdawać się w dyskusję o względnych zaletach filozofii „wolnego oprogramowania”, więc nie będę brać udziału w żadnych dalszych komentarzach na ten temat]
Ira Baxter,
9
Zgadzam się z tym, co tu powiedziałeś, i dlatego sformułowałem pytanie tak, jak to zrobiłem. Wydaje mi się, że na podstawie tej odpowiedzi mamy wyczuć, że uważasz, że jest ona zbyt ściśle związana z Twoimi dochodami i nie ma w tym absolutnie nic złego - po prostu pomyślałem, że warto o to zapytać.
TML
3
@IraBaxter Po prostu mówisz popularne idiomy na temat praktyk związanych z komputerem, które można zastosować w wielu innych praktykach. Jedyną ciekawą rzeczą we wszystkim, co napisałeś, są linki do semanticdesigns.com (która jest Twoją firmą)
amirouche
1
W swoich odpowiedziach często podajesz linki do stron związanych z Clang. To tylko dowodzi, że ktoś inny może stworzyć stronę internetową. Większość z nas zakłada, że ​​dobrze napisana strona internetowa oznacza, że ​​stoi za nią poważna, prawdziwa praca, a nie tylko oszukańcza próba oszukania czytelnika, jak zdaje się sugerować w swojej odpowiedzi. Czy naprawdę uważasz, że strona internetowa jest fałszywa? Strona zawiera informacje odniesienia do „odpowiedniego” źródła; jest anonimizowane, ponieważ wymagała tego umowa o pracę. Że nie mogę pomóc.
Ira Baxter
13

Moja odpowiedź będzie dotyczyła konkretnego zadania analizy Pythona w celu przetłumaczenia go na inny język, a nie aspektów wyższego poziomu, do których Ira dobrze się odniósł w swojej odpowiedzi.

W skrócie: nie używaj modułu parsera, jest prostszy sposób.

astModuł, dostępny od Pythonie 2.6 jest o wiele bardziej odpowiednie dla swoich potrzeb, ponieważ daje gotową AST pracować. Napisałem artykuł na ten temat w zeszłym roku, ale w skrócie, użyj parsemetody astdo parsowania kodu źródłowego Pythona w AST. parserModuł daje składniowy drzewo, a nie AST. Uważaj na różnicę .

Teraz, ponieważ AST Pythona są dość szczegółowe, biorąc pod uwagę AST, zadanie front-end nie jest strasznie trudne. Przypuszczam, że można mieć prosty prototyp niektórych części funkcjonalności gotowy dość szybko. Jednak znalezienie kompletnego rozwiązania zajmie więcej czasu, głównie ze względu na różną semantykę języków. Prosty podzbiór języka (funkcje, podstawowe typy itd.) Można łatwo przetłumaczyć, ale kiedy już przejdziesz do bardziej złożonych warstw, będziesz potrzebować ciężkiej maszyny, aby emulować rdzeń jednego języka w innym. Na przykład weźmy pod uwagę generatory Pythona i listy składane, które nie istnieją w PHP (według mojej najlepszej wiedzy, co jest wprawdzie słabe, gdy w grę wchodzi PHP).

Aby dać ci ostatnią wskazówkę, rozważ 2to3narzędzie stworzone przez programistów Pythona do tłumaczenia kodu Python 2 na kod Python 3. Jeśli chodzi o front-end, zawiera większość elementów potrzebnych do przetłumaczenia Pythona na coś . Ponieważ jednak rdzenie Pythona 2 i 3 są podobne, nie jest tam wymagana żadna maszyna emulująca.

Eli Bendersky
źródło
No dobrze. 2to3to tylko AST do AST. Nie obsługuje robienia niczego, co wykracza poza możliwości astmodułu. Zauważ, że wszystkie tłumaczenia przechodzą od składni obsługiwanej przez proces języka Python hosta do składni obsługiwanej przez proces języka Python hosta. Nie ma tłumacza, który dodaje, powiedzmy, adnotacje funkcji, ponieważ 2.6 tego nie obsługuje.
habnabit
... a pytanie OP może być sformułowane w krótkiej perspektywie, jak przejść z Pythona 2.6 AST do ... czegoś w PHP. Moduł ast prawdopodobnie nie będzie chciał dobrze reprezentować składni PHP, więc nie jest to nawet ast.
Ira Baxter
2
@Aaron: 2to3można potraktować jako przykład wykorzystania AST wygenerowanego z ast.
Eli Bendersky,
AFAIK, 2to3 prawdopodobnie jest łatwiejszym tłumaczeniem niż Python na PHP (w końcu jego Python na Python, prawda)? I nawet to nie działa szczególnie dobrze. Zwróć uwagę na dużą ilość Pythona 2.6, która nie została jeszcze przepchnięta przez 2to3 ... ponieważ najwyraźniej jest jeszcze kilka ręcznych łatek po tłumaczeniu, które wciąż trzeba wykonać. Gdyby był w 100% zautomatyzowany, Python 2.6 byłby martwy.
Ira Baxter
5

Pisanie tłumacza nie jest niemożliwe, zwłaszcza biorąc pod uwagę, że Joel's Intern robił to latem.

Jeśli chcesz używać jednego języka, to jest łatwe. Jeśli chcesz zrobić więcej, jest to trochę trudniejsze, ale nie za dużo. Najtrudniejsze jest to, że podczas gdy każdy kompletny język turing może robić to, co inny turing kompletny język, wbudowane typy danych mogą fenomenalnie zmienić to, co robi język.

Na przykład:

word = 'This is not a word'
print word[::-2]

wymaga duplikowania dużej ilości kodu C ++ (ok, cóż, możesz to zrobić dość krótko z niektórymi konstrukcjami pętlowymi, ale nadal).

Myślę, że to trochę na marginesie.

Czy kiedykolwiek napisałeś tokenizer / parser oparty na gramatyce języka? Prawdopodobnie będziesz chciał się nauczyć, jak to zrobić, jeśli tego nie zrobiłeś, ponieważ to główna część tego projektu. Wymyśliłbym podstawową kompletną składnię Turinga - coś dość podobnego do kodu bajtowego Pythona . Następnie tworzysz lekser / parser, który pobiera gramatykę języka (być może używając BNF ) i na podstawie gramatyki kompiluje język do twojego języka pośredniego. Następnie zechcesz zrobić odwrotnie - utwórz parser ze swojego języka na języki docelowe w oparciu o gramatykę.

Najbardziej oczywistym problemem, jaki widzę, jest to, że na początku prawdopodobnie utworzysz okropnie nieefektywny kod, szczególnie w potężniejszych * językach, takich jak Python.

Ale jeśli zrobisz to w ten sposób, prawdopodobnie będziesz w stanie wymyślić sposoby optymalizacji wyników w trakcie pracy. Podsumowując:

  • przeczytaj podaną gramatykę
  • skompiluj program do pośredniej (ale również kompletnej Turinga) składni
  • skompilować program średniozaawansowany na język docelowy (na podstawie dostarczonej gramatyki)
  • ...?
  • Zysk!(?)

* przez potężne rozumiem, że zajmuje to 4 linie:

myinput = raw_input("Enter something: ")
print myinput.replace('a', 'A')
print sum(ord(c) for c in myinput)
print myinput[::-1]

Pokaż mi inny język, który może zrobić coś takiego w 4 wierszach, a pokażę ci język, który jest tak potężny jak Python.

Wayne Werner
źródło
„Czy kiedykolwiek napisałeś tokenizer / parser oparty na gramatyce języka?” Zrobiłem to za pomocą JavaCC.
NullUserException,
2
Stażysta Joela przez lato wykonywał częściową pracę. Jego język źródłowy był podzbiorem istniejącego języka i przypuszczalnie ten podzbiór można było nieco skorygować. To znacznie ułatwia pracę. Podobnie, NullPointerException może chcieć zacząć od łatwiejszych części Pythona, być może przechodząc przez trudniejsze rzeczy do ręcznej konwersji (jak zaznaczono w pytaniach).
David Thornley,
@NullUserException: Będziesz miał pewne ujawnienie, ale w zasadzie będziesz robił ponowną implementację JavaCC, tylko zamiast Javy jako języka wyjściowego, będziesz robić <wstaw język tutaj>. @David, całkiem tak. Nawet Thistle potrzebuje pomocy w niektórych konstrukcjach językowych. Gdybym był OP, najpierw wybrałbym funkcjonalność, a następnie optymalizację, w przeciwnym razie utknąłbym na zawsze próbując zmusić C ++ do cięcia ciągów (z krokami): p
Wayne Werner
@WayneWerner Dla przypomnienia, języki takie jak C # w ogóle nie wymagają znaków nowej linii. (Przynajmniej nie po usunięciu jednowierszowych komentarzy). Możesz więc napisać dowolny program C # w jednej linii. Ale oczywiście rozumiem, do czego zmierzasz.
leviathanbadger
@ aboveyou00: Nie sądzę, że to prawda. Jeśli nie zezwolisz na warunki preprocesora, możesz mieć rację.
Ira Baxter
3

Jest kilka odpowiedzi, które mówią ci, żebyś się nie przejmował. Jak bardzo to pomocne? Chcesz się uczyć? Możesz się uczyć. To jest kompilacja. Tak się składa, że ​​językiem docelowym nie jest kod maszynowy, ale inny język wysokiego poziomu. Odbywa się to cały czas.

Rozpoczęcie pracy jest stosunkowo łatwe. Najpierw pobierz http://sourceforge.net/projects/lime-php/ (jeśli chcesz pracować w PHP) lub coś takiego i przejrzyj przykładowy kod. Następnie możesz napisać analizator leksykalny, używając sekwencji wyrażeń regularnych i przekazując tokeny do generowanego parsera. Twoje działania semantyczne mogą albo generować kod bezpośrednio w innym języku, albo tworzyć jakąś strukturę danych (obiekty myślowe, człowiek), którą możesz masować i przemierzać, aby wygenerować kod wyjściowy.

Masz szczęście z PHP i Pythonem, ponieważ pod wieloma względami są one tym samym językiem, ale mają inną składnię. Najtrudniejsze jest pokonanie semantycznych różnic między formami gramatycznymi i strukturami danych. Na przykład Python ma listy i słowniki, podczas gdy PHP ma tylko tablice pomocnicze.

Podejście „uczącego się” polega na zbudowaniu czegoś, co działa poprawnie dla ograniczonego podzbioru języka (takiego jak tylko instrukcje drukowania, prosta matematyka i przypisanie zmiennych), a następnie stopniowe usuwanie ograniczeń. Zasadniczo to właśnie robili wszyscy „duzi” faceci w terenie.

Aha, a ponieważ w Pythonie nie ma typów statycznych, najlepiej byłoby pisać i polegać na funkcjach PHP, takich jak „python_add”, które dodają liczby, ciągi znaków lub obiekty zgodnie ze sposobem, w jaki robi to Python.

Oczywiście może się to znacznie zwiększyć, jeśli na to pozwolisz.

Ian
źródło
3
Właściwie nie powiedziałem „nie przejmuj się”. Powiedziałem, że „tłumaczenie języków na ogół jest bardzo trudne”. Jeśli OP podąży swoją pierwotną ścieżką używania drzew Pythona do generowania PHP, wiele się nauczy i jestem zwolennikiem nauki; Tam też zacząłem. Nie będzie mógł łatwo dodawać nowych języków.
Ira Baxter
@IraBaxter Nie mogę poprzeć twojego oświadczenia, zrobienie Python-> PHP i PHP-> Javascript byłoby dość łatwe. por. ostatnia część stackoverflow.com/a/22850139/140837 w środku odpowiedzi Zajmuję się również twoją "argumentacją"
amirouche
2

Na drugim miejscu podam punkt widzenia @EliBendersky dotyczący używania ast.parse zamiast parsera (o czym wcześniej nie wiedziałem). Serdecznie polecam również przejrzenie jego bloga. Użyłem ast.parse do tłumaczenia Pythona-> JavaScript (@ https://bitbucket.org/amirouche/pythonium ). Wymyśliłem projekt Pythonium, przeglądając nieco inne implementacje i wypróbowując je samodzielnie. Rozwidliłem Pythonium z https://github.com/PythonJS/PythonJS, które również zacząłem, to właściwie kompletne przepisanie. Ogólny projekt został zainspirowany PyPy i http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-89-1.pdf papierem .

Wszystko, czego próbowałem, od początku do najlepszego rozwiązania, nawet jeśli wygląda to na marketing w Pythonium, tak naprawdę nie jest (nie wahaj się powiedzieć mi, jeśli coś nie wydaje się poprawne z netykietą):

  • Zaimplementuj semantykę języka Python w zwykłym starym języku JavaScript przy użyciu dziedziczenia prototypów: AFAIK nie można zaimplementować dziedziczenia wielokrotnego w Pythonie przy użyciu systemu obiektów prototypowych JS. Próbowałem to zrobić później, używając innych sztuczek (por. Getattribute). O ile wiem, w JavaScript nie ma implementacji wielokrotnego dziedziczenia w Pythonie, najlepsze, co istnieje, to Dziedziczenie pojedyncze + mieszanki i nie jestem pewien, czy obsługują dziedziczenie diamentów. Coś podobnego do Skulpt, ale bez Google Clojure.

  • Próbowałem z Google Clojure, tak jak Skulpt (kompilator), zamiast faktycznie czytać kod Skulpt #fail. W każdym razie, ponieważ system obiektowy oparty na prototypie JS jest nadal niemożliwy. Tworzenie bindowania było bardzo trudne, trzeba napisać JavaScript i dużo standardowego kodu (por. Https://github.com/skulpt/skulpt/issues/50, gdzie jestem duchem). W tym czasie nie było jasnego sposobu na zintegrowanie wiązania w systemie kompilacji. Myślę, że Skulpt to biblioteka i wystarczy, że włączysz swoje pliki .py w html do wykonania, żadna faza kompilacji nie jest wymagana przez programistę.

  • Próbowałem pyjaco (kompilator), ale tworzenie powiązań (wywoływanie kodu Javascript z kodu Pythona) było bardzo trudne, za każdym razem było zbyt wiele gotowego kodu do utworzenia. Teraz myślę, że pyjaco jest bardziej zbliżone do Pythonium. pyjaco jest napisane w Pythonie (również ast.parse), ale wiele jest napisanych w JavaScript i wykorzystuje dziedziczenie prototypów.

Tak naprawdę nigdy nie udało mi się uruchomić programu Pyjamas #fail i nigdy więcej nie próbowałem odczytać kodu #fail. Ale moim zdaniem piżama wykonywała translację API-> API (lub framework do frameworka), a nie tłumaczenie Pythona na JavaScript. Struktura JavaScript zużywa dane, które są już na stronie lub dane z serwera. Kod Pythona jest tylko „hydrauliką”. Potem odkryłem, że piżama była w rzeczywistości prawdziwym tłumaczem Pythona-> js.

Nadal myślę, że możliwe jest tłumaczenie API-> API (lub framework-> framework) i to jest w zasadzie to, co robię w Pythonium, ale na niższym poziomie. Prawdopodobnie Piżama używa tego samego algorytmu co Pythonium ...

Potem odkryłem brythona w pełni napisanego w Javascript, jak Skulpt, bez potrzeby kompilacji i dużo kłopotów ... ale napisane w JavaScript.

Od pierwszej linii napisanej w trakcie tego projektu wiedziałem o PyPy, nawet o zapleczu JavaScript dla PyPy. Tak, możesz, jeśli go znajdziesz, bezpośrednio wygenerować interpreter Pythona w JavaScript z PyPy. Ludzie mówią, że to była katastrofa. Nie czytałem, gdzie, dlaczego. Ale myślę, że powodem jest to, że język pośredni, którego używają do implementacji interpretera, RPython, jest podzbiorem Pythona dostosowanym do tłumaczenia na C (i być może ASM). Ira Baxter mówi, że zawsze robisz założenia, kiedy coś budujesz i prawdopodobnie dostrajasz to, aby było najlepsze w przypadku tłumaczenia PyPy: Python-> C. Założenia te mogą nie mieć zastosowania w innym kontekście, jeśli mogą wpływać na koszty ogólne, w przeciwnym razie bezpośrednie tłumaczenie najprawdopodobniej zawsze będzie lepsze.

Posiadanie tłumacza napisanego w Pythonie brzmiało jak (bardzo) dobry pomysł. Ale bardziej interesował mnie kompilator ze względu na wydajność, a tak naprawdę łatwiej jest skompilować Python do JavaScript niż go zinterpretować.

Zacząłem PythonJS z pomysłem połączenia podzbioru Pythona, który mógłbym łatwo przetłumaczyć na JavaScript. Na początku nawet nie zawracałem sobie głowy wdrażaniem systemu OO ze względu na wcześniejsze doświadczenia. Podzbiór Pythona, który udało mi się przetłumaczyć na JavaScript, to:

  • funkcja o pełnych parametrach semantycznych zarówno w definicji, jak i wywołaniu. To jest część, z której jestem najbardziej dumny.
  • while / if / elif / else
  • Typy Pythona zostały przekonwertowane na typy JavaScript (nie ma żadnych typów Pythona)
  • ponieważ może iterować tylko po tablicach JavaScript (dla tablicy in)
  • Przejrzysty dostęp do JavaScript: jeśli napiszesz Array w kodzie Pythona, zostanie on przetłumaczony na Array w javascript. To największe osiągnięcie pod względem użyteczności w stosunku do konkurencji.
  • Możesz przekazać funkcję zdefiniowaną w źródle Pythona do funkcji javascript. Uwzględnione zostaną domyślne argumenty.
  • Add ma specjalną funkcję o nazwie new, która jest tłumaczona na JavaScript new, np. New (Python) (1, 2, spam, "egg") jest tłumaczone na "new Python (1, 2, spam," egg ").
  • „zmienne” są automatycznie obsługiwane przez tłumacza. (bardzo ładne odkrycie od Bretta (współautora PythonJS).
  • słowo kluczowe globalne
  • domknięcia
  • lambdy
  • lista zrozumienia
  • import jest obsługiwany przez requirejs
  • dziedziczenie pojedynczej klasy + mieszanie przez classyjs

Wydaje się, że to dużo, ale w rzeczywistości bardzo wąskie w porównaniu do pełnej semantyki Pythona. To naprawdę JavaScript ze składnią Pythona.

Wygenerowany JS jest doskonały tj. nie ma narzutów, nie można go poprawić pod względem wydajności poprzez dalszą edycję. Jeśli możesz ulepszyć wygenerowany kod, możesz to zrobić również z pliku źródłowego Pythona. Ponadto kompilator nie polegał na żadnych sztuczkach JS, które można znaleźć w .js napisanym przez http://superherojs.com/ , więc jest bardzo czytelny.

Bezpośrednim następcą tej części PythonJS jest tryb Pythonium Veloce. Pełną implementację można znaleźć @ https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pythonium/veloce/veloce.py?at=master 793 SLOC + około 100 SLOC kodu współdzielonego z innym tłumaczem.

Zaadaptowaną wersję pystones.py można przetłumaczyć w trybie Veloce por. https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pystone/?at=master

Po skonfigurowaniu podstawowego tłumaczenia Python-> JavaScript wybrałem inną ścieżkę do przetłumaczenia pełnego Pythona na JavaScript. Sposób tworzenia kodu zorientowanego obiektowo w języku glib, z wyjątkiem języka docelowego, to JS, więc masz dostęp do tablic, obiektów podobnych do map i wielu innych sztuczek, a cała ta część została napisana w Pythonie. IIRC nie ma kodu javascript napisanego przez tłumacza Pythonium. Uzyskanie pojedynczego dziedziczenia nie jest trudne. Oto trudne elementy zapewniające pełną zgodność Pythonium z Pythonem:

  • spam.eggw Pythonie jest zawsze tłumaczone na. getattribute(spam, "egg")Nie profilowałem tego w szczególności, ale myślę, że tracę dużo czasu i nie jestem pewien, czy mogę to poprawić za pomocą asm.js lub czegokolwiek innego.
  • kolejność rozwiązywania metod: nawet z algorytmem napisanym w Pythonie, przetłumaczenie go na kod zgodny z Python Veloce było dużym przedsięwzięciem.
  • getattributre : rzeczywisty algorytm rozpoznawania getattribute jest dość skomplikowany i nadal nie obsługuje deskryptorów danych
  • w oparciu o klasę metaklasy: Wiem, gdzie podłączyć kod, ale nadal ...
  • last bu not least: some_callable (...) jest zawsze tłumaczone na „call (some_callable)”. ODPOWIEDŹ, tłumacz w ogóle nie używa wnioskowania, więc za każdym razem, gdy wykonujesz połączenie, musisz sprawdzić, jaki rodzaj obiektu ma nazywać go tak, jak ma być nazywany.

Ta część jest uwzględniona w https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pythonium/compliant/runtime.py?at=master Jest napisana w Pythonie zgodnym z Python Veloce.

Rzeczywisty zgodny tłumacz https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pythonium/compliant/compliant.py?at=master nie generuje kodu JavaScript bezpośrednio, a co najważniejsze nie wykonuje transformacji ast-> ast . Wypróbowałem ast-> ast i ast, nawet jeśli ładniej niż cst, nie jest przyjemny w pracy z ast.NodeTransformer i co ważniejsze, nie muszę robić ast-> ast.

W moim przypadku robienie python ast do python ast może być przynajmniej poprawą wydajności, ponieważ czasami sprawdzam zawartość bloku przed wygenerowaniem powiązanego z nim kodu, na przykład:

  • var / global: aby móc coś zmienić, muszę wiedzieć, czego potrzebuję, a nie var. Zamiast generować blok śledzący, która zmienna jest tworzona w danym bloku i wstawiać ją na wierzchu wygenerowanego bloku funkcyjnego, po prostu szukam rewelacyjnego przypisania zmiennej, kiedy wchodzę do bloku przed faktyczną wizytą w węźle potomnym, aby wygenerować powiązany kod.
  • wydajność, generatory mają na razie specjalną składnię w JS, więc muszę wiedzieć, która funkcja Pythona jest generatorem, kiedy chcę napisać "var my_generator = function"

Więc tak naprawdę nie odwiedzam każdego węzła raz dla każdej fazy tłumaczenia.

Cały proces można opisać jako:

Python source code -> Python ast -> Python source code compatible with Veloce mode -> Python ast -> JavaScript source code

Wbudowane Python są napisane w kodzie Pythona (!), IIRC istnieje kilka ograniczeń związanych z typami ładowania początkowego, ale masz dostęp do wszystkiego, co może tłumaczyć Pythonium w trybie zgodności. Zajrzyj na https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pythonium/compliant/builtins/?at=master

Czytanie kodu JS wygenerowanego zgodnie z Pythonium jest zrozumiałe, ale mapy źródłowe bardzo pomogą.

Cenną radą, jaką mogę ci dać w świetle tego doświadczenia, są dobre stare pierdy:

  • obszernie przejrzeć temat zarówno w literaturze, jak i istniejących projektach zamkniętych lub bezpłatnych. Kiedy przeglądałem różne istniejące projekty, powinienem był poświęcić mu znacznie więcej czasu i motywacji.
  • zadawać pytania! Gdybym wiedział wcześniej, że backend PyPy był bezużyteczny z powodu narzutu spowodowanego niedopasowaniem semantycznym C / Javascript. Być może wpadłbym na pomysł na Pythonium już 6 miesięcy temu, może 3 lata temu.
  • wiedzieć, co chcesz zrobić, mieć cel. W przypadku tego projektu miałem różne cele: poćwiczyć trochę javascript, dowiedzieć się więcej o Pythonie i móc napisać kod Pythona, który będzie działał w przeglądarce (więcej i to poniżej).
  • porażka to doświadczenie
  • mały krok to krok
  • zacznij od małych
  • miej wielkie marzenia
  • robić dema
  • powtarzać

Jestem bardzo zadowolony z samego trybu Python Veloce! Ale po drodze odkryłem, że tak naprawdę szukam uwolnienia mnie i innych od Javascript, ale co ważniejsze, możliwości wygodnego tworzenia . To doprowadziło mnie do schematu, DSL, modeli i ostatecznie modeli specyficznych dla domeny (por. Http://dsmforum.org/ ).

O odpowiedzi Iry Baxtera:

Szacunki nie są w ogóle pomocne. Zajęło mi mniej więcej 6 miesięcy wolnego czasu zarówno na PythonJS, jak i Pythonium. Mogę więc oczekiwać więcej od 6 miesięcy w pełnym wymiarze godzin. Myślę, że wszyscy wiemy, co 100 osobolat w kontekście przedsiębiorstwa może oznaczać, a nie znaczy wcale ...

Kiedy ktoś mówi, że coś jest trudne lub częściej niemożliwe, odpowiadam, że „znalezienie rozwiązania problemu, który jest niemożliwy, zajmuje tylko trochę czasu”. W przeciwnym razie nic nie jest niemożliwe, chyba że okaże się niemożliwe w tym przypadku dowód matematyczny ...

Jeśli okaże się to niemożliwe, pozostawia miejsce na wyobraźnię:

  • znalezienie dowodu na to, że to niemożliwe

i

  • Jeśli jest to niemożliwe, może istnieć „gorszy” problem, który można rozwiązać.

lub

  • jeśli nie jest to niemożliwe, znalezienie rozwiązania

To nie tylko optymistyczne myślenie. Kiedy zaczynałem Python-> Javascript, wszyscy mówili, że to niemożliwe. PyPy niemożliwe. Metaklasy są zbyt trudne. etc ... Myślę, że jedyną rewolucją, która przenosi PyPy na papier Scheme-> C (który ma 25 lat) jest jakieś automatyczne generowanie JIT (myślę, że oparte na podpowiedziach napisane w interprecie RPython).

Większość ludzi, którzy mówią, że coś jest „trudne” lub „niemożliwe”, nie podaje powodów. C ++ jest trudny do przeanalizowania? Wiem, że nadal są (darmowym) parserem C ++. Zło tkwi w szczegółach? Wiem to. Mówienie, że to niemożliwe, samo w sobie nie jest pomocne, jest nawet gorsze niż „nie pomocne”, to zniechęcające, a niektórzy chcą zniechęcić innych. Słyszałem o tym pytaniu na /programming/22621164/how-to-automatically-generate-a-parser-code-to-code-translator-from-a-corpus .

Jaka byłaby dla ciebie perfekcja ? W ten sposób definiujesz następny cel i być może osiągasz cel ogólny.

Bardziej interesuje mnie wiedza, jakie rodzaje wzorców mogę zastosować w kodzie, aby ułatwić tłumaczenie (np. IoC, SOA?), Niż sposób wykonania tłumaczenia.

Nie widzę wzorców, których nie można przetłumaczyć z jednego języka na inny język, przynajmniej w sposób mniej niż doskonały. Ponieważ tłumaczenie z języka na język jest możliwe, lepiej najpierw dążyć do tego. Ponieważ myślę, że zgodnie z http://en.wikipedia.org/wiki/Graph_isomorphism_problem , tłumaczenie między dwoma językami komputerowymi to drzewo lub izomorfizm DAG. Nawet jeśli już wiemy, że oba są kompletne, więc ...

Framework-> Framework, który lepiej wizualizuję jako API-> API Translation, może nadal być czymś, o czym możesz pamiętać jako sposób na ulepszenie wygenerowanego kodu. Np .: Prolog jako bardzo specyficzna składnia, ale nadal można wykonywać obliczenia podobne do Prologu, opisując ten sam wykres w Pythonie ... Gdybym miał zaimplementować translator Prolog na Python, nie zaimplementowałbym unifikacji w Pythonie, ale w bibliotece C i przyszedłbym z "składnią Pythona", która jest bardzo czytelna dla Pythonisty. Ostatecznie składnia to tylko „malowanie”, któremu nadajemy znaczenie (dlatego zacząłem schemat). Zło tkwi w szczegółach języka i nie mówię o składni. Pojęcia używane w języku getattributehook (możesz bez niego żyć), ale wymagane funkcje VM, takie jak optymalizacja rekurencji ogona, mogą być trudne do rozwiązania. Nie obchodzi cię, czy program początkowy nie używa rekurencji ogonowej, a nawet jeśli w języku docelowym nie ma rekurencji ogonowej, możesz ją emulować za pomocą pętli greenlets / event loop.

W przypadku języków docelowych i źródłowych poszukaj:

  • Wielkie i konkretne pomysły
  • Małe i wspólne pomysły

Z tego wynikną:

  • Rzeczy, które łatwo przetłumaczyć
  • Rzeczy trudne do przetłumaczenia

Prawdopodobnie będziesz także w stanie wiedzieć, co zostanie przetłumaczone na szybki i wolny kod.

Jest też kwestia standardowej biblioteki lub biblioteki, ale nie ma jednoznacznej odpowiedzi, zależy to od twoich celów.

Kod idiomatyczny czy wygenerowany kod czytelny mają też rozwiązania ...

Kierowanie na platformę taką jak PHP jest znacznie łatwiejsze niż kierowanie na przeglądarki, ponieważ możesz zapewnić implementację w języku C powolnej i / lub krytycznej ścieżki.

Biorąc pod uwagę, że pierwszym projektem jest tłumaczenie Pythona na PHP, przynajmniej dla podzbioru PHP3, o którym wiem, dostosowywanie veloce.py jest najlepszym rozwiązaniem. Jeśli możesz zaimplementować veloce.py dla PHP, prawdopodobnie będziesz w stanie uruchomić zgodny tryb ... Również jeśli możesz przetłumaczyć PHP na podzbiór PHP, możesz wygenerować za pomocą php_veloce.py, oznacza to, że możesz przetłumaczyć PHP na podzbiór Pythona, który veloce.py może zużywać, co oznaczałoby, że można przetłumaczyć PHP na JavaScript. Tylko mówię...

Możesz też zajrzeć do tych bibliotek:

Może Cię również zainteresować ten post na blogu (i komentarze): https://www.rfk.id.au/blog/entry/pypy-js-poc-jit/

amirouche
źródło
Jedyne, co mnie ekscytuje, jeśli chodzi o tłumaczenie z języka komputerowego jeden do jednego na język komputerowy, jest opisane na stackoverflow.com/questions/22621164/…
amirouche
Druga odpowiedź dotycząca typów danych. W Pythonium nawet nie planowałem obsługi poprawnego typu integer i float w trybie zgodności bez asm.js.
amirouche
OK, więc jeśli dam ci pakiet Pythona z 100K SLOC i uruchomisz go przez twojego "translatora", czy otrzymam działający program? Ile pracy ręcznej po tłumaczeniu potrzeba, aby to naprawić? Powiedziałeś tutaj: „biorąc pod uwagę istniejący już dobry parser dla Pythona, który buduje AST, mogę zbudować częściowego tłumacza w 6 miesięcy”. Nikt się nie dziwi. Sześć miesięcy nie jest według większości ludzi „dość łatwe” (cytując inny z Twoich komentarzy). Rozwiązanie pozostałych problemów będzie wymagało więcej wysiłku. Moja odpowiedź brzmiała: „robienie tego nie jest łatwe”, a „robienie tego w ogólny sposób jest trudne”.
Ira Baxter,
... ten ostatni punkt w odpowiedzi na pierwotne pragnienie OP: „w idealnym przypadku byłbym w stanie dodać inne języki z (względną) łatwością”.
Ira Baxter,
Błagam się nie zgodzić, zwłaszcza gdy wiesz, co robisz, jest to łatwe, a to, co następuje, nie jest trudne, to tylko kwestia załatwienia sprawy. Nie jestem pewien, gdzie masz do czynienia z czymkolwiek związanym z tym pytaniem. W 4 lub 5 akapitach mówisz, że Twoja firma to robi i jest to trudne. Ale poza tym rozpowszechniasz FUD na ten temat, będąc miłym poza tematem, tak jak jesteś na stackoverflow.com/questions/22621164/ ... . W pełnym wymiarze godzin przez 6 miesięcy napisałbym pełnego tłumacza.
amirouche,
0

Możesz rzucić okiem na kompilator Vala , który tłumaczy Vala (język podobny do C #) na C.

ptomato
źródło
Założeniem projektowym Vali było przetłumaczenie go na język C i ułatwienie programowania za pomocą bibliotek gnome.
amirouche