您的位置:首页 > 其它

利用Yacc生成LR语法分析器的关键点…

2014-02-24 15:03 155 查看
  实际上利用Yacc生成LR语法分析器(一般是Look
ahead分析方法)还是比较简单的,而且写一个文法满足Yacc的要求也非常容易,主要的工作量体现在一个产生式被识别后的动作如何编写,尤其是有时候需要在一个产生式中嵌入一个动作,这个动作产生的时机是什么,产生这个动作时当前的token是什么一定要非常清楚,通过这几天的研究,基本上对这个问题已经有了一个了解,这里做一些总结。

 
首先,Yacc中判断如果DFA的当前状态中的某一项S->a.发生归约时,执行产生式S->a后面带的动作。但是S->a.归约发生前可能需要获得下一个token,也有可能不需要获得,这个要具体情况具体分析。另外对于嵌入动作,Yacc会在产生式中插入一个新的非终结符E,其中E->空,当E->空.归约发生时,嵌入动作执行。

例1:

%{

#include <ctype.h>

#include <stdio.h>

#define YYSTYPE double

int c;

%}

%token NUMBER

%%

lines    :
expr           
{ printf("%d\n", $1); }

       
;

expr   
: expr{printf("a%c\n",c);} '+'
NUMBER               
{ $$ = $1 + $4; }

       
| expr {printf("b%c\n",c);}'-'
NUMBER                
{ $$ = $1 - $4; }

       
| NUMBER{printf("c%c\n",c);}

       
;

%%

int main(void)

{

    return
yyparse();

}

int yylex(void)

{

    while ((c =
getchar()) == ' ');

    if
(isdigit(c)) {

       
ungetc(c, stdin);

       
scanf("%d", &yylval);

       
return NUMBER;

    }

   
if(c=='\n')

    {

    c='$';

    return
0;

    }

    return
c;

}

如果输入:2

输出结果如下:

2

c2

2

也就是说在执行printf("c%c\n",c);}时,当前token是2,这是因为在当前状态下只有一个exp->number.可以执行归约,因为不需要获得下一个token.

例2:

expr   
: NUMBER{printf("a%c\n",c);} '+'
NUMBER               
{ $$ = $1 + $4; }

       
| NUMBER {printf("b%c\n",c);}'-'
NUMBER                
{ $$ = $1 - $4; }

       
| NUMBER{printf("c%c\n",c);}

       
;

如果输入:2

输出结果如下:

2

c$

2

也就是说在执行也就是说在执行printf("c%c\n",c);}时,当前token是$,这是因为当前状态下除了exp->number.可以以执行归约之外,还有E1->空(第一个嵌入动作)和E2->空(第二个嵌入动作)也都可以执行归约,到底需要执行哪个归约需要看下一个token。

例3:

expr   
: NUMBER{printf("a%c\n",c);} '+'
NUMBER               
{ printf("aa%c\n",c);$$ = $1 + $4; }

       
| NUMBER {printf("b%c\n",c);}'-'
NUMBER                
{ $$ = $1 - $4; }

       
| NUMBER{printf("c%c\n",c);}

如果输入:2+3

输出结果如下:

2+3

a+

aa3

5

从上可以看出,在执行printf("a%c\n",c);}时,当前token是+,这是因为当前状态下有E1->空(第一个嵌入动作)和E2->空(第二个嵌入动作)都可以执行归约,到底需要执行哪个归约需要看下一个token,如果是+号则需要执行第一个嵌入动作,如果是-号则需要执行第二个嵌入动作。至于执行printf("aa%c\n",c)时,当前token是3原因也很简单,那就是当前状态下只有exp->NUMBER
E1 '+' NUMBER 可以执行归约。

例4:

expr   
: NUMBER{printf("a%c\n",c);} '+'
NUMBER               
{ printf("aa%c\n",c);$$ = $1 + $4; }

如果输入:2+3

输出结果如下:

2+3

a2

aa3

5

从上可以看出,在执行printf("a%c\n",c);}时,当前token是2,这是因为当前状态下只有E1->空(第一个嵌入动作)可以执行归约。

例5:

expr   
: ID{printf("a%c\n",c);} '+'
ID               
{ printf("aa%c\n",c);$$ = $1 + $4; }

       
;

ID     
: NUMBER{printf("c%c\n",c);$$=$1;}

如果输入:2+3

输出结果如下:

2+3

c2  //执行printf("c%c\n",c);}时,当前token是2

a2 //执行printf("a%c\n",c);}时,当前token是2

c3

aa3

5

例6:

expr   
: ID{printf("a%c\n",c);} '+'
ID               
{ printf("aa%c\n",c);$$ = $1 + $4; }

       
| ID{printf("b%c\n",c);} '-'
ID               
{ $$ = $1 - $4; }

       
;

ID     
: NUMBER{printf("c%c\n",c);$$=$1;}

如果输入:2+3

输出结果如下:

2+3

c2 ///执行printf("c%c\n",c);}时,当前token是2

a+  //执行printf("a%c\n",c);}时,当前token是+

c3

aa3

5
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: