递归下降分析法实现LL(1)文法的语法分析器


   本文将就编译原理中比较常用的一个表达式文法,通过递归下降语法分析法来编写分析器。文中将为您提供如何通过FIRST、FOLLOW和SELECT集合来判断LL(1)方法,然后如何用递归下降语法分析法分析LL(1)方法的基本递归流程,以及如何用C语言来编程实现分析器。
 

题目: 编程识别由下列文法所定义的表达式的递归下降语法分析器。

                    EàE+T | E-T | T

                    TàT*F | T/F |F

                    Fà(E) | i

          输入:每行含一个表达式的文本文件。

                输出:分析成功或不成功信息。

       

解答:

 

1)分析            

a) ∵E=>E+T=>E+T*F=>E+T*(E)即有E=>E+T*(E)存在左递归。用直接改写法消除左递归,得到如下:

E  à TE’

E’  à +TE’ | −TE’|ε

T  à FT’

T’  à *FT’ | /FT’|ε

F  à (E) | i

b) 对于以上改进的方法。可得:

对于E’:         FIRST( E’ )=FIRST(+TE’)∪FIRST(-TE)∪{ε}={+,ε

对于T’:         FIRST( T’ )=FIRST(*FT’)∪FIRST(/FT)∪{ε}={*,ε

而且:         FIRST( E ) = FIRST( T ) = FIRST( F )=FIRST((E))∪FIRST(i)={(i }

由此我们容易得出各非终结符的FOLLOW集合如下:

FOLLOW( E )= { )#}

FOLLOW(E’)= FOLLOW(E)={ )#}

FOLLOW( T )= FIRST(E’)\ε∪FOLLOW(E’)={+)#}

FOLLOW( T’ ) = FOLLOW( T ) ={+)#}

FOLLOW( F )=FIRST(T’)\ε∪FOLLOW(T’)={*+)#}

由以上FOLLOW集可以我们可以得出SELECT集如下:

E        SELECTEàTE’=FIRST(TE’)=FIRST(T)={ (i }

E’       SELECTE’ à+TE’={ + } 

       SELECTE’ à −TE’={ − }

       SELECTE’ àε={ε)#}

T        SELECTTàFT’={(i}

T’       SELECTT’ à*FT’={ * }  

SELECTT’ à ∕FT’={ ∕ }

SELECTT’ àε={ε+)#}

F        SELECTFà(E) ={ ( }

SELECTFài={ i }

∴   SELECT(E’ à+TE’)∩SELECTE’ à −TE’∩SELECTE’ àε=F

SELECT(T’ à*FT’)∩SELECTT’ à ∕FT’∩SELECTT’ àε=F

SELECTFà(E) ∩SELECTFàiF

由上可知,有相同左部产生式的SELECT集合的交集为空,所以文法是LL1)文法。因此,转化后的文法可以用递归下降分析法作语法分析。

 

2)设计

这里采用递归下降分析法形象描述递归子程序。程序中将要用到的几个重要数据如下:

       一个全局变量ch,存放由文件输入得到的字符。

       一个函数宏READ(ch),实现读取文件中的字符。

       五个子函数:P(E)P(E’)P(T)P(T’)P(F)

程序主要的子函数模块流程图如下:

 

程序子模块图

 

3)程序代码如下

/************************************************************************

 *    文件名:ana.c

 *    文件描述:递归下降语法分析器。分析如下方法:

 *                                              E->E+T | E-T | T

 *                                              T->T*F | T/F |F

 *                                              F->(E) | i

 *                                              输入:每行含一个表达式的文本文件。

 *                                              输出:分析成功或不成功信息。

 *    创建人:余洪周 <nickhome@163.com>2006-12-8

 *    版本号:1.0

 ***********************************************************************/

 

#include

#include

 

#define READ(ch) ch=getc(fp)     /*宏:READ(ch)*/

 

char         ch;                               /*声明为全局变量*/

int            right=0;         

FILE       *fp;

 

struct struCH{

       char              ch;

       struct      struCH *next;

}struCH,*temp,*head,*shift;

/*head指向字符线性链表的头结点*/

/*shift指向动态建成的结点(游标)*/

 

void main(int argc,char *argv[]){

       void E ();        /* P(E) */

       void E1();       /* P(E')*/

       void T ();        /* P(T) */

       void T1();       /* P(T')*/

       void F ();        /* P(F) */

      

       int errnum=0,k=0,m=0,countchar=0,rownum;

       int charerr=0;  /*开关控制量*/

      

       /************************以只读方式打开文件*********************/        

       if((fp=fopen(argv[1],"r"))==NULL)           

       {

              printf("\n\tCan not open file %s,or not exist it!\n",argv[1]);

              exit(0);      /*文件不存在or打不开时,正常退出程序*/

       }

       else printf("\n\tSuccess open file: %s\n",argv[1]);       /*成功打开文件*/

             

       /******************遍历整个文件检测是否有非法字符********************/

      

       /*如果用while(!feof(fp))语言,将会多出一个字符

        *所以这里采用以下方法遍历整个文件检测其否有非法字符

*/  

       /*[1]计算文件中字符数量*/

       while(!feof(fp)){

              READ(ch);                   /*这里读取字符只是让文件指针往前移*/

              countchar++;                /*统计文件中的字符数(包括换行符及文件结束符)*/

       }

       rewind(fp);                /*fp文件指针重新指向文件头处,以备后面对文件的操作*/

       if(countchar==0){                /*空文件*/

              printf("\t%s is a blank file!\n",argv[1]);

              exit(0);                         /*正常退出本程序*/

       }    

       /*[2]开始遍历文件*/

       while(k<(countchar-1)){      /*加换行符后countchar仍多了一个,不知为何*/

              ch=getc(fp);          

              if(!(ch=='('||ch==')'||ch=='i'||ch=='+'||ch=='-'||ch=='*'||ch=='/'||ch=='#'||ch=='\n')){

                     charerr=1;errnum++;      /*charerror出错标记,errnum统计出错个数*/

              }

              k++;      

       }

       rewind(fp);            /*fp文件指针重新指向文件头处,以备后面的建链表操作*/

       if(charerr==1){      /*文件中有非法字符*/

              printf("\n\t%d Unindentify characters in file %s \n",errnum,argv[1]);

              exit(0);           /*正常退出本程序*/

       }

      

       /*******************非空且无非法字符,则进行识别操作*****************/

       for(rownum=1;m<(countchar-1);rownum++){    /*识别所有行,rownum记录行号*/

              /*初始变量及堆栈和*/

              right=1;

              /*初始存放待识别的表达式的线性链表头*/

              shift=malloc(sizeof(struCH));/**/

              shift->next=NULL;

              head=shift;

             

              /*读取一行形成线性链表*/

              READ(ch);putchar(ch);m++;

              while(ch!='\n'&&m<(countchar)){ /*行末or到文件尾。最后会读取文件结束符*/

                     /*读取ch,读取存入结点,这样每行便形成一个线性链表*/

                     temp=malloc(sizeof(struCH));

                     temp->ch=ch;

                     temp->next=NULL;

                     shift->next=temp;

                     shift=shift->next;

                     READ(ch);

                     if(m!=(countchar-1)) putchar(ch);  /*不输出最后一次读取的文件结束符*/

                     m++;

              }

              head=head->next;         /*消去第一个空头结点,并使head指向非空线性链表头*/

              shift=head;                           /*shift指向头结点,以便后面识别操作*/

              putchar('\n');

              E();                                      /*开始识别一行*/               

              if(shift->ch=='#'&&right)      /*正确提示:[文件名] Line [行号]:right expression!*/

                     printf("%s  Line %d:\t right expression!\n",argv[1],rownum);

              else                                      /*错误提示:[文件名] Line [行号]:error expression!*/

                     printf("%s  Line %d:\t error expression!\n",argv[1],rownum); 

              putchar('\n');

       }/*end for*/

       printf("Completed!\n");

       fclose(fp);      /*关闭文件*/

       exit(0);           /*正常结束程序*/

}

 

/*以下函数分别对应于子模块程序*/ 

 

void E(){

       T();

       E1();

}

 

void E1(){

       if(shift->ch=='+'||shift->ch=='-'){

              shift=shift->next;

              T();

              E1();

       }

       else{

              if(shift->ch=='#'||shift->ch==')')

                     return;

              else

                     right=0;

       }

}

 

void T(void){

              F();

              T1();

}

 

void T1(void){

       if(shift->ch=='*'||shift->ch=='/'){

              shift=shift->next;

              F();

              T1();

       }

       else{

              if(shift->ch!='#'&&shift->ch!=')'&&shift->ch!='+'&&shift->ch!='-')

                     right=0;   /*如果不是'#'or')'or'+'or'+'or'-'则出错*/

       }

}

 

void F(void){

       if(shift->ch=='i')

              shift=shift->next;

       else{

              if(shift->ch=='('){

                     shift=shift->next;

                     E();

                     if(shift->ch==')')

                            shift=shift->next;

                     else

                            right=0;

              }

              else

                     right=0;

       }

}

 

4)调试

1.编译:在Windows平台下,用Turbo C 2编译连接生成后ana.exe

2.输入表达式:在ana.exe程序同一目录下新建一文本文件(如:test.txt)。往文本文件中输入要识别的表达式,表达式以“#”结束,可输入多行。同一行“#”以后的内容在识别过种中将自动丢弃。如将以下内容存入test.txt文件中:

i-i#

#

i#

++i#

(i+i)*i#++

3.运行:打开Dos控制台,进入程序所在的目录(如C:\>)。

 输入:[程序名] [存放表达式的txt文件名],如:ana test.txt

以上test.txt文本实例运行的结果如图:

 

 

 

test.txt实例运行结果图

异常结果注释:

a)       文件不存在or打不开时,提示“Can not open file [filename],or not exist it!”正常退出。

b)      打开文件成功,提示:Success open file:[filename]

c)       若为空文件,提示:[filename] is a blank file! 并正常退出程序。

d)      先检测文件中是否含有非法字符,若有,则统计并输出其个数:[errnum] Unindentify characters in file [filename]

e)       程序将分析每行的表达式,输出每行执行的结果,如上图可知。

 

 



结束语

 

      文中代码部分也可以用其他语言实现,而思想基本上都是一样的,请读者自行实现。水平有限,时间仓促,有什么不足之处,敬请读者批评指正。

 

参考资料

 

关于题目的来源

   * 编译原理实验二 递归下降语法分析 华南热带农业大学 计算机科学与技术系

关于LL(1)分析方法

*
 何炎祥,编译原理,高等教育出版社,2004-8

 

关于指针线性链表代码实现思想

*
 黄记瑶,编译实验(二)递归下降语法分析,2005-11-9


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM