利用Yacc生成LR語法分析器的關鍵點…

  實際上利用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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章