So che questa domanda ha 4 o più anni, ma sento che dovrei aggiungere una risposta più dettagliata.
Gli alberi sintattici astratti non vengono creati diversamente dagli altri alberi; l'affermazione più vera in questo caso è che i nodi Syntax Tree hanno una quantità variabile di nodi come necessario.
Un esempio sono espressioni binarie come 1 + 2
Un'espressione semplice come quella creerebbe un singolo nodo radice che contiene un nodo destro e uno sinistro che contiene i dati relativi ai numeri.
In linguaggio C, sarebbe simile a
struct ASTNode;
union SyntaxNode {
int64_t llVal;
uint64_t ullVal;
struct {
struct ASTNode *left, *right;
} BinaryExpr;
};
enum SyntaxNodeType {
AST_IntVal, AST_Add, AST_Sub, AST_Mul, AST_Div, AST_Mod,
};
struct ASTNode {
union SyntaxNode *Data;
enum SyntaxNodeType Type;
};
La tua domanda era anche come attraversare? Il traversamento in questo caso è chiamato Nodi di visita . Per visitare ciascun nodo è necessario utilizzare ogni tipo di nodo per determinare come valutare i dati di ciascun nodo della sintassi.
Ecco un altro esempio di quello in C in cui stampo semplicemente il contenuto di ciascun nodo:
void AST_PrintNode(const ASTNode *node)
{
if( !node )
return;
char *opername = NULL;
switch( node->Type ) {
case AST_IntVal:
printf("AST Integer Literal - %lli\n", node->Data->llVal);
break;
case AST_Add:
if( !opername )
opername = "+";
case AST_Sub:
if( !opername )
opername = "-";
case AST_Mul:
if( !opername )
opername = "*";
case AST_Div:
if( !opername )
opername = "/";
case AST_Mod:
if( !opername )
opername = "%";
printf("AST Binary Expr - Oper: \'%s\' Left:\'%p\' | Right:\'%p\'\n", opername, node->Data->BinaryExpr.left, node->Data->BinaryExpr.right);
AST_PrintNode(node->Data->BinaryExpr.left); // NOTE: Recursively Visit each node.
AST_PrintNode(node->Data->BinaryExpr.right);
break;
}
}
Si noti come la funzione visita ricorsivamente ciascun nodo in base al tipo di nodo con cui si ha a che fare.
Aggiungiamo un esempio più complesso, un costrutto di dichiarazione if
! Ricorda che se le istruzioni possono anche avere una clausola else opzionale. Aggiungiamo l'istruzione if-else alla nostra struttura del nodo originale. Ricorda che se le affermazioni stesse possono avere anche delle istruzioni, può verificarsi una specie di ricorsione all'interno del nostro sistema di nodi. Le istruzioni else sono facoltative, quindi il campo elsestmt
può essere NULL che la funzione visitatore ricorsiva può ignorare.
struct ASTNode;
union SyntaxNode {
int64_t llVal;
uint64_t ullVal;
struct {
struct ASTNode *left, *right;
} BinaryExpr;
struct {
struct ASTNode *expr, *stmt, *elsestmt;
} IfStmt;
};
enum SyntaxNodeType {
AST_IntVal, AST_Add, AST_Sub, AST_Mul, AST_Div, AST_Mod, AST_IfStmt, AST_ElseStmt, AST_Stmt
};
struct ASTNode {
union SyntaxNode *Data;
enum SyntaxNodeType Type;
};
nella nostra funzione di stampa dei visitatori del nodo chiamata AST_PrintNode
, possiamo accomodare il costrutto AST if
dell'istruzione aggiungendo questo codice C:
case AST_IfStmt:
puts("AST If Statement\n");
AST_PrintNode(node->Data->IfStmt.expr);
AST_PrintNode(node->Data->IfStmt.stmt);
AST_PrintNode(node->Data->IfStmt.elsestmt);
break;
Semplice come quello! In conclusione, Syntax Tree non è molto più di un albero di un'unione taggata dell'albero e dei suoi dati stessi!