授课语音

树和抽象语法树(Abstract Syntax Tree, AST)

1. 树(Tree)

树是一种非线性的数据结构,由节点(Node)和边(Edge)组成。它具有以下特点:

  • 根节点(Root Node):树的顶端节点,没有父节点。
  • 子节点(Child Nodes):与父节点连接的节点。
  • 叶子节点(Leaf Nodes):没有子节点的节点,位于树的末端。
  • 父节点(Parent Nodes):与子节点相连的节点。
  • 子树(Subtree):由某个节点及其所有后代节点构成的树。

在树结构中,每个节点可以有零个或多个子节点。树的层级关系是父子节点之间的层次结构,通常用于表示分层数据、层级关系等。

树的基本术语

  • 度(Degree):一个节点的子节点数量。
  • 深度(Depth):节点到根节点的路径长度。
  • 高度(Height):树中最大路径长度,从根节点到最远叶子节点。
  • 路径(Path):从某个节点到另一个节点的边的序列。

树的类型

  • 二叉树:每个节点最多有两个子节点,分别为左子节点和右子节点。
  • N-叉树:每个节点可以有最多 N 个子节点。
  • 平衡树:树的所有叶子节点都处于相同的深度(如 AVL 树)。

树在计算机科学中有广泛的应用,比如用于组织文件目录、表示决策树、表示解析树等。


2. 抽象语法树(Abstract Syntax Tree, AST)

抽象语法树(AST)是编译器和解释器中常见的数据结构,用于表示源代码的语法结构。它是一种简化的语法树,仅保留程序的核心信息,而忽略掉不必要的细节,如某些符号、括号等。AST 提供了一个更高层次的表示,帮助进行程序的分析、优化和代码生成。

AST 的特点

  • 简化的语法树:AST 丢弃了语法分析中的一些细节,比如括号和语法糖,保留程序的核心结构。
  • 表达程序的结构:AST 节点表示程序中的运算符、操作数、表达式等,树的结构反映了程序的语法和操作顺序。
  • 无重复信息:AST 不会包含像代码中的括号、语法规则等具体实现细节,它只关心表达式和语句的核心结构。

AST 中的节点

在 AST 中,节点通常表示以下几种元素:

  • 操作符节点:如加法(+)、减法(-)、乘法(*)等。
  • 操作数节点:如常量(数字)、变量等。
  • 表达式节点:表示特定的表达式,例如 a + b
  • 语句节点:表示程序中的各个语句,如赋值语句、条件语句等。

AST 与语法树的区别

特性 语法树 (Parse Tree) 抽象语法树 (AST)
层次 包含详细的语法信息,包括符号、括号等 简化了不必要的细节,保留程序的核心语法结构
节点 每个节点表示语法规则或词法单元 节点表示程序中的运算符、操作数、语句等
大小 较大,包含更多冗余信息 更简洁,去除了冗余信息
用途 主要用于语法分析阶段 主要用于语义分析、优化、代码生成等

3. AST 的构建过程

AST 的构建通常发生在编译过程中,经过以下几个步骤:

  1. 词法分析(Lexical Analysis):将源代码分解为一系列的词法单元(Token),如关键字、标识符、常量等。
  2. 语法分析(Syntax Analysis):通过语法规则将词法单元组成一个语法树(Parse Tree)。
  3. 生成抽象语法树(AST Generation):从语法树中去除不必要的部分(如括号、无用的语法规则),形成更简洁的抽象语法树。

4. AST 示例

假设我们要处理一个简单的数学表达式:a + b * c,其语法树和 AST 可能如下所示。

语法树(Parse Tree)

        Expr
       / |  \
     Id +  Expr
         /    \
       Id *   Id

在语法树中,操作符 +* 都是树的内部节点,而操作数 abc 是叶子节点。该语法树保留了运算符的优先级关系(即乘法优先于加法),但有些信息(如括号)是不必要的。

抽象语法树(AST)

     +
    / \
   a   *
      / \
     b   c

在 AST 中,节点只保留了表达式的核心结构,删除了不必要的语法信息。例如,表达式 a + (b * c) 在 AST 中仅有加法节点和乘法节点,而语法树则会包含更多的中间节点。

5. AST 的应用

AST 在编译器、解释器以及一些开发工具中扮演着重要的角色:

  • 优化:编译器可以在 AST 上进行各种优化操作,比如常量折叠(constant folding)、代码运动(code motion)等。
  • 代码生成:编译器根据 AST 生成目标机器代码或字节码。
  • 语义分析:分析代码中的语义错误,如类型检查等。
  • 代码转换:在解释型语言中,AST 用于逐步解释源代码,执行相应的操作。
  • 代码格式化:工具如 Prettier 会分析 AST,按照一定的规则格式化源代码。

6. Java 实现 AST

以下是一个简单的 AST 示例,在 Java 中表示表达式 a + b * c

class ASTNode {
    String value;
    ASTNode left, right;

    public ASTNode(String value) {
        this.value = value;
    }
}

public class ASTExample {
    public static void main(String[] args) {
        // 构造 AST
        ASTNode root = new ASTNode("+");
        root.left = new ASTNode("a");
        root.right = new ASTNode("*");
        root.right.left = new ASTNode("b");
        root.right.right = new ASTNode("c");

        // 输出 AST
        printAST(root, 0);
    }

    // 输出 AST
    static void printAST(ASTNode node, int level) {
        if (node == null) return;
        for (int i = 0; i < level; i++) {
            System.out.print("  ");
        }
        System.out.println(node.value);
        printAST(node.left, level + 1);
        printAST(node.right, level + 1);
    }
}

7. 总结

  • 是一种基本的层次结构,用于表示具有父子关系的元素。
  • 抽象语法树(AST) 是一种用于表示程序结构的树,它去除了不必要的语法信息,保留了程序的核心语法结构。
  • AST 在编译器、解释器以及代码分析工具中有着广泛的应用,能够帮助进行程序分析、优化和生成目标代码。
去1:1私密咨询

系列课程: