Número 57
31 de maio de 2023

As leccións do compilador

Hai uns meses comecei a escribir un compilador de Pascal. Non é que precise del para un proxecto: fíxeno como hobby, por afección, a ver se sería quen de facelo. Chameino “Pascual”, e na Folla de hoxe quero falar de dúas leccións que aprendín mentres o escribía.

Blaise Pascal.

Moitos atallos non levan a ningures.

Sempre quixen escribir o meu compilador de Pascal en Pascal, o que fixo xurdir a cuestión de como compilar a primeira versión do novo compilador — o problema do bootstrap.

A miña primeira idea foi escribir un compilador mínimo en C e despois usalo para compilar a primeira versión de Pascual. Como non quería esforzarme moito neste compilador mínimo (xa que só tiña pensado usalo unha vez) procurei a maneira de non incluír un sistema de tipos nin nada complicado: quería só converter código en Pascal a código en C como quen fai unha busca e substitución.

Lamentablemente, isto non funcionou. Pronto me decatei de que, moitas veces, era imprescindible saber se unha variable era de tipo ‘char’ ou ‘string’ ou o tamaño dun array. Non me quedou máis remedio que incluír un sistema de tipos no compilador e, chegado a ese punto, decidín non facer un compilador de usar e tirar, senón comezar directamente a escribir o meu Pascual e usar outro compilador xa existente para facer o bootstrap.

Como eu son de aprendizaxe lenta, tiven un problema semellante uns días despois. Para aforrar traballo, tivera a xenial idea de compilar as expresións directamente sen construír unha árbore de sintaxe. Porén, resultou que para xerar o código correcto tendo en conta as precedencias dos operadores tiña que saber que operadores se usaban en cada subexpresión. É dicir: tiven que borrar o meu código e facelo outra vez, pero sen atallos, para que funcionara correctamente.

Só se precisa de pezas simples para facer programas sofisticados.

No principio pensaba que ía ter que implementar moitas funcionalidades para que Pascual puidera compilarse a si mesmo, pero non foi así. A primeira versión capaz de autocompilarse só recoñecía catro tipos de datos básicos, enumeracións, arrays, records e subrutinas, pero non tiña punteiros, memoria dinámica, constantes ou ficheiros binarios. Por non ter, non tiña nin comentarios!

Pouco a pouco fun mellorando Pascual, engadindo novos tipos de datos, conxuntos, subrangos, variantes, punteiros e outras funcións cada vez máis sofisticadas, pero o código de Pascual descende directamente daquel compilador primitivo, e hai moitos sitios nos que se nota.

Por exemplo, para buscar o nome dun tipo, variable, constante ou subrutina, Pascual non usa unha táboa hash, que é a maneira “correcta” de facelo. No seu lugar, Pascual percorre a lista de definicións activas comparando o nome de cada entrada.

Funcionaría máis rápido Pascual se tivera unha táboa hash? Posiblemente, pero Pascual é capaz de autocompilarse en menos de dous segundos, así que o beneficio sería mínimo e, a cambio, eu tería que escribir e manter unha implementación dunha táboa hash.

Moito mellor é usar a estrutura de datos simple, que funciona ben dabondo e ten menos sitios no que se pode agochar un erro.

Estas son as dúas leccións que teño hoxe para vós. Hei seguir traballando en Pascual, que quero escribir un bo manual de usuario para el, así que, con sorte, no futuro terei máis leccións para vos transmitir.

Lección extra: escribir un compilador é máis doado do que un pensa.

Escribir un compilador bo é unha historia totalmente distinta.

A ilustración desta Folla é un retrato de Blaise Pascal.