编译原理中处理语法错误问题的研究
编译原理语法分析实验报告

编译原理语法分析实验报告编译原理语法分析实验报告引言编译原理是计算机科学中的重要课程,它研究的是如何将高级语言转化为机器语言的过程。
语法分析是编译过程中的一个关键步骤,它负责将输入的源代码转化为抽象语法树,为后续的语义分析和代码生成提供便利。
本实验旨在通过实践,加深对语法分析的理解,并掌握常见的语法分析算法。
实验环境本次实验使用的是Python编程语言,因为Python具有简洁的语法和强大的库支持,非常适合用于编译原理的实验。
实验步骤1. 词法分析在进行语法分析之前,需要先进行词法分析,将源代码划分为一个个的词法单元。
词法分析器的实现可以使用正则表达式或有限自动机等方式。
在本实验中,我们选择使用正则表达式来进行词法分析。
2. 文法定义在进行语法分析之前,需要先定义源代码的文法。
文法是一种形式化的表示,它描述了源代码中各个语法成分之间的关系。
常见的文法表示方法有巴科斯范式(BNF)和扩展巴科斯范式(EBNF)。
在本实验中,我们选择使用BNF来表示文法。
3. 自顶向下语法分析自顶向下语法分析是一种基于产生式的语法分析方法,它从文法的起始符号开始,逐步展开产生式,直到生成目标字符串。
自顶向下语法分析的关键是选择合适的产生式进行展开。
在本实验中,我们选择使用递归下降分析法进行自顶向下语法分析。
4. 自底向上语法分析自底向上语法分析是一种基于移进-归约的语法分析方法,它从输入串的左端开始,逐步将输入符号移入分析栈,并根据产生式进行归约。
自底向上语法分析的关键是选择合适的归约规则。
在本实验中,我们选择使用LR(1)分析法进行自底向上语法分析。
实验结果经过实验,我们成功实现了自顶向下和自底向上两种语法分析算法,并对比了它们的优缺点。
自顶向下语法分析的优点是易于理解和实现,可以直接根据产生式进行展开,但缺点是对左递归和回溯的处理比较困难,而且效率较低。
自底向上语法分析的优点是可以处理任意文法,对左递归和回溯的处理较为方便,而且效率较高,但缺点是实现相对复杂,需要构建分析表和使用分析栈。
编译原理中语法分析探讨及其应用

(一)语法分析概述语法分析是对高级语言的句子结构进行分析,在编译过程中处于核心地位,它的主要任务是在词法分析识别出正确单词符号串的基础上,根据语言定义的语法规则,从单词符号串中识别出各种语法成分,并进行语法检查和错误处理,生成相应的语法树。
生成语法树的方法有两种,一种是自上而下另一种是自下而上。
[1]自上而下的语法分析方法是对由单词种别构成的源程序,尝试用所有可能的途径,从语法树的根结点出发,从上至下为输入符号串建立一棵语法树。
也可以说成是为输入符号串构造一个最左推导。
整个分析过程就是一种试探的过程,通过不断使用不同产生式谋求匹配输入符号串的过程。
自上而下的语法分析方法分为确定的和不确定的两种。
只有LL(1)文法才能进行确定的自上而下语法分析,在这种分析方法中面临两个问题,回溯和死循环。
通过对文法的产生式进行改造来解决这两个问题,在回溯中改造的方法是提取公共左因子,比如:A→αβ1|αβ2,改造后为A→αA’,A’→β1|β2。
死循环的改造方法是消除左递归,比如:A→Aα|β改造后为A→βA’,A’→αA’|ε。
而不确定的有递归下降分析和预测分析,前者适合手工实现,后者适合自动生成。
递归下降分析是用子程序来实现的,为每个非终结符创造一个子程序,每个子程序里面都有一个函数体,这个函数体是根据非终结符的产生式而定义展开的,当分析过程中遇到终结符时就直接匹配,如果遇到非终结符就调用相应的非终结符对应的子程序。
预测分析方法需要借助一个状态栈和一个二维分析表来实现,两者必须联合控制才能更好的实现预测分析方法。
自下而上的语法分析方法和自上而下的语法分析方法是完全不同的两种方法,但是它们产生的结果是相同的,都是构造一棵语法树,只是构造的过程不同罢了。
语法树创建的过程是把分析符号串的各个符号作为叶子结点,按照文法定义的规则,把产生式左部的非终结符作为父结点,自下而上构造此树的过程。
它的基本思想是“移进—归约”。
编译原理语法分析试验报告

