Supongamos que X es el idioma de entrada, Z es el idioma de salida, luego f es el compilador, que está escrito en el lenguaje Y.
f = X -> Z
Como f es solo un programa, creo que Y puede ser cualquier lenguaje, ¿verdad? Entonces podemos tener compiladores f1, f2, cada uno escrito en Y1, Y2.
f1 = f Y1
f2 = f Y2
g = Z -> M
h = g . f # We get a compiler X -> M
Tome el compilador cpython por ejemplo, X es Python, Z es el código de Python VM, Y es C.
cpython = Python -> PythonVMCode C
interpreter = PythonVMCode -> Nothing
interpreter2 = PythonVMCode -> MachineCode
Las fuentes de Python se compilan en el código de Python VM, los archivos .pyc, luego son interpretados por el intérprete. Parece que es posible que exista un compilador que pueda hacer directamente Python -> MachineCode, aunque es muy difícil de implementar:
hardpython = interpreter2 . cpython
También podemos escribir otro compilador para hacer el trabajo Python -> PythonVMCode, en otro idioma, digamos Python.
mypython = Python -> PythonVMCode Python
mypython2 = Python -> PythonVMCode Ruby
Ahora, aquí está el complicado ejemplo de PyPy. Solo soy un novato de PyPy, corrígeme si me equivoco:
Documento de PyPy http://doc.pypy.org/en/latest/architecture.html#pypy-the-translation-framework
Nuestro objetivo es proporcionar una posible solución al problema de los implementadores del lenguaje: tener que escribir l * o * p intérpretes para l lenguajes dinámicos y p plataformas con o decisiones de diseño cruciales.
Podemos pensar que l es X, p es Y. Existe un programa que traduce todos los programas RPython a C:
rpython_compiler = RPython -> C Python
pypy = Python -> Nothing RPython
translate = compile the program pypy written in RPython using rpython_compiler
py2rpy = Python -> RPython Python
py2c = Python -> C Python
py2c = rpython_compiler . py2rpy
Los programas RPython son como las instrucciones de VM, rpython_compiler es la VM.
q1. Pypy es el intérprete, un programa RPython que puede interpretar el código Python, no hay lenguaje de salida, por lo que no podemos considerarlo como un compilador, ¿verdad?
Adicional:
- Acabo de descubrir que, incluso después de la traducción, pypy sigue siendo un intérprete, solo que esta vez escrito en C.
- Si observamos profundamente el intérprete pypy, creo que debe existir algún tipo de compilador, que compila las fuentes de Python para algún AST, luego ejecuta
Me gusta esto:
compiler_inside_pypy = Python -> AST_or_so
q2. ¿Puede existir el compilador py2rpy, transformando todos los programas de Python a RPython? En qué idioma está escrito es irrelevante. En caso afirmativo, obtenemos otro compilador py2c. ¿Cuál es la diferencia entre pypy y py2rpy en la naturaleza? ¿Py2rpy es mucho más difícil de escribir que pypy?
q3. ¿Hay algunas reglas generales o teorías disponibles sobre esto?
Más compiladores:
gcc_c = C -> asm? C # not sure, gimple or rtl?
g++ = C++ -> asm? C
clang = C -> LLVM_IR C++
jython = Python -> JVMCode java
ironpython = Python -> CLI C#
q4. Dado f = X -> Z, un programa P escrito en X. Cuando queremos acelerar P, ¿qué podemos hacer? Posibles formas:
reescribir P en un algoritmo más eficiente
reescribe f para generar una mejor Z
si se interpreta Z, escriba un mejor intérprete de Z (¿PyPy está aquí?)
acelerar los programas escritos en Z recursivamente
obtener una mejor máquina
PD. Esta pregunta no se trata de los aspectos tecnológicos de cómo escribir un compilador, sino de la viabilidad y complejidad de escribir un compilador de cierto tipo.
Respuestas:
PyPy es similar a CPython, ambos tienen un compilador + intérprete. CPython tiene un compilador escrito en C que compila el código de bytes Python to Python VM y luego ejecuta el bytecode en un intérprete escrito en C. PyPy tiene un compilador escrito en RPython que compila el código de bytes Python to Python VM, luego lo ejecuta en PyPy Interpreter escrito en RPython.
¿Puede existir un compilador py2rpy? Teóricamente si. La integridad de Turing lo garantiza.
Un método para construir
py2rpy
es simplemente incluir el código fuente de un intérprete de Python escrito en RPython en el código fuente generado. Un ejemplo del compilador py2rpy, escrito en Bash:ahora, cada vez que necesite traducir un código de Python a código de RPython, llama a este script, que produce, en $ outputdir, un RPython
main.rpy
, el código fuente del intérprete de Python de RPython y un blob binario prog.py. Y luego puede ejecutar el script RPython generado llamandorpython main.rpy
.(nota: dado que no estoy familiarizado con el proyecto rpython, la sintaxis para llamar al intérprete rpython, la capacidad de importar pypy y hacer pypy.execfile, y la extensión .rpy está puramente inventada, pero creo que entiendes el punto)
Sí, cualquier idioma de Turing Complete se puede traducir teóricamente a cualquier idioma de Turing Complete. Algunos idiomas pueden ser mucho más difíciles de traducir que otros idiomas, pero si la pregunta es "¿es posible?", La respuesta es "sí"
No hay duda aquí.
fuente
Does pypy have to be written in RPython in your compiler?
No, no es necesario que esté escrito en RPython, pero RPython debe poder decirle al "intérprete auxiliar" / "tiempo de ejecución" que ejecute un código de Python. Sí, es cierto, este no es un "compilador" en sentido práctico, pero es una prueba constructiva de que es posible escribirPython -> RPython
.Is pypy still using the Python VM?
Creo que pypy no usa CPython en absoluto (podría estar equivocado), en cambio PyPy tiene su propia implementación de "Python VM" que está escrita en RPython.Python -> RPython
compilador más práctico .Para responder solo a q2, hay un libro compilador de William McKeeman en el que se explora la teoría de compiladores para el lenguaje X escrita en el lenguaje Y que produce el lenguaje de salida Z a través de un sistema de diagramas T. Publicado en la década de 1970, título no a mano, lo siento.
fuente
q1. Generalmente, un intérprete no es un compilador. La diferencia clave entre un compilador y un intérprete es que un intérprete comienza de nuevo, con código fuente en el idioma fuente, cada vez. Si su pypy era en cambio pyAST, o código pyP, y luego tenía un intérprete de AST o código P, entonces podría llamar a pyAST un compilador. Así es como funcionaba el antiguo compilador PASCAL de UCSD (así como algunos otros): compilaron algún código P, que se interpretó cuando se ejecutó el programa. (Incluso .NET proporciona algo como esto, cuando la compacidad del código objeto generado es mucho más importante que la velocidad).
q2. Sí, por supuesto. Ver UCSD PASCAL (y muchos otros).
q3. Profundiza en los textos clásicos de la informática. Lea sobre PASCAL concurrente, por Per Brinch-Hansen (si la memoria me sirve). Mucho se ha escrito sobre compiladores y generación de código. Generar un pseudocódigo independiente de la máquina suele ser mucho más fácil que generar código de máquina: el pseudocódigo generalmente no tiene las peculiaridades que las máquinas reales contienen invariablemente.
q4. Si desea que su objeto generado se ejecute más rápido, puede hacer que el compilador sea más inteligente, para una mejor optimización. Si su objeto es interpretado, considera empujar operaciones más complejas hacia pseudoinstrucciones primitivas (CISC vs. RISC es la analogía), entonces hace todo lo posible para optimizar el frack de su intérprete.
Si desea que su compilador se ejecute más rápido, debe mirar TODO lo que hace, incluido repensar su código fuente. Después de cargar el compilador en sí, la parte más lenta de la compilación es SIEMPRE leer el código fuente en el compilador. (Considere C ++ por ejemplo. Todas las demás cosas son relativamente iguales, un compilador que tiene que reducir 9,000 (o quizás 50,000) líneas de #incluir archivos para compilar un simple programa "Hola, Mundo" nunca será tan rápido como uno eso solo tiene que leer cuatro o cinco líneas).
No recuerdo dónde lo leí, pero el compilador original de Oberon en ETH-Zurich tenía un mecanismo de tabla de símbolos muy sofisticado, bastante elegante. El punto de referencia de Wirth para el rendimiento del compilador fue el tiempo que le tomó al compilador compilarse. Una mañana, entró, sacó la hermosa tabla de símbolos de árbol múltiple con múltiples enlaces y la reemplazó con una simple matriz lineal y búsquedas lineales rectas. Los estudiantes graduados en su grupo fueron CONMOCIONADOS. Después del cambio, el compilador fue más rápido, porque los módulos que estaba compilando siempre eran lo suficientemente pequeños como para que el monstruo elegante impusiera más sobrecarga total que la matriz lineal y la búsqueda lineal.
fuente
Sus preguntas como se indica me llevan a creer que lo que realmente quiere / necesita es una explicación de qué es un compilador, qué es un intérprete y las diferencias entre los dos.
Un compilador asigna un programa escrito en lenguaje X a un programa funcionalmente equivalente escrito en lenguaje Y. Como ejemplo, un compilador de Pascal a C podría compilar
a
La mayoría de los compiladores compilan 'hacia abajo', por lo que compilan lenguajes de programación de nivel superior en lenguajes de nivel inferior, siendo el último lenguaje de nivel inferior el código de máquina.
La mayoría de los compiladores compilan directamente en código máquina, pero algunos (especialmente los lenguajes Java y .NET) compilan en 'bytecode' ( Java bytecode y CIL ). Piense en bytecode como código de máquina para una computadora hipotética. Este bytecode luego se interpreta o JITted cuando se ejecuta (más sobre eso más adelante).
Un intérprete ejecuta un programa escrito en algún lenguaje Z. Un intérprete lee un programa poco a poco, ejecutándolo a medida que avanza. Por ejemplo:
Imagine al intérprete mirando esa línea de programa por línea, examinando la línea, ejecutando lo que hace, mirando la siguiente línea y así sucesivamente.
El mejor ejemplo de un intérprete es la CPU de su computadora. Interpreta el código de la máquina y lo ejecuta. El funcionamiento de la CPU se especifica por cómo está físicamente construido. El funcionamiento de un programa de intérprete se especifica por el aspecto de su código. Por lo tanto, la CPU interpreta y ejecuta el programa de intérprete, que a su vez interpreta y ejecuta su entrada. Puede encadenar intérpretes de esta manera.
Un JITter es un compilador Just-In-Time. Un JITter es un compilador. La única diferencia es el tiempo que se ejecuta: la mayoría de los programas se escriben, compilan, envían a sus usuarios y luego se ejecutan, pero el código de bytes de Java y CIL se envían primero a sus usuarios, y luego, justo antes de que se ejecuten, se compilan en la máquina código de sus usuarios.
C # -> (compilar) -> CIL -> enviado al cliente -> (compilar justo antes de la ejecución) -> código de máquina -> (ejecutar)
Lo último que querrá saber es la integridad de Turing ( enlace ). Un lenguaje de programación es Turing Complete si puede calcular todo lo que puede hacer una ' máquina de Turing ', es decir, es al menos tan 'poderoso' como una máquina de Turing. La tesis de Church-Turing establece que una máquina de Turing es al menos tan poderosa como cualquier máquina que podamos construir. Se deduce que cada lenguaje completo de Turing es exactamente tan poderoso como la máquina de Turing y, por lo tanto, todos los idiomas completos de Turing son igualmente poderosos.
En otras palabras, mientras su lenguaje de programación esté Turing completo (casi todos lo están), no importa qué idioma elija, ya que todos pueden calcular las mismas cosas. Esto también significa que no es muy relevante el lenguaje de programación que elija para escribir su compilador o su intérprete. Por último, pero no menos importante, significa que siempre puedes escribir un compilador del lenguaje X a Y si X e Y están completos en Turing.
Tenga en cuenta que estar Turing completo no dice nada sobre si su lenguaje es eficiente, ni sobre todos los detalles de implementación de su CPU y otro hardware, o la calidad del compilador que usa para el idioma. Además, su sistema operativo podría decidir que su programa no tiene los derechos para abrir un archivo, pero eso no impide su capacidad de calcular nada; deliberadamente no definí la informática, ya que eso requeriría otro muro de texto.
fuente