Me propongo hacer un proyecto paralelo que tiene como objetivo traducir código de un lenguaje de programación a otro. Los lenguajes con los que estoy comenzando son PHP y Python (Python a PHP debería ser más fácil para comenzar), pero lo ideal sería que pudiera agregar otros lenguajes con (relativa) facilidad. El plan es:
Esto está orientado al desarrollo web. El código original y de destino se colocará encima de los marcos (que también tendré que escribir). Estos marcos adoptarán un patrón de diseño MVC y seguirán estrictas convenciones de codificación. Esto debería facilitar la traducción.
También estoy analizando la IOC y la inyección de dependencias, ya que podrían hacer que el proceso de traducción sea más fácil y menos propenso a errores.
Haré uso del módulo analizador de Python , que me permite jugar con el árbol de sintaxis abstracta. Aparentemente, lo más cercano que puedo estar con PHP es token_get_all () , que es un comienzo.
A partir de entonces puedo construir el AST, las tablas de símbolos y el flujo de control.
Entonces creo que puedo empezar a generar código. No necesito una traducción perfecta . Aún tendré que revisar el código generado y solucionar problemas. Idealmente, el traductor debería señalar las traducciones problemáticas.
Antes de preguntar "¿Cuál es el punto de esto?" La respuesta es ... Será una experiencia de aprendizaje interesante. Si tiene alguna idea sobre cómo hacer que esto sea menos desalentador, hágamelo saber.
EDITAR:
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.
fuente
Respuestas:
He estado construyendo herramientas (DMS Software Reengineering Toolkit) para realizar manipulación de programas de propósito general (siendo la traducción de idiomas un caso especial) desde 1995, con el apoyo de un sólido equipo de científicos informáticos. DMS proporciona análisis genérico, construcción AST, tablas de símbolos, control y análisis de flujo de datos, aplicación de reglas de traducción, regeneración del texto fuente con comentarios, etc., todo parametrizado por definiciones explícitas de lenguajes de computadora.
La cantidad de maquinaria que necesita para hacer esto bien es enorme (especialmente si desea poder hacer esto para varios idiomas de manera general), y luego necesita analizadores confiables para idiomas con definiciones poco confiables (PHP es un ejemplo perfecto de esto ).
No tiene nada de malo pensar en construir un traductor de idioma a idioma o intentarlo, pero creo que encontrará que esta es una tarea mucho más grande para los idiomas reales de lo que espera. Tenemos unos 100 años-hombre invertidos en DMS y otros 6-12 meses en cada definición de lenguaje "confiable" (incluida la que construimos dolorosamente para PHP), mucho más para lenguajes desagradables como C ++. Será una "gran experiencia de aprendizaje"; ha sido para nosotros. (Es posible que la sección de artículos técnicos del sitio web anterior le resulte interesante para iniciar ese aprendizaje).
Las personas a menudo intentan construir algún tipo de maquinaria generalizada comenzando con alguna pieza de tecnología con la que están familiarizados, que hace parte del trabajo. (Los AST de Python son un gran ejemplo). La buena noticia es que parte del trabajo está hecho. La mala noticia es que la maquinaria tiene un trillón de suposiciones incorporadas, la mayoría de las cuales no descubrirá hasta que intente luchar para hacer otra cosa. En ese momento, descubres que la maquinaria está programada para hacer lo que originalmente hace, y realmente resistirá tu intento de hacer que haga otra cosa. (Sospecho que intentar hacer que Python AST modele PHP va a ser muy divertido).
La razón por la que comencé a construir DMS originalmente fue para construir cimientos que tuvieran muy pocas suposiciones incorporadas. Tiene algunas que nos dan dolores de cabeza. Hasta ahora, no hay agujeros negros. (La parte más difícil de mi trabajo durante los últimos 15 años es tratar de evitar que tales suposiciones se filtren).
Mucha gente también comete el error de asumir que si pueden analizar (y quizás obtener un AST), están en camino de hacer algo complicado. Una de las lecciones difíciles es que necesita tablas de símbolos y análisis de flujo para realizar un buen análisis o transformación de programas. Los AST son necesarios pero no suficientes. Esta es la razón por la que el libro del compilador de Aho & Ullman no se detiene en el capítulo 2. (El OP tiene este derecho en el sentido de que planea construir maquinaria adicional más allá del AST). Para obtener más información sobre este tema, consulte Vida después del análisis .
El comentario sobre "No necesito una traducción perfecta" es problemático. Lo que hacen los traductores débiles es convertir el 80% "fácil" del código, dejando el 20% difícil para hacerlo a mano. Si la aplicación que desea convertir es bastante pequeña y solo tiene la intención de convertirla bien una vez, ese 20% está bien. Si desea convertir muchas aplicaciones (o incluso la misma con cambios menores a lo largo del tiempo), esto no es bueno. Si intenta convertir 100K SLOC, entonces el 20% son 20,000 líneas originales de código que son difíciles de traducir, comprender y modificar en el contexto de otras 80,000 líneas de programa traducido que ya no comprende. Eso requiere un gran esfuerzo. En el nivel de un millón de líneas, esto es simplemente imposible en la práctica.más difícil y normalmente se dan cuenta de manera dolorosa con retrasos prolongados, altos costos y, a menudo, fallas totales).
Lo que tiene que buscar para traducir sistemas a gran escala es un alto porcentaje de conversión de los noventa, o es probable que no pueda completar la parte manual de la actividad de traducción.
Otra consideración clave es el tamaño del código a traducir. Se necesita mucha energía para construir un traductor sólido y que funcione, incluso con buenas herramientas. Si bien parece atractivo y genial construir un traductor en lugar de simplemente hacer una conversión manual, para bases de código pequeñas (por ejemplo, hasta aproximadamente 100K SLOC en nuestra experiencia) la economía simplemente no lo justifica. A nadie le gusta esta respuesta, pero si realmente tiene que traducir solo 10K SLOC de código, probablemente sea mejor que muerda la bala y lo haga. Y sí, eso es doloroso.
Considero que nuestras herramientas son extremadamente buenas (pero estoy bastante sesgado). Y todavía es muy difícil construir un buen traductor; nos lleva entre 1,5 y 2 años-hombre y sabemos cómo utilizar nuestras herramientas. La diferencia es que con tanta maquinaria, tenemos mucho más éxito de lo que fracasamos.
fuente
Mi respuesta abordará la tarea específica de analizar Python para traducirlo a otro idioma, y no los aspectos de nivel superior que Ira abordó bien en su respuesta.
En resumen: no use el módulo analizador, hay una manera más fácil.
El
ast
módulo, disponible desde Python 2.6, es mucho más adecuado para sus necesidades, ya que le brinda un AST listo para usar para trabajar. Escribí un artículo sobre esto el año pasado, pero en resumen, use elparse
método deast
para analizar el código fuente de Python en un AST. Elparser
módulo le dará un árbol de análisis, no un AST. Tenga cuidado con la diferencia .Ahora, dado que los AST de Python son bastante detallados, dado un AST, el trabajo de front-end no es terriblemente difícil. Supongo que puede tener un prototipo simple para algunas partes de la funcionalidad listo con bastante rapidez. Sin embargo, llegar a una solución completa llevará más tiempo, principalmente porque la semántica de los lenguajes es diferente. Un subconjunto simple del lenguaje (funciones, tipos básicos, etc.) se puede traducir fácilmente, pero una vez que ingrese a las capas más complejas, necesitará maquinaria pesada para emular el núcleo de un idioma en otro. Por ejemplo, considere los generadores de Python y las comprensiones de listas que no existen en PHP (que yo sepa, que es ciertamente pobre cuando PHP está involucrado).
Para darle un consejo final, considere la
2to3
herramienta creada por los desarrolladores de Python para traducir el código Python 2 al código Python 3. En cuanto al front-end, tiene la mayoría de los elementos que necesita para traducir Python a algo . Sin embargo, dado que los núcleos de Python 2 y 3 son similares, no se requiere maquinaria de emulación allí.fuente
2to3
es solo de AST a AST. No admite hacer nada que vaya más allá de las capacidades delast
módulo. Tenga en cuenta que todas las traducciones van desde la sintaxis admitida por el proceso de Python del host a la sintaxis admitida por el proceso de Python del host. No hay ningún traductor que agregue, digamos, anotaciones de funciones, porque 2.6 no lo admite.2to3
puede verse como un ejemplo del uso del AST generado a partir deast
.Escribir un traductor no es imposible, especialmente considerando que Joel's Intern lo hizo durante un verano.
Si quieres aprender un idioma, es fácil. Si quieres hacer más, es un poco más difícil, pero no demasiado. La parte más difícil es que, mientras que cualquier lenguaje turing completo puede hacer lo que hace otro lenguaje turing completo, los tipos de datos incorporados pueden cambiar fenomenalmente lo que hace un lenguaje.
Por ejemplo:
se necesita mucho código C ++ para duplicarlo (bueno, puedes hacerlo bastante corto con algunas construcciones en bucle, pero aún así).
Eso es un poco aparte, supongo.
¿Alguna vez ha escrito un tokenizador / analizador basado en la gramática del idioma? Probablemente querrá aprender a hacer eso si no lo ha hecho, porque esa es la parte principal de este proyecto. Lo que haría es crear una sintaxis completa básica de Turing, algo bastante similar al bytecode de Python . Luego, crea un lexer / parser que toma una gramática del idioma (quizás usando BNF ) y, basándose en la gramática, compila el idioma en su idioma intermedio. Entonces, lo que querrá hacer es hacer lo contrario: crear un analizador de su idioma en los idiomas de destino según la gramática.
El problema más obvio que veo es que al principio probablemente creará un código terriblemente ineficiente, especialmente en lenguajes más potentes * como Python.
Pero si lo hace de esta manera, probablemente podrá encontrar formas de optimizar la salida a medida que avanza. Para resumir:
* por poderoso me refiero a que esto toma 4 líneas:
Muéstreme otro lenguaje que pueda hacer algo así en 4 líneas y le mostraré un lenguaje que es tan poderoso como Python.
fuente
Hay un par de respuestas que le dicen que no se moleste. Bueno, ¿qué tan útil es eso? ¿Tu quieres aprender? Puedes aprender. Esta es la compilación. Da la casualidad de que su idioma de destino no es el código máquina, sino otro idioma de alto nivel. Esto se hace todo el tiempo.
Hay una forma relativamente sencilla de empezar. Primero, obtenga http://sourceforge.net/projects/lime-php/ (si desea trabajar en PHP) o algo así y revise el código de ejemplo. A continuación, puede escribir un analizador léxico utilizando una secuencia de expresiones regulares y alimentar tokens al analizador que genera. Sus acciones semánticas pueden generar código directamente en otro idioma o construir alguna estructura de datos (piense en objetos, hombre) que puede masajear y recorrer para generar código de salida.
Tienes suerte con PHP y Python porque en muchos aspectos son el mismo lenguaje que el otro, pero con diferente sintaxis. La parte difícil es superar las diferencias semánticas entre las formas gramaticales y las estructuras de datos. Por ejemplo, Python tiene listas y diccionarios, mientras que PHP solo tiene matrices asociadas.
El enfoque de "aprendiz" es construir algo que funcione bien para un subconjunto restringido del lenguaje (como solo declaraciones impresas, matemáticas simples y asignación de variables) y luego eliminar progresivamente las limitaciones. Eso es básicamente lo que hicieron todos los "grandes" en el campo.
Ah, y dado que no tiene tipos estáticos en Python, podría ser mejor escribir y confiar en funciones PHP como "python_add" que agrega números, cadenas u objetos de acuerdo con la forma en que Python lo hace.
Obviamente, esto puede ser mucho más grande si lo dejas.
fuente
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:
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 agetattribute(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.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:
Por lo tanto, no visito cada nodo una vez para cada fase de la traducción.
El proceso general se puede describir como:
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:
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:
y
o
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.
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:
De esto surgirá:
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/
fuente
Podría echar un vistazo al compilador de Vala , que traduce Vala (un lenguaje similar a C #) a C.
fuente