编译原理语法分析试验报告语法分析是编译原理中的重要内容之一,主要用于对源程序进行语法检查,判断其是否符合给定的语法规则。
本次试验通过使用ANTLR工具,对C语言的子集进行了语法分析的实现。
一、实验目的:1.了解语法分析的基本概念和方法;2.使用ANTLR工具生成语法分析器;3.掌握ANTLR工具的基本使用方法;4.实现对C语言子集的语法分析。
二、实验内容:本次试验主要内容是使用ANTLR工具生成C语言子集的语法分析器,并对给定的C语言子集进行语法分析。
三、实验步骤:1.学习ANTLR工具的基本概念和使用方法;2.根据C语言子集的语法规则,编写ANTLR的语法文件(.g文件);3.使用ANTLR工具生成语法分析器;4.编写测试代码,对给定的C语言子集进行语法分析。
四、实验结果:经过以上的步骤,得到了一个完整的C语言子集的语法分析器,并且通过测试代码对给定的C语言子集进行了语法分析。
五、实验总结:通过本次实验,我对语法分析有了更深入的了解,掌握了使用ANTLR工具生成语法分析器的基本方法,同时也巩固了对C语言的基本语法规则的理解。
在实验过程中,遇到了一些问题,例如在编写ANTLR的语法文件时,对一些特殊语法规则的处理上有些困惑,但通过查阅资料和与同学的探讨,最终解决了这些问题。
本次试验对于我的学习有很大的帮助,我了解到了编译原理中的重要内容之一,也更深入地理解了语法分析的基本原理和方法。
通过实验,我发现使用ANTLR工具能够更方便地生成语法分析器,大大提高了开发效率。
总之,本次试验让我对编译原理中的语法分析有了更深入的了解,并且提高了我的编程能力和分析问题的能力。
在今后的学习和工作中,我将继续深入研究编译原理相关的知识,并应用到实际项目中。
编译原理语法分析实验报告

编译原理语法分析实验报告第一篇:编译原理语法分析实验报告实验2:语法分析1.实验题目和要求题目:语法分析程序的设计与实现。
实验内容:编写语法分析程序,实现对算术表达式的语法分析。
要求所分析算术表达式由如下的文法产生。
E→E+T|E-T|TT→T*F|T/F|F F→id|(E)|num实验要求:在对输入表达式进行分析的过程中,输出所采用的产生式。
方法1:编写递归调用程序实现自顶向下的分析。
方法2:编写LL(1)语法分析程序,要求如下。
(1)编程实现算法4.2,为给定文法自动构造预测分析表。
(2)编程实现算法4.1,构造LL(1)预测分析程序。
方法3:编写语法分析程序实现自底向上的分析,要求如下。
(1)构造识别所有活前缀的DFA。
(2)构造LR分析表。
(3)编程实现算法4.3,构造LR分析程序。
方法4:利用YACC自动生成语法分析程序,调用LEX自动生成的词法分析程序。
实现(采用方法1)1.1.步骤:1)对文法消除左递归E→TE'E'→+TE'|-TE'|εT→FT'T'→*FT'|/FT'|εF→id|(E)|num2)画出状态转换图化简得:3)源程序在程序中I表示id N表示num1.2.例子:a)例子1 输入:I+(N*N)输出:b)例子2 输入:I-NN 输出:第二篇:编译原理实验报告编译原理实验报告报告完成日期 2018.5.30一.组内分工与贡献介绍二.系统功能概述;我们使用了自动生成系统来完成我们的实验内容。
我们设计的系统在完成了实验基本要求的前提下,进行了一部分的扩展。
增加了声明变量类型、类型赋值判定和声明的变量被引用时作用域的判断。
从而使得我们的实验结果呈现的更加清晰和易懂。
三.分系统报告;一、词法分析子系统词法的正规式:标识符(|)* 十进制整数0 |(1|2|3|4|5|6|7|8|9)(0|1|2|3|4|5|6|7|8|9)* 八进制整数0(1|2|3|4|5|6|7)(0|1|2|3|4|5|6|7)* 十六进制整数0x(0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f)(0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f)* 运算符和分隔符 +| * | / | > | < | = |(|)| <=|>=|==;对于标识符和关键字: A5—〉 B5C5 B5—〉a | b |⋯⋯| y | z C5—〉(a | b |⋯⋯| y | z |0|1|2|3|4|5|6|7|8|9)C5|ε综上正规文法为: S—〉I1|I2|I3|A4|A5 I1—〉0|A1 A1—〉B1C1|ε C1—〉E1D1|ε D1—〉E1C1|εE1—〉0|1|2|3|4|5|6|7|8|9 B1—〉1|2|3|4|5|6|7|8|9 I2—〉0A2 A2—〉0|B2 B2—〉C2D2 D2—〉F2E2|ε E2—〉F2D2|εC2—〉1|2|3|4|5|6|7 F2—〉0|1|2|3|4|5|6|7 I3—〉0xA3 A3—〉B3C3 B3—〉0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f C3—〉(0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f)|C3|εA4—〉+ |-| * | / | > | < | = |(|)| <=|>=|==; A5—〉 B5C5 B5—〉a | b |⋯⋯| y | z C5—〉(a | b |⋯⋯| y | z |0|1|2|3|4|5|6|7|8|9)C5|ε状态图流程图:词法分析程序的主要数据结构与算法考虑到报告的整洁性和整体观感,此处我们仅展示主要的程序代码和算法,具体的全部代码将在整体的压缩包中一并呈现另外我们考虑到后续实验中,如果在bison语法树生成的时候推不出目标的产生式时,我们设计了报错提示,在这个词的位置出现错误提示,将记录切割出来的词在code.txt中保存,并记录他们的位置。
编译原理实践--语法出错处理

