递归下降法的语法分析器-3.1-编译原理
递归下降法的语法分析器
一、实验目的
掌握语法分析器的构造原理,掌握递归下降法的编程方法。
二、实验内容
用递归下降法编写一个语法分析程序,使之与词法分析器结合,能够根据语言的上下文无关文法,识别输入的单词序列是否文法的句子。(注意,需要改写文法,消除左递归等)
为减轻实验编程负担,这里只要求实现部分产生式,文法的开始符号为program。(完整的源语言的文法定义见教材附录 A.1,p394)
program → block
block → { stmts }
stmts → stmt stmts | e
stmt → id= expr ;
| if ( bool ) stmt
| if ( bool) stmt else stmt
| while (bool) stmt
| do stmt while (bool ) ;
| break ;
| block
bool → expr < expr
| expr <= expr
| expr > expr
| expr >= expr
| expr
expr → expr + term
| expr - term
| term
term → term * factor
| term / factor
| factor
factor → ( expr ) | id| num
三、实验要求
1.个人完成,提交实验报告。
2.实验报告中给出采用测试源代码片断,及其对应的最左推导过程(形式可以自行考虑)。
例如,程序片断
{
i = 2;
while (i <=100)
{
sum = sum + i;
i = i + 2;
}
}
对应的推导过程为:
program Þ block
Þ { stmts }
Þ { stmtstmts }
Þ { id= expr ; stmts }
Þ { id= num; stmts }
Þ { id= num; stmt stmts }
Þ { id= num; while (bool) stmt stmts }
Þ { id= num; while (expr<= expr) stmt stmts }
Þ { id= num; while (id<= expr) stmt stmts }
Þ { id= num; while (id<= num) stmt stmts }
Þ { id= num; while (id<= num) block stmts }
Þ { id= num; while (id<= num) { stmts }stmts }
Þ .......
四、实验指导
- 1. 词法分析器的说明
可以使用“实验2 用FLEX实现词法分析器”的生成的代码,也可以使用自己编写的词法分析器,来获取词法单元(token)。如果使用FLEX的话,每次调用一次yylex()函数就可以获取一个词法单元。
- 2. 左递归和公共左因子的处理
上述文法存在左递归和公共左因子,需要考虑改造文法。消除直接左递归参见教材p123。提取公共左因子参见教材p125。
- 3. 预测分析器
假设已经将上述文法改造为LL(1)文法,可采用带预测的递归下降进行语法分析。预测分析器的伪代码可参见教材p40。
- 4. if语句二义性的处理
上述文法中的if语句具有二义性。可以有不同的处理方式:
(1) 第一种方法是改写文法。
stmt ® matched _stmt | unmatched_stmt
matched_stmt ® if ( bool ) matched_stmt else matched_stmt | 其它语句
unmatched_stmt ® if ( bool ) stmt | if ( bool ) matched_stmt else unmatched_stmt
这种方法要多引进两个非终结符matched_stmt和unmatched_stmt,就要多编写两个函数,但编写这两个函数不会花太多时间。
(2) 第二种方法是对产生式加限制。
为了适合LL(1)文法的需要,我们首先对stmt → if ( bool ) stmt | if ( bool ) stmt else stmt提取左公因子,结果如下:
stmt → if ( bool ) stmt else_part | 其它语句
else_part → else stmt | e
但是,这样还是不能消除二义性,还是不能确定该选择else_part → else stmt | e中的哪个产生式,因为First(else stmt ) ÇFollow(else_part)= { else }。我们通过加限制的方法来确定该选哪个产生式:如果lookahead是词法单元else时,使用产生式else_part → else stmt;否则使用产生式else_part → e。
实际上,因为产生式stmt → if ( bool ) stmt | if ( bool ) stmt else stmt比较特殊(一个产生式的右部是另一个产生式的右部的前缀),所以无需提取左公因子。具体处理如下:当匹配完 if ( bool ) stmt 后,继续读入一个词法单元(token),此时:(a)如果该词法单元是ELSE_TOKEN,那么就继续匹配,也就是说,此时使用产生式stmt ® if ( bool ) stmt else stmt。(b)如果该词法单元不是ELSE_TOKEN,那么就将该词法单元回退(retract)给词法分析器,也就是说,此时使用产生式stmt ® if ( bool ) stmt。
在这种处理下,能满足C语言的“else和最靠近的if结合”的规定,即该方法的效果和上一种方法的效果是一样的。
可见,在二义性文法的基础上加以某些限制(例如“如果有else,则继续匹配”),就可以去除一些我们不希望的语法树,从而只获得一棵语法树,这样就变成了无二义性的文法(对任一个合法的词法单元序列,只有一棵对应的语法树)。
#include<stdio.h> #include"yytex.h" void yytex(); void BeginCompileOneFile( const char * filename ); void EndCompileOneFile(void); int yylex(void); void Expr(); int token; int Lookahead; extern char *yytext; //yytext 也就是现在获得的字符。 void program(); void parse() {//要先lookahean // int token; // char filename[1000]; printf("请输入要编译的源程序文件名:"); //gets(filename); // BeginCompileOneFile( filename ); //当flex扫描到文件末尾,yylex函数返回0 // while( ( token = yylex() ) > 0 ) ; BeginCompileOneFile("a.txt"); Lookahead=yylex(); printf("program="); program();//函数调用 printf("\ngrogram end\n"); EndCompileOneFile(); //getchar(); } void match(int t) {//用来匹配字符 用已经定义的字符比如define if 500 如果match(if) 于是就是词法分析器获得了if 字符串。 //yylex为返回if字符串对应的500 是要return给yylex函数的。 yytext为if保存字符串的字符数组 yyval 是具体的符号, yylex是类符号 //match return后面的符号。 if(Lookahead==t) { Lookahead=yylex(); // printf(" %d ",Lookahead); } else printf("\nmatch() syntax error Lookahead:%d \n",Lookahead); } void block(); void program() { printf("\n program->block"); block(); } void stmts(); void block() { printf("\nblock->{ "); printf("stmts "); printf(" }\n"); match(LEFT_BIBRACKET); stmts(); match(RIGHT_BIBRACKET); return; } void stmt(); void stmts() { if(Lookahead==ID_TKN||Lookahead==IF_TKN||Lookahead==WHILE_TKN||Lookahead==DO_TKN||Lookahead==BREAK_TKN||Lookahead==LEFT_BIBRACKET)//first follow { printf(" stmts->stmt "); printf("stmts"); stmt(); stmts(); } else if(Lookahead==RIGHT_BIBRACKET) return; else printf(" stmts() error lookahead:%d\n",Lookahead); return; } void bool_(); void stmt() { switch(Lookahead) { case ID_TKN: printf("\nstmt->ID=expr;\n"); match(ID_TKN);match(RELOP_TKN);Expr();match(RELOP_TKN);break; case IF_TKN: printf("\nstmt->IF(bool) stmt \n"); match(IF_TKN);match(LEFT_BRACKET);bool_();match(RIGHT_BRACKET);stmt();break; case WHILE_TKN: printf("\nstmt->WHILE (bool) stmt \n"); match(WHILE_TKN);match(LEFT_BRACKET);bool_();match(RIGHT_BRACKET);stmt();break; case DO_TKN: { printf("\nstmt->DO stmt while(bool)\n"); stmt(); match(WHILE_TKN); match(LEFT_BRACKET); bool_(); match(RIGHT_BRACKET); match(RELOP_SENI); break; } case BREAK_TKN: printf("\nstmt->BREAK ;\n"); match(BREAK_TKN),match(RELOP_TKN);break; default: printf("stmt->block"); block(); } } void bool_() { Expr(); switch(Lookahead){ case RELOP_TKN: printf("bool_->RELOP_TKN "); match(RELOP_TKN);Expr();break; default: break; /* switch(Lookahead){ case RELOP_LT: printf("\nbool_->LT "); match(RELOP_LT);Expr();break; case RELOP_LE: printf("\nbool_->LE "); match(RELOP_LE);Expr();break; case RELOP_GT: printf("\nbool_->GT "); match(RELOP_GT);Expr();break; case RELOP_GE: printf("\nbool_->GE "); match(RELOP_GE);Expr();break; default: match(520); match(510);*/ } } /*void Expr() { Expr(); switch(Lookahead){ case RELOP_AD: ; case RELOP_AD: }*/ void Factor() { /*switch( Lookahead ) { case '(': match('('); Expr(); match(')');break; default: match(Lookahead);break; }*/ if(Lookahead=='(') { match('('); Expr(); match(')'); } if(Lookahead==ID_TKN) //其中id_tkn与num_tkn为looahead的值 如何取得具体的值num值呢 { // printf("id "); // printf("%s\n",yytext); match(Lookahead); } if(Lookahead==NUM_TKN) { //printf("num "); //printf("%s\n",yytext); match(Lookahead); } // match(Lookahead); } void Morefactors(); void Term() { Factor(); Morefactors(); } void Morefactors() { switch( Lookahead ) { case '*': match('*'); Factor(); putchar('*'); Morefactors(); // rest --> + term {print('+')} rest break; case '/': match('/'); Term(); putchar('/'); Morefactors(); // rest --> - term {print('-')} rest break; default: // rest --> 空 break; } } void Moreterms() { switch( Lookahead ) { case '+': match('+'); Term(); putchar('+'); Moreterms(); // rest --> + term {print('+')} rest break; case '-': match('-'); Term(); putchar('-'); Moreterms(); // rest --> - term {print('-')} rest break; default: // rest --> 空 break; } } void Expr() { Term(); Moreterms(); }