Ich weiß, dass diese Frage mehr als 4 Jahre alt ist, aber ich denke, ich sollte eine detailliertere Antwort hinzufügen.
Abstrakte Syntaxbäume werden nicht anders als andere Bäume erstellt. Die wahrere Aussage in diesem Fall ist, dass Syntaxbaumknoten eine unterschiedliche Anzahl von Knoten haben, WIE ERFORDERLICH.
Ein Beispiel sind binäre Ausdrücke wie 1 + 2
Ein einfacher Ausdruck wie dieser würde einen einzelnen Wurzelknoten erzeugen, der einen rechten und einen linken Knoten enthält, der die Daten über die Zahlen enthält. In C würde es ungefähr so aussehen
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;
};
Ihre Frage war auch, wie man überquert? Das Verfahren wird in diesem Fall als Besuchsknoten bezeichnet . Für den Besuch jedes Knotens müssen Sie für jeden Knotentyp festlegen, wie die Daten jedes Syntaxknotens ausgewertet werden sollen.
Hier ist ein weiteres Beispiel dafür in C, wo ich einfach den Inhalt jedes Knotens drucke:
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;
}
}
Beachten Sie, wie die Funktion jeden Knoten rekursiv besucht, je nachdem, um welchen Knotentyp es sich handelt.
Fügen wir ein komplexeres Beispiel hinzu, ein if
Anweisungskonstrukt! Denken Sie daran, dass if-Anweisungen auch eine optionale else-Klausel enthalten können. Fügen wir die if-else-Anweisung zu unserer ursprünglichen Knotenstruktur hinzu. Denken Sie daran, dass if-Anweisungen selbst auch if-Anweisungen haben können, so dass eine Art Rekursion innerhalb unseres Knotensystems auftreten kann. Andere Anweisungen sind optional, sodass das elsestmt
Feld NULL sein kann, was von der rekursiven Besucherfunktion ignoriert werden kann.
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;
};
Zurück in unserer aufgerufenen Node-Visitor-Print-Funktion AST_PrintNode
können wir die if
Anweisung AST-Konstrukt aufnehmen, indem wir diesen C-Code hinzufügen:
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;
So einfach ist das! Zusammenfassend ist der Syntaxbaum nicht viel mehr als ein Baum aus einer getaggten Vereinigung des Baums und seiner Daten selbst!