语法出错处理概论 常见错误的分析处理 不可预料错误的处理 PL/0语法出错处理程序及其执行
1.语法出错处理概论
出错处理要解决的主要问题
出错(procedure error(n:integer)) (P83)
• 指出错误类型、halt
一次运行,尽可能多的发现语法错误
语法分析程序program4的缺点:遇到 错误就调用error过程,中断程序的执 行 分析程序可能的出错情况和解决方案:
Example2: 正确: const m=7,n=85; var x,y,z,q,r; 错误:
const m=7;n=85, const m=7,n=85, const m=7,n=85
• 容易犯的错误:将中间的逗号写成分号;将结束的分 号写成逗号;遗漏结束时候的分号 • 解决方法:修改block语法分析程序
. ; ) rop + - end then do
. ; ) rop + - * / end then do
例如,考察因子的语法分析。在过程FACTOR的入口处调用 一次TEST过程,它的实参S1是因子开始符号集合。S2是每 个过程的形参FSYS调用时实参的传递值。当编译程序第一次 调用BLOCK时,FSYS的实参为[.]与说明开始符号和语句 开始符号集合的和。以后随着调用语法分析程序层次的深入 逐步增加。如在调用语句时增加了[;]和[endsym],在表达 式语法分析中调用项时又增加[+]和[-],而在项中调用因子 时又增加了[*]和[/],这样在进入因子分析程序时即使当前 符号不是因子开始符,出错后只要跳过一定的符号,遇到当 时输入的单词符号在FSYS中或在因子开始符号集合中,均可 继续正常进行语法分析。在因子过程的出口处也调用了测试 过程,当时的FSYS集合的单词符号都是因子正常出口时允许 的单词符号。
编译原理语法分析实验报告

