Come Funziona un Compilatore

Il compilatore è quel programma che legge le istruzioni in formato sorgente, in qualche linguaggio di programmazione e lo traduce in linguaggio oggetto che può essere un altro linguaggio di programmazione oppure un linguaggio macchina. I primi compilatori sono nati negli anni ’50 e da allora le basi di funzionamento sono pressoché rimaste uguali anche se è cambiato l’approccio ed oggi la progettazione di un compilatore impegna decisamente meno tempo e risorse. Il modello di compilazione più diffuso è quello basato sulle parti di analisi e sintesi. La parte di analisi serve a dividere il programma sorgente nelle sue parti costituenti e a creare una rappresentazione intermedia utilizzata dalla fase di sintesi per creare il programma oggetto. Durante la fase di analisi vengono archiviate le istruzioni del programma in una struttura ad albero, l’albero sintattico nel quale i nodi rappresentano le operazioni e le foglie gli argomenti di quell’istruzione. Per creare il file eseguibile abbiamo quindi bisogno di alcuni passaggi:

  1. in alcuni linguaggi occorre una prima analisi del preprocessore
  2. il compilatore trasforma il sorgente in oggetto
  3. traduzione da parte dell’assemblatore nel codice macchina
  4. creazione del file eseguibile da parte del loader/linker che provvede a collegare i file ogggetto rilocabili e le eventuali librerie richiamate dalle funzioni

Un compilatore può essere schematicamente rappresentato da una serie di fasi: analisi, sintesi e due trsversali a queste come la gestione della tavola dei simboli e la gestione degli errori. Le varie fasi di analisi dipendono dal linguaggio di programmazione utilizzato e sono indipendenti dal sistema, per questo sono chiamate front end, compresa la tavola dei simboli e la gestione degli errori. Le fasi che non dipendono dal linguaggio utilizzato ma soltanto da quello intermedio e quindi dal tipo di sistema sono definite back end. Tenere separate la fase di front end da quella di back end consente di creare parti del compilatore riutilizzabili anche per altri sistemi.

Funzionamento del Compilatore
La strutturazione schematica del compilatore con le fasi di analisi e sintesi

Le varie fasi di analisi in front end si possono suddividere in:

  1. Analisi lessicale ( detta anche lineare o scanning ): legge da sinistra verso destra la sequenza di caratteri che costituiscono il programma sorgente e li raggruppa in “token”, ossia sequenze di caratteri come le parole chiave, i nomi degli identificatori, i nomi delle funzioni e così via. Durante questa fase vengono eliminati tutti gli spazi, le tabulazioni ed i caratteri speciali.
  2. Analisi sintattica ( detta anche gerarchica o parsing ): raggruppa i vari caratteri e token ritrovati nelle frasi della grammatica del linguaggio, con una struttura ad albero ( parse tree ) nel rispetto delle regole grammaticali utilizzate.
  3. Analisi semantica: controlla il programma sorgente alla ricerca di errori semantici e fornisce tutte le informazioni sui tipi ( type checking ) alla successiva fase di generazione del codice.

Dopo le fasi di analisi il compilatore genera una rappresentazione intermedia del programma sorgente. In questa fase si ricorre all’ottimizzazione del codice che permetta di generare un codice macchina più efficiente, ricordo che fino a questo punto abbiamo un codice intermedio astratto. Entriamo quindi nella fase della tavola dei simboli che è la parte più importante del compilatore, i simboli e le variabili vengono memorizzate in apposite strutture dati, per le funzioni vengono archiviate le informazioni sul numero e tipo di argomenti ed al tipo di ritorno, inoltre vengono controllati gli scope, ossia la porzione di codice in cui una variabile è valida. Da tutte queste analisi vengono anche controllati gli errori e si entra nella fase di gestione degli errori che avverte il programmatore di aver commesso un errore di ortografia. Il compilatore non deve fermarsi al primo errore riscontrato poiché non sarebbe efficiente e farebbe perdere solo del tempo, quindi questa fase riguarda tutto il sorgente con una lista di errori scritti sulla console in modo che il programmatore possa aggiustare interamente il codice.

Di Giampaolo Rossi

Sviluppatore software da oltre 16 anni.