Wiem, że to pytanie ma ponad 4 lata, ale uważam, że powinienem dodać bardziej szczegółową odpowiedź.
Streszczenie Składnia Drzewa nie są tworzone inaczej niż inne drzewa; bardziej prawdziwe stwierdzenie w tym przypadku jest takie, że węzły drzewa składni mają różnorodną liczbę węzłów, JAK POTRZEBUJĄ.
Przykładem są wyrażenia binarne, takie jak 1 + 2
Proste wyrażenie, takie jak to, tworzyłoby pojedynczy węzeł główny z prawym i lewym węzłem, który przechowywałby dane o liczbach. W języku C wyglądałoby to mniej więcej tak
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;
};
Twoje pytanie brzmiało także, jak przejść. W tym przypadku przemierzanie nazywa się węzłami odwiedzającymi . Odwiedzanie każdego węzła wymaga użycia każdego typu węzła w celu ustalenia sposobu oceny danych każdego węzła składni.
Oto kolejny przykład tego w C, w którym po prostu drukuję zawartość każdego węzła:
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;
}
}
Zauważ, jak funkcja rekurencyjnie odwiedza każdy węzeł zgodnie z typem węzła, z którym mamy do czynienia.
Dodajmy bardziej złożony przykład, if
konstrukcję instrukcji! Przypomnij sobie, że jeśli instrukcje mogą mieć również opcjonalną klauzulę else. Dodajmy instrukcję if-else do naszej oryginalnej struktury węzła. Pamiętaj, że jeśli same instrukcje mogą również zawierać instrukcje if, może wystąpić pewnego rodzaju rekurencja w naszym systemie węzłów. Inne instrukcje są opcjonalne, więc elsestmt
pole może mieć wartość NULL, którą funkcja odwiedzającego rekurencyjnego może zignorować.
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;
};
z powrotem w nazwie funkcji drukowania odwiedzającego węzeł AST_PrintNode
, możemy dostosować konstrukcję if
instrukcji AST, dodając ten kod 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;
Tak proste jak to! Podsumowując, drzewo składniowe nie jest niczym więcej niż drzewem oznaczonego związku drzewa i jego danych!