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.egg
w 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/