编译原理语法分析实验报告编译原理实验报告-语法分析班级:XXX学号:XXX姓名:XXX年⽉⽇1、摘要:⽤递归⼦程序法实现对pascal的⼦集程序设计语⾔的分析程序2、实验⽬的:通过完成语法分析程序,了解语法分析的过程和作⽤3、任务概述实验要求:对源程序的内码流进⾏分析,如为⽂法定义的句⼦输出”是”否则输出”否”,根据需要处理说明语句填写写相应的符号表供以后代码⽣成时使⽤4、实验依据的原理递归⼦程序法是⼀种⾃顶向下的语法分析⽅法,它要求⽂法是LL(1)⽂法。
通过对⽂法中每个⾮终结符编写⼀个递归过程,每个过程的功能是识别由该⾮终结符推出的串,当某⾮终结符的产⽣式有多个候选式时,程序能够按LL(1)形式唯⼀地确定选择某个候选式进⾏推导,最终识别输⼊串是否与⽂法匹配。
递归⼦程序法的缺点是:对⽂法要求⾼,必须满⾜LL(1)⽂法,当然在某些语⾔中个别产⽣式的推导当不满⾜LL(1)⽽满⾜LL(2)时,也可以采⽤多向前扫描⼀个符号的办法;它的另⼀个缺点是由于递归调⽤多,所以速度慢占⽤空间多,尽管这样,它还是许多⾼级语⾔,例如PASCAL,C等编译系统常常采⽤的语法分析⽅法。
为适合递归⼦程序法,对实验⼀词法分析中的⽂法改写成⽆左递归和⽆左共因⼦的BNF如下:<程序>→<程序⾸部><分程序>。
<程序⾸部>→PROGRAM标识符;<分程序>→<常量说明部分><变量说明部分><过程说明部分> <复合语句><常量说明部分>→CONST<常量定义><常量定义后缀>;|ε<常量定义>→标识符=⽆符号整数<常量定义后缀>→,<常量定义><常量定义后缀> |ε<变量说明部分>→V AR<变量定义><变量定义后缀> |ε<变量定义>→标识符<标识符后缀>:<类型>;<标识符后缀>→,标识符<标识符后缀> |ε<变量定义后缀>→<变量定义><变量定义后缀> |ε<类型>→INTEGER | LONG<过程说明部分>→<过程⾸部><分程序>;<过程说明部分后缀>|ε<过程⾸部>→PROCEDURE标识符<参数部分>;<语句>→<赋值或调⽤语句>|<条件语句>|<当型循环语句>|<读语句>|<写语句>|<复合语句>|ε<赋值或调⽤语句>→标识符<后缀><后缀>→:=<表达式>|(<表达式>)|ε<条件语句>→IF<条件>THEN<语句><当型循环语句>→WHILE<条件>DO <语句><读语句>→READ(标识符<标识符后缀>)<写语句>→WRITE(<表达式><表达式后缀>)<表达式后缀>→,<表过式><表达式后缀>|ε<复合语句>→BEGIN<语句><语句后缀>END<语句后缀>→;<语句><语句后缀>|ε<条件>→<表达式><关系运算符><表达式>|ODD<表达式><表达式>→+<项><项后缀>|-<项><项后缀>|<项><项后缀><项后缀>→<加型运算符><项><项后缀>|ε<项>→<因⼦><因⼦后缀><因⼦后缀>→<乘型运算符><因⼦><因⼦后缀>|e<因⼦>→标识符|⽆符号整数|(<表达式>)<加型运算符>→+|-<乘型运算型>→*|/<关系运算符>→=|<>|<|<=|>|>=5、程序设计思想为每个⾮终结符设计⼀个识别的⼦程序,寻找该⾮终结符也就是调⽤相应的⼦程序。
编译原理语法分析实验报告

