Je sais que cette question a 4 ans ou plus mais je pense que je devrais ajouter une réponse plus détaillée.
Syntaxe abstraite Les arbres ne sont pas créés différemment des autres arbres. L'instruction la plus vraie dans ce cas est que les nœuds de l'arborescence de syntaxe ont une quantité variadique de nœuds AU BESOIN.
Un exemple est celui des expressions binaires. 1 + 2
Une expression simple comme celle-ci créerait un seul nœud racine contenant un nœud droit et un nœud gauche contenant les données relatives aux nombres. En langage C, cela ressemblerait à quelque chose comme
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;
};
Votre question était aussi comment traverser? Le parcours dans ce cas s'appelle Nœuds visiteurs . Pour visiter chaque nœud, vous devez utiliser chaque type de nœud pour déterminer comment évaluer les données de chaque nœud de syntaxe.
Voici un autre exemple de cela en C où j'imprime simplement le contenu de chaque nœud:
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;
}
}
Notez que la fonction visite récursivement chaque nœud en fonction du type de nœud avec lequel nous traitons.
Ajoutons un exemple plus complexe, une if
construction d'instruction! Rappelez-vous que les instructions if peuvent également avoir une clause else optionnelle. Ajoutons l'instruction if-else à notre structure de nœud d'origine. Rappelez-vous que si les instructions elles-mêmes peuvent également avoir des instructions if, une sorte de récursivité dans notre système de nœuds peut se produire. Les autres instructions étant facultatives, le elsestmt
champ peut être NULL, ce que la fonction de visiteur récursif peut ignorer.
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;
};
De retour dans notre fonction d'impression de visiteur de noeud appelée AST_PrintNode
, nous pouvons adapter la if
construction AST de déclaration en ajoutant ce code 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;
Aussi simple que cela! En conclusion, l'arbre de syntaxe n'est pas beaucoup plus qu'un arbre d'une union étiquetée de l'arbre et de ses données!