Linguaggi e compilatori
Corso di Laurea in Informatica
Mauro Leoncini
A.A. 2023/2024
Mauro Leoncini L&C Anno Accademico 2023/24 1 / 48
Linguaggi e compilatori
1 Generazione dell'AST
Architettura del front-end
Rappresentazione astratta di un programma
Verso l'ASD di Kaleidoscope 1.0
Mauro Leoncini L&C Anno Accademico 2023/24 2 / 48
Generazione dell'AST Architettura del front-end
Linguaggi e compilatori
1 Generazione dell'AST
Architettura del front-end
Rappresentazione astratta di un programma
Verso l'ASD di Kaleidoscope 1.0
Mauro Leoncini L&C Anno Accademico 2023/24 3 / 48
Generazione dell'AST Architettura del front-end
Organizzazione modulare
Il nostro obiettivo è progettare (e programmare) un front-end per la
prima versione di Kaleidoscope (che per comodità chiameremo
Kaleidoscope 1.0 )
L'output desiderato per il frot-end è, al momento, solo l' Abstract
Syntax Tree (AST) del programma in input
Iniziamo con delineare la struttura dell'applicazione
Potremmo scrivere tutto in due sole unità di programma: lo scanner e
il parser, includendo in quest'ultimo un main program che coordini
tutte le attività
Proponiamo invece una soluzione modulare, più facilmente
comprensibile, manutenibile e debuggabile
La stessa architettura modulare costituisce da sola una migliore
documentazione dell'applicazione
Mauro Leoncini L&C Anno Accademico 2023/24 4 / 48
Generazione dell'AST Architettura del front-end
I moduli dell'applicazione
L'applicazione consta dunque dei seguenti moduli
Un modulo scanner per la lettura del le e l'analisi lessicale, generato
utilizzando Flex
Un modulo per il parsing e la costruzione dell'AST, generato
utilizzando Bison
Un modulo driver, che include tutte le classi C++ di cui i nodi
dell'AST costituiscono istanze
Il modulo driver denisce anche una classe (omonima) che rappresenta
il c.d. parsing context, ovvero una struttura dati che consente
un'ecace condivisione di dati fra parser e scanner (e main program)
prescindendo dall'uso di variabili globali
Un client che include il main program e il cui compito è di eettuare il
parsing dei parametri forniti dall'utilizzatore nella riga di comandi e di
lanciare la compilazione
Mauro Leoncini L&C Anno Accademico 2023/24 5 / 48
Generazione dell'AST Architettura del front-end
Il programma client
#include <iostream>
#include "[Link]"
int main (int argc, char *argv[])
{ driver drv;
for (int i = 1; i < argc; ++i)
if (argv[i] == std::string ("-p"))
drv.trace_parsing = true;
else if (argv[i] == std::string ("-s"))
drv.trace_scanning = true;
else if (![Link] (argv[i])) {
[Link]->visit();
std::cout << std::endl;
} else return 1;
return 0;
}
Mauro Leoncini L&C Anno Accademico 2023/24 6 / 48
Generazione dell'AST Architettura del front-end
Il client e l'uso della classe driver
Come si vede, l'architettura modulare consente di avere un client
molto semplice
A parte le ovvie operazioni di parsing della linea di comando, dal codice
presentato si può iniziare a comprendere l'uso della classe driver
In due opportune variabili della classe, trace_scanning e
trace_parsing, il driver inserisce informazioni che saranno utlizzate
dallo scanner e (indirettamente) dal parser
Un metodo della classe (parse) consente di dare inizio al processo di
compilazione
A ne compilazione, la variabile root contiene la radice dell'AST, da
cui deve iniziare il processo di visita
Le prossime due slide riportano la denizione della classe driver;
quelle immediatamente seguenti mostrano invece gracamente
l'architettura generale
Mauro Leoncini L&C Anno Accademico 2023/24 7 / 48
Generazione dell'AST Architettura del front-end
La classe driver: denizione
class driver {
public:
driver();
void scan_begin(); // Implementata nello scanner
void scan_end(); // Implementata nello scanner
int parse (const std::string& f);
RootAST* root; // Radice dell'AST costruito
yy::location location; //
std::string file; // File sorgente
bool trace_parsing; // Per trace debug nel parser
bool trace_scanning; // Per trace debug nello scanner
};
Mauro Leoncini L&C Anno Accademico 2023/24 8 / 48
Generazione dell'AST Architettura del front-end
La classe driver: implementazione (parziale)
driver::driver(): trace_parsing (false),
trace_scanning (false) {};
int driver::parse (const std::string &f) {
file = f;
[Link](&file);
scan_begin();
yy::parser parser(*this);
parser.set_debug_level(trace_parsing);
int res = [Link]();
scan_end();
return res;
}
Mauro Leoncini L&C Anno Accademico 2023/24 9 / 48
Generazione dell'AST Architettura del front-end
File coinvolti
[Link]
[Link]
definisce
definisce
classi *AST classe driver [Link]
implementa
implementa
[Link]
[Link]
Mauro Leoncini L&C Anno Accademico 2023/24 10 / 48
Generazione dell'AST Architettura del front-end
Compilazione di Lexer e Parser
[Link]
[Link]
definisce
[Link]
definisce genera
classi *AST classe driver [Link]
genera
implementa
implementa
[Link]
[Link]
include
[Link]
genera
[Link]
Mauro Leoncini L&C Anno Accademico 2023/24 11 / 48
Generazione dell'AST Architettura del front-end
Dopo la compilazione
[Link]
[Link]
definisce
[Link]
definisce
classi *AST classe driver
implementa
implementa
[Link]
[Link]
[Link]
Mauro Leoncini L&C Anno Accademico 2023/24 12 / 48
Generazione dell'AST Architettura del front-end
Dipendenze
[Link]
[Link] include
definisce
[Link]
definisce
classi *AST classe driver
implementa
implementa
[Link]
[Link]
include
[Link]
Mauro Leoncini L&C Anno Accademico 2023/24 13 / 48
Generazione dell'AST Architettura del front-end
Dipendenze
include [Link]
[Link]
definisce
[Link]
definisce
classi *AST classe driver
implementa
implementa
[Link]
[Link]
[Link]
Mauro Leoncini L&C Anno Accademico 2023/24 14 / 48
Generazione dell'AST Architettura del front-end
Esistenza di una dipendenza circolare
Le prossime due slide mostrano l'esistenza di una mutua dipendenza di
driver e parser
Per un verso, infatti, il parser necessita di includere [Link]
Questa è la dipendenza più evidente perché il driver include le classi
*AST (che il parser deve usare per costruire l'albero sintattico) e la
classe driver che funge da contesto
Tuttavia, anche il driver necessita di includere [Link]:
perché è nel parser che è denita la classe location e questa classe è
un fondamentale dato da inserire nel contesto
perché la denizione del tipo di ritorno di yylex è una enumerazione
dei vari token name, che sono deniti dal parser
Per risolvere la circolarità, Bison ha la direttiva %code requires, che
consente di inserire forward declaration nell'header le
Mauro Leoncini L&C Anno Accademico 2023/24 15 / 48
Generazione dell'AST Architettura del front-end
Dipendenze
[Link]
[Link] include
definisce
[Link]
definisce
classi *AST classe driver
implementa
implementa
[Link]
[Link]
[Link]
Mauro Leoncini L&C Anno Accademico 2023/24 16 / 48
Generazione dell'AST Architettura del front-end
Dipendenze
[Link]
[Link]
definisce
[Link]
definisce
classi *AST classe driver
implementa
implementa
[Link]
[Link]
include
[Link]
Mauro Leoncini L&C Anno Accademico 2023/24 17 / 48
Generazione dell'AST Architettura del front-end
Il usso informativo
[Link]
usa
usa [Link]
usa
classe driver
Token
stream
usa
classi *AST
Token
request
[Link]
Mauro Leoncini L&C Anno Accademico 2023/24 18 / 48
Generazione dell'AST Rappresentazione astratta di un programma
Linguaggi e compilatori
1 Generazione dell'AST
Architettura del front-end
Rappresentazione astratta di un programma
Verso l'ASD di Kaleidoscope 1.0
Mauro Leoncini L&C Anno Accademico 2023/24 19 / 48
Generazione dell'AST Rappresentazione astratta di un programma
AST
L' Abstract Syntax Tree (ASD ) di un'espressione è un albero radicato
in cui i nodi interni rappresentano operatori mentre le foglie
rappresentano operandi
Il numero di gli di un nodo interno corrisponde all' arietà (cioè il
numero di operandi) dell'operatore associato a quel nodo interno
L'albero riette in modo naturale la struttura ricorsiva di
un'espressione: come un operatore può avere, come operando,
un'intera sotto-espressione, così il glio di un nodo interno può essere
a sua volta radice di un sotto-albero
La stessa struttura gerarchica impone poi un'ordine alle operazioni al
ne di valutare l'espressione: se un nodo (interno) X è un discendente
di Y, allora l'operazione rappresentata da Y deve essere logicamente
eseguita prima di (quella di) X
L'intera espressione rappresentata da un ASD può infatti essere
valutata mediante una visita dell'albero in ordine posticipato
Mauro Leoncini L&C Anno Accademico 2023/24 20 / 48
Generazione dell'AST Rappresentazione astratta di un programma
Esempio
√
L'ASD dell'espressione x· 1 + y + 2 · y2 è
· ·
x sqrt 2 exp
+ y 2
1 y
Mauro Leoncini L&C Anno Accademico 2023/24 21 / 48
Generazione dell'AST Rappresentazione astratta di un programma
ASD per strutture programmative
Non solo le espressioni aritmetiche o logiche ma anche le più
importanti strutture di controllo (condizionali, iterazioni determinate e
indeterminate, oltre che assegnamento e sequenza) possono essere
descritte mediante un albero
Ad esempio, la struttura condizionale a due vie:
if ⟨condition⟩ {⟨true section⟩} else {⟨false section⟩}
può essere rappresentata mediante il seguente ASD
if_else
Tc Tts Tf s
dove if_else è un codice che indica il condizionale a due vie mentre
Tc , Ttc e Tf c sono rispettivamente gli ASD per la condizione di
controllo e per le istruzioni della sezioni true e false
Mauro Leoncini L&C Anno Accademico 2023/24 22 / 48
Generazione dell'AST Rappresentazione astratta di un programma
ASD per strutture programmative
Altri due esempi: sequenza e iterazione indeterminata (while)
⟨statement1⟩ ; {⟨rest⟩} while ⟨condition⟩ {⟨body⟩}
; while
Ts Tr Tc Tb
dove Ts indica l'ASD che rappresenta ⟨statement1⟩, Tr l'ASD che
rappresenta tutte le istruzioni che seguono ⟨statement1⟩ (il resto del
programma) e, inne, Tc e Tb sono gli ASD che descrivono la
condizione di controllo del while e il suo body.
Mauro Leoncini L&C Anno Accademico 2023/24 23 / 48
Generazione dell'AST Rappresentazione astratta di un programma
Un semplice esempio completo
L'ASD per il frammento di codice: x=10;
y=1;
while (x>0) {
x = x-1;
y = 2*y;
; }
= ;
id num = while
id num > ;
id num = =
id - id ×
id num id num
Mauro Leoncini L&C Anno Accademico 2023/24 24 / 48
Generazione dell'AST Rappresentazione astratta di un programma
Qualche osservazione
Le foglie del precedente ASD denotano identicatori o valori numerici
Questa è la situazione generale, nel senso che le foglie denotano token
che possiedono un valore lessicale (il token value )
Nella foglia viene memorizzato sia il token name (evidenziato nella
gura) sia tipicamente un puntatore ad una opportuna entry della
symbol table, dove appunto è registrato il valore lessicale del token
Anche se, come chiarito dagli esempi, un ASD può essere costruito
pure per tipici programmi imperativi, la presenza di istruzioni di
controllo del usso di esecuzione rende tuttavia sensibilmente diverso il
processo di interpretazione (rispetto al caso di semplici espressioni)
Una semplice visita in post-ordine non è infatti in generale suciente
per eseguire il programma descritto dall'ASD
Essa è però suciente nel caso in cui l'obiettivo sia di generare codice,
piuttosto che interpretare il programma
Mauro Leoncini L&C Anno Accademico 2023/24 25 / 48
Generazione dell'AST Verso l'ASD di Kaleidoscope 1.0
Linguaggi e compilatori
1 Generazione dell'AST
Architettura del front-end
Rappresentazione astratta di un programma
Verso l'ASD di Kaleidoscope 1.0
Mauro Leoncini L&C Anno Accademico 2023/24 26 / 48
Generazione dell'AST Verso l'ASD di Kaleidoscope 1.0
La grammatica per il parser di Bison
Ricordiamo che Kaleidoscope 1.0 deve permettere la scrittura di
programmi che includono solo pochi tipi dierenti di istruzioni:
denizioni di funzioni esterne
dichiarazioni di funzioni
espressioni aritmetiche, che costituiscono il body delle funzioni
dichiarate
Un programma può includere un numero arbitrario di tali istruzioni,
separate da punto e virgola
Per kaleidoscope 1.0 abbiamo visto una grammatica adatta al parsing
top-down
Qui riscriviamo la grammatica pensando all'algoritmo di parsing
incluso in Bison, che è di tipo LR
Mauro Leoncini L&C Anno Accademico 2023/24 27 / 48
Generazione dell'AST Verso l'ASD di Kaleidoscope 1.0
Grammatica LR Kaleidoscope 1.0
⟨program⟩ ::= ⟨top⟩ ‘; ‘ ⟨program⟩ | ϵ
⟨top⟩ ::= ⟨def⟩ | ⟨external⟩ | ⟨expr⟩ | ϵ
⟨def⟩ ::= def ⟨proto⟩⟨expr⟩
⟨external⟩ ::= extern ⟨proto⟩
⟨proto⟩ ::= id ‘(‘ ⟨idseq⟩ ‘)
⟨idseq⟩ ::= id ⟨idseq⟩ | ϵ
⟨expr⟩ ::= ⟨expr⟩ ‘ + ‘ ⟨expr⟩ | ⟨expr⟩ ‘ − ‘ ⟨expr⟩ |
⟨expr⟩ ‘ ∗ ‘ ⟨expr⟩ | ⟨expr⟩ ‘/‘ ⟨expr⟩ |
‘(‘ ⟨expr⟩ ‘)‘ | ⟨idexpr⟩ | number
⟨idexpr⟩ ::= id | id ‘(‘ ⟨optexpr⟩ ‘)‘
⟨optexpr⟩ ::= ⟨exprlist⟩ | ϵ
⟨exprlist⟩ ::= ⟨expr⟩ | ⟨expr⟩ ‘, ‘ ⟨exprlist⟩
Mauro Leoncini L&C Anno Accademico 2023/24 28 / 48
Generazione dell'AST Verso l'ASD di Kaleidoscope 1.0
Qualche osservazione sulla grammatica
Si nota subito che, in vista del parsing LR, non è stato necessario
introdurre la (non elegante) soluzione di avere identicatori diversi per
funzioni e variabili
Una grammatica per il parsing LR può avere infatti pressi comuni
Riguardo l'uso di produzioni ambigue per le espressioni ricordiamo (si
vedano le slide della lezione introduttiva a Bison) che l'ambiguità
viene risolta con l'uso delle direttive %left e %right
Accorpando le produzioni per le espressioni, abbiamo eliminato alcuni
simboli non terminali intermedi, inutili nell'ottica della costruzione
dell'AST
Mauro Leoncini L&C Anno Accademico 2023/24 29 / 48
Generazione dell'AST Verso l'ASD di Kaleidoscope 1.0
I nodi dell'AST
Dal punto di vista programmativo, l'AST altro non è che una
struttura dati e dunque la prima decisione da prendere riguarda proprio
la struttura da utilizzare
L'idea è di rappresentare ogni nodo come oggetto di una classe
La classe di appartenenza di un nodo può variare (naturalmente in
dipendenza di ciò che il nodo vuole rappresentare), tuttavia tutte le
classi dovranno ereditare da una superclasse comune
Come vedremo, con qualche eccezioni, le classi che andremo a denire
corrisponderanno ai simboli non terminali della grammatica
In generale non esiste però una corrispondenza uno-a-uno fra classi e
non terminali
Inizieremo a riettere su come rappresentare le espressioni
Mauro Leoncini L&C Anno Accademico 2023/24 30 / 48
Generazione dell'AST Verso l'ASD di Kaleidoscope 1.0
Rappresentazione delle espressioni
Negli esempi di AST che abbiamo visto all'inizio esistevano
apparentemente nodi diversi anche solo per operatori dierenti
Tale distinzione non deve necessariamente portare all'introduzione di
più classi, che dierirebbero solo per il tipo di operazioni
Per le espressioni introdurremo invece una classe da cui deriveremo 4
classi, corrispondenti a: (1) operatori binari, (2) variabili, (3) costanti
numeriche e (4) chiamata di funzione
Le 4 sottoclassi rappresentano altrettanti elementi costituenti
un'espressione. Esse rappresentano però caratteristiche diverse:
variabili e costanti numeriche corrispondono a foglie dell'ASD e sono
elementi con valore lessicale (di natura dierente)
gli operatori binari denotano nodi interni con due gli; tralasciando il
particolare operatore, richiedono tutti lo stesso trattamento
come variabili e costanti, anche una chiamata di funzione etichetta una
foglia di un AST ma il trattamento degli argomenti (che naturalmente
sono a loro volta espressioni) richiede un collegamento ad un numero
arbitrario di altri ASD
Mauro Leoncini L&C Anno Accademico 2023/24 31 / 48
Generazione dell'AST Verso l'ASD di Kaleidoscope 1.0
La gerarchia completa
In tutto introdurremo 9 classi, così organizzate
Root
Seq Prototype Function Expr
Number Variable Binary Call
I nomi eettivi che daremo alle classi sono leggermente dierenti
rispetto alla gura (dove sno stati accorciati per ragioni di spazio)
Tutti i nomi hanno cioè il susso AST che, per le sottoclassi di Expr,
diviene ExprAST
Le classi vengono denite nel le [Link] mentre
l'implementazione dei metodi è data nel le [Link]
Il driver è essenzialmente l'applicazione (in questo caso un front-end
per Kaleidoscope 1.0) che coordina le attività di parser e lexer
Mauro Leoncini L&C Anno Accademico 2023/24 32 / 48
Generazione dell'AST Verso l'ASD di Kaleidoscope 1.0
La classe RootAST
La classe base della gerarchia include solo metodi virtuali (si veda la
prossima slide per un veloce ripasso sull'uso di tali metodi)
typedef std::variant<std::string,double> lexval;
const lexval NONE = 0.0;
class RootAST {
public:
virtual ~RootAST() = default;
virtual RootAST *left() {return nullptr;};
virtual RootAST *right() {return nullptr;};
virtual lexval getLexVal() {return NONE;};
virtual void visit() {};
};
I metodi saranno opportunamente rideniti nelle sottoclassi
Mauro Leoncini L&C Anno Accademico 2023/24 33 / 48
Generazione dell'AST Verso l'ASD di Kaleidoscope 1.0
Una digressione: metodi virtuali e loro utilizzo
Si denisce virtuale un metodo dichiarato all'interno di una classe
base e ridenito (overriden) in una classe derivata
Si tratta di uno strumento per implementare polimorsmo a tempo di
esecuzione
Per varie ragioni, può essere necessario fare riferimento ad un metodo
M di una classe derivata D usando un puntatore il cui tipo è quello
di una classe base B
In questo caso, tecnicamente, il compilatore potrebbe sollevare
un'eccezione se B non denisce M oppure se la denisce in modo
diverso
Se M è denita in B come virtuale, a tempo di esecuzione viene
(cercata e) chiamata la funzione corretta denita in D
Per i distruttori questa costruzione è di fatto obbligatoria (per far sì
che venga distrutto l'oggetto giusto)
Mauro Leoncini L&C Anno Accademico 2023/24 34 / 48
Generazione dell'AST Verso l'ASD di Kaleidoscope 1.0
La classe base per le espressioni
La classe ExprASt non include metodi virtuali e potrebbe in teoria non
essere presente
In tal caso tutte le classi erediterebbero direttamente da RootAST
La classe viene introdotta principalmente per una maggiore pulizia
del progetto complessivo
Consideriamo il caso di un'espressione E costituita, ad es., dalla
somma di due (sotto)-espressioni E1 edE2
E1 ed E2 possono essere di uno qualsiasi dei 4 tipi che abbiamo
individuato (numero, identicatore, espressione binaria o chiamata di
funzione)
È chiaro dunque che ci deve essere una classe comune per indicare
queste espressioni
Se la classe fosse direttamente RootAST, potrebbe risultare lecito
comporre un'espressione anche con oggetti delle altre tre classi, ad
esempio SeqAST, ma questo non sarebbe corretto
Mauro Leoncini L&C Anno Accademico 2023/24 35 / 48
Generazione dell'AST Verso l'ASD di Kaleidoscope 1.0
La classe NumberExprAST
Denizione
class NumberExprAST : public ExprAST {
private:
double Val;
public:
NumberExprAST(double Val);
void visit();
lexval getVal() const;
};
Implementazione
NumberExprAST::NumberExprAST(double Val): Val(Val) {};
void NumberExprAST::visit() {
std::cout << Val << " "; };
lexval NumberExprAST::getLexVal() {
lexval lval = Val; return lval; };
Mauro Leoncini L&C Anno Accademico 2023/24 36 / 48
Generazione dell'AST Verso l'ASD di Kaleidoscope 1.0
La classe VariableExprAST
Denizione
class VariableExprAST : public ExprAST {
private:
std::string Name;
public:
VariableExprAST(std::string &Name);
void visit();
lexval getLexVal(); };
Implementazione
VariableExprAST::variableExprAST(std::string &Name):
Name(Name) {};
void VariableExprAST::visit() {
std::cout << Name << " "; };
lexval VariableExprAST::getLexVal() {
lexval lval = Name; return lval; };
Mauro Leoncini L&C Anno Accademico 2023/24 37 / 48
Generazione dell'AST Verso l'ASD di Kaleidoscope 1.0
La classe BinaryExprAST: denizione
class BinaryExprAST : public ExprAST {
private:
char Op;
ExprAST* LHS;
ExprAST* RHS;
public:
BinaryExprAST(char Op, ExprAST* LHS, ExprAST* RHS);
ExprAST* left();
ExprAST* right();
void visit();
};
Mauro Leoncini L&C Anno Accademico 2023/24 38 / 48
Generazione dell'AST Verso l'ASD di Kaleidoscope 1.0
La classe BinaryExprAST: implementazione
BinaryExprAST::BinaryExprAST(char Op, ExprAST* LHS,
ExprAST* RHS): Op(Op), LHS(LHS), RHS(RHS) {};
ExprAST* BinaryExprAST::left() {
return LHS;
};
ExprAST* BinaryExprAST::right() {
return RHS;
};
void BinaryExprAST::visit() {
std::cout << "(" << Op << " ";
LHS->visit();
if (RHS!=nullptr) RHS->visit();
std::cout << ")";
};
Mauro Leoncini L&C Anno Accademico 2023/24 39 / 48
Generazione dell'AST Verso l'ASD di Kaleidoscope 1.0
La classe CallExprAST: denizione
class CallExprAST : public ExprAST {
private:
std::string Callee;
std::vector<ExprAST*> Args;
public:
CallExprAST(std::string Callee,
std::vector<ExprAST*> Args);
lexval getLexVal() const;
void visit();
};
Mauro Leoncini L&C Anno Accademico 2023/24 40 / 48
Generazione dell'AST Verso l'ASD di Kaleidoscope 1.0
La classe CallExprAST: implementazione
CallExprAST::CallExprAST(std::string Callee,
std::vector<ExprAST*> Args): Callee(Callee),
Args(std::move(Args)) {};
lexval CallExprAST::getLexVal() const {
lexval lval = Callee;
return lval;
};
void CallExprAST::visit() {
std::cout<< std::get<std::string>(getLexVal())<< "( ";
for (ExprAST* arg : Args) {
arg->visit();
};
std::cout << ')';
};
Mauro Leoncini L&C Anno Accademico 2023/24 41 / 48
Generazione dell'AST Verso l'ASD di Kaleidoscope 1.0
Le altre classi
Le ultime tre classi sono di natura dierente
Una prima classe, SeqAST, rappresenta la sequenza di istruzioni
Negli AST che andremo a costruire, gli oggetti di tipo SeqAST
costituiscono la dorsale (con linguaggio gurato, diciamo il versante
destro dell'AST)
Le altre due classi rappresentano la denizione di funzioni (classe
FunctionAST) e la denizione di prototipi di funzione (classe
PrototypeAST), quest'ultima utilizzata nella denizione della prima
Mauro Leoncini L&C Anno Accademico 2023/24 42 / 48
Generazione dell'AST Verso l'ASD di Kaleidoscope 1.0
La classe SeqAST: denizione
class SeqAST : public RootAST {
private:
RootAST* first;
RootAST* continuation;
public:
SeqAST(RootAST* first, RootAST* continuation);
RootAST *left();
RootAST *right();
void visit();
};
Mauro Leoncini L&C Anno Accademico 2023/24 43 / 48
Generazione dell'AST Verso l'ASD di Kaleidoscope 1.0
La classe SeqAST: implementazione
SeqAST::SeqAST(RootAST* first, RootAST* continuation):
first(first), continuation(continuation) {};
RootAST* SeqAST::left() { return first; };
RootAST* SeqAST::right() { return continuation; };
void SeqAST:: visit() {
if (first != nullptr) {
first->visit();
} else {
if (continuation == nullptr) {
return;
};
};
std::cout << ";";
continuation->visit();
};
Mauro Leoncini L&C Anno Accademico 2023/24 44 / 48
Generazione dell'AST Verso l'ASD di Kaleidoscope 1.0
La classe PrototypeAST: denizione
class PrototypeAST : public RootAST {
private:
std::string Name;
std::vector<std::string> Args;
public:
PrototypeAST(std::string Name, std::vector<std::string>
lexval getLexVal() const;
const std::vector<std::string> &getArgs() const;
void visit();
int argsize();
};
Mauro Leoncini L&C Anno Accademico 2023/24 45 / 48
Generazione dell'AST Verso l'ASD di Kaleidoscope 1.0
La classe PrototypeAST: implementazione
PrototypeAST::PrototypeAST(std::string Name,
std::vector<std::string> Args):
Name(Name), Args(std::move(Args)) {};
lexval PrototypeAST::getLexVal() const {
lexval lval = Name; return lval; };
const std::vector<std::string>& PrototypeAST::getArgs()
const { return Args; };
void PrototypeAST::visit() {
std::cout << "extern " << Name << "( ";
for (auto it=[Link](); it!=[Link](); ++it) {
std::cout << *it << ' '; };
std::cout << ')';
};
int PrototypeAST::argsize() {
return [Link](); };
Mauro Leoncini L&C Anno Accademico 2023/24 46 / 48
Generazione dell'AST Verso l'ASD di Kaleidoscope 1.0
La classe FunctionsAST: denizione
class FunctionAST : public RootAST {
private:
PrototypeAST* Proto;
ExprAST* Body;
bool external;
public:
FunctionAST(PrototypeAST* Proto, ExprAST* Body);
void visit();
int nparams();
};
Mauro Leoncini L&C Anno Accademico 2023/24 47 / 48
Generazione dell'AST Verso l'ASD di Kaleidoscope 1.0
La classe FunctionAST: implementazione
FunctionAST::FunctionAST(PrototypeAST* Proto,
ExprAST* Body): Proto(Proto), Body(Body) {
if (Body == nullptr) external=true;
else external=false; };
void FunctionAST::visit() {
std::cout << std::get<std::string>
(Proto->getLexVal()) << "( ";
for (auto it=Proto->getArgs().begin();
it != Proto->getArgs().end(); ++it) {
std::cout << *it << ' ';
};
std::cout << ')';
Body->visit();
};
int FunctionAST::nparams() {
return Proto->argsize(); };
Mauro Leoncini L&C Anno Accademico 2023/24 48 / 48