Je vais seconder le point de vue de @EliBendersky concernant l'utilisation d'ast.parse au lieu de parser (que je ne connaissais pas auparavant). Je vous recommande également vivement de consulter son blog. J'ai utilisé ast.parse pour faire le traducteur Python-> JavaScript (@ https://bitbucket.org/amirouche/pythonium ). J'ai inventé la conception de Pythonium en examinant quelque peu d' autres implémentations et en les essayant moi-même. J'ai forké Pythonium de https://github.com/PythonJS/PythonJS que j'ai également commencé, c'est en fait une réécriture complète. La conception générale est inspirée du papier PyPy et http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-89-1.pdf .
Tout ce que j'ai essayé, du début à la meilleure solution, même si cela ressemble à du marketing Pythonium, ce n'est vraiment pas le cas (n'hésitez pas à me dire si quelque chose ne semble pas correct à la nétiquette):
Implémentation de la sémantique Python dans Plain Old JavaScript en utilisant l'héritage prototype: AFAIK, il est impossible d'implémenter l'héritage multiple Python en utilisant le système d'objet prototype JS. J'ai essayé de le faire en utilisant d'autres astuces plus tard (cf. getattribute). Autant que je sache, il n'y a pas d'implémentation de l'héritage multiple Python en JavaScript, le meilleur qui existe est l'héritage unique + mixins et je ne suis pas sûr qu'ils gèrent l'héritage de diamant. Un peu similaire à Skulpt mais sans Google Clojure.
J'ai essayé avec Google clojure, tout comme Skulpt (compilateur) au lieu de lire le code Skulpt #fail. Quoi qu'il en soit à cause du système d'objet basé sur un prototype JS encore impossible. Créer une reliure a été très très difficile, il faut écrire du JavaScript et beaucoup de code passe-partout (cf. https://github.com/skulpt/skulpt/issues/50 où je suis le fantôme). À cette époque, il n'y avait pas de moyen clair d'intégrer la liaison dans le système de construction. Je pense que Skulpt est une bibliothèque et il suffit d'inclure vos fichiers .py dans le html à exécuter, aucune phase de compilation n'est requise par le développeur.
J'ai essayé pyjaco (compilateur) mais créer des liaisons (appeler du code Javascript à partir de code Python) était très difficile, il y avait trop de code standard à créer à chaque fois. Maintenant je pense que pyjaco est celui qui est le plus proche de Pythonium. pyjaco est écrit en Python (ast.parse aussi) mais beaucoup est écrit en JavaScript et utilise l'héritage de prototype.
Je n'ai jamais réussi à exécuter Pyjamas #fail et je n'ai jamais essayé de relire le code #fail. Mais dans mon esprit, pyjamas faisait de la traduction API-> API (ou framework vers framework) et non de la traduction Python vers JavaScript. Le framework JavaScript consomme des données qui se trouvent déjà dans la page ou des données du serveur. Le code Python n'est que de la "plomberie". Après cela, j'ai découvert que pyjamas était en fait un véritable traducteur python-> js.
Pourtant, je pense qu'il est possible de faire de la traduction API-> API (ou framework-> framework) et c'est essentiellement ce que je fais dans Pythonium mais à un niveau inférieur. Pyjama utilise probablement le même algorithme que Pythonium ...
Puis j'ai découvert brython entièrement écrit en Javascript comme Skulpt, pas besoin de compilation et beaucoup de fluff ... mais écrit en JavaScript.
Depuis la ligne initiale écrite au cours de ce projet, je connaissais PyPy, même le backend JavaScript pour PyPy. Oui, vous pouvez, si vous le trouvez, générer directement un interpréteur Python en JavaScript depuis PyPy. Les gens disent que c'était une catastrophe. Je n'ai lu nulle part pourquoi. Mais je pense que la raison est que le langage intermédiaire qu'ils utilisent pour implémenter l'interpréteur, RPython, est un sous-ensemble de Python conçu pour être traduit en C (et peut-être asm). Ira Baxter dit que vous faites toujours des hypothèses lorsque vous construisez quelque chose et que vous l'ajustez probablement pour qu'il soit le meilleur dans ce qu'il est censé faire dans le cas de la traduction PyPy: Python-> C. Ces hypothèses pourraient ne pas être pertinentes dans un autre contexte, pires qu'elles peuvent entraîner des frais généraux, sinon la traduction directe sera probablement toujours meilleure.
Avoir l'interpréteur écrit en Python semblait être une (très) bonne idée. Mais j'étais plus intéressé par un compilateur pour des raisons de performances.Il est en fait plus facile de compiler Python en JavaScript que de l'interpréter.
J'ai commencé PythonJS avec l'idée de créer un sous-ensemble de Python que je pourrais facilement traduire en JavaScript. Au début, je n'ai même pas pris la peine de mettre en œuvre le système OO en raison de l'expérience passée. Le sous-ensemble de Python que j'ai réussi à traduire en JavaScript est:
- fonction avec des paramètres sémantiques complets à la fois dans la définition et l'appel. C'est la partie dont je suis le plus fier.
- while / if / elif / else
- Les types Python ont été convertis en types JavaScript (il n'y a aucun type Python d'aucune sorte)
- pour pourrait itérer sur des tableaux Javascript uniquement (pour un tableau in)
- Accès transparent à JavaScript: si vous écrivez Array dans le code Python, il sera traduit en Array en javascript. C'est la plus grande réussite en termes de convivialité par rapport à ses concurrents.
- Vous pouvez passer la fonction définie dans la source Python aux fonctions javascript. Les arguments par défaut seront pris en compte.
- Il ajoute une fonction spéciale appelée new qui est traduite en JavaScript new par exemple: new (Python) (1, 2, spam, "egg") est traduit en "new Python (1, 2, spam," egg ").
- "var" sont automatiquement gérés par le traducteur. (très belle découverte de Brett (contributeur PythonJS).
- mot-clé global
- fermetures
- lambdas
- compréhension de liste
- les importations sont prises en charge via requirejs
- héritage de classe unique + mixin via classyjs
Cela semble beaucoup mais en fait très restreint par rapport à la sémantique complète de Python. C'est vraiment du JavaScript avec une syntaxe Python.
Le JS généré est parfait ie. il n'y a pas de surcharge, il ne peut pas être amélioré en termes de performances en l'éditant davantage. Si vous pouvez améliorer le code généré, vous pouvez également le faire à partir du fichier source Python. De plus, le compilateur ne s'appuyait sur aucune astuce JS que vous pouvez trouver dans .js écrit par http://superherojs.com/ , donc c'est très lisible.
Le descendant direct de cette partie de PythonJS est le mode Pythonium Veloce. L'implémentation complète peut être trouvée @ https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pythonium/veloce/veloce.py?at=master 793 SLOC + environ 100 SLOC de code partagé avec l'autre traducteur.
Une version adaptée de pystones.py peut être traduite en mode Veloce cf. https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pystone/?at=master
Après avoir configuré la traduction de base de Python-> JavaScript, j'ai choisi un autre chemin pour traduire Python complet en JavaScript. La manière de glib de faire du code basé sur une classe orientée objet, sauf le langage cible, est JS, vous avez donc accès à des tableaux, des objets de type carte et de nombreuses autres astuces et toute cette partie a été écrite en Python. IIRC il n'y a pas de code javascript écrit par dans le traducteur Pythonium. Obtenir un héritage unique n'est pas difficile, voici les parties difficiles qui rendent Pythonium entièrement compatible avec Python:
spam.egg
en Python est toujours traduit en getattribute(spam, "egg")
Je n'ai pas profilé cela en particulier, mais je pense que là où cela perd beaucoup de temps et je ne suis pas sûr de pouvoir l'améliorer avec asm.js ou autre chose.
- ordre de résolution des méthodes: même avec l'algorithme écrit en Python, le traduire en code compatible Python Veloce était un gros effort.
- getattributre : l'algorithme de résolution réel de getattribute est assez délicat et il ne prend toujours pas en charge les descripteurs de données
- basé sur la classe métaclasse: je sais où brancher le code, mais quand même ...
- last but not least: some_callable (...) est toujours transalté en "call (some_callable)". AFAIK le traducteur n'utilise pas du tout l'inférence, donc chaque fois que vous effectuez un appel, vous devez vérifier de quel type d'objet il s'agit de l'appeler comme il est censé être appelé.
Cette partie est prise en compte dans https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pythonium/compliant/runtime.py?at=master Il est écrit en Python compatible avec Python Veloce.
Le traducteur conforme actuel https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pythonium/compliant/compliant.py?at=master ne génère pas de code JavaScript directement et, surtout, n'effectue pas de transformation ast-> ast . J'ai essayé la chose ast-> ast et même si plus agréable que cst n'est pas agréable de travailler même avec ast.NodeTransformer et surtout je n'ai pas besoin de faire ast-> ast.
Faire de python ast à python ast dans mon cas au moins serait peut-être une amélioration des performances puisque j'inspecte parfois le contenu d'un bloc avant de générer le code qui lui est associé, par exemple:
- var / global: pour pouvoir var quelque chose, je dois savoir ce dont j'ai besoin et non pas var. Au lieu de générer un bloc de suivi des variables créées dans un bloc donné et de l'insérer au-dessus du bloc de fonction généré, je recherche simplement une attribution de variable révélatrice lorsque j'entre dans le bloc avant de visiter réellement le nœud enfant pour générer le code associé.
- rendement, les générateurs ont, pour le moment, une syntaxe spéciale dans JS, donc j'ai besoin de savoir quelle fonction Python est un générateur lorsque je veux écrire le "var my_generator = function"
Je ne visite donc pas vraiment chaque nœud une fois pour chaque phase de la traduction.
Le processus global peut être décrit comme:
Python source code -> Python ast -> Python source code compatible with Veloce mode -> Python ast -> JavaScript source code
Les buildins Python sont écrits en code Python (!), IIRC il y a quelques restrictions liées aux types de bootstraping, mais vous avez accès à tout ce qui peut traduire Pythonium en mode conforme. Jetez un œil à https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pythonium/compliant/builtins/?at=master
La lecture du code JS généré à partir de la compatibilité pythonium peut être comprise, mais les cartes sources seront grandement utiles.
Les précieux conseils que je peux vous donner à la lumière de cette expérience sont de bons vieux pets:
- faire une revue approfondie du sujet à la fois dans la littérature et dans les projets existants en source fermée ou gratuite. Quand j'ai passé en revue les différents projets existants, j'aurais dû lui donner beaucoup plus de temps et de motivation.
- poser des questions! Si je savais à l'avance que le backend PyPy était inutile à cause de la surcharge due à l'incompatibilité sémantique C / Javascript. J'aurais peut-être eu une idée de Pythonium bien avant il y a 6 mois peut-être 3 ans.
- savoir ce que vous voulez faire, avoir une cible. Pour ce projet j'avais différents objectifs: pratiquer un peu le javascript, en savoir plus sur Python et être capable d'écrire du code Python qui fonctionnerait dans le navigateur (plus et cela ci-dessous).
- l'échec est l'expérience
- un petit pas est un pas
- commencer petit
- rêve grand
- faire des démos
- répéter
Avec le mode Python Veloce uniquement, je suis très content! Mais en cours de route, j'ai découvert que ce que je cherchais vraiment était de me libérer, moi et les autres, de Javascript, mais surtout de pouvoir créer de manière confortable. Cela m'a conduit à Scheme, DSL, Models et éventuellement à des modèles spécifiques au domaine (cf. http://dsmforum.org/ ).
À propos de la réponse d'Ira Baxter:
Les estimations ne sont pas du tout utiles. J'ai pris plus ou moins 6 mois de temps libre pour PythonJS et Pythonium. Je peux donc m'attendre à plus de 6 mois à temps plein. Je pense que nous savons tous ce que 100 années-homme dans un contexte d'entreprise peuvent signifier et ne pas signifier du tout ...
Quand quelqu'un dit que quelque chose est difficile ou plus souvent impossible, je réponds que "cela ne prend que du temps pour trouver une solution à un problème qui est impossible" autrement dit que rien n'est impossible sauf s'il s'avère impossible dans ce cas une preuve mathématique ...
Si cela n'est pas impossible, cela laisse place à l'imagination:
- trouver une preuve prouvant que c'est impossible
et
- Si c'est impossible, il peut y avoir un problème «inférieur» qui peut avoir une solution.
ou
- si ce n'est pas impossible, trouver une solution
Ce n'est pas seulement une pensée optimiste. Quand j'ai commencé Python-> Javascript, tout le monde disait que c'était impossible. PyPy impossible. Métaclasses trop difficiles. etc ... Je pense que la seule révolution qui apporte PyPy sur le papier Scheme-> C (qui a 25 ans) est une génération automatique de JIT (basée sur des indices écrits dans l'interpréteur RPython je pense).
La plupart des gens qui disent qu'une chose est «difficile» ou «impossible» n'en donnent pas les raisons. C ++ est difficile à analyser? Je sais que, ils sont toujours des analyseurs C ++ (gratuits). Le mal est dans le détail? Je le sais. Dire que c'est impossible à lui seul n'est pas utile, c'est encore pire que «pas utile», c'est décourageant, et certaines personnes veulent en décourager les autres. J'ai entendu parler de cette question via /programming/22621164/how-to-automatically-generate-a-parser-code-to-code-translator-from-a-corpus .
Quelle serait la perfection pour vous ? C'est ainsi que vous définissez le prochain objectif et que vous atteignez peut-être l'objectif global.
Je suis plus intéressé à savoir quels types de modèles je pourrais appliquer sur le code pour faciliter la traduction (ie: IoC, SOA?) Le code que comment faire la traduction.
Je ne vois aucun modèle qui ne puisse pas être traduit d'une langue à une autre au moins d'une manière moins que parfaite. Étant donné que la traduction d'une langue à une autre est possible, vous feriez mieux de viser cette première. Depuis, je pense que selon http://en.wikipedia.org/wiki/Graph_isomorphism_problem , la traduction entre deux langages informatiques est un isomorphisme arborescent ou DAG. Même si nous savons déjà qu'ils sont tous les deux terminés, alors ...
Framework-> Framework que je visualise mieux comme API-> La traduction d'API pourrait encore être quelque chose que vous pourriez garder à l'esprit comme moyen d'améliorer le code généré. Par exemple: Prolog comme syntaxe très spécifique mais vous pouvez quand même faire Prolog comme le calcul en décrivant le même graphe en Python ... Si je devais implémenter un traducteur Prolog en Python, je n'implémenterais pas l'unification en Python mais dans une bibliothèque C et je viendrais avec une "syntaxe Python" qui est très lisible pour un pythoniste. Au final, la syntaxe n'est que "peinture" pour laquelle on donne un sens (c'est pour cela que j'ai commencé le schéma). Le mal est dans le détail du langage et je ne parle pas de la syntaxe. Les concepts utilisés dans le langage getattributehook (vous pouvez vous en passer) mais les fonctionnalités VM requises telles que l'optimisation de la récursivité de la queue peuvent être difficiles à gérer. Vous ne vous souciez pas si le programme initial n'utilise pas la récursivité de queue et même s'il n'y a pas de récursivité de queue dans la langue cible, vous pouvez l'émuler en utilisant des greenlets / boucle d'événement.
Pour les langues cible et source, recherchez:
- Des idées grandes et spécifiques
- Idées partagées minuscules et communes
De cela émergera:
- Des choses faciles à traduire
- Des choses difficiles à traduire
Vous pourrez également probablement savoir ce qui sera traduit en code rapide et lent.
Il y a aussi la question du stdlib ou de n'importe quelle bibliothèque mais il n'y a pas de réponse claire, cela dépend de vos objectifs.
Le code idiomatique ou le code généré lisible ont aussi des solutions ...
Cibler une plate-forme comme PHP est beaucoup plus facile que de cibler les navigateurs puisque vous pouvez fournir une implémentation en C d'un chemin lent et / ou critique.
Étant donné que votre premier projet consiste à traduire Python en PHP, au moins pour le sous-ensemble PHP3 que je connais, la personnalisation de veloce.py est votre meilleur pari. Si vous pouvez implémenter veloce.py pour PHP, vous pourrez probablement exécuter le mode conforme ... De plus, si vous pouvez traduire PHP dans le sous-ensemble de PHP, vous pouvez générer avec php_veloce.py cela signifie que vous pouvez traduire PHP en sous-ensemble de Python que veloce.py peut consommer, ce qui signifie que vous pouvez traduire PHP en Javascript. Juste en disant ...
Vous pouvez également jeter un œil à ces bibliothèques:
Cet article de blog (et commentaires) pourrait également vous intéresser: https://www.rfk.id.au/blog/entry/pypy-js-poc-jit/