Secondo @EliBendersky il punto di vista sull'uso di ast.parse invece del parser (di cui non ero a conoscenza prima). Ti consiglio anche vivamente di rivedere il suo blog. Ho usato ast.parse per eseguire il traduttore Python-> JavaScript (@ https://bitbucket.org/amirouche/pythonium ). Ho ideato il design Pythonium rivedendo in qualche modo altre implementazioni e provandole da solo. Ho biforcato Pythonium da https://github.com/PythonJS/PythonJS che ho anche iniziato, in realtà è una riscrittura completa. Il design complessivo è ispirato da PyPy e http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-89-1.pdf paper.
Tutto ciò che ho provato, dall'inizio alla soluzione migliore, anche se sembra che il marketing di Pythonium in realtà non lo sia (non esitate a dirmi se qualcosa non sembra corretto nella netiquette):
Implementa la semantica di Python in Plain Old JavaScript utilizzando l'ereditarietà del prototipo: AFAIK è impossibile implementare l'ereditarietà multipla di Python utilizzando il sistema di oggetti prototipo JS. Ho provato a farlo usando altri trucchi in seguito (cfr. Getattribute). Per quanto ne so non esiste alcuna implementazione dell'ereditarietà multipla di Python in JavaScript, la migliore che esiste è l'ereditarietà singola + mixin e non sono sicuro che gestiscano l'ereditarietà del diamante. Un po 'simile a Skulpt ma senza google clojure.
Ho provato con Google clojure, proprio come Skulpt (compilatore) invece di leggere effettivamente il codice Skulpt #fail. Comunque a causa del sistema di oggetti basato su prototipi JS ancora impossibile. Creare il binding è stato molto molto difficile, è necessario scrivere JavaScript e molto codice boilerplate (cfr. Https://github.com/skulpt/skulpt/issues/50 dove sono io il fantasma). A quel tempo non c'era un modo chiaro per integrare l'associazione nel sistema di compilazione. Penso che Skulpt sia una libreria e devi solo includere i tuoi file .py nell'html per essere eseguito, nessuna fase di compilazione deve essere eseguita dallo sviluppatore.
Ho provato pyjaco (compilatore) ma la creazione di associazioni (chiamando il codice Javascript dal codice Python) è stata molto difficile, c'era troppo codice boilerplate da creare ogni volta. Ora penso che il pyjaco sia quello più vicino a Pythonium. pyjaco è scritto in Python (anche ast.parse) ma molto è scritto in JavaScript e utilizza l'ereditarietà dei prototipi.
Non riesco mai a eseguire Pyjamas #fail e non ho mai provato a leggere di nuovo il codice #fail. Ma nella mia mente il pigiama stava facendo la traduzione API-> API (o da framework a framework) e non da Python a JavaScript. Il framework JavaScript utilizza i dati già presenti nella pagina o i dati dal server. Il codice Python è solo "idraulico". Dopo di che ho scoperto che il pigiama era in realtà un vero traduttore python-> js.
Tuttavia penso che sia possibile fare la traduzione API-> API (o framework-> framework) e questo è fondamentalmente quello che faccio in Pythonium ma a un livello inferiore. Probabilmente i pigiami usano lo stesso algoritmo di Pythonium ...
Poi ho scoperto brython completamente scritto in Javascript come Skulpt, senza bisogno di compilazione e un sacco di confusione ... ma scritto in JavaScript.
Sin dalla riga iniziale scritta nel corso di questo progetto, conoscevo PyPy, anche il backend JavaScript per PyPy. Sì, puoi, se lo trovi, generare direttamente un interprete Python in JavaScript da PyPy. La gente dice che è stato un disastro. Ho letto da nessuna parte perché. Ma penso che il motivo sia che il linguaggio intermedio che usano per implementare l'interprete, RPython, è un sottoinsieme di Python su misura per essere tradotto in C (e forse asm). Ira Baxter dice che fai sempre supposizioni quando costruisci qualcosa e probabilmente lo metti a punto per essere il migliore in quello che dovrebbe fare nel caso della traduzione PyPy: Python-> C. Quelle ipotesi potrebbero non essere rilevanti in un altro contesto, peggio che possono derivare da sovraccarico, altrimenti la traduzione diretta sarà molto probabilmente sempre migliore.
Avere l'interprete scritto in Python suonava come una (molto) buona idea. Ma ero più interessato a un compilatore per motivi di prestazioni, inoltre è in realtà più facile compilare Python in JavaScript che interpretarlo.
Ho iniziato PythonJS con l'idea di mettere insieme un sottoinsieme di Python che avrei potuto facilmente tradurre in JavaScript. All'inizio non mi sono nemmeno preoccupato di implementare il sistema OO a causa dell'esperienza passata. Il sottoinsieme di Python che ho ottenuto per tradurre in JavaScript sono:
- funzione con parametri semantici completi sia in definizione che in chiamata. Questa è la parte di cui sono più orgoglioso.
- mentre / if / elif / else
- I tipi Python sono stati convertiti in tipi JavaScript (non esistono tipi Python di alcun tipo)
- perché potrebbe iterare solo su array Javascript (per un array in)
- Accesso trasparente a JavaScript: se scrivi Array nel codice Python verrà tradotto in Array in javascript. Questo è il più grande risultato in termini di usabilità rispetto ai suoi concorrenti.
- Puoi passare la funzione definita nel sorgente Python alle funzioni javascript. Verranno presi in considerazione gli argomenti predefiniti.
- Aggiunge ha una funzione speciale chiamata new che viene tradotta in JavaScript new es: new (Python) (1, 2, spam, "egg") viene tradotto in "new Python (1, 2, spam," egg ").
- "var" vengono gestiti automaticamente dal traduttore. (scoperta molto bella da Brett (collaboratore di PythonJS).
- parola chiave globale
- chiusure
- lambdas
- elenchi di comprensioni
- le importazioni sono supportate tramite requirejs
- ereditarietà di una singola classe + mixin tramite classyjs
Questo sembra molto ma in realtà molto stretto rispetto alla semantica in piena regola di Python. È davvero JavaScript con una sintassi Python.
Il JS generato è perfetto cioè. non c'è overhead, non può essere migliorato in termini di prestazioni modificandolo ulteriormente. Se puoi migliorare il codice generato, puoi farlo anche dal file sorgente di Python. Inoltre, il compilatore non si basava su alcun trucco JS che puoi trovare in .js scritto da http://superherojs.com/ , quindi è molto leggibile.
Il discendente diretto di questa parte di PythonJS è la modalità Pythonium Veloce. L'implementazione completa può essere trovata @ https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pythonium/veloce/veloce.py?at=master 793 SLOC + circa 100 SLOC di codice condiviso con l'altro traduttore.
Una versione adattata di pystones.py può essere tradotta in modalità Veloce cfr. https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pystone/?at=master
Dopo aver impostato la traduzione di base Python-> JavaScript, ho scelto un altro percorso per tradurre Python completo in JavaScript. Il modo in cui glib esegue codice basato su classi orientate agli oggetti, tranne il linguaggio di destinazione, è JS, quindi si ha accesso a array, oggetti simili a mappe e molti altri trucchi e tutta quella parte è stata scritta in Python. IIRC non esiste un codice javascript scritto da nel traduttore Pythonium. Ottenere l'ereditarietà singola non è difficile: ecco le parti difficili che rendono Pythonium completamente compatibile con Python:
spam.egg
in Python è sempre tradotto in getattribute(spam, "egg")
Non l'ho profilato in particolare ma penso che perda molto tempo e non sono sicuro di poterlo migliorare con asm.js o qualsiasi altra cosa.
- ordine di risoluzione del metodo: anche con l'algoritmo scritto in Python, tradurlo in codice compatibile con Python Veloce è stato un grande sforzo.
- getattributre : l'attuale algoritmo di risoluzione getattribute è un po 'complicato e ancora non supporta i descrittori di dati
- basato sulla classe metaclass: so dove inserire il codice, ma comunque ...
- ultimo ma non meno importante: some_callable (...) viene sempre tradotto in "call (some_callable)". Per quanto ne so, il traduttore non usa affatto l'inferenza, quindi ogni volta che fai una chiamata devi controllare quale tipo di oggetto è per chiamarlo nel modo in cui deve essere chiamato.
Questa parte è fattorizzata in https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pythonium/compliant/runtime.py?at=master È scritto in Python compatibile con Python Veloce.
Il traduttore conforme effettivo https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pythonium/compliant/compliant.py?at=master non genera direttamente codice JavaScript e, soprattutto, non esegue la trasformazione ast-> ast . Ho provato la cosa ast-> ast e ast anche se più bella di cst non è piacevole lavorare con ast.NodeTransformer e, cosa più importante, non ho bisogno di fare ast-> ast.
Fare da python ast a python ast nel mio caso almeno sarebbe forse un miglioramento delle prestazioni poiché a volte ispeziono il contenuto di un blocco prima di generare il codice ad esso associato, ad esempio:
- var / global: per poter var qualcosa devo sapere di cosa ho bisogno e non var. Invece di generare un blocco che tiene traccia di quali variabili vengono create in un dato blocco e inserirle sopra il blocco funzione generato, cerco solo un'assegnazione di variabile rilevante quando entro nel blocco prima di visitare effettivamente il nodo figlio per generare il codice associato.
- yield, i generatori hanno ancora una sintassi speciale in JS, quindi ho bisogno di sapere quale funzione Python è un generatore quando voglio scrivere "var my_generator = function"
Quindi non visito davvero ogni nodo una volta per ogni fase della traduzione.
Il processo complessivo può essere descritto come:
Python source code -> Python ast -> Python source code compatible with Veloce mode -> Python ast -> JavaScript source code
I builtin di Python sono scritti in codice Python (!), IIRC ci sono alcune restrizioni relative ai tipi di bootstrap, ma hai accesso a tutto ciò che può tradurre Pythonium in modalità conforme. Dai un'occhiata a https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pythonium/compliant/builtins/?at=master
La lettura del codice JS generato da pythonium compatibile può essere compresa, ma le mappe sorgente saranno di grande aiuto.
I preziosi consigli che posso darti alla luce di questa esperienza sono delle vecchie scoregge gentili:
- rivedere ampiamente l'argomento sia in letteratura che in progetti esistenti closed source o gratuiti. Quando ho esaminato i diversi progetti esistenti avrei dovuto dargli più tempo e motivazione.
- fare domande! Se avessi saputo in anticipo che il backend PyPy era inutile a causa del sovraccarico dovuto alla mancata corrispondenza semantica di C / Javascript. Forse avrei avuto un'idea di Pythonium molto prima di 6 mesi fa, forse 3 anni fa.
- sapere cosa vuoi fare, avere un obiettivo. Per questo progetto avevo diversi obiettivi: praticare un po 'javascript, imparare di più su Python ed essere in grado di scrivere codice Python che sarebbe stato eseguito nel browser (altro e quello sotto).
- il fallimento è esperienza
- un piccolo passo è un passo
- inizia in piccolo
- sogna in grande
- fare demo
- iterare
Solo con la modalità Python Veloce, sono molto felice! Ma lungo la strada ho scoperto che quello che stavo veramente cercando era liberare me e gli altri da Javascript, ma soprattutto essere in grado di creare in modo confortevole. Questo mi ha portato a Scheme, DSL, Models e, infine, a modelli specifici di dominio (cfr. Http://dsmforum.org/ ).
A proposito della risposta di Ira Baxter:
Le stime non sono affatto utili. Mi ci sono voluti più o meno 6 mesi di tempo libero sia per PythonJS che per Pythonium. Quindi posso aspettarmi di più da 6 mesi a tempo pieno. Penso che tutti sappiamo cosa possono significare e non significano affatto 100 anni / uomo in un contesto aziendale ...
Quando qualcuno dice che qualcosa è difficile o più spesso impossibile, rispondo che "ci vuole solo tempo per trovare una soluzione per un problema che è impossibile" altrimenti detto niente è impossibile tranne se si è dimostrato impossibile in questo caso una prova matematica ...
Se non è dimostrato impossibile, lascia spazio all'immaginazione:
- trovare una prova che dimostri che è impossibile
e
- Se è impossibile, potrebbe esserci un problema "inferiore" che può avere una soluzione.
o
- se non è impossibile, trovare una soluzione
Non è solo un pensiero ottimistico. Quando ho avviato Python-> Javascript tutti dicevano che era impossibile. PyPy impossibile. Metaclassi troppo duri. ecc ... Penso che l'unica rivoluzione che porta PyPy su Scheme-> C paper (che ha 25 anni) sia una generazione JIT automatica (suggerimenti basati sull'interprete RPython credo).
La maggior parte delle persone che dicono che una cosa è "difficile" o "impossibile" non fornisce le ragioni. C ++ è difficile da analizzare? Lo so, ancora sono parser C ++ (gratuiti). Il male è nei dettagli? Lo so. Dire che è impossibile da soli non è utile, è anche peggio che "non utile" è scoraggiante, e alcune persone intendono scoraggiare gli altri. Ho sentito parlare di questa domanda tramite /programming/22621164/how-to-automatically-generate-a-parser-code-to-code-translator-from-a-corpus .
Quale sarebbe la perfezione per te ? È così che definisci il prossimo obiettivo e forse raggiungi l'obiettivo generale.
Sono più interessato a sapere quali tipi di pattern potrei applicare al codice per rendere più facile tradurre (ad esempio: IoC, SOA?) Il codice piuttosto che come fare la traduzione.
Non vedo schemi che non possano essere tradotti da una lingua a un'altra lingua almeno in modo meno che perfetto. Poiché la traduzione da lingua a lingua è possibile, è meglio mirare prima a questo. Dal momento che, secondo http://en.wikipedia.org/wiki/Graph_isomorphism_problem , la traduzione tra due linguaggi di computer è un albero o isomorfismo DAG. Anche se sappiamo già che sono entrambi completati, quindi ...
Framework-> Framework che visualizzo meglio come API-> traduzione API potrebbe ancora essere qualcosa che potresti tenere a mente come un modo per migliorare il codice generato. Ad esempio: Prolog come sintassi molto specifica, ma puoi comunque eseguire calcoli simili a Prolog descrivendo lo stesso grafico in Python ... Se dovessi implementare un traduttore da Prolog a Python non implementerei l'unificazione in Python ma in una libreria C e verrei con una "sintassi Python" che è molto leggibile per un Pythonist. Alla fine, la sintassi è solo "pittura" a cui diamo un significato (ecco perché ho iniziato lo schema). Il male è nei dettagli del linguaggio e non sto parlando della sintassi. I concetti utilizzati nella lingua getattributehook (puoi vivere senza di esso) ma le funzionalità VM richieste come l'ottimizzazione della ricorsione della coda possono essere difficili da gestire. Non ti interessa se il programma iniziale non usa la ricorsione in coda e anche se non c'è ricorsione in coda nella lingua di destinazione puoi emularla usando greenlets / event loop.
Per le lingue di destinazione e di origine, cerca:
- Idee grandi e specifiche
- Idee condivise minuscole e comuni
Da questo emergeranno:
- Cose facili da tradurre
- Cose difficili da tradurre
Probabilmente sarai anche in grado di sapere cosa verrà tradotto in codice veloce e lento.
C'è anche la questione dello stdlib o di qualsiasi libreria ma non c'è una risposta chiara, dipende dai tuoi obiettivi.
Anche il codice idiomatico o il codice generato leggibile hanno soluzioni ...
Scegliere come target una piattaforma come PHP è molto più facile che indirizzare i browser poiché è possibile fornire l'implementazione C del percorso lento e / o critico.
Dato che il tuo primo progetto è tradurre Python in PHP, almeno per il sottoinsieme PHP3 che conosco, la personalizzazione di veloce.py è la soluzione migliore. Se puoi implementare veloce.py per PHP allora probabilmente sarai in grado di eseguire la modalità conforme ... Inoltre se puoi tradurre PHP nel sottoinsieme di PHP puoi generare con php_veloce.py significa che puoi tradurre PHP nel sottoinsieme di Python che veloce.py può consumare, il che significherebbe che puoi tradurre PHP in Javascript. Sto solo dicendo ...
Puoi anche dare un'occhiata a quelle librerie:
Inoltre potresti essere interessato da questo post del blog (e commenti): https://www.rfk.id.au/blog/entry/pypy-js-poc-jit/