Я знаю, что этому вопросу 4+ года, но я чувствую, что должен добавить более подробный ответ.
Абстрактный синтаксис Деревья создаются не иначе, как другие деревья; более верное утверждение в этом случае состоит в том, что узлы синтаксического дерева имеют вариативное количество узлов, КАК НУЖНО.
Примером являются бинарные выражения, такие как 1 + 2
Простое выражение, подобное этому, создающее один корневой узел, содержащий правый и левый узел, который содержит данные о числах. На языке C это будет выглядеть примерно так
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. Давайте добавим оператор if-else к нашей исходной структуре узла. Помните, что сами операторы if могут также иметь операторы if, поэтому может произойти своего рода рекурсия в нашей системе узлов. Остальные операторы являются необязательными, поэтому 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;
Так просто, как, что! В заключение, синтаксическое дерево - это не что иное, как дерево тегового объединения дерева и самих его данных!