Nota: quando ho usato "complesso" nel titolo, intendo che l'espressione ha molti operatori e operandi. Non che l'espressione stessa sia complessa.
Di recente ho lavorato su un semplice compilatore per l'assemblaggio x86-64. Ho terminato il front-end principale del compilatore - il lexer e il parser - e ora sono in grado di generare una rappresentazione ad albero di sintassi astratta del mio programma. E poiché la mia lingua verrà digitata staticamente, sto facendo la fase successiva: digitare il controllo del codice sorgente. Tuttavia, ho riscontrato un problema e non sono stato in grado di risolverlo ragionevolmente da solo.
Considera il seguente esempio:
Il parser del mio compilatore ha letto questa riga di codice:
int a = 1 + 2 - 3 * 4 - 5
E convertito nel seguente AST:
=
/ \
a(int) \
-
/ \
- 5
/ \
+ *
/ \ / \
1 2 3 4
Ora deve digitare controllare l'AST. inizia dal primo tipo controllando l' =
operatore. Per prima cosa controlla il lato sinistro dell'operatore. Vede che la variabile a
è dichiarata come un numero intero. Quindi ora deve verificare che l'espressione sul lato destro restituisca un numero intero.
Capisco come questo potrebbe essere fatto se l'espressione fosse solo un singolo valore, come 1
o 'a'
. Ma come si farebbe per espressioni con più valori e operandi - un'espressione complessa - come quella sopra? Per determinare correttamente il valore dell'espressione, sembra che il correttore di tipi debba effettivamente eseguire l'espressione stessa e registrare il risultato. Ma questo sembra ovviamente vanificare lo scopo di separare le fasi di compilazione ed esecuzione.
L'unico altro modo in cui immagino che ciò possa essere fatto è controllare ricorsivamente la foglia di ogni sottoespressione nell'AST e verificare che tutti i tipi di foglia corrispondano al tipo di operatore previsto. Quindi, a partire =
dall'operatore, il controllo del tipo eseguirà quindi la scansione di tutti gli AST sul lato sinistro e verificherebbe che i fogli sono tutti numeri interi. Lo ripeterebbe quindi per ciascun operatore nella sottoespressione.
Ho provato a cercare l'argomento nella mia copia di "The Dragon Book" , ma non sembra entrare nei dettagli, e semplicemente ribadisce ciò che già conosco.
Qual è il solito metodo usato quando un compilatore sta controllando il tipo di espressioni con molti operatori e operandi? Sono utilizzati alcuni dei metodi che ho menzionato sopra? In caso contrario, quali sono i metodi e come funzionano esattamente?
double a = 7/2
cercherebbe di interpretare il lato destro come doppio, quindi cercherebbe di interpretare il numeratore e il denominatore come doppio e di convertirli se necessario; di conseguenza a = 3.5
. Il bottom-up eseguirà la divisione di interi e convertirà solo sull'ultimo passaggio (assegnazione), quindi a = 3.0
.
int a = 1 + 2 - 3 * 4 - 5
ma aint a = 5 - ((4*3) - (1+2))