Я знаю, що це питання віком 4+ років, але я думаю, що мені слід додати більш детальну відповідь.
Абстрактні синтаксичні дерева створені нічим не відрізняються від інших дерев; Більш істинне твердження в цьому випадку полягає в тому, що вузли синтаксичного дерева мають різну кількість вузлів, як це потрібно.
Прикладом є бінарні вирази на зразок 1 + 2
Простий вираз, подібний до того, який би створив єдиний кореневий вузол, утримуючи правий і лівий вузол, який містить дані про числа. На мові С це виглядатиме щось на кшталт
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;
};
Ваше питання також було, як пройти? Подорож у такому випадку називається відвідуванням вузлів . Для відвідування кожного вузла потрібно використовувати кожен тип вузла, щоб визначити, як оцінити дані кожного синтаксичного вузла.
Ось ще один приклад цього на C, де я просто друкую вміст кожного вузла:
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;
}
}
Зауважте, як функція рекурсивно відвідує кожен вузол відповідно до того, з яким типом вузла ми маємо справу.
Додамо складніший приклад, if
констатація заяви! Нагадаємо, що якщо заяви також можуть мати необов'язкове застереження. Додамо оператор if-else до нашої вихідної структури вузлів. Пам’ятайте, що якщо і самі заяви можуть мати, якщо заяви, то може відбуватися певна рекурсія в нашій системі вузлів. Інші оператори необов’язкові, тому elsestmt
поле може бути NULL, а рекурсивна функція відвідувача може ігнорувати.
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;
};
повертаючись у нашу функцію друку відвідувача вузла, яку називають AST_PrintNode
, ми можемо розмістити if
оператор AST конструкта, додавши цей код 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;
Так просто! На закінчення, Синтаксичне дерево - це не набагато більше, ніж дерево позначеного об'єднання дерева та саме його даних!