[编译原理]Stanford斯坦福CS143第三周第六章


错误处理(Error Handling)

编译器的主要目标:

  • 检测无效程序
  • 编译有效程序

有很多种程序可能产生的错误,拿C语言举例:

错误类型举例检测器
Lexical(词法错误)…$…Lexer(词法检测阶段)
Syntax(语法错误)…x*$…Parser(解析器)
Semantic(语义错误)…int x; y = x(3);(类型不匹配)Type checker(类型检查器)
CorrectnessYour favourite programTester/User

最后一项是编译通过,可能需要其他用户或者测试者发现一些可以优化的问题。


错误处理的要求

  • 精准清晰地报告错误
  • 可以从错误中很快恢复
  • 不会使编译有效代码的速度变慢

错误处理的类型

Panic Mode(恐慌模式)

这是最简单也是最流行的方法,当检测到错误时,解释器将抛弃令牌(token)知道有一个明确角色的出现,然后重新启动继续。

举例:

( 1 + + 2 ) + 3 (1++2)+3 (1++2)+3

恐慌模式做的补救工作:

  • 跳过第二个 ➕ ➕ ,知道检测到合法的字符( 2 2 2),然后继续。

B i s o n Bison Bison使用终端关键字error来描述如何在输入中跳过错误:

E   − >   i n t   ∣   E   +   E   ∣   (   E   )   ∣   e r r o r   i n t   ∣   (   e r r o r   ) E\ ->\ int\ |\ E\ +\ E\ |\ (\ E\ )\ |\ error\ int\ |\ (\ error\ ) E > int  E + E  ( E )  error int  ( error )

表示的是当前面三项不匹配时,匹配后面两项,抛弃所有输入直到下一个整数出现;或者当括号中出现错误时,将括号中的都抛弃。

Error Productions

指定了常见的程序错误。

缺点:

  • 时编译器变得复杂。

上面两种错误处理的类型是现在编译器中使用的。

自动全局或局部错误修正(Automatic local or global correction)

思路:找到一个正确的附近的程序

  • 尝试令牌插入或者删除(用到最小编辑距离算法)
  • 在一定范围内进行详尽的搜索

缺点:

  • 难以实现
  • 减缓对正确程序的解析速度
  • “附近”不一定是想要的程序结果

抽象语法树(Abstract Syntax Trees)

抽象语法树(AST)类似于解析树,但是忽略了一些细节。

举例:

  1. 语法:

E   − >   i n t   ∣   (   E   )   ∣   E   +   E E\ ->\ int\ |\ (\ E\ )\ |\ E\ +\ E E > int  ( E )  E + E

  1. 字符串:

5   +   (   2   +   3   ) 5\ +\ (\ 2\ +\ 3\ ) 5 + ( 2 + 3 )

  1. 词法分析之后(lexical analysis):

生成了一系列的令牌: i n t 5     ′ + ′     ′ ( ′   i n t 2     ′ + ′   i n t 3   ′ ) ′ int_5\ \ '+'\ \ '('\ int_2\ \ '+'\ int_3\ ')' int5  +  ( int2  + int3 )

  1. 解析器过程生成了解析树:

在这里插入图片描述

解析树的特点:

  • 追踪了解析器中的符号
  • 显示了嵌套结构
  • 但是有太多信息,比如括号和单孩子节点。

所以我们使用AST简化解析树:

在这里插入图片描述
特点:

  • 也包含嵌套结构
  • 只包含有实际意义的句法(更加易用、紧凑)
  • 是编译器中的一个非常重要的数据结构

递归下降解析(Recursing Descent Parsing)

是一个自顶向下的算法。解析树是自上而下、从左向右生成的。

考虑如下语法:

E   − >   T   ∣   T   +   E E\ ->\ T\ |\ T\ +\ E E > T  T + E
T   − >   i n t   ∣   i n t   ∗   T   ∣   (   E   ) T\ ->\ int\ |\ int\ *\ T\ |\ (\ E\ ) T > int  int  T  ( E )

令牌流:

(   i n t 5   ) (\ int_5\ ) ( int5 )

该算法从最高层的非终端 E E E开始,依次尝试 E E E的所有规则,没有符合的就反向跟踪。

步骤:

图中的指针指向当前读入的字符,当算法遍历到字符串最后一个字符的后面位置时,表示解析成功。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

限制

观察如下代码:

TOKEN包含 INT, OPEN, CLOSE, PLUS, TIMES

// 判断下一个字符串和当前tok是否匹配
bool term(TOKEN tok)
{
	// 先判断是否相等,再自加1
	return *next++ == tok; // next是全局变量,指向输入字符串下一个位置,无论是否匹配成功,next都自增1
}

// E规则的两个推导
bool E1() { return T1(); }
bool E2() { return T() && term(PLUS) && E(); }
	
// 尝试E规则的所有推导
bool E()
{
	TOKEN *save = next; 
	return (next = save, E1()) || (next = save, E2());
}

bool T1(){ return term(INT); }
bool T2(){ return term(INT) && term(TIMES) && T(); }
bool T3(){ return term(OPEN) && E() && term(CLOSE); }

bool T()
{ 
	TOKEN *save = next;
	return (next = save, T1()) || (next = save. T2()) || (next = save. T3());
}

如果对于token输入为int * int,那么程序先调用E(),再调用E1(),接着T(),T1(),得到int,是匹配成功的,所以所有函数都返回True,但是我的token串并没有遍历完全,所以编译器拒绝这个值。所以程序问题出在没有后退机制(即如果一个一个非终端X匹配成功,它将没有办法后退尝试另外一个匹配)。

因此,目前展示的递归下降解析算法不是通用的,但是对于非终端最多一个规则(Production)的语法是使用的。

左递归

左递归是递归下降算法的主要困难,在使用该算法之前必须先消除左递归现象。

  • 举例:

对于下面语法和实现代码:

S   − >   S   a S\ ->\ S\ a S > S a

bool S1(){ return S() && term(a); }
bool S(){ return S1(); 

由此可以看出,当解析时,S()会进入一个死循环。

  • 另外一个例子:

语法: S   − >   S   a   ∣   β S\ ->\ S\ a\ |\ β S > S a  β

该语法解析出来的样式将会是βα+(前面一个S,后面一个以上的β)。左递归有一个非终端S,有 S   − > +   S α S\ ->^+\ Sα S >+ Sα。递归下降算法的解析想要先看到第一部分匹配出来,而这种情况下是无穷匹配后面的项,最前面的S最后匹配,所以对这种情况不适用。

  • 解决方法:

可以改写成右递归。上述的例子可以改写为:

S   − >   β S ′ S\ ->\ βS' S > βS
S ′   − >   α S ′   ∣   ε S'\ ->\ αS'\ |\ ε S > αS  ε

  • 推广到一般情况:

有如下的左递归的规则:
S   − >   S α 1   ∣   . . .   ∣   S α n   ∣   β 1   ∣   . . .   ∣   β m S\ ->\ Sα_1\ |\ ...\ |\ Sα_n\ |\ β_1\ |\ ...\ |\ β_m S > Sα1  ...  Sαn  β1  ...  βm
都可以改写成如下对应的右递归:
S   − >   β 1 S ′   ∣   . . .   ∣   β m S ′ S\ ->\ β_1S'\ |\ ...\ |\ β_mS' S > β1S  ...  βmS
S ′   − >   α 1 S ′   ∣   . . .   ∣   α n S ′   ∣   ε S'\ ->\ α_1S'\ |\ ...\ |\ α_nS'\ |\ ε S > α1S  ...  αnS  ε


《编译原理》龙书

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值