熟悉Parse Generator

/*Powered by Keamou@CS@CITS@NKU */

1.         將所有的詞法分析功能均放在yygettoken函數內實現,爲+-*/()每個運算符及整數分別定義一個單詞類別,在yygettoken內實現代碼,能識別這些單詞,並將單詞類別返回給詞法分析程序。

答:

定義單詞類別定義如下:

 

記號

類別名

+

ADD

-

SUB

*

MUL

/

DIV

(

LE

)

RE

數字

NUMBER

字母

LETTER

文件尾

FEND

 

Yacc程序獲得輸入的字符是通過int yygettoken(void) 函數獲得的,因此可以在int yygettoken(void)函數裏先分析輸入字符,根據上表返回相應的類別名。

符號定義段裏:

%token NUMBER    

%token ADD

%token SUB

%token MUL

%token DIV

%token LETTER

%token LE      //左括號

%token RE      //右括號

%token FEND    //文件尾標誌

 

%left ADD SUB

%left MUL DIV

%right UMINUS

因此,例如,中綴變後綴的語法定義段程序爲:

lines     :      lines expr     FEND { fprintf(fc, "Here is the postfix expr : %s/n", $2); printf("/nHere is the postfix expr : %s/n", $2);  exit(1); }

             |     lines FEND       { exit(1); }

             |

             ;

 

expr      :      expr ADD expr    { strcpy($$, $1); strcat($$, $3); strcat($$,"+"); }

             |     expr SUB expr     { strcpy($$, $1); strcat($$, $3); strcat($$,"-"); }

             |     expr MUL expr    { strcpy($$, $1); strcat($$, $3); strcat($$,"*"); }

             |     expr DIV expr      { strcpy($$, $1); strcat($$, $3); strcat($$,"/"); }

             |     LE expr RE   { strcpy($$, $2); }

             |     SUB expr %prec UMINUS       { strcpy($$, $2); strcat($$,"-"); }

             |   alpha           { strrev( $$ );   }    //符號逆轉

             |     digit           { strrev( $$ );   }    //符號逆轉

             ;

 

alpha:       LETTER             { sprintf($$,"%c",pchar); }

        |    alpha  LETTER      { sprintf($$,"%s%c",$1,pchar); }

        ;

 

digit   :   NUMBER             { itoa( num - '0' ,$$ ,10 ); }

        |   digit NUMBER       { itoa( num - '0' ,$$ ,10 ); strcat($$ , $1); }

        ;

int yygettoken(void) 裏識別詞素並返回詞素類別,代碼如下:

int yygettoken(void)

{

      // place your token retrieving code here

      int token;

      token = fgetc(fp);

      while ( token == ' ' || token == '/n' || token == '/t' )  //如果是空格、回車、製表符,則再讀取文件的下一個字符

      {

           token = fgetc(fp);

      }

      if(feof(fp))          //如果到了文件尾,則返回符號FEND

      {

          return FEND ;

      }

      printf("%c",token);   //在屏幕上輸出從文件讀出的非空白符的字符

      switch(token)

      {

         case '+':

              return ADD;

              break;

         case '-':

              return SUB;

              break;

         case '*':

              return MUL;

              break;

         case '/':

              return DIV;

              break;

         case '(':

              return LE;

              break;

         case ')':

              return RE;

              break;

         case '0':

         case '1':

         case '2':

         case '3':

         case '4':

         case '5':

         case '6':

         case '7':

         case '8':

         case '9':

              num = token ;

              return NUMBER;

              break;

         default:

              if( ( token >= 'a' && token <= 'z' ) || ( token >= 'A' && token <= 'Z' ) )

              {

                  pchar = token ;

                  return LETTER;

              }

              else

                  return token;

      }

}

2.         實現功能更強的詞法分析程序,可識別並忽略空格、製表符、回車等空白符,能識別多位十進制整數。

答:

修改int yygettoken(void)函數以支持忽略空格、製表符、回車:

int yygettoken(void)

{

       int token;

       token = getchar();

       while(token == ' ' || token == '/n' || token == '/t' )

          token = getchar();

       return token;

}

在語法規則段裏添加如下代碼可實現多位十進制整數的算術運算

DIGIT   :   NUMBER            { $$ = $1; }

        |   NUMBER DIGIT      { $$ = $1 * 10 + $2;}

        ;

若添加如下代碼則可實現多位十進制整數的字符串表示:

digit   :    NUMBER            { strcpy($$, $1); }

        |   NUMBER digit       { strcpy($$, $1); strcat($$, $2);}

        |

        ;

 

3.         修改Yacc程序,不進行表達式的計算,而是實現中綴表達式到後綴表達式的轉換。

答:

由於老師已經把實現中綴表達式到後綴表達式的轉換的Yacc程序上傳了,爲了使本題更有趣,我將上面兩所實現功能都在本題裏體現

除此之外,爲了體驗在Yacc裏的文件操作以及輸入的方便,我還實現了文件讀寫的功能。用fp指針打開輸入文件,讀取expr.txt文件(此文件儲存要轉換的中綴表達式),用fc打開輸出文件,結果把轉換後的後綴表達式儲存在postfix.txt文件中。只所以不採用main(argc,argv[])的方法是因爲這樣比較方便,不用多次輸入文件名。

同時,也將輸入和輸出的內容在屏幕上顯示,以隨時便捷地驗證結果的正確。

 

(思考)實現符號表功能。

答:

通過定義結構將遇到的符號和其值添加到符號表中

struct tokenlist        //符號表結構

{

    char token[30];

    float value;

    struct tokenlist * next ;

}*head,*temp;

當遇到一個符號時,先判斷是否已經在符號表裏,在將其值添加或修改。如果是數字,則直接存儲其值,如果是字母,若尚無存儲,則初值爲0。代碼如下:

void storetoken(char* tn, float tv )

{

    temp = head ;

    while (temp != NULL )    //首先判斷一下要存儲的符號是否已經在符號表裏

    {

        if( strcmp ( temp->token , tn ) == 0 )

        {

           temp->value = tv ;

           return;

        }

        temp = temp->next ;

    }

    temp = head ;

    head = (struct tokenlist*)malloc(sizeof (struct tokenlist));

    strcpy(head->token , tn) ;

    head->value = tv ;

    head->next = temp;

}   

爲了從符號表裏取出符號的值,編寫了如下代碼:

float findtoken(char* tn)

{

    temp = head ;

    while (temp != NULL )

    {

        if( strcmp ( temp->token , tn ) == 0 )

           return temp->value ;

        temp = temp->next ;

    }

    return 0 ;

}

爲了能顯示符號表的內容,編寫了如下代碼:

void displaylist()

{

    temp = head ;

    printf("---------The Token List-----------/n");

    while ( temp != NULL )

    {

      printf("%s/t%lf/n",temp->token,temp->value);

      temp = temp->next ;

    }

    printf("--------------End-----------------/n");

}

在編譯的過程中,實現無用的符號從符號表裏刪除

void deletetoken(char* tn)

{

    temp = head ;

    if( temp != NULL )

       if ( strcmp ( temp->token , tn ) == 0 )

       {

           head = temp->next ;

           free( temp ) ;

           return;

       }

    while ( temp->next != NULL )

    {

         if ( strcmp ( temp->next->token , tn ) == 0 )

         {

              struct tokenlist * othertmp = temp->next ;

              temp->next = temp->next->next ;

              free( othertmp ) ;

              return;

         }

         temp = temp->next ;

    }

    return;

}

發佈了27 篇原創文章 · 獲贊 1 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章