语法分析实验报告实验目的编制一个递归下降分析程序,实现对词法分析程序所提供的单词序列的语法检查和结构分析。
二、实验要求利用C语言编制递归下降分析程序,并对简单语言进行语法分析。
2.1 待分析的简单语言的语法用扩充的BNF表示如下:1)<程序>::=begin<语句串>end2)<语句串>::=<语句>{;<语句>}3)<语句>::=<赋值语句>4)<赋值语句>::=ID:=<表达式>5)<表达式>::=<项>{+<项> | -<项>}6)<项>::=<因子>{*<因子> | /<因子>7)<因子>::=ID | NUM | (<表达式>)2.2 实验要求说明输入单词串,以“#”结束,如果是文法正确的句子,则输出成功信息,打印“success”,否则输出“error”。
例如:输入 begin a:=9; x:=2*3; b:=a+x end #输出语法分析成功输入 x:=a+b*c end #输出语法分析2.3 语法分析程序的酸法思想1)主程序示意图如图2-1所示。
图2-1 语法分析主程序示意图2)递归下降分析程序示意图如图2-2所示。
3)语句串分析过程示意图如图2-3所示。
图2-2 递归下降分析程序示意图图2-3 语句串分析示意图4)statement语句分析程序流程如图2-4、2-5、2-6、2-7所示。
图2-4 statement语句分析函数示意图图2-5 expression表达式分析函数示意图图 2-6 term分析函数示意图图2-7 factor 分析过程示意图三、语法分析程序的C 语言程序源代码#include <stdio.h>#include <stdlib.h>#include <string.h>char prog[100],ch,token[8];int p=0,syn,n,i;char *keyword[6]={"begin","then","if","while","do","end"};void scaner();void Irparse();void statement();void expression_r();void term();void factor();void main(){int select=-1;p=0;printf("please input sentence, end of '#' !\n");do{ch=getchar();prog[p++]=ch;}while(ch!='#');p=0;printf("请输入1 或 2 \n 1.词法分析\n 2.语法分析\n");scanf("%d",&select);if(select==1){do{scaner();switch(syn){case -1:printf("词法分析出错\n");break;default :printf("<%d,%s>\n",syn,token);break; }}while(syn!=0);printf("词法分析成功\n");}else if(select==2){scaner();if(syn==1){Irparse();}//beginelse{printf("语法分析出错! 请检查begin关键字\n");return;} if(syn==6)//end{scaner();if(syn==0){printf("恭喜语法分析成功\n");}else{printf("语法分析出错! 请检查是否缺少'#'\n");} }else{printf("语法分析出错! 请检查是否缺少'end'\n");}}getchar();}void scaner(){for(n=0;n<8;n++){token[n]='\0';}n=0;ch=prog[p++];while(ch==' '){ch=prog[p++];}if((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z')){do{token[n++]=ch;ch=prog[p++];}while((ch>='a'&&ch<='z')||(ch>='a'&&ch<='z')||(ch>='0'&&ch <='9'));syn=10;for(n=0;n<6;n++){if(strcmp(token,keyword[n])==0){syn=n+1;}}p--;//return;}else if(ch>='0'&&ch<='9'){p--;do{token[n++]=prog[p++];ch=prog[p];}while(ch>='0'&&ch<='9');syn=11;return;}else{//ch=prog[p++];switch(ch){case '+':syn=13;token[0]=ch;break;case '-':syn=14;token[0]=ch;break;case '*':syn=15;token[0]=ch;break;case '/':syn=16;token[0]=ch;break;case ':':syn=17;token[0]=ch;ch=prog[p++];if(ch=='='){token[1]=ch;syn++;}else p--;break;case '<':syn=20;token[0]=ch;ch=prog[p++];if(ch=='>'){token[1]=ch;syn++;}else if(ch=='='){token[1]=ch;syn=syn+2;} else p--;break;case '>':syn=23;token[0]=ch;ch=prog[p++];if(ch=='='){token[1]=ch;syn++;}else p--;break;case '=':syn=25;token[0]=ch;break;case ';':syn=26;token[0]=ch;break;case '(':syn=27;token[0]=ch;break;case ')':syn=28;token[0]=ch;break;case '#':syn=0;token[0]=ch;break;default: printf("词法分析出错! 请检查是否输入非法字符\n");syn=-1;break;}//return;}}void Irparse(){scaner();statement();while(syn==26)//;{scaner();statement();}}void statement(){if(syn==10){scaner();if(syn==18){scaner();expression_r();}else{printf("语法分析出错! 请检查表达式是否正确\n");return;}}else{printf("语法分析出错! 请检查语句是否正确\n");return; }}void expression_r(){term();while(syn==13||syn==14)//+ -{scaner();term();}}void term(){factor();while(syn==15||syn==16)//* /{scaner();factor();}}void factor(){if(syn==10||syn==11){scaner();}else if(syn==27){scaner();expression_r();if(syn==28){scaner();}else {printf("语法分析出错! 请检查是否缺少')'\n");return;}}else {printf("语法分析出错! 请检查是否输入非法字符\n");return;}}四、程序测试结果1)开始调试的界面2)对源程序begin a:=9; x:=2*3; b:=a+x end #的源文件,经过语法分析后如下图4-1所示:图 4-1 正确结果输出3)对源程序x:=a+b*c end #的源文件,经过语法分析后如下图4-2所示:图 4-2 错误结果输出五、总结通过对语法分析程序的设计和编写,使自己获得了很大的收获,并且使自己对语法分析程序的功能有了更进一步认识。
编译原理语法分析实验报告

