天天看点

编译原理学习笔记 9.4 错误处理技术一、错误处理方法二、错误改正三、错误局部化处理

前言

参考课上PPT内容。 该学习笔记目前仅打算个人使用。

后续会进一步整理,包括添加笔记内容,标明参考资料。

更新中。。。

跳过目录

目录

  • 一、错误处理方法
  • 二、错误改正
  • 三、错误局部化处理
    • 一般原则
    • 错误局部化处理的实现(递归下降分析法)
    • 提高错误局部化程度的方法
    • LL分析的错误恢复——补充(第四章内容!)

一、错误处理方法

发现错误后,在报告错误的同时还要对错误进行处理,以便编译能继续进行下去。

目前有两种处理办法:

  • 错误改正
  • 错误局部化处理

二、错误改正

指编译诊察出错误以后,根据语言的文法和对程序意图作某种猜测,进行错误改正。

例:

A[i, j := B + * C
           
  • 编译可根据 A 的定义(声明),查符号表知道 A 是二维数据,所以应加上“ ] ”;
  • 根据表达式定义去掉

    *

    (但也有可能去掉

    +

但不是总能做到改正错误

例:

  • 多一个右括号还是少一个左括号?
  • 若是少了左括号,少在何处?

所以,要正确地改写错误是很困难的。

三、错误局部化处理

指当编译程序发现错误后,尽可能将把错误的影响限制在一个局部的范围,避免错误扩散和影响程序其它部分的分析。

一般原则

当诊断到错误以后,就暂停对后面符号的分析,跳过错误所在的语法成分(一旦跳过就认为该语法成分是正确的)然后继续往下分析。

词法分析:

  • 发现不合法字符,显示错误,并跳过该标识符(单词)继续往下分析。

语法语义分析:

  • 跳过所在的语法成分(短语或语句),一般是跳到语句右界符,然后从新语句继续往下分析。

错误局部化处理的实现(递归下降分析法)

CX:全局变量,存放错误信息。

  • 用递归下降分析时,如果发现错误,便将有关错误信息(字符串或者编号)送CX,然后转出错误处理程序;
  • 出错程序先打印或显示出错位置以及出错信息,然后跳出一段源程序,直到跳到语句的右界符(如

    end

    ;

    ),或正在分析的语法成分的合法后继符号为止,然后再往下分析。

例:条件语句分析

有如下分析程序:

void if_statement()
{
    nextsym(); // 读下个单词符号
    B();       // 调用布尔表达式处理程序
    if (class ! = thensy)
    {
        cx = 错误'缺then'的编号;  // 错误性质送cx
        error();                  // 调用出错处理程序
    }
    else
    {
        nextsym();
        statement(); // 调用语句分析程序
    }
    if (class == elsesy)
    {
        nextsym();
        statement();
    }
} // end of if_statement()
           

局部化处理的出错程序为:

void error()
{
    printf("%d, %d, %d\n", cx, 源程序行号, 序号);
    do
    {
        nextsym();
    }
    until(class == semicolon || class == endsy || class == elsesy)

} //end of error()
           

可以看出,如发现错误就立即跳到语句结尾处(语句右界符 end , ; 等)。这样的处理比较粗糙,将跳过太多。上例中缺then,就将跳过整个条件语句,使得then后面的语句都被跳过而不分析,其中若有错误就发现不了。

例:ALGOL说明

real x, 1a, a, bed, 2fg;
           

语法分析时,可发现第二个标识符有错,按上述处理办法则跳到该语句的右界符

;

,这导致最后一个标识符的错误未检查出来,且因为正确说明的标识符 a 、 bed 未进

行分析,则后面对这两个标识符 的引用都将被判定为未 声 明 。

提高错误局部化程度的方法

设:

  • S1:合法后继符号集 (某语法成分的后继符号)
  • S2:停止符号(跳读必须停止的符号集)

当发现错误时:error( S1, S2 )

procedure error( S1, S2 )
	begin
		write( line-no, char-no, cx);
		repeat
			nextsym
		until( class in S1 or class in S2 );
	end error;
           

上面例题中的 if 语句中,若<B>有错,则可跳到then;

若statement有错,则可跳到 else。

LL分析的错误恢复——补充(第四章内容!)

当符号栈顶的终结符和下一个输入符号不匹配,或栈顶是非终结符A,输入符号a,而M [A , a]为空白(即error)时,则分析发现错误。

错误恢复的基本思想是:跳过一些输入符号,直到期望的同步符号之一出现为止。

同步符号(可重新开始继续分析的输入符号)集合通常可按以下方法确定:

  1. 把FOLLOW(A)的所有符号加入A的同步符号集。如果我们跳读一些输入符号直到出现FOLLOW(A)的符号,之后把 A 从栈中弹出,继续往下分析即可。
  2. 只用FOLLOW(A)作为非终结符A的同步符号集是不够的(容易造成跳读过多,如输入串中缺少语句结束符分号时)。此时可将作为语句开头的关键字加入它的同步符号集,从而避免这种情况的发生。
  3. 把FIRST(A)的符号加入非终结符A的同步符号集中。
  4. 如果非终结符A可以产生空串,那么推导ε的产生式可以作为缺省的情况。这样做可以推迟某些错误检查,但不会漏过错误。
  5. 如果终结符在栈顶而不能匹配,则可弹出该终结符并发出一条信息后继续分析。这好比把所有其他符号均作为该符号的同步集合元素。
  1. 目标程序运行时错误检测与处理

    对于这类错误,要正确地报告出错误位置很难,因为目标程序与源程序之间难以建立位置上的对应关系。

    在编译时生成检测该类错误的代码。

    • 下标变量下标值越界

    • 计算结果溢出

    • 动态存储分配数据区溢出

    一般处理办法:

    当目标程序运行检测到这类错误时,就调用异常处理,打印错误信息和运行现场(寄存器和存储器中的值)等,然后停止程序运行。

继续阅读