The Decaf Book
  • Introduction
  • 语言规范
  • Java 框架分阶段指导
    • 本章导引
    • PA1-A:语法分析器的自动构造
      • 阶段任务
      • 词法分析
      • 抽象语法树
      • 文法分析
    • PA1-B:基于 LL(1) 的语法分析器半自动构造
      • 阶段任务
      • LL(1) 文法分析
      • 错误恢复
    • PA2:语义分析
      • 阶段任务
      • 类型、符号和作用域
      • 访问者模式
      • 符号表构建
      • 类型检查
    • PA3:中间代码生成
      • 阶段任务
      • TAC 程序
      • 面向对象机制
      • 控制流的翻译
    • PA4:中间代码优化
      • 实验内容
      • 基本块
      • 数据流分析概述
      • 数据流优化概述
      • 公共表达式提取
      • 复写传播
      • 常量传播
      • 死代码消除
  • Scala 框架分阶段指导
    • 本章导引
    • PA1:语法分析器的自动构造
      • 阶段任务
      • 词法分析
      • 抽象语法树
      • 文法分析
      • 访问者模式
    • PA2:语义分析
      • 阶段任务
      • 类型、符号和作用域
      • 符号表构建
      • 类型检查
    • PA3:中间代码生成
      • 阶段任务
      • TAC 程序
      • 面向对象机制
      • 控制流的翻译
    • PA3-JVM:JVM 字节码生成
      • 阶段任务
      • JVM 字节码简介
      • 翻译过程
    • PA4:中间代码优化
      • 实验内容
      • 基本块
      • 数据流分析概述
      • 数据流优化概述
      • 公共表达式提取
      • 复写传播
      • 常量传播
      • 死代码消除
  • Rust 框架分阶段指导
    • 本章导引
    • PA1-A:语法分析器的自动构造
      • 实验内容
      • lalr1使用指导
        • 编写lexer
        • impl块的可选属性
        • 产生式和语法动作
        • 解决冲突
        • 一个完整的例子
      • 抽象语法树
      • 框架中部分实现的解释
      • 文件结构
    • PA1-B:基于 LL(1) 的语法分析器半自动构造
      • 实验内容
      • lalr1使用指导
      • 错误恢复
      • 文件结构
    • PA2:语义分析
      • 实验内容
      • 语义分析
      • 符号表
      • visitor模式
    • PA3:中间代码生成
      • 实验内容
      • 中间代码
      • 中间代码中的类型信息
      • 运行时存储布局
      • 面向对象机制
      • tacvm简述
    • PA4:中间代码优化
      • 基本块
      • 数据流分析概述
      • 数据流优化概述
      • 公共表达式提取
      • 复写传播
      • 常量传播
      • 死代码消除
    • PA5:寄存器分配
      • 实验内容
      • 图着色基本原理
      • 着色算法
      • 预着色节点
      • 干涉图节点合并
      • 调用约定
Powered by GitBook
On this page

Was this helpful?

  1. Rust 框架分阶段指导
  2. PA1-A:语法分析器的自动构造

抽象语法树

当lexer和parser成功解析一个字符串时,输出结果是一棵抽象语法树(ast,Abstract Syntax Tree)。

ast是指一种只跟我们关心的内容有关的语法树表示形式。抽象语法是相对于具体语法而言的,所谓具体语法是指针对字符串形式的语法规则,而且这样的语法规则没有二义性,适合于指导语法分析过程。抽象语法树是一种非常接近源代码的中间表示,它的特点是:

  1. 不含我们不关心的终结符,例如逗号等

  2. 不具体体现语法分析的细节步骤,例如对于List -> List E | E这样的规则,按照语法分析的细节步骤来记录的话应该是一棵二叉树,但是在ast中我们只需要表示成一个Vec,这样可能更高效且便于后续处理

  3. 可能在结构上含有二义性,例如加法表达式在抽象语法中可能是Expr -> Expr + Expr,但是这种二义性并不影响对于ast的分析,因为现在已经获得了ast,脱离了具体语法,源程序中的优先级和结合性等都已经没有意义了

  4. 依然能体现源程序的语法结构

使用抽象语法树表示程序的最大好处是把语法分析结果保存下来,后面可以反复利用。

在oo的语言中描述ast往往使用继承的结构,一般而言产生式的左端项为基类,每种右端项对应于一个子类。但在rust中,更常用的表示方法是使用enum这个内建的tagged-union机制。

然而rust完全不支持继承,在ast的表示上也的确造成了一些麻烦。ast的一种常见的模式是,许多种节点(例如各种Expr)包含一些公有的成员(如类型和位置),每个节点也有自己独有的成员。如果想在rust中表示这种模式,就像框架中使用的一样,是将每个节点特有的成员放在enum中,作为一个struct的成员。例如框架中Expr的实现:

pub struct Expr<'a> {
  pub loc: Loc,
  pub ty: Cell<Ty<'a>>,
  pub kind: ExprKind<'a>,
}

pub enum ExprKind<'a> {
  VarSel(VarSel<'a>),
  IndexSel(IndexSel<'a>),
  ...
}

这样看起来还是不太直接,而且想从各个节点中访问公有的成员比较困难。我认为描述ast的节点一个更方便的结构是类似于scala中的case class或者kotlin中的sealed class,既支持继承,也支持模式匹配。rust中没有内置的机制支持这样的结构,只能说现在这个方案还算可以接受,我个人不是很满意。

Previous一个完整的例子Next框架中部分实现的解释

Last updated 5 years ago

Was this helpful?