Crea tu propio compilador – Cap. 2 – Preparando el ambiente de trabajo

2.1 Introducción

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 cuál es el esquema de trabajo que queremos lograr.

2.2 ¿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>

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 de ejecutable.

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 compilar un código fuente, que podría ser «input.tit» y el resultado final debería ser «input.exe»:

Podemos esquematizar este flujo:

Figura 2.1 – Flujo de trabajo para generar un ejecutable con Titan

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 también.

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.

Todos los aplicativos mostrados (nuestro compilador, el ensamblador y el enlazador) se llamarán por línea de comandos. De esta forma, se podrá trabajar de forma más automatizada, si creamos un archivo *.bat.

2.3 La carpeta de trabajo

Como dijimos en el artículo anterior, este compilador va a compilar sobre Windows, y también para Windows (compilación nativa), 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 «D:\masm32\» así que crearé mi carpeta de trabajo en D: y la llamaré «D:\Proyectos_Software\Titan».

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.

2.4 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»:

Figura 2.2 – Creación del proyecto en Lazarus

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

Figura 2.3 – Programa de consola

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 (D:\Proyectos_Software\Titan). Para esto usamos el menú: >Proyecto>Guardar Proyecto como… y le damos el nombre «titan»:

Figura 2.4 – Guardado del proyecto

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

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

2.5 Configurando y probando 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.

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 «D:\Proyectos_Software\Titan\», un archivo con el nombre «input.asm» que es el 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:

Figura 2.5 – Hola mundo en ensamblador

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. Por ahora, no es importante entender este programa. La siguiente sección estará describiendo el lenguaje ensamblador con más detalle.

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

Figura 2.6 – Ensamblado del programa

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.

Llamamos al ensamblador con «\masm32\bin\ml» porque tenemos instalado el MASM32 en la misma unidad D. Esta es una limitación del MASM32, por cuestiones de su diseño. Afortunadamente, el MASM32 es solo una carpeta que puede ser fácilmente movida de una unidad a otra.

Si todo sale bien, esto debe generarnos el archivo «input.obj»:

Figura 2.7 – Archivos del proyecto

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:

Figura 2.8 – Enlazando el archivo «input.obj»

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.

Figura 2.9 – Ejecutando nuestro programa

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 compilado, ensamblado, enlazado y, de paso, la ejecución. Para ello crearemos el archivo: «test.bat» con el siguiente contenido:

@echo off
    REM *** Set path and files ***
    SET THIS_DIR=%~dp0
    SET THIS_UNIT=%~d0
    SET OBJ_FILE=%THIS_DIR%input.obj
    SET ASM_FILE=%THIS_DIR%input.asm
    SET EXE_FILE=%THIS_DIR%input.exe

    %THIS_UNIT%
    cd %THIS_DIR%

    REM *** Compile ***
    cls
    titan.exe
    if errorlevel 1 goto TheEnd

    REM *** Assemble and link ***

    if exist "input.obj" del %OBJ_FILE%
    if exist "input.exe" del %EXE_FILE%

    \masm32\bin\ml /c /coff %ASM_FILE%
    if errorlevel 1 goto errasm

    \masm32\bin\PoLink /SUBSYSTEM:CONSOLE %OBJ_FILE%
    if errorlevel 1 goto errlink
    goto success

:errlink
    echo _
    echo !!!Link error
    goto TheEnd

:errasm
    echo _
    echo !!!Assembly Error
    goto TheEnd
:success
    del %OBJ_FILE%
    echo "Executing..."
    %EXE_FILE%

:TheEnd

Este «script» llama a nuestro compilador «titan.exe» que deberá generar el archivo «input.asm» (a partir de un archivo «input.tit») que luego será ensamblado y enlazado.

Como aún no hemos creado nuestro compilador «titan.exe» este script no funcionará aún, pero lo dejaremos ya preparado para que nos sirva en nuestras pruebas futuras.

Además, del compilado, 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 fuente, podemos lanzar este «bat» para compilar/ensamblar/enlazar/ejecutar de forma sencilla. El proceso sigue el flujo indicado en la figura 2.1.

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.

2.6 Configurando al Visual Studio Code

El editor VSCode (Visual Studio Code )tiene como característica el de ser muy configurable y se adapta a diversos entornos de desarrollo. Por lo mismo, cada quien puede instalar los complementos que considere necesarios y personalizarlo a su gusto.

El primer paso para iniciar la configuración de VSCode será abrir nuestra carpeta de trabajo «D:\Proyectos_Software\Titan\» para poder editar cómodamente nuestros archivos. Aquí necesitamos crear nuestro archivo de entrada «input.tit», en blanco por ahora.

Luego, mi recomendación es trabajar en dos ventanas laterales, mostrando al archivo «input.tit» por un lado, y al archivo «input.asm» por el otro. Así se nos facilitará ver la salida de nuestro compilador:

Figura 2.10 – Vista de VSCode

Para mejorar la apariencia del texto mostrado (principalmente el color del texto), es recomendable configurar un resaltador de sintaxis para ambos tipos de archivo. En el caso de «input.asm» es probable que el VSCode configure automáticamente uno y muestre el texto coloreado. De otra forma, se puede elegir alguno cercano al ensamblador de Intel x86.

Para el caso del archivo *.tit, es casi seguro que no se configure automáticamente algún resaltador. De momento, se puede trabajar con algún lenguaje cercano a la sintaxis de nuestro lenguaje. Podría ser el del lenguaje Go o uno similar. De todas formas, como aún no definimos el lenguaje, no podremos probar el coloreado de la sintaxis. Luego de definir el lenguaje, podremos elegir mejor un resaltador o inclusive crear uno personalizado.

Otra configuración muy útil, sería configurar una tarea (Task) para ejecutar directamente a nuestro archivo «test.bat». Para amplia información sobre las tareas, se puede consultar a https://code.visualstudio.com/docs/editor/tasks.

En nuestro caso, nos bastará con crear una tarea accediendo al menú «Terminal>Configure Default Build Task…» y luego elegir la opción «Create tasks.json file from template»:

Figura 2.11 – Configurando una tarea

Luego elegir «MSBuild» y se nos creará un archivo con una plantilla:

Figura 2.12 – Archivo «tasks.json»

Para adaptar esta tarea, con nuestro archivo «test.bat» debemos reemplazar el contenido con este texto:

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "label": "Ejecutar Titan",
            "type": "shell",
            "command": "./scripts/test.sh",
            "windows": {
                "command": ".\\test.bat"
            },
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "presentation": {
                "clear": true ,
                "showReuseMessage": false
            }, 
            "problemMatcher": [
                {
                    "owner": "content-linter",
                    "fileLocation": ["autoDetect", "${workspaceFolder}"],
                    "pattern": {
                        "regexp": "^(ERROR|WARNING):\\s*(.*)\\((\\d+),(\\d+)\\):\\s+(.*)$",
                        "file": 2,
                        "line": 3,
                        "column": 4,
                        "severity": 1,
                        "message": 5
                    }
                }
            ],
            "options": {
                "statusbar": {
                    "label": "$(check-all) Validate",
                    "color": "#00FF00"
                }
            }
        }
    ]
  }

Luego ya tendremos acceso a ejecutar «tets.bat» directamente, desde el terminal de VSCode, con la combinación de teclas <Shift>+<Ctrl>+<B>, o la combinación que se haya asignado a la tarea «Build Task».

De momento, y como no tenemos creado aún nuestro compilador, al ejecutar la tarea, solo obtendremos un mensaje de error en el terminal, como el que se muestra a continuación:

Figura 2.13 – Ejecución de «test.bat» desde VSCode

Cualquier otro mensaje de error, indicará que hay algo malo en el archivo «test.bat» o en la configuración de VSCode.

Esta configuración en «tasks.json» no solo nos permitirá compilar, ensamblar y enlazar desde el terminal del VScode, sino que también nos identificará los errores de compilación y los indicará en el archivo fuente, lo que le dará un aire más profesional a nuestro compilador y lenguaje.

Aunque no todas las configuraciones mencionadas, son necesarias, resultan de mucha ayuda para este proyecto. Ahora estamos ya preparados para iniciar el desarrollo del compilador, pero antes daremos un repaso al ensamblador, en el siguiente artículo.


Sé el primero en comentar

Dejar una contestacion

Tu dirección de correo electrónico no será publicada.


*