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:语法分析器的自动构造
  3. lalr1使用指导

impl块的可选属性

除了#[lalr1(Start)]和#[lex(TomlOfLexer)]这两个必要的属性外,还可以添加几个额外的属性,目前支持的有以下几个:

  1. #[verbose(OutputPath)]

    把lalr(1)的action表打印到文件OutputPath,出现冲突警告的时候可以利用这个文件来帮助查找语法中的问题。

    action表中包含每个节点包含的产生式及点的位置(不包含向前看符号),以及每个节点处遇到终结符时的移进/规约/接受动作。每个动作都会在后面加上一个表示冲突情况的符号,示例如下:

     A => Shift(1) (✓)
     A => Reduce(1) (-)
     B => Shift(2) (✓)
     B => Reduce(2) (✗)

    其中(✓)表示它最后被解析器采用,(-)表示它被利用优先级和结合性消除了,(✗)表示它被"强行"消除了,对应与一个冲突警告。详见后面的解决冲突一节。

    如果选择了ll(1)来生成parser,则会打印ll(1)的预测集合(predict set),同样(✗)表示一个产生式被"强行"消除了,而因为ll(1)中没有利用优先级和结合性来消除冲突的机制,所以不存在(-)。

  2. #[log_token]

    每当新解析出一个非_Eps的终结符时,输出它的相关信息,也就是输出一个struct Token,包括名字,对应源代码,行号列号。

  3. #[log_reduce]

    每当执行一次规约时,输出产生式。

  4. #[use_unsafe]

    使用一些unsafe来减少运行时检查,以期提高性能。例如将不可达断言转化成不可达hint,取消下标越界检查等。

    如果你编写的parser是正确的,那么无论输入的程序是是什么,这些unsafe都不会导致真正不安全的结果(至少lalr1的目标是这个,至于是否真的达到了,目前暂且不能做出保证)。

  5. #[expand]

    会在编译时输出生成的代码。之所以添加这个选项而不建议大家使用cargo expand来查看生成的代码,是因为后者把所有宏都展开了,不利于阅读或者调试,而且后者要求rustc的编译至少成功进行到了某个阶段(具体哪个阶段我还不清楚)才会输出,这对帮助解决编译错误可能是没有帮助的。

    由于lalr1生成的rust代码不包含位置信息,所以parser中的编译错误和运行错误都无法得知具体在什么位置,而如果使用#[expand]输出的代码来代替整个impl块,效果是完全等同的,这样可以方便调试或者看自己具体那里编译出错了。

  6. #[show_fsm(OutputPath)]和#[show_dfa(OutputPath)]

    分别是把parser的lr fsm和lexer的dfa的图形以dot文件的形式输出到对应路径中。其实是没啥用的一个功能,因为这两个自动机都太大了,甚至dot文件都不一定能顺利地渲染成图片,更不用说靠人眼从中提取什么有用的信息了。这两个选项可以说纯属娱乐,如果电脑性能比较好的话不妨尝试一下看看自动机到底长啥样。

    不过这个功能也并不是完全没用的,re2dfa和lalr1中都提供了生成dot文件的接口,如果是一些简单一点的例子,的确是可以用来输出有意义的图片的(例如作业题什么的)。

Previous编写lexerNext产生式和语法动作

Last updated 5 years ago

Was this helpful?