Procesadores de lenguaje de programaci贸n

    Introducci贸n

    Hoy en d铆a, la mayor铆a de los programas est谩n escritos en un lenguaje de alto nivel como C, Java o Python. Estos lenguajes est谩n dise帽ados m谩s para personas que para m谩quinas, al ocultar al programador algunos detalles de hardware de una computadora espec铆fica.

    En pocas palabras, los lenguajes de alto nivel simplifican el trabajo de decirle a una computadora qu茅 hacer. Sin embargo, dado que las computadoras solo entienden las instrucciones en c贸digo de m谩quina (en forma de 1 y 0), no podemos comunicarnos adecuadamente con ellas sin alg煤n tipo de traductor.

    Esta es por eso que existen los procesadores de lenguaje.

    El procesador de lenguaje es un sistema traductor especial que se utiliza para convertir un programa escrito en un lenguaje de alto nivel, que llamamos “c贸digo fuente”, en c贸digo m谩quina, al que llamamos “programa objeto” o “c贸digo objeto”.

    Para dise帽ar un procesador de lenguaje, se necesita una descripci贸n muy precisa del l茅xico y la sintaxis, as铆 como la sem谩ntica de un lenguaje de alto nivel.

    Hay tres tipos de procesadores de idioma:

    • Ensamblador
    • Interprete
    • Compilador

    En las siguientes secciones, repasaremos cada uno de estos tipos de procesadores y discutiremos su prop贸sito, diferencias, etc.

    Lenguajes de ensamblaje y ensamblador

    La mayor铆a de los lenguajes ensambladores son muy similares al c贸digo de m谩quina (por eso son espec铆ficos de la arquitectura de una computadora o sistema operativo), pero en lugar de usar n煤meros binarios para describir una instrucci贸n, usa s铆mbolos mnem贸nicos.

    Cada s铆mbolo mnem贸nico representa un c贸digo de operaci贸n o instrucci贸n, y normalmente necesitamos varios de ellos en conjunto para hacer algo 煤til. Estas instrucciones se pueden usar para mover valores entre registros (en la arquitectura Intel86-64 este comando ser铆a MOV), para realizar operaciones aritm茅ticas b谩sicas con valores como suma, resta, multiplicaci贸n y divisi贸n (ADD, SUB, MUL, DIV), as铆 como las operaciones l贸gicas b谩sicas como desplazar un n煤mero hacia la izquierda o hacia la derecha o la negaci贸n (SHL, SHR, NEG). Tambi茅n puede usar saltos incondicionales y condicionales, lo cual es 煤til para implementar un bucle “for”, un bucle “while” o una instrucci贸n “if” (JMP, JE, JLE…).

    Por ejemplo, si el procesador interpreta el comando binario 10110 como “pasar de un registro a otro registro”, un lenguaje ensamblador lo reemplazar铆a con un comando, como MOV.

    Cada registro tambi茅n tiene un identificador binario, como 000. Esto tambi茅n se puede reemplazar con un nombre m谩s “humano”, como EAX, que es uno de los registros generales en x86.

    Si, por ejemplo, quisi茅ramos mover un valor a un registro, el c贸digo de la m谩quina se ver铆a as铆:

    00001 000 00001010
    
    • 00001: Es el comando de movimiento
    • 000: Es el identificador del registro
    • 00001010: Es el valor que queremos mover

    En un lenguaje ensamblador, esto se puede escribir como algo como:

    MOV EAX, A
    
    • MOV es el comando de movimiento
    • EAX es el identificador del registro
    • A es el valor hexadecimal que queremos mover (10 en decimal)

    Si quisi茅ramos escribir una expresi贸n simple EAX = 7 + 4 - 2 en c贸digo de m谩quina, se ver铆a as铆:

    00001 000 00000111
    00001 001 00000100
    00010 000 001
    00001 001 00000010
    00011 000 001
    
    • 00001 es el comando “mover”
    • 00010 es el comando de “suma”
    • 00011 es el comando de “resta”
    • 000, 001 son los identificadores de los registros
    • 00000111, 00000100, 00000010 son los valores enteros que estamos usando en estas expresiones

    En ensamblado, este grupo de n煤meros binarios se escribir铆a como:

    MOV EAX, 7
    MOV R8, 4
    ADD EAX, R8
    MOV R9, 2
    SUB EAX, R9
    
    • MOV es el comando de movimiento
    • ADD es el comando de suma
    • SUB es el comando de resta
    • EAX, R8, R9 son los identificadores de los registros
    • 7, 4, 2: son los valores enteros que estamos usando en estas expresiones

    Aunque todav铆a no es tan legible como un lenguaje de alto nivel, sigue siendo mucho m谩s legible por humanos que el comando binario. Los componentes de hardware de la CPU y los registros son mucho m谩s abstractos.

    Esto hace que sea m谩s f谩cil para un programador escribir c贸digo fuente, sin necesidad de manipular n煤meros para programar. La traducci贸n a c贸digo objeto en lenguaje de m谩quina es simple y directa, realizada por un ensamblador.

    Dado que el c贸digo fuente ya es bastante similar al c贸digo de m谩quina, no hay necesidad de compilar o interpretar el c贸digo, est谩 ensamblado tal cual.

    Idiomas interpretados y el int茅rprete

    Cada programa tiene una fase de traducci贸n y una fase de ejecuci贸n. En los lenguajes interpretados, estas dos fases est谩n entrelazadas: las instrucciones escritas en un lenguaje de programaci贸n de alto nivel se ejecutan directamente sin convertirse previamente en c贸digo objeto o c贸digo m谩quina.

    Ambas fases las realiza un Interprete – un procesador de lenguaje que traduce una sola declaraci贸n (l铆nea de c贸digo), la ejecuta inmediatamente y luego pasa a la siguiente l铆nea. Si se enfrenta a un error, un int茅rprete finaliza el proceso de traducci贸n en esa l铆nea y muestra un error. No puede pasar a la siguiente l铆nea y ejecutarla a menos que se elimine el error anterior.

    Se han utilizado int茅rpretes desde 1952 y su trabajo consist铆a en facilitar la programaci贸n dentro de las limitaciones de las computadoras en ese momento (por ejemplo, hab铆a mucho menos espacio de almacenamiento en la primera generaci贸n de computadoras que el que hay ahora). El primer lenguaje interpretado de alto nivel fue Lisp, implementado por primera vez en 1958 en una computadora IBM704.

    Los lenguajes de programaci贸n interpretados m谩s comunes hoy en d铆a son Python, Perl y Ruby.

    Lenguajes compilados y el compilador

    A diferencia de los lenguajes de programaci贸n interpretados, la fase de traducci贸n y la fase de ejecuci贸n en los lenguajes de programaci贸n compilados est谩n completamente separadas, y la traducci贸n la realiza un compilador.

    El compilador es un procesador de lenguaje que lee el c贸digo fuente completo escrito en un lenguaje de alto nivel y lo traduce a un c贸digo objeto equivalente como un todo. Normalmente, este c贸digo de objeto se almacena en un archivo. Si hay alg煤n error en el c贸digo fuente, el compilador lo especifica al final de la compilaci贸n, junto con las l铆neas en las que se encontraron los errores. Despu茅s de su eliminaci贸n, el c贸digo fuente se puede volver a compilar.

    Los lenguajes de bajo nivel generalmente se compilan porque, al ser traducidos directamente al c贸digo de m谩quina, permiten al programador mucho m谩s control sobre los componentes de hardware como la memoria o la CPU.

    El primer lenguaje de programaci贸n compilado de alto nivel fue FORTRAN, creado en 1957 por un equipo dirigido por John Backus en IBM.

    Los lenguajes compilados m谩s comunes hoy en d铆a son C ++, Rust y Haskell.

    Idiomas de c贸digos de bytes

    Los lenguajes de c贸digo de bytes, tambi茅n llamados lenguajes de “c贸digo port谩til” o “c贸digo p”, son el tipo de lenguajes de programaci贸n que se incluyen en categor铆as de lenguajes interpretados y compilados, ya que utilizan tanto la compilaci贸n como la interpretaci贸n al traducir y ejecutar el c贸digo.

    Bytecode es, en pocas palabras, un c贸digo de programa que se ha compilado a partir del c贸digo fuente en un c贸digo de bajo nivel dise帽ado para un int茅rprete de software. Despu茅s de la compilaci贸n (desde el c贸digo fuente hasta el c贸digo de bytes), se puede compilar en c贸digo de m谩quina, que es reconocido por la CPU, o se puede ejecutar mediante una m谩quina virtual, que luego act煤a como int茅rprete.

    El c贸digo de bytes es universal y se puede transferir en el estado compilado a otros dispositivos (con todas las ventajas del c贸digo compilado). Luego, la CPU lo convierte en el c贸digo de m谩quina espec铆fico para el dispositivo. Dicho esto, puede compilar el c贸digo fuente una vez y ejecutarlo en todas partes, siempre que el dispositivo tenga otra capa, que se utiliza para convertir el c贸digo de bytes en c贸digo de m谩quina.

    La m谩quina virtual m谩s conocida para la interpretaci贸n de c贸digos de bytes es Java Virtual Machine (JVM), que es tan com煤n que varios lenguajes tienen implementaciones construidas para ejecutarse en ella.

    Cr茅dito: ViralPatel

    Cuando el programa se ejecuta por primera vez en un lenguaje de c贸digo de bytes, hay un retraso mientras el c贸digo se compila en c贸digo de bytes, pero la velocidad de ejecuci贸n aumenta significativamente en comparaci贸n con los lenguajes interpretativos est谩ndar (ya que el c贸digo fuente est谩 optimizado para el int茅rprete).

    Una de las mayores ventajas de los lenguajes de c贸digo de bytes es su independencia de plataforma, que sol铆a ser t铆pica solo para los lenguajes interpretados, mientras que los programas son mucho m谩s r谩pidos que los lenguajes interpretados normales en lo que respecta a la ejecuci贸n.

    Otra cosa que vale la pena mencionar aqu铆 es la compilaci贸n justo a tiempo (JIT). A diferencia de la compilaci贸n anticipada (AOT), el c贸digo se compila mientras se ejecuta. Esto esencialmente mejora la velocidad de compilaci贸n y utiliza los beneficios de rendimiento de la compilaci贸n con la flexibilidad de la interpretaci贸n.

    Por otra parte, la compilaci贸n din谩mica no siempre tiene que ser mejor / m谩s r谩pida que la compilaci贸n est谩tica; depende principalmente del tipo de proyecto en el que est茅 trabajando.

    Los lenguajes insignia que se compilan en c贸digo de bytes son Java y C # y con ellos est谩n los lenguajes como Clojure, Groovy, Kotlin y Scala.

    Ventajas y desventajas: compilado frente a interpretado

    Actuaci贸n

    Dado que un compilador traduce un c贸digo fuente completo de un lenguaje de programaci贸n en c贸digo de m谩quina ejecutable para CPU, se necesita una gran cantidad de tiempo para analizar el c贸digo fuente, pero una vez que el an谩lisis y la compilaci贸n est谩n terminados, la ejecuci贸n general es mucho m谩s r谩pida.

    Por otro lado, el int茅rprete traduce el c贸digo fuente l铆nea por l铆nea, cada uno se ejecuta a medida que se traduce, lo que conduce a un an谩lisis m谩s r谩pido del c贸digo fuente, pero la ejecuci贸n es significativamente m谩s lenta.

    Depuraci贸n

    La depuraci贸n es mucho m谩s f谩cil cuando se trata de lenguajes de programaci贸n interpretados porque el c贸digo se va traduciendo hasta que se cumple el error, por lo que sabemos exactamente d贸nde est谩 y es m谩s f谩cil de arreglar.

    Por el contrario, depurar en un lenguaje compilado es mucho m谩s tedioso. Si un programa est谩 escrito en un lenguaje compilado, debe compilarse manualmente, que es un paso adicional para ejecutar un programa. Puede que esto no parezca un problema, y 鈥嬧媙o lo es con los programas peque帽os.

    Tenga en cuenta que la compilaci贸n de proyectos masivos puede llevar decenas de minutos e incluso horas.

    Adem谩s, el compilador genera el mensaje de error despu茅s de haber escaneado el c贸digo fuente en su totalidad, por lo que el error podr铆a estar en cualquier parte del programa. Incluso si se especifica la l铆nea de un error, despu茅s de cambiar el c贸digo fuente y corregirlo, necesitamos volver a compilarlo y solo entonces se puede ejecutar la versi贸n mejorada. Esto puede no parecer un problema, y 鈥嬧媙o lo es con programas peque帽os.

    Tenga en cuenta que los proyectos masivos pueden tardar decenas de minutos y algunos incluso horas en compilarse. Afortunadamente, se pueden notar muchos errores antes de la compilaci贸n con la ayuda de IDE, pero no todos.

    C贸digo fuente vs c贸digo de objeto

    Para lenguajes de programaci贸n interpretados, el c贸digo fuente es necesario para la ejecuci贸n. Esto significa que el c贸digo fuente de la aplicaci贸n est谩 expuesto al usuario, como JavaScript est谩 expuesto en el navegador.

    Permitir que los usuarios lean completamente el c贸digo fuente puede permitir a los usuarios malintencionados manipular y encontrar lagunas en la l贸gica. Esto puede, hasta cierto punto, limitarse mediante la ofuscaci贸n de c贸digo, pero sigue siendo mucho m谩s accesible que el c贸digo compilado.

    Por otro lado, una vez que el programa escrito en un lenguaje de programaci贸n compilado se compila en un c贸digo objeto, se puede ejecutar un n煤mero infinito de veces y el c贸digo fuente ya no es necesario.

    Por eso, cuando se pasa el programa a un usuario, basta con enviarle el c贸digo objeto, y no el c贸digo fuente, normalmente en forma de .exe archivo en Windows.

    El c贸digo interpretado es m谩s susceptible a los ataques de inyecci贸n de c贸digo y el hecho de que no se verifique el tipo nos presenta un nuevo conjunto de excepciones y errores de programaci贸n.

    Conclusi贸n

    No existe una forma “mejor” de traducir el c贸digo fuente, y los lenguajes de programaci贸n tanto compilados como interpretados tienen sus ventajas y desventajas, como se mencion贸 anteriormente.

    En muchos casos, la l铆nea entre “compilado” e “interpretado” no est谩 claramente definida cuando se trata de un lenguaje de programaci贸n m谩s moderno, en realidad, no hay nada que le impida escribir un compilador para un lenguaje interpretado, por ejemplo.

    Etiquetas:

    Deja una respuesta

    Tu direcci贸n de correo electr贸nico no ser谩 publicada. Los campos obligatorios est谩n marcados con *