编译原理语法分析试验报告.docx
《编译原理语法分析试验报告.docx》由会员分享,可在线阅读,更多相关《编译原理语法分析试验报告.docx(14页珍藏版)》请在冰点文库上搜索。
编译原理语法分析试验报告
二、语法分析
(一)实验题目
编写程序,实现对词法分析程序所提供的单词序列进行语法检查和结构分析。
(二)实验内容和要求
1.要求程序至少能分析的语言的内容有:
1)变量说明语句
2)赋值语句
3)条件转移语句
4)表达式(算术表达式和逻辑表达式)
5)循环语句
6)过程调用语句
2.此外要处理:
包括依据文法对句子进行分析;出错处理;输出结果的构造。
3.输入输出的格式:
输入:
单词文件(词法分析的结果)
输出:
语法成分列表或语法树(都用文件表示),错误文件(对于不合文法的句子)。
4.实现方法:
可以采用递归下降分析法,LL
(1)分析法,算符优先法或LR分析法的任何一种,也可以针对不同的句子采用不同的分析方法。
(三)实验分析与设计过程
1.待分析的C语言子集的语法:
该语法为一个缩减了的C语言文法,估计是整个C语言所有文法的60%(各种关键字的定义都和词法分析中的一样),具体的文法如下:
语法:
100:
program->declaiationjist
101:
declarationjist->declarationjistdeclarationdeclaration
102:
declaiation->vai_declaiation|fijn_declaration
103:
vai.declaration->type_specifierID;|tvpe_specifierID[NUM];
104:
typJsp亡cifki->mt|void|float|chai-|long|double|
105:
fun_declaration->type_specifierID(params)|compound_stmt
106:
paranis->params_list|void
107:
paramjist->paiam_list.paiam|paiam
108:
param->type-spectifierED|type_specifierLD[]
109:
compound_stmt->{locaLdeclaiationsstatementjist}
110:
locaLdeclarations->local_declarationsvar_declaration|empty
111:
statementjist->statementjiststatement|emptv
112:
statement->epiesion_stmt|conipound_stmt
selection.stmtiteration_stmt|retuin_stmt
113:
expression^stmt->expression;!
;
114:
selection_stmt->if{expressionjstatementif(expression)statementelsestatement
115:
iteration_stmt->wliile{expression)statement
116:
return_stmt->return;|returnexpression;
117:
expression->var=expression|siinple-expression
118:
var->ID|ID[expression]
119:
suuple_expression->
additive_expressionrelopadditive_expression|additive_expression
120:
relop-><=|<|>|>=|==|!
=
121:
additive_expression->additive_expressionaddopterm|term
122:
addop->+|-
123:
term->termmulopfactorfactor
124:
mulop->*|/
125:
factor->(expression)|var|call|NUM
126:
call->ID(aigs)
127:
args->aig_list|empty
128:
arg_list->arg_list,expression|expression
该文法满足了实验的要求,而且多了很多的内容,相当于一个小型的文法
说明:
把文法标号从100到128是为了程序中便于找到原来的文法。
2.实现方法的选择:
因为时间的问题,我选择了递归下降的方法进行开发。
3.消除左递归
因为递归下降要求文法中不能出现有左递归的产生式,因此必须把待分析的C语言子集的语法中带有左递归的都消除左递归。
其中产生式101、110、111、121、123、128均为左递归文法。
这些产生式消除左递归后的文法如下:
1)101消除左递归后的产生式分别为:
101:
declaration_list->declarationdeclaration」ist_zdg
135:
declaration_list_zdg->declarationdeclaration_list_zdg
empty
2)110消除左递归后的产生式分别为:
110:
local_declarations->empty1ocal_dec1arations_zdg
133:
local_declarations_zdg->
var_declarationlocal_declarations_zdg
empty
3)111消除左递归后的产生式分别为:
111:
statement_list->emptystatement_1ist_zdg
134:
statement_list_zdg->statementstatement_list_zdgempty
4)121消除左递归后的产生式分别为:
121:
additiv亡.expression->termadditive_expression_zdg
131:
additive_expression_zdg->addoptermadditive_expression_zdg
empty
5)123消除左递归后的产生式分别为:
123:
term->factorterm_zdg
132:
term_zdg->mulopfactorterm_zdgempy
6)128消除左递归后的产生式分别为:
128:
arg_list■>expressionarg_list_zdg
129:
arg_list_zdg->rexpressionarg_list_zdgempty
说明:
empty代表可以为空。
4.词法分析和语法分析的关系
由于语法分析要用到词法分析的结果,在词法分析中,我逐个得到单词,而语法分析的整个过程中都必须利用从词法分析中得到的每一个单词,因此从语法分析的入口点开始就必须利用token读取源C程序的第一个词,然后再语法分析的过程中,采取相关的措施,在匹配当前单词后继续利用token读取下一个单词。
5.递归下降的思想
递归下降的思想在于“递归”二字,对于每一个消除了左递归的产生式,通过为每一个产生式建立一个函数,函数名为相应的产生式的左部,然后对产生式的每一项进行展开,如果产生式的右部碰到一个非终结符,则调用这个非终结符的函数(该非终结符肯定是一个产生式的左部,为之建立一个函数),如果遇到的是一个非终结符,则调用match()方法(见下文),match方法除了匹配当前的非终结符之外,还调用了词法分析的token方法,读取下一个单词。
这里举一个例子來说明:
下面的产生式是一个关于变量定义的产生式:
vai_declaration->type^specifierID:
來说,编写的函数名为var_declaration的方法:
简单的表达如下:
publicvoidvar_declaration(){
type^specifiei();
ID();
match(SEMICOLON):
//SENflCOLON表示分号
}
其他的产生式的函数编写都跟这个var^declaration函数类似。
只是,但是还有的产生式有比这复杂的地方,在后面继续说明。
6.结果输出形式和错误处理
1)结果的输出形式
当碰到一个单词,我首先输出这个单词的类型,因为用到的是递归函数,对于每个产生式,当展开到产生式的末尾的时候,我就把反映该产生式的源代码(规约)都输出,当最后到达产生是式pegiam(即分析出了是一个程序),便输出整个程序,因此这种方法是一个反映了我语法分析过程的结果输出。
当遇到一个单词或者遇到了一个产生式的规约,便输出结果,方法为:
publicvoidsyiitaxPnnt(Strmgmessage,Strmgblk,mtn)其中参数message为:
当当前的输出为当前单词时,message为当前的词类型,当当前的输出为一个产生式的规约时,message为当前用于规约的产生式。
参数blk是为了方便结果输出的时候,在程序中间加上必要的空格。
参数11表示当前用于规约的产生式的项的个数,当知道个数后,便与把保存在栈顶的n个元素连接起來并且输出。
2)错误处理
因为语法分析的时候,源程序有很多的不符合语法的地方,因此就需要处理,更重要的是需要进行错误恢复,以便语法分析输出当前的错误后继续正确地进行分析。
这里我采用了栈的形式來进行错误恢复(见数据结构设计与建立)
7.数据结构(栈)
这里用到了栈,栈的运用在这里主要就是用于结果的输出和错误处理。
当分析一个产生式的时候,如果发现当前的字符匹配,则把当前的字符压入栈中,或者当前的不是一个字符,而是一个非终结符(一个函数),则因为该函数也进行了相应的处理,当函数结束的时候把一个字符串压在栈中,因此直接调用这个函数的时候己经实现了压栈的动作,然后在输出该非终结符所表示的源程序的时候,把在该非终结符的函数中压入栈中的内容合并成一个字符串输出后再压回栈顶。
实现了递归调用。
如果在该非终结符的函数展开过程中,出现的错误,也即当前的字符并不是产生式所需要的,那么便往栈中压入一个空字符,即做了错误恢复,从而继续进行分析。
个人感觉利用栈让我很好地解决了分析结果的输出和错误处理恢复。
&产生式函数的流程图
这个问题是我在语法分析中遇到的比较棘手的儿个问题之一,主要分成以下的几种情况。
1)产生式的右部只有一种情况,如产生式102、126等以126:
call->ED(args)
为例,说明我处理这种情况的处理方法:
因为产生式有右部的F1攻集为ID(即标识符),的程序流程图,见下面的流程图(3)
流程图(3)
2)产生式的右部有两个或者两个以上的规约产生式,
但是右部的这两个或者两个以上的产生式的Fust集并不相同,如产生式112、113等,其中还有复杂一点的情况,因为有的产生式的右部的Fust有很多,或者是Fust集的寻找比较困难,需要找到很多的产生式,这个属于Fust集的寻找,在这里就不详细描述了。
我以产生式112
statement->expiession_stmt|compound_stmt|selection_stmt
|iteiation_stmt|retum_stmt
为例来说明处理这种情况的产生式的方法,其中iteiation_stmt的Fiist集为WHILE,selection_stmt的Fust集为if,retuin_stmt的Fust集为RETURN,compound_stmt的Fust集为LH,expression_stmt|的First集为LB、SEMICOLON和ID,详细的流程图见下图流程图(4)
流程图(4)
3)产生式的右部有两个或者两个以上的规约产生式,但是右部的这两个或者两个以上的产生式的Fust集的第一个或者前几个都相同,如产生式103、108、116等,则在这种情况下,就不像前两种情况那么简单了,因为知道了当前的单词并不能知道到底使用哪个产生式來进行规约,因此当当前字符相同的时候,需要读下一个字符,如果下一个字符仍然相等,则继续往下读取,一直到遇到的字符可以区分是使用哪个产生式为止。
在这种情况下,就需要在第一个字符的时候,记住当前的字符的位置,以便在判断知道是使用哪个产生式的时候可以回退到函数刚开始的字符的位置,进而用正确的产生式进行展开。
下面以产生式116
116:
retum_stmt->return;|returnexpression;
为例來说明这种情况的产生式的处理方法,因为产生式右部的两个产生式的Fust集都为leUun,因此先记录当前的状态,判断下一个词,如果为分号,则用左边的产生式,如果为ID、NUMBER或LB(expiession的Fust集为LD、NUMBER或LB)则回退到当前字符为return的状态,用产生式:
retum_stmt->returnexpression;详细的分析见流程图(5)
流程图(5)
4)关于产生式中的empty的处理
消除了左递归之后的那些产生式中都有empty的现,empty代表可以为空。
因此在这种情况下,就往栈中压入一个空字符:
tlus.mystack.push("H);
5)其他的各种情况都在上述的几种情况之中,大同小异因此这里不再赘述。
9.语法分析的流程图
在把所有的产生式的函数都写好后,语法分析的过程就变得很简单了,因为程序的入口点就是函数p】ognim(),只要用token为其读取第一个单词即可进行以后的分析,以后的分析中在其他的各个函数中实现了单词的读取和结果的输出以及错误的恢复处理等。
流程图见流程图(6)o
流程图(6)
(四)实验编写代码和测试
1.代码的编写,由于递归的实现就是为每个函数建立一个函数,对不同的产生式类型用不同的方法进行编写,但由于逻辑的判断和转换比较多,因此很容易出错和导致思维的混乱。
由于用到的栈处理比较多,因此对栈的使用前先做了不少的测试。
2.程序的测试用例和结果输出,以及相应的错误恢复与错误显示。
1)正确的测试用例以及结果
测试用例:
voidniain(void)/*主函数*7{
intss[32];
if(a==b)
returna;elsereturnb;
wliile(a>=b)a=b+a;
\
结果显小:
分析结果显示>>>type_specifiervoidvoidparains->voidvoidtype_specifiermtiiitvai_declaration^type^specifierIDfNUNI];mtss[32];locaLdeclaiations_zdg->var_declarationlocal_declaiations_zdgemptymtss[32];
factor->vai-a
term->factorterm_zdga
additive_expression->termadditive_expression_zdgafactor->vai-b
term->factorterm_zdgb
additive_expression->termadditive_expression_zdgbsiinple_expression->additive_expressionrelopadditive^expressiona==bexpression->smiple_expiessiona=b
factor->vai-a
term->factorterm_zdgaadditive_expression->termadditive_expression_zdgasiinple_expression->additive_expressionaexpression->smiple_expressiona
retiiin^stint->returnexpression;returna;statement->return_stmtleturna;
selection_stmt->if(expression)statementif(a=b)returna;statement->selection_stmtif(a==b)returna;
factor->vai-a
term->factorterm_zdgaadditive_expression->termadditive_expression_zdgafactor->vai-b
term->factorterm_zdgb
additive_expression->termadditive_expression_zdgbsiinple_expression->additive_expressionrelopadditive^expressiona>=bexpression->smiple_expiessiona>=b
factor->vai-b
term->factorterm_zdgb
addop->++
factor->vai-a
term->factorterm_zdga
additive_expression_zdg->addoptermadditive_expression_zdg|empty+aadditive_expression->termadditive_expression_zdgb+asiinple_expression->additive_expressionb+aexpression->smiple_expressionb+aexpression->vai=expressiona=b+a
expression_stint->expression;a=b+a;statement->epresion_stmta=b+a;
iteration_stmt->while(expression)statementwhile(a>=b)a=b+a;statement->iteration_stmtwliile(a>=b)a=b+a;
statement_list_zdg->statementstatementjist_zdg|emptvwhile(a>=b)a=b+a;
statement_list_zdg->statementstatement_list_zdgemptyif(a==b)ren]ina;wliile(a>=b)a=b+a;statement_list->statement_list_zdgif(a=b)returna;wliile(a>=b)a=b+a;
compound_stint->{local_declaiationsstatementjist}{iiitss[32];
if(a=b)ieturna;wliile(a>=b)a=b+a;}
declaration->fiin_declarationvoidniaiii(void){iiitss[32];if(a==b)returna;wliile(a>=b)a=b+a;}declarationjist->declarationsdeclaration_list_zdgvoidmain()){mtss[32];if(a=b)returna;wliile(a>=b)a=b+a;}
program->declaration_listvoidmain(void){iiitss[32];if(a=b)returna;wliile(a>=b)a=b+a;}
2)测试用例中含有错误,错误显示含错误的测试用例:
intfiinction(param,ints[])/*参数没有定义
{paiain=paiaiii*paianV*失分号*/}intfunctioiiB(floatparam,ints[ddss])/*数组参数中含有非法字符*/
{
paiam=paiam*paiam;}
floatfunctionC(floatparan^ints[)/*数组参数s中缺少T*/
{
paiam=paiam*paiam;}
voidniam(void)
{
intaa;
if(aa>=bb)
{
dd=aa-bb;/*if语句缺少丁*7
else
return/*return语句缺少‘严/
wliile(aa{
aa=aa+bb;
function(a);
}
错误显示:
(输入语法分析阶段的错误不在这里显示)
第1行有语法错误!
>>参数param没有定义类型或者缺失类型声明!
第3行有语法错误!
>>:
・丢失!
第4行有语法错误!
>>数组参数s中卩中含有非法字符!
第4行有语法错误!
>>数组参数s中卩中含有太多的非法字符!
第7行有语法错误!
>>数组参数s中卩中含有非法字符!
第7行有语法错误!
>>数组参数s中缺失丁!
第16行有语法错误!
>>语句缺少丁!
第18行有语法错误!
>>return语句缺少:
!
第19行有语法错误!
>>wlule语句缺少)
说明:
如果有的错误是出现在词法分析中的,就会导致语法分析的错误不会太全面,或者会造成语法分析中的一些程序上的多次循环,但是不管怎么样,被分析的程序只要出现错误范围是违背了我的产生式的情况的,编译器都应该可以找出來的。