Segundo el punto de vista de @EliBendersky con respecto al uso de ast.parse en lugar de parser (que no conocía antes). También te recomiendo encarecidamente que revises su blog. Usé ast.parse para hacer Python-> traductor de JavaScript (@ https://bitbucket.org/amirouche/pythonium ). Se me ocurrió el diseño de Pythonium revisando un poco otras implementaciones y probándolas por mi cuenta. Bifurqué Pythonium de https://github.com/PythonJS/PythonJS, que también comencé, en realidad es una reescritura completa. El diseño general está inspirado en PyPy y http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-89-1.pdf paper.
Todo lo que probé, desde el principio hasta la mejor solución, incluso si parece que el marketing de Pythonium realmente no lo es (no dude en decirme si algo no parece correcto para la etiqueta de la red):
Implementar la semántica de Python en Plain Old JavaScript usando la herencia de prototipos: AFAIK, es imposible implementar la herencia múltiple de Python usando el sistema de objetos prototipo JS. Intenté hacerlo usando otros trucos más tarde (cf. getattribute). Hasta donde yo sé, no existe una implementación de la herencia múltiple de Python en JavaScript, lo mejor que existe es Herencia única + mixins y no estoy seguro de que manejen la herencia de diamantes. Algo similar a Skulpt pero sin google clojure.
Intenté con Google clojure, al igual que Skulpt (compilador) en lugar de leer el código Skulpt #fail. De todos modos, debido a que el sistema de objetos basado en prototipos JS sigue siendo imposible. Crear enlaces fue muy, muy difícil, necesitas escribir JavaScript y mucho código repetitivo (cf. https://github.com/skulpt/skulpt/issues/50 donde soy el fantasma). En ese momento no había una forma clara de integrar el enlace en el sistema de compilación. Creo que Skulpt es una biblioteca y solo tiene que incluir sus archivos .py en el html para que se ejecuten, no es necesario que el desarrollador realice una fase de compilación.
Probé pyjaco (compilador) pero crear enlaces (llamar al código Javascript desde el código Python) fue muy difícil, había demasiado código repetitivo para crear cada vez. Ahora creo que pyjaco es el que más se acerca a Pythonium. pyjaco está escrito en Python (ast.parse también) pero mucho está escrito en JavaScript y usa la herencia de prototipos.
En realidad, nunca logré ejecutar Pyjamas #fail y nunca intenté leer el código #fail nuevamente. Pero en mi mente, pijamas estaba haciendo API-> traducción de API (o de marco a marco) y no de Python a traducción de JavaScript. El marco de JavaScript consume datos que ya están en la página o datos del servidor. El código Python es solo "plomería". Después de eso descubrí que pijamas era en realidad un verdadero traductor de python-> js.
Aún así, creo que es posible hacer la traducción de API-> API (o framework-> framework) y eso es básicamente lo que hago en Pythonium pero en un nivel inferior. Probablemente Pyjamas use el mismo algoritmo que Pythonium ...
Luego descubrí brython completamente escrito en Javascript como Skulpt, sin necesidad de compilación y mucha pelusa ... pero escrito en JavaScript.
Desde la línea inicial escrita en el curso de este proyecto, sabía sobre PyPy, incluso el backend de JavaScript para PyPy. Sí, puede, si lo encuentra, generar directamente un intérprete de Python en JavaScript desde PyPy. La gente dice que fue un desastre. No leí por qué. Pero creo que la razón es que el lenguaje intermedio que usan para implementar el intérprete, RPython, es un subconjunto de Python diseñado para ser traducido a C (y tal vez asm). Ira Baxter dice que siempre haces suposiciones cuando construyes algo y probablemente lo ajustas para que sea el mejor en lo que debe hacer en el caso de PyPy: traducción Python-> C. Esos supuestos pueden no ser relevantes en otro contexto, peor pueden inferir gastos generales, de lo contrario, la traducción directa probablemente siempre será mejor.
Tener el intérprete escrito en Python sonaba como una (muy) buena idea. Pero estaba más interesado en un compilador por razones de rendimiento, y en realidad es más fácil compilar Python en JavaScript que interpretarlo.
Comencé PythonJS con la idea de armar un subconjunto de Python que pudiera traducir fácilmente a JavaScript. Al principio ni siquiera me molesté en implementar el sistema OO debido a la experiencia pasada. El subconjunto de Python que logré traducir a JavaScript es:
- función con parámetros semánticos completos tanto en la definición como en la llamada. Esta es la parte de la que estoy más orgulloso.
- while / if / elif / else
- Los tipos de Python se convirtieron a tipos de JavaScript (no hay tipos de Python de ningún tipo)
- para podría iterar solo sobre matrices de Javascript (para una matriz)
- Acceso transparente a JavaScript: si escribe Array en el código Python, se traducirá a Array en javascript. Este es el mayor logro en términos de usabilidad sobre sus competidores.
- Puede pasar una función definida en la fuente de Python a funciones de JavaScript. Se tendrán en cuenta los argumentos predeterminados.
- Tiene una función especial llamada nueva que se traduce a JavaScript nuevo, por ejemplo: nuevo (Python) (1, 2, spam, "huevo") se traduce a "nuevo Python (1, 2, spam," huevo ").
- "var" son manejados automáticamente por el traductor. (muy buen hallazgo de Brett (colaborador de PythonJS).
- palabra clave global
- cierres
- lambdas
- lista de comprensiones
- las importaciones son compatibles a través de requirejs
- herencia de clase única + mixin a través de classyjs
Esto parece mucho, pero en realidad es muy estrecho en comparación con la semántica completa de Python. Es realmente JavaScript con sintaxis de Python.
El JS generado es perfecto, es decir. no hay gastos generales, no se puede mejorar en términos de rendimiento editándolo más. Si puede mejorar el código generado, también puede hacerlo desde el archivo fuente de Python. Además, el compilador no se basó en ningún truco de JS que pueda encontrar en .js escrito por http://superherojs.com/ , por lo que es muy legible.
El descendiente directo de esta parte de PythonJS es el modo Pythonium Veloce. La implementación completa se puede encontrar en https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pythonium/veloce/veloce.py?at=master 793 SLOC + alrededor de 100 SLOC de código compartido con el otro traductor.
Una versión adaptada de pystones.py se puede traducir en modo Veloce cf. https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pystone/?at=master
Después de haber configurado la traducción básica de Python-> JavaScript, elegí otra ruta para traducir Python completo a JavaScript. La forma de hacer glib haciendo código basado en clases orientado a objetos, excepto el lenguaje de destino, es JS, por lo que tiene acceso a matrices, objetos tipo mapa y muchos otros trucos, y toda esa parte se escribió en Python. IIRC no hay código javascript escrito por en el traductor de Pythonium. Obtener una herencia única no es difícil, aquí están las partes difíciles que hacen que Pythonium sea totalmente compatible con Python:
spam.egg
en Python siempre se traduce a getattribute(spam, "egg")
No perfilé esto en particular, pero creo que se pierde mucho tiempo y no estoy seguro de poder mejorarlo con asm.js o cualquier otra cosa.
- orden de resolución del método: incluso con el algoritmo escrito en Python, traducirlo al código compatible con Python Veloce fue un gran esfuerzo.
- getattributre : el algoritmo de resolución de getattribute real es un poco complicado y todavía no admite descriptores de datos
- basado en la clase de metaclase: sé dónde conectar el código, pero aún así ...
- último pero no menos importante: some_callable (...) siempre se traduce como "llamar (some_callable)". AFAIK, el traductor no usa inferencia en absoluto, por lo que cada vez que realiza una llamada, debe verificar qué tipo de objeto es para llamarlo de la manera en que debe llamarse.
Esta parte se incluye en https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pythonium/compliant/runtime.py?at=master Está escrito en Python compatible con Python Veloce.
El traductor compatible real https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pythonium/compliant/compliant.py?at=master no genera código JavaScript directamente y, lo más importante, no realiza una transformación ast-> ast . Probé la cosa ast-> ast y ast incluso si es más agradable que cst no es agradable trabajar incluso con ast.NodeTransformer y lo más importante no necesito hacer ast-> ast.
Hacer python ast a python ast en mi caso al menos tal vez sería una mejora del rendimiento, ya que en algún momento inspecciono el contenido de un bloque antes de generar el código asociado con él, por ejemplo:
- var / global: para poder variar algo debo saber lo que necesito y no var. En lugar de generar un bloque que rastrea qué variables se crean en un bloque dado e insertarlas en la parte superior del bloque de funciones generado, solo busco una asignación de variable relevante cuando entro al bloque antes de visitar el nodo hijo para generar el código asociado.
- rendimiento, los generadores tienen, hasta el momento, una sintaxis especial en JS, por lo que necesito saber qué función de Python es un generador cuando quiero escribir la "var my_generator = function"
Por lo tanto, no visito cada nodo una vez para cada fase de la traducción.
El proceso general se puede describir como:
Python source code -> Python ast -> Python source code compatible with Veloce mode -> Python ast -> JavaScript source code
Las incorporaciones de Python están escritas en código Python (!), IIRC hay algunas restricciones relacionadas con los tipos de arranque, pero tiene acceso a todo lo que puede traducir Pythonium en modo compatible. Eche un vistazo a https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pythonium/compliant/builtins/?at=master
Se puede entender la lectura del código JS generado a partir de pythonium, pero los mapas de origen serán de gran ayuda.
El valioso consejo que puedo darte a la luz de esta experiencia son los viejos y amables pedos:
- Revisar extensamente el tema tanto en la literatura como en proyectos existentes de código cerrado o gratuito. Cuando revisé los diferentes proyectos existentes debería haberle dado más tiempo y motivación.
- ¡hacer preguntas! Si supiera de antemano que el backend de PyPy es inútil debido a la sobrecarga debido a la falta de coincidencia semántica de C / Javascript. Quizás hubiera tenido una idea de Pythonium antes de hace 6 meses, quizás hace 3 años.
- sepa lo que quiere hacer, tenga un objetivo. Para este proyecto tenía diferentes objetivos: practicar un poco de javascript, aprender más de Python y poder escribir código Python que se ejecutaría en el navegador (más y eso más abajo).
- el fracaso es experiencia
- un pequeño paso es un paso
- empieza pequeño
- Sueño grande
- hacer demostraciones
- iterar
Solo con el modo Python Veloce, ¡estoy muy feliz! Pero en el camino descubrí que lo que realmente estaba buscando era liberarme a mí y a otros de Javascript pero, lo que es más importante, poder crear de una manera cómoda. Esto me llevó a Scheme, DSL, Modelos y eventualmente modelos específicos de dominio (cf. http://dsmforum.org/ ).
Sobre la respuesta de Ira Baxter:
Las estimaciones no ayudan en absoluto. Me tomé más o menos 6 meses de tiempo libre tanto para PythonJS como para Pythonium. Así que puedo esperar más de 6 meses a tiempo completo. Creo que todos sabemos lo que 100 años-hombre en un contexto empresarial pueden significar y no significar en absoluto ...
Cuando alguien dice que algo es difícil o más a menudo imposible, yo respondo que "solo se necesita tiempo para encontrar una solución a un problema que es imposible", de lo contrario, dije que nada es imposible excepto si se demuestra que es imposible en este caso una prueba matemática ...
Si no se demuestra que es imposible, deja espacio para la imaginación:
- encontrar una prueba que demuestre que es imposible
y
- Si es imposible, puede haber un problema "inferior" que puede tener solución.
o
- si no es imposible encontrar una solución
No es solo un pensamiento optimista. Cuando comencé Python-> Javascript, todos decían que era imposible. PyPy imposible. Metaclases demasiado duras. etc ... Creo que la única revolución que trae PyPy sobre el papel Scheme-> C (que tiene 25 años) es alguna generación automática de JIT (sugerencias basadas en el intérprete de RPython, creo).
La mayoría de las personas que dicen que algo es "difícil" o "imposible" no dan las razones. ¿C ++ es difícil de analizar? Lo sé, todavía son analizadores de C ++ (gratuitos). ¿El mal está en los detalles? Yo sé eso. Decir que es imposible solo no es útil. Es incluso peor que "no es útil", es desalentador y algunas personas tienen la intención de desanimar a otras. Escuché sobre esta pregunta a través de /programming/22621164/how-to-automatically-generate-a-parser-code-to-code-translator-from-a-corpus .
¿Cuál sería la perfección para ti ? Así es como se define el próximo objetivo y tal vez se alcance el objetivo general.
Estoy más interesado en saber qué tipo de patrones podría aplicar en el código para facilitar la traducción (es decir, ¿IoC, SOA?) Del código que en cómo hacer la traducción.
No veo patrones que no puedan traducirse de un idioma a otro, al menos de una manera menos que perfecta. Dado que la traducción de un idioma a otro es posible, es mejor que apuntes a esto primero. Dado que, creo que según http://en.wikipedia.org/wiki/Graph_isomorphism_problem , la traducción entre dos lenguajes informáticos es un árbol o isomorfismo DAG. Incluso si ya sabemos que ambos se están completando, entonces ...
Framework-> Framework que mejor visualizo como API-> La traducción de API aún puede ser algo que debe tener en cuenta como una forma de mejorar el código generado. Por ejemplo: Prolog como una sintaxis muy específica, pero aún puede hacer Prolog como un cálculo describiendo el mismo gráfico en Python ... Si tuviera que implementar un traductor de Prolog a Python, no implementaría la unificación en Python sino en una biblioteca C y vendría con una "sintaxis de Python" que es muy legible para un Pythonist. Al final, la sintaxis es solo "pintura" a la que le damos un significado (por eso comencé a planificar). El mal está en el detalle del lenguaje y no me refiero a la sintaxis. Los conceptos que se utilizan en el idioma getattributehook (puede vivir sin él) pero las características de VM requeridas como la optimización de recursividad de cola pueden ser difíciles de manejar. No le importa si el programa inicial no usa la recursividad de cola e incluso si no hay recursión de cola en el idioma de destino, puede emularlo usando greenlets / event loop.
Para los idiomas de origen y de destino, busque:
- Ideas grandes y específicas
- Pequeñas y comunes ideas compartidas
De esto surgirá:
- Cosas que son fáciles de traducir
- Cosas que son difíciles de traducir
Probablemente también podrá saber qué se traducirá a código rápido y lento.
También está la cuestión del stdlib o cualquier biblioteca, pero no hay una respuesta clara, depende de sus objetivos.
El código idiomático o el código generado legible también tienen soluciones ...
Apuntar a una plataforma como PHP es mucho más fácil que apuntar a navegadores, ya que puede proporcionar una implementación C de una ruta lenta y / o crítica.
Dado que su primer proyecto es traducir Python a PHP, al menos para el subconjunto PHP3 que conozco, personalizar veloce.py es su mejor opción. Si puede implementar veloce.py para PHP, entonces probablemente podrá ejecutar el modo compatible ... Además, si puede traducir PHP al subconjunto de PHP que puede generar con php_veloce.py, significa que puede traducir PHP al subconjunto de Python que veloce.py puede consumir, lo que significa que puede traducir PHP a Javascript. Solo digo...
También puede echar un vistazo a esas bibliotecas:
También puede estar interesado en esta publicación de blog (y comentarios): https://www.rfk.id.au/blog/entry/pypy-js-poc-jit/