本文参考《龙书》
语法分析器
- 在设计语言时,每种程序设计语言都有一组精确的规则来描述良构 (well-formed) 程序的语法结构
- 比如,在C语言中,一个程序由多个函数组成, 一个函数由声明和语句组成, 一个语句由表达式组成…
- 程序设计语言构造的语法可以使用 上下文无关文法(CFG) 或者 BNF 表示法来描述,属于上下文有关的语法特性在语义分析阶段检查
- 任何一个歧义文法不存在与其对应的语法分析器,只有文法是非歧义的,语法分析才可唯一进行。因此需要为程序设计语言选择能进行确定语法分析的非歧义文法
- 文法的非歧义性是不可判定的
- 虽然不存在算法能在有穷步内确定任意文法是否歧义,但对某些具体文法存在判定算法
- 语法分析器根据词法分析器提供的单词流以及程序语言的语法规则,在词法分析输出的单词符号串中识别出各类语法成分
- 也就是说,在语法分析阶段, 定义语法的那些终结符号,其实是词法分析阶段已经识别出来的词法单元;例如,标识符,
i
f
if
if,
w
h
i
l
e
while
while…
- 也就是说,在语法分析阶段, 定义语法的那些终结符号,其实是词法分析阶段已经识别出来的词法单元;例如,标识符,
i
f
if
if,
w
h
i
l
e
while
while…
- 同时我们期望语法分析器能够以易于理解的方式报告语法错误,并且能够从常见的错误中恢复并继续处理程序的其余部分
- 语法分析是编译过程的核心部分
语法错误的处理
程序可能有不同层次的错误,很多错误都以语法错误的方式出现,并且在不能继续进行语法分析时暴露出来
- 词法错误,包括标识符、关键字或运算符拼写错误…
- 语法错误,包括分号放错地方、花括号多余或缺失
- 语义错误,包括运算符和运算分量之间的类型不匹配
- 逻辑错误,可以是因程序员的错误推理而引起的任何错误。比如在一个C程序中应该使用比较运算符
==
的地方使用了赋值运算符=
。这样的程序可能是良构的,但是却没有正确反映出程序员的意图
一个错误处理程序应该如何报告出现的错误?至少,它必须报告在源程序的什么位置检测到错误,因为实际的错误很可能就出现在这个位置之前的几个词法单元处
- 一个常用的策略是打印出有问题的那一行,然后用一个指针指向检测到错误的地方
错误恢复策略
- 最简单的方法是让语法分析器在检测到第一个错误时给出错误提示信息,然后退出;如果语法分析器能够把自己恢复到某个状态,且有理由预期从那里开始继续处理输入将提供有意义的诊断信息,那么它通常会发现更多的错误。如果错误太多,那么最好让编译器在超过某个错误数量上界之后停止分析
恐慌模式的恢复
- 语法分析器一旦发现错误就不断丢弃输入中的符号,一次丢弃一个符号,直到找到同步词法单元(synchronizing token)集合中的某个元素为止。同步词法单元通常是界限符,比如 ; ; ; 或者 } \} } 。它们在源程序中的作用是清晰、无二义的。编译器的设计者必须为源语言选择适当的同步词法单元
- 恐慌模式的错误纠正方法常常会跳过大量输入,不检查被跳过部分的其他错误。但是它很简单,并且能够保证不会进入无限循环
短语层次的恢复
- 当发现一个错误时,语法分析器可以在余下的输入上进行局部性纠正。也就是说,它可能将余下输入的某个前缀替换为另一个串,使语法分析器可以继续分析
- 常用的局部纠正方法包括将一个逗号替换为分号、删除一个多余的分号或者插入一个遗漏的分号。我们必须小心选择替换方法,以避免进入无限循环。比如,如果我们总是在当前输入符号之前插入符号,就会出现无限循环
- 它主要的不足在于它难以处理实际错误发生在被检测位置之前的情况
错误产生式
- 通过预测可能遇到的常见错误,我们可以在当前语言的文法中加入特殊的产生式。这些产生式能够产生含有错误的构造,从而基于增加了错误产生式的文法构造得到一个语法分析器
- 如果语法分析过程中使用了某个错误产生式,语法分析器就检测到了一个预期的错误,就能够据此生成适当的错误诊断信息