Crea tu propio compilador – Parte 2 – Preparando el ambiente de trabajo

En el artículo anterior hicimos una introducción sobre este proyecto y sus alcances. También vimos las herramientas que vamos a usar. Ahora vamos a configurar estas herramientas y preparar el ambiente de trabajo para iniciar el desarrollo, de una forma cómoda. Pero primeramente aclararemos cual es el esquema de trabajo que queremos lograr.

¿Cómo compilará nuestro compilador?

El compilador que estamos creando funcionará en la forma común en que trabajan la mayoría de compiladores que compilan a código nativo (como C, C++, Pascal, o Go, no como Java o C#), excepto tal vez por la necesidad de un paso adicional.

La idea de los compiladores es generar un ejecutable a partir de un código fuente. En el caso de un programa en C, el proceso sería algo así:

    <prueba.c> 
         |   
     Compilador
         |
    <prueba.obj>  
         |
     Enlazador
         |
    <prueba.exe>

En donde se puede ver que el compilado es solo el primer paso. Luego es necesario usar un enlazador para generar finalmente el archivo ejecutable. El compilador genera un archivo *.obj (código objeto) que es ya código máquina, pero sin estructura.

El enlazador hace el trabajo de construir un ejecutable analizando las llamadas a las subrutinas y módulos que se hacen en el programa. Una de las funciones del enlazador es, por ejemplo, quitar las subrutinas que no son usadas.

En este proyecto crearemos un compilador, no un enlazador y tampoco generaremos un archivo *.obj directamente, sino que generaremos un archivo en ensamblador *.asm.

La mayor parte del trabajo que realizaremos, en este proyecto, será obtener un ejecutable que será nuestro compilador y se llamará “titan.exe”.

Entonces podríamos usar este programa para conpilar un código fuente, que podría ser “prueba.tit” y el resultado final debería ser “prueba.exe”:

Podemos esquematizar este flujo:

     <prueba.tit> 
          |   
Nuestro Compilador "titan"
          |
     <prueba.asm>
          |  
  Ensamblador "MASM32"
          |
     <prueba.obj>  
         |
  Enlazador "Polink"
          |
     <prueba.exe>

Como se puede apreciar, el flujo de compilación pasa por diferentes etapas, creando archivos intermedios en cada etapa. El enlazador “Polink” viene con MASM32, así que si hemos instalado el MASM32 ya lo tenemos.

Salvo, por el uso del ensamblador, nuestro compilador trabajará igual a la mayoría de compiladores de código nativo. Pero esto no es nada raro, porque existen muchos compiladores que usan también un ensamblador para generar código objeto.

La carpeta de trabajo

Como dijimos en el artículo anterior, este compilador va a compilar sobre Windows, y también para Windows, así que está claro que necesitaremos un sistema operativo Windows. Si bien nuestro compilador va a generar código binario para 32 bits, podemos trabajar en Windows de 32 o 64 bits.

Primeramente, debemos elegir una carpeta en nuestro disco, en donde iremos creando los archivos necesarios. Por comodidad conviene crear la carpeta en la misma unidad en donde tengamos instalado el MAS32. En mi caso lo he instalado en “C:\masm32\” así que crearé mi carpeta de trabajo en C: y la llamaré “c:\Titan”, porque este es el nombre del proyecto, al igual que al lenguaje de programación que vamos a crear (Ver el siguiente artículo).

Esta será la única carpeta de trabajo, y aquí iremos colocando todos los archivos que componen el proyecto.

Es importante asegurarse de que el usuario tiene accesos y privilegios de lectura, escritura, eliminación y modificación sobre esta carpeta para no entorpecer el proceso de desarrollo.

Creando el proyecto en Lazarus

Como primer paso vamos a crear el proyecto en Lazarus que usaremos para crear a nuestro compilador.

Como mencioné, voy a usar Lazarus porque es una IDE gratuita y bastante cómoda de trabajar, además espero que el lenguaje a usar, sea parecido a la sintaxis de Pascal y algo de Basic, ya que no pienso crear lenguajes al viejo estilo de C, porque de esos ya hay muchos (C++, Java, PHP, awk, …) y personalmente no apoyo ese estilo de sintaxis.

De todas formas, a este nivel, el lector puede elegir su entorno favorito (y su lenguaje) de desarrollo. El objetivo aquí es solo partir de una herramienta que me permita crear una versión inicial del compilador con una sintaxis sencilla. Inclusive se puede usar un intérprete (como Python) y no necesariamente un compilador, porque la idea es que este proyecto sea un compilador funcional, pero finalmente el código será reescrito en el lenguaje del mismo compilador.

Para crear el proyecto, abrimos la IDE de Lazarus y creamos un proyecto nuevo de tipo “Programa simple”:

Esto nos creará una aplicación de consola (sin GUI), y con el código mínimo:

En mi caso, yo he configurado mi editor de Lazarus con fondo negro usando las opciones del menú: >Herramientas>Opciones>Editor>Colores.

Es necesario crear una aplicación de consola porque no olvidemos que este código será reescrito luego en el lenguaje del mismo compilador, y deberá ser, también, una aplicación de consola por su simplicidad.

Ahora debemos guardar el proyecto en la carpeta nueva que hemos creado (C:\Titan\). Para esto usamos el menú: >Proyecto>Guardar Proyecto como… y le damos el nombre “titan”:

Luego de esto, ya tendremos nuestro proyecto creado y listo para empezar desarrollarlo. Pero aún no escribiremos nada.

En la carpeta “C:\Titan” se deben haber creado 3 archivos (titan.lpi, titan.lpr, y titan.lps) que corresponden a los 3 archivos de este proyecto.

Configurando el MASM32

El siguiente paso consiste en configurar el ensamblado y enlazado de un archivo en ensamblador, usando el MASM32.

Esta tarea es importante porque en el desarrollo del compilador necesitaremos, realizar muchas sesiones de “Compilación->Ensamblado->Enlazado” .

Es recomendable que el usuario se familiarice primero con el uso del MASM32 para tener un mejor entendimiento de este paso. Hay suficiente información en la red.

El objetivo de este paso es lograr en ensamblado y enlazado de un archivo en ensamblador para generar un ejecutable.

Lo primero que vamos a hacer es crear, en la carpeta “C:\Titan\”, un archivo con el nombre “input.asm” que es archivo donde colocaremos el código fuente en ensamblador. Lo podemos hacer con cualquier editor de texto. Dentro de este archivo podemos colocar el siguiente código:

Este será nuestro programa “Hola mundo” en ensamblador que vamos a procesar con el MASM32. El único trabajo que hace este código es escribir “Hola mundo” en la consola. No es tema de estos artículos, enseñar a programar en ensamblador. El lector haría bien en repasar información al respecto como la que se muestra en: http://www.cs.virginia.edu/~evans/cs216/guides/x86.html o http://win32assembly.programminghorizon.com/tutorials.html.

Para ensamblar este código, podemos abrir el CMD y ejecutar el ensamblador del MASM32:

Llamamos al programa “ml”, porque este es el nombre del ensamblador de MASM32, y usamos el parámetro “/c” para que solo realice el ensamblado porque “ml” puede también realizar el enlazado. El parámetro /coff es para definir el formato del archivo objeto.

Si todo sale bien, esto debe generarnos el archivo “input.obj”:

Pero el archivo “input.obj” no es todavía el ejecutable, así que el siguiente paso es enlazar este código objeto, usando un enlazador:

Este enlazador es un programa silencioso y no mostrará mensajes mientras no haya errores. El parámetro /SUBSYSTEM es para indicarle qué tipo de aplicación queremos generar.

Si todo ha ido bien, ya tendremos nuestro archivo ejecutable, y podremos ejecutarlo, obteniendo la siguiente salida.

Lo que hemos visto aquí es una sesión completa de Escritura, Ensamblado y Enlazado, y es lo que se solía hacer antes cuando se programaba en ensamblador puro.

Como no queremos estar constantemente ejecutando los mismos comandos, resulta conveniente escribir un archivo *.bat, que automatice el ensamblado, enlazado y, de paso, la ejecución. Para ello crearemos el archivo: “test.bat” con el siguiente contenido:

Además, el código de ensamblado y enlazado,  lo que hace este código es verificar los posibles errores, para ejecutar el programa solo si no ha habido errores.

Luego, cada vez que se modifique el programa en ensamblador, podemos lanzar este “bat” para ensamblar/enlazar/ejecutar de forma sencilla.

Si usted no sabe lo que es un archivo “bat” o ha tenido dificultades en entender alguno de los pasos que se indican aquí, es recomendable leer un poco, e investigar, sobre los temas difíciles, antes de proseguir.

Asumiendo que hemos podido lograr estos paso con éxito, ya estamos preparados para iniciar el desarrollo del compilador, pero antes definiremos un lenguaje de programación, en el siguiente artículo.

Puntuación: 4.5 / Votos: 2

Deja un comentario

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