语法分析
- 文法
- 定义
- 文法 ,其中:
- :非空有限非终结符号集合。
- :非空有限终结符号集合。
- :开始符号。
- :有限产生式集合。
- ,。
- 语言 :从 的 开始,不断用产生式替换非终结符号,得到的只有终结符号的串集合。
- 文法 ,其中:
- 推导和规约
- 直接推导:当且仅当 时,。 直接推导出 , 直接规约到 。
- 多步推导: 经过 , 个推导得到 ,则 。如果 或 ,则 。
- 最左/右推导:从 推导到 ,总是替换最左/右边的非终结符号。
- 规范推导:最右推导又称规范推导,逆序为规范规约。
- 句型与句子
- 句型: 为 的句型。
- 句子: 为 的句子。
- 规范句型/句子:仅用规范推导得到的句型/句子。
- 递归
- 直接递归: 存在产生式 时, 为直接递归文法。
- 间接递归: 可以有 和推导 时, 为间接递归文法。
- 递归文法:直接与间接递归文法统称递归文法。
- 左递归: 且 。
- 右递归: 且 。
- 表示
- BNF
- 产生式格式:。
- EBNF
- 与 BNF 类似,并包括扩展语法:
- : 到 个 。
- : 或 个 。
- :可以使用分配律。
- 与 BNF 类似,并包括扩展语法:
- 语法图
- 椭圆:非终结符。
- 矩形:终结符。
- 箭头:匹配字符串的路径。
- BNF
- 二义性
- 定义:存在一个字符串,一个文法可以推导得到两个不同的语法树,则文法是二义的。
- 常见原因:
- 运算符优先级不明确。
- 运算符结合性不明确。
- 定义
- 自上而下分析
- 文法变换
- 直接左递归消除
- 对于 ,则变换为 ,。
- 对于 ,则变换为 ,。
- 间接左递归消除
- 按顺序枚举所有非终结符 ,对于 :
- 对于 ,如果 ,则带入 的产生式到 中。
- 消除带入后的 产生式的直接左递归。
- 按顺序枚举所有非终结符 ,对于 :
- 回溯消除
- FIRST
- 。即 推导的所有句子的开头。
- 如果 ,则 ,反之也成立。
- 文法不带回溯的条件:对于 中任意非终结符 , 两两无交集。
- 文法不带回溯的候选式选择:对于 和当前符号 ,选择满足 的 。
- 的构造是自下而上的。
- 。即 推导的所有句子的开头。
- 消除方法
- 如果 和 有交集,则交集部分就是左公因子。
- 将两个候选式分别拆分出交集部分 和剩余部分 ,。
- FIRST
- 直接左递归消除
- 递归下降
- 按照文法编写函数,每个非终结符对应一个函数,对候选式的推导就是调用函数。
- LL(1)
- 算法过程
- LL(1) 算法包括:
- 总控程序:算法本身。
- 分析栈:存放待分析单词。
- 分析表 : 为非终结符, 为单词, 表示识别 需要的产生式 。如果是空则表示出错。
- 初始时,分析栈从底部存放结束标志和 。
- 设栈顶为 ,当前单词为 ,循环:
- 若 是非终结符:
- 若 非空,则根据 选取产生式,把 出栈,把 倒序入栈。
- 若 为空,出错。
- 若 是终结符:
- 若 为结束标志,则完全识别成功。
- 若 且不为结束标志,则识别 成功,出栈 ,读取下一个输入。
- 若 ,则出错。
- 若 是非终结符:
- LL(1) 算法包括:
- FOLLOW
- ,即语言中在 后的所有可能的非终结符 。
- 计算方法:
- 对于 ,。
- 对于 或 且 ,则 。
- 自上而下计算。
- 分析表构造
- 对于 :
- 若 ,则 。
- 若 且 ,则 。
- 对于 :
- 算法过程
- 文法变换
- 自下而上分析
- 基本概念
- 短语:若 且 ,则 是句型 相对于 的短语。
- 从语法树上理解,即选取非终结符节点 ,则其子树所有叶子节点按顺序排列是短语。
- 一般省略句型,使用整个输入串作为分析的句型。
- 直接短语:,即在短语基础上,只需要一步推导。
- 从语法树上理解,即 的子节点都是叶子节点。
- 句柄:最左边的直接短语。
- 活前缀:规范句型的前缀,且此前缀的末端不超过句柄的末端。
- 可行前缀:最长的活前缀,后缀就是句柄。
- 短语:若 且 ,则 是句型 相对于 的短语。
- LR 分析
- LR 分析类方法使用 LR 分析器,分析器算法都相同,区别在于分析表的构造方法。
- LR 分析器结构:
- 输入串
- 分析栈:元素为符号和状态的二元组。
- 分析表:
- 每一行对应一个状态,每一列对应一个符号。终结符的列是 ACTION 表,非终结符的列是 GOTO 表。
- 的取值:
- :移进操作,输入串指针向前移动一位,转换到状态 。
- :规约操作,选取第 个产生式 ,弹出所有匹配的符号和状态,插入产生式左部和 ,其中 是弹出后的栈顶。
- 规约操作不会消费输入符号,不同解析器只会基于输入符号决策。
- 接受:分析成功。
- 错误:遇到无法识别的符号。
- 的取值:
- :规约操作中要转移的下一个状态。
- 空: 状态下不可能规约 ,分析表构造正确时不可能到达这里。
- 分析的基本方法:不断规约句柄,无法规约时则移进,最终规约到开始符号。
- LR(0)
- LR(0) 项目
- 把所有产生式的候选式插入圆点,则为 LR(0) 项目。
- 对于开始符号 ,额外增加 和 。
- 对于 ,改为 。
- 对于 ,改为 、、。
- 对于 ,则分别改 和 。
- 用于表示这个候选式中, 前面的部分已经在分析栈中,后面的部分还没有。
- 项目的分类:
- 接受项目:,表示整体分析成功。
- 规约项目:,其中 。
- 分析栈中是可归前缀,后缀就是句柄 ,表示可以规约。
- 移进项目:,其中 。
- 分析栈中是活前缀,后缀不是句柄,可以继续读 ,表示可以移进。
- 待约项目:,其中 。
- 需要先规约后面的输入到 ,才能获得可归前缀 。
- 把所有产生式的候选式插入圆点,则为 LR(0) 项目。
- 构造 LR(0) 状态
- 方法 1
- 找出所有的项目后,给每个项目分配编号。
- 构造 NFA,NFA 识别所有的活前缀:
- 起始状态为 ,表示没有扫描和匹配任意的符号。
- 接受状态为所有的规约项目对应的状态。
- 如果存在 和 ,则连边 。
- 这表示扫描符号 可以转移状态。
- 如果存在 和 ,则连边 。
- 这表示不消费符号而先进入规约 的状态。
- 使用子集法确定化 NFA:
- 得到的 DFA 的每个节点则为 LR(0) 项目的集合,称为项目集。
- DFA 的节点集合称为项目集规范族。
- 方法 2
- 定义项目集闭包 :
- 。
- 如果存在 和 ,则 。
- 这与方法一 含义类似。
- 项目集闭包也是项目集, 中的状态可以不需要消费符号而转移到 ,类似于单向的“等价”。
- 定义 为从 中的项目消费 可以到达的其他项目组成的项目集。
- 。
- 如果存在 和 ,则 。
- 类似方法 1 中的 。
- 把 看作 DFA 的状态,则 就是 DFA 的状态转移函数。
- 构造整个项目集规范族:
- 从 开始,计算 。
- 对于所有的 ,计算 。
- 重复上一步,用上一步得出的 继续计算。
- 定义项目集闭包 :
- 方法 1
- 构造 LR(0) 分析表
- 对于当前状态/项目集 :
- 如果项目集为一个接受项目,则填写 。
- 如果项目集为一个规约项目,则填写整行 ,其中 为对应的候选式编号。
- 复制 到 和 。
- 对于终结符 的状态转移 ,填写 。
- 对于非终结符 的状态转移 ,填写 。
- LR(0) 中,规约项目、接受项目是互斥性的,可能存在以下冲突:
- 移进-规约冲突
- 规约-规约冲突
- 接受-规约冲突
- 如果存在冲突,则无法按照上面的过程构造分析表,文法也就不是 LR(0) 文法。
- 对于当前状态/项目集 :
- LR(0) 项目
- SLR(1)
- 与 LR(0) 大致类似,但是在构造分析表时,对于规约项目的处理:
- 设规约项目为 ,仅对所有的 填写 。
- 项目集中可以存在多个规约项目,只要各自对应的 不交。
- 项目冲突类别与 LR(0) 相同,冲突条件为 相交。
- 与 LR(0) 大致类似,但是在构造分析表时,对于规约项目的处理:
- LR(1)
- LR(1) 项目
- LR(1) 项目由 LR(0) 项目和一个搜索符组成,记作 。
- 。
- 对于规约项目,只能在当前读入符号是 时才可以规约。
- 有效项目:若 对活前缀 有效,当且仅当:
- 存在规范推导 ,其中 ,
- 或 时 。
- LR(1) 项目包括了搜索符,在规约时更加精确,可以完全避免规约冲突。
- LR(1) 项目由 LR(0) 项目和一个搜索符组成,记作 。
- 构造 LR(1) 状态机
- 定义项目集闭包 为:
- 。
- 如果存在 和 且 ,则 。
- 依赖 的规约,所以后者的搜索符需要在 的最前面。
- 定义
- 如果存在 和 ,则 。
- 不涉及规约,所以搜索符不变。
- 如果存在 和 ,则 。
- 构造 LR(1) 状态机的方法与 LR(0) 一样,区别只有 和 。
- 定义项目集闭包 为:
- 构造 LR(1) 分析表
- 考虑每个项目集 中的每一个项目:
- 如果是接受项目,同 LR(0)。
- 如果是规约项目 ,填写 。
- 仅根据搜索符 精确填写第 列。
- 同 LR(0) 复制 到 和 。
- LR(1) 不存在冲突。
- 考虑每个项目集 中的每一个项目:
- LR(1) 项目
- LALR(1)
- LR(1) 中的两个项目集如果核心项目相同,则为同心项目集。
- LALR(1) 合并所有的同心项目集,搜索符取所有的并集。
- LALR(1) 仅可能存在规约-规约冲突,其他特性继承 LR(1)。
- 基本概念