Fasi del compilatore con esempio: processo e passaggi di compilazione
Quali sono le fasi della progettazione del compilatore?
Compiler opera in varie fasi, ciascuna fase trasforma il programma sorgente da una rappresentazione all'altra. Ogni fase prende input dalla fase precedente e invia il proprio output alla fase successiva del compilatore.
Ci sono 6 fasi in un compilatore. Ognuna di queste fasi aiuta a convertire la lingua di alto livello del codice macchina. Le fasi di un compilatore sono:
- Analisi lessicale
- Analisi della sintassi
- Analisi semantica
- Generatore di codice intermedio
- Ottimizzatore del codice
- Generatore di codici
Tutte queste fasi convertono il codice sorgente dividendolo in token, creando alberi di analisi e ottimizzando il codice sorgente in diverse fasi.
Fase 1: Analisi lessicale
L'analisi lessicale è la prima fase in cui il compilatore esegue la scansione del codice sorgente. Questo processo può essere svolto da sinistra a destra, carattere per carattere, e raggruppare questi personaggi in token.
Qui il flusso di caratteri del programma sorgente viene raggruppato in sequenze significative identificando i token. Effettua l'inserimento dei ticket corrispondenti nella tabella dei simboli e passa quel token alla fase successiva.
Le funzioni principali di questa fase sono:
- Identificare le unità lessicali in un codice sorgente
- Classifica le unità lessicali in classi come costanti, parole riservate e inseriscile in tabelle diverse. Ignorerà i commenti nel programma sorgente
- Identificare il token che non fa parte della lingua
Esempio:
x = y + 10
Tokens
X | identificatore |
= | Operatore di assegnazione |
Y | identificatore |
+ | Operatore di addizione |
10 | Numero |
Fase 2: Analisi della sintassi
L'analisi della sintassi riguarda la scoperta della struttura nel codice. Determina se un testo segue o meno il formato previsto. Lo scopo principale di questa fase è assicurarsi che il codice sorgente scritto dal programmatore sia corretto o meno.
L'analisi della sintassi si basa sulle regole basate sullo specifico linguaggio di programmazione costruendo l'albero di analisi con l'aiuto di token. Determina anche la struttura della lingua di partenza e la grammatica o la sintassi della lingua.
Ecco un elenco delle attività eseguite in questa fase:
- Ottieni token dall'analizzatore lessicale
- Controlla se l'espressione è sintatticamente corretta o meno
- Segnala tutti gli errori di sintassi
- Costruire una struttura gerarchica nota come albero sintattico
Esempio
Qualsiasi identificatore/numero è un'espressione
Se x è un identificatore e y+10 è un'espressione, allora x= y+10 è un'istruzione.
Considerare l'albero sintattico per il seguente esempio
(a+b)*c
Nell'albero di analisi
- Nodo interno: scheda con una scheda operatore e due schede per bambini
- Foglia: record con 2/più campi; uno per il token e altre informazioni sul token
- Assicurarsi che i componenti del programma si adattino insieme in modo significativo
- Raccoglie informazioni sul tipo e verifica la compatibilità del tipo
- Gli operandi di controllo sono consentiti dalla lingua di origine
Fase 3: Analisi Semantica
L'analisi semantica verifica la coerenza semantica del codice. Utilizza l'albero della sintassi della fase precedente insieme alla tabella dei simboli per verificare che il codice sorgente fornito sia semanticamente coerente. Controlla inoltre se il codice trasmette un significato appropriato.
Semantic Analyser controllerà la mancata corrispondenza dei tipi, gli operandi incompatibili, una funzione chiamata con argomenti impropri, una variabile non dichiarata, ecc.
Le funzioni della fase di analisi semantica sono:
- Ti aiuta a memorizzare le informazioni sul tipo raccolte e a salvarle nella tabella dei simboli o nell'albero della sintassi
- Consente di eseguire il controllo del tipo
- Nel caso di mancata corrispondenza del tipo, dove non esistono regole esatte di correzione del tipo che soddisfino l'operazione desiderata, viene mostrato un errore semantico
- Raccoglie informazioni sul tipo e verifica la compatibilità del tipo
- Controlla se la lingua di origine consente o meno gli operandi
Esempio
float x = 20.2; float y = x*30;
Nel codice precedente, l'analizzatore semantico digiterà l'intero 30 in float 30.0 prima della moltiplicazione
Fase 4: generazione intermedia del codice
Una volta terminata la fase di analisi semantica, il compilatore genera codice intermedio per la macchina di destinazione. Rappresenta un programma per una macchina astratta.
Il codice intermedio è tra il linguaggio di alto livello e quello di livello macchina. Questo codice intermedio deve essere generato in modo tale da facilitarne la traduzione nel codice macchina di destinazione.
Funzioni sulla generazione del Codice Intermedio:
- Dovrebbe essere generato dalla rappresentazione semantica del programma sorgente
- Contiene i valori calcolati durante il processo di traduzione
- Ti aiuta a tradurre il codice intermedio nella lingua di destinazione
- Consente di mantenere l'ordine di precedenza della lingua di origine
- Contiene il numero corretto di operandi dell'istruzione
Esempio
Per esempio,
total = count + rate * 5
Il codice intermedio con l'aiuto del metodo del codice indirizzo è:
t1 := int_to_float(5) t2 := rate * t1 t3 := count + t2 total := t3
Fase 5: ottimizzazione del codice
La fase successiva è l'ottimizzazione del codice o codice intermedio. Questa fase rimuove la riga di codice non necessaria e organizza la sequenza di istruzioni per accelerare l'esecuzione del programma senza spreco di risorse. L'obiettivo principale di questa fase è migliorare il codice intermedio per generare un codice che venga eseguito più velocemente e occupi meno spazio.
Le funzioni principali di questa fase sono:
- Ti aiuta a stabilire un compromesso tra velocità di esecuzione e velocità di compilazione
- Migliora il tempo di esecuzione del programma di destinazione
- Genera codice semplificato ancora nella rappresentazione intermedia
- Rimozione del codice irraggiungibile ed eliminazione delle variabili inutilizzate
- Rimozione di istruzioni che non vengono modificate dal ciclo
Esempio:
Considera il seguente codice
a = intofloat(10) b = c * a d = e + b f = d
Può diventare
b =c * 10.0 f = e+b
Fase 6: generazione del codice
La generazione del codice è l'ultima e finale fase di un compilatore. Riceve input dalle fasi di ottimizzazione del codice e di conseguenza produce il codice della pagina o il codice oggetto. L'obiettivo di questa fase è allocare lo spazio di archiviazione e generare codice macchina rilocabile.
Alloca inoltre posizioni di memoria per la variabile. Le istruzioni nel codice intermedio vengono convertite in istruzioni macchina. Questa fase converte il codice ottimizzato o intermedio nella lingua di destinazione.
La lingua di destinazione è il codice macchina. Pertanto durante questa fase vengono selezionate e assegnate anche tutte le locazioni di memoria ed i registri. Il codice generato da questa fase viene eseguito per ricevere input e generare output attesi.
Esempio
a = b + 60.0
Potrebbe essere tradotto in registri.
MOVF a, R1 MULF #60.0, R2 ADDF R1, R2
Gestione della tabella dei simboli
Una tabella dei simboli contiene un record per ciascun identificatore con campi per gli attributi dell'identificatore. Questo componente semplifica la ricerca del record dell'identificatore da parte del compilatore e il suo recupero rapido. La tabella dei simboli aiuta anche nella gestione dell'ambito. La tabella dei simboli e il gestore degli errori interagiscono con tutte le fasi e la tabella dei simboli si aggiorna di conseguenza.
Routine di gestione degli errori
Nel processo di progettazione del compilatore possono verificarsi errori in tutte le fasi indicate di seguito:
- Analizzatore lessicale: token scritti in modo errato
- Analizzatore di sintassi: parentesi mancante
- Generatore di codice intermedio: operandi non corrispondenti per un operatore
- Code Optimizer: quando l'istruzione non è raggiungibile
- Code Generator: Quando la memoria è piena o i registri corretti non vengono allocati
- Tabelle dei simboli: errore di più identificatori dichiarati
Gli errori più comuni sono sequenze di caratteri non valide nella scansione, sequenze di token non valide nel tipo, errore di ambito e analisi nell'analisi semantica.
L'errore può verificarsi in una qualsiasi delle fasi precedenti. Dopo aver trovato gli errori, la fase deve gestire gli errori per continuare con il processo di compilazione. Questi errori devono essere segnalati al gestore errori che gestisce l'errore per eseguire il processo di compilazione. Generalmente gli errori vengono segnalati sotto forma di messaggio.
Sommario
- Il compilatore opera in varie fasi, ciascuna fase trasforma il programma sorgente da una rappresentazione all'altra
- Sei fasi di progettazione del compilatore sono 1) Analisi lessicale 2) Analisi sintattica 3) Analisi semantica 4) Generatore di codice intermedio 5) Ottimizzatore di codice 6) Codice Generator
- L'analisi lessicale è la prima fase in cui il compilatore esegue la scansione del codice sorgente
- L'analisi della sintassi riguarda la scoperta della struttura del testo
- L'analisi semantica verifica la coerenza semantica del codice
- Una volta terminata la fase di analisi semantica del compilatore, generare codice intermedio per la macchina di destinazione
- La fase di ottimizzazione del codice rimuove la riga di codice non necessaria e organizza la sequenza di istruzioni
- La fase di generazione del codice riceve input dalla fase di ottimizzazione del codice e di conseguenza produce il codice della pagina o il codice oggetto
- Una tabella dei simboli contiene un record per ciascun identificatore con campi per gli attributi dell'identificatore
- La routine di gestione degli errori gestisce errori e report durante molte fasi