//输出转化后
的文法规则串
{
printf("%s\n",text[i]);
}
for(i=0;i<x;i++) /*求每个终结符的推导结果(去掉"->"后的转化文
法,用于最后的规约)*/
{ string[i][0]=text[i][0];
for(j=3,l=1;text[i][j]!='\0';j++,l++)
表1-2 G[S]的算符优先关系矩阵表
#
i
+
*
(
)
#
i
+
*
(
)
二.程序设计
1.总体设计
求FIRSTVT模块 求LASTVT模块 识别终结符模块
读入文法规则 创建文法关系表模块
求下标模块
2.子程序设计
识别终结符模块
读取语法分析串 输入串分析模块 图2-1 总体设计图
求FIRSTVT模块 求LASTVT模块
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
编译原理中处理语法错误问题的研究
摘要:本文分析了编译系统以及其错误处理能力对于程序设计语言的重要性,对其中处理语法错误问题进行了深入研究,并从语法错误的诊察与报告,到利用递归下降分析法对错误进行恢复和纠正处理,直至最后的限制重复报告错误信息及其中涉及的关键技术进行了介绍,从而帮助学习者和开发者牢固掌握相关的理论和技术。
关键词:编译系统;语法错误处理;递归下降分析法
1 前言
在计算机应用领域,目前多数用户都是通过高级语言实现所需要的计算。
而对于任何高级语言来说,其编译系统内容丰富,具有严密的逻辑性,对提高学习者和开发者的计算机软件素质具有很大作用,使其不但能认识计算机信息处理的实质,还可以综合运用所学的软件设计技术来分析解决问题[1]。
因此,编译系统是计算机系统软件最重要的组成部分之一,也是用户最直接关心的工具之一,它不但要接受程序语言的所有标准定义,以便源代码实现跨平台的可移植性,还必须生成高效、正确的目标代码。
因此编译系统本身是一个大而复杂的程序,值得我们深入分析研究。
我们知道,在编译原理的学习和编译系统的构建过程中,语法分析是其中最为重要的一个组成部分。
而在实际的编译系统中,语法分析器的错误处理能力与其构造原理和技术一样重要,这通常是编译原理教学环节中容易忽视的地方,不利于学习者进行实际的编译系统的开发工作。
因此,本文对C++编译系统中递归下降的语法分析过程进行了研究,找到了发现并纠正语法错误问题的有效方法。
2 语法错误
编程人员在编写程序时,很难一次就将程序写的完美无误,尤其是一些比较复杂的程序,往往会存在程序错误。
程序错误的种类有很多,比如违反语言的语法和语义规定的错误,源程序超出了计算机系统的某种限制而引发的错误,等等。
其中语法错误是指源程序中含有不符合语法规则的成分时所产生的错误,一般是有关语言结构上的错误,如单词拼写错、表达式中缺少操作数、begin和end不匹配等。
语法分析结果的质量将直接影响到编译系统后期各阶段的工作,因此,为了帮助编程人员发现并纠正这一阶段可能出现的错误,编译系统的语法分析器应该具有错误处理的能力,其不但可以对语法上正确的源程序进行正确的编译,同时还能够对有错误的源程序报错,甚至在一定程度上对错误进行改正[2]。
当然,进行出错处理是件很麻烦的事,想象一个设计良好的编译调试环境,比如Visual Studio,我们在用它开发编译程序时,不光可以知道哪一句错了,而且可以获得出错的原因。
3 语法错误处理技术
3.1 错误的诊察与报告
语法错误可以采用系统的方式解决,不依赖于出现的上下文。
这些错误比较容易发现,通常出现在表1所示的翻译代码error中。
表1分析函数翻译代码
这里,编译系统使用EBNF文法描述语言,为每个非终结符计算FIRST集合和FOLLOW集合,编写分析函数将非终结符的每个产生式翻译成可执行代码。
翻译的规则可由文法产生式的可能形式导出。
对每一种产生式形式α,用T(α)表示α的翻译代码,全局变量t表示从词法分析器读入的当前单词,调用函数gettok可以获取下一个输入单词,此外,
当然,表1的代码也可用其他代码序列表示,以解决有时会出现的代码冗长问题。
编译系统在查找到源程序中的语法错误后,要对这些错误进行报告,报告的主要内容是错误发生的位置以及错误的性质。
有了这两点内容,编程人员就可以比较方便地确定错误的性质并对其进行改正。
文本所采用的错误报告方式为:每发现一处错误,就把该错误信息打印出来,包括源程序的名字、错误所在行、错误的具体内容等,同时用箭头指向出错的位置。
例如,对于程序段:
for (i=0; i>i
其中的错误,编译系统将报告如下错误信息:
Test.cpp: 15: missing ‘;’ before ‘)’
location: class PrintTest
for (i=0; i>i
止分析,这样做显然不合理,错误处理的大部分工作用于对错误进行适当的处理——错误恢复,以便系统分析过程可以继续下去。
语法错误的恢复方法是通过对输入程序添加遗漏的单词或忽略某些单词,从而将错误的输入转换为合法的句子。
遗憾的是,找到合适的恢复方法非常困难,因为编程人员的意图有时候是很难推断出来的,这种推测在一定程度上增加了编译系统的复杂程度,如果选择错误将导致分析程序混乱,甚至导致后面本来文法上正确的输入也产生大量的语法错误。
而对于递归下降分析器的结构而言,有助于选取合适的错误恢复策略。
分析器由许多函数组成,每个函数只完成分析任务的一小部分,因此,目标被划分成若干个子目标,每个子目标调用相关的分析函数。
具体而言,假设构造了一个非终结符X的分析函数X,如果下一个输入单词不属于非终结符X的FOLLOW集合,函数就略过它,反复执行直至遇到X的FOLLOW集合中的单词,在处理完属于FOLLOW(X)的单词后,要将所有合法的部分返回给函数X的调用者。
但这样做也有不完善之处,它不能处理含有非终结符X的句型。
例如,非终结符X 出现在句型αXβ中,函数X会略过D(β)中的元素,由于D(β)通常比FOLLOW(X)小,当分析识别出一个属于FOLLOW(X),但不属于D(β)的输入单词时,程序丢弃该单词,X停止继续执行。
因此,如果D(β)已知,函数必须使用D(β),否则使用FOLLOW(X),实现方法如下面error.cpp中的输出函数所示。
比如,当分析函数分析for语句的第三个表达式时,函数就利用了集合{ ; ) }恢复表达式存在的
语法错误。
<error.cppexportedfunctions>≡
externvoidtestARGS((inttok,charset[]));
该函数检查下一个单词是否等于tok,如果不等,则发出提示信息,并跳过当前单词,反复执行直至遇到一个属于{tok}∪set的单词。
set集合包含了所有不能忽略的元素,保证输入不会无限地忽略。
<error.cppfunctions>≡
viodtest(tok,set)inttok;charset[];{
if(t==tok)
t=gettok();
else{
expect(tok);
skipto(tok,set);
if(t==tok)
t=gettok();
}
}
函数test调用函数expect报错,调用函数skipto跳过错误的单词。
skipto定义如下:
<error.cppexportedfunctions>+≡
externvoidskiptoARGS((inttok,charset[]));
skipto不断跳过输入单词,直至遇到单词t(t要么与tok相等,要么使得kind[t]
包含在无效终结符数组set中)。
kind[t]是一个单词编码,它意味着一个含有t的集合。
例如,编码ID表示集合FIRST(expression),kind[t]与ID相等。
利用数组{ID,0}作为函数skipto的第二个参数,指示函数跳过若干个无关的单词直至找到一个属于FIRST(expression)的元素。
表2概括了所有的kind值。
对于表中未提及的单词,kind[t]就等于t。
总之,如果t与tok相等,或者t 属于kind[t],那么skipto将不会跳过任何单词。
表2数组kind的值
3.3 限制重复报告错误信息
当我们在编写程序时,有些错误会不止一次地出现,比如语句的最后忘记写“;”,实际上这种错误没有必要重复报告,这就要求语法分析具有制止重复报告错误信息的功能。
我们设计了一张出错名字表,一旦发现一个出错名字后,先查出错名字表,查找有无同名且同性质的出错名字,如果有,则不再报告此错误,否则将此出错名字添加进名字表并显示出错信息。
4 总结
错误处理能力是衡量编译器性能的重要方面,本文列举了一些编译系统在实际应用中的案例,说明了系统的错误处理能力体现在编译过程的各个环节,不可忽视。
系统的错误处理能力在帮助编程人员尽快修改程序方面起到了非常重要的作用,是编译系统的一个重要组成部分。
因此,我们应尽量地把它设计完善,方便用户的使用。
参考文献:
[1] 黄贤英,刘贞,刘全利. “编译原理”课程的地位及教改思路[J].重庆科技学院学报(社会科学版),2005,(3):
103-105.
[2] 王雷,刘志成,等. 编译原理课程设计[M]. 北京:机械工业出版社,2005.。