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:语法分析器的自动构造

lalr1使用指导

Previous实验内容Next编写lexer

Last updated 5 years ago

Was this helpful?

是一个基于lalr(1)或者ll(1)文法的parser generator,其中lalr(1)的部分在本次pa1a中使用,ll(1)的部分在pa1b中使用。lalr1并没有经过系统的测试和长期的使用,可能还有一些不稳定的因素,如果产生了影响使用的bug,请大家积极汇报。

lalr(1)分析表的生成算法是基于龙书给出的一个较为高效的算法,感兴趣的同学可以直接阅读代码或者参考龙书相关章节,这里不详细介绍了。生成decaf的分析表大约耗时0.1s,相比于rustc的编译时间可以忽略不计了。

lalr1有两套代码生成的方式,比较传统的一种是读取配置文件并输出rust代码;另一种是利用rust的过程宏,在rust代码中嵌入产生式的信息,在编译时生成rust代码作为rustc的输入。我们的实验使用后者。

过程宏大致的编写方式是在你希望让它成为一个parser的struct的impl块上添加#[lalr1(Start)]标记,其中Start是一个非终结符,是parser希望规约出来的最终结果。

另外一个必要的属性是#[lex(TomlOfLexer)],其中TomlOfLexer以toml字符串的形式配置了词法解析器。之所以选择借助嵌入在过程宏中的toml而不是直接使用过程宏,是因为这样更能清晰地描述lexer。正则表达式到有限状态机的转换由来提供,与lalr1一样,它也没有怎么经过测试,如果大家遇到使用上的bug请及时汇报。

此外,还可以为impl块添加一些可选的属性,后面会详细描述。

对于impl块中的每个函数,都使用rust的正常函数的语法来编写语义动作,同时用函数级别的属性来描述产生式。

生成出来的rust代码中不会保留这个impl块,也就是说这里面编写的东西都不会直接被编译。输出的结果包含两个enum,即TokenKind和StackItem,两个struct,即Token和Lexer,并为你希望成为parser的struct提供一个parse函数。根据我的使用经验,现在的ide或者编辑器不太可能把这些符号识别出来并且给予正常的语法提示,如果希望了解具体的api的话,可以使用之前提到的#[expand],也可以使用rust doc,它会在展开过程宏后再分析api。

生成出来的Lexer是可以独立于Parser使用的,下面的代码可以依次输出从一个字符串中解析出的全部token:

let mut lexer = Lexer::new("your string here".as_bytes());
loop {
  let token = lexer.next();
  println!("{:?}", token);
  if token.ty == TokenKind::_Eof { break; }
}
lalr1
re2dfa