1. 程式人生 > >【編譯原理】Lex詞法分析器

【編譯原理】Lex詞法分析器

一、實驗目的

設計並實現一個詞法分析器,深刻理解編譯原理中詞法分析器的原理。

二、實驗內容

通過使用自己熟悉的語言設計並實現一個詞法分析器,是此法分析器按要求的格式輸出經過分析的程式段。

要求分析一下程式片段:

  1. const a=10;  
  2. var b,c;  
  3. procedure p;  
  4.   begin
  5.     c:=b+a;  
  6.   end;  
  7. begin
  8.  read(b);  
  9.  while b#0do
  10.   begin
  11.    call p;writeln(2*c);read(b);  
  12.   end
  13. end.  

該程式片段存放在example.txt中。

三、實驗步驟

 1、對PL\0文法中各類單詞分類:

  (1)保留字:const、var、procedure、begin、end、if、then、while、do、read、call、write、writeln

  (2)常數:由0-----9這幾個數字組成

  (3)識別符號:由字母打頭的字母和數字的字串

  (4)運算子:+,-,*,/,:=,>=,<=,#

  (5)分界符:,、.、(、)、;

  2、將各類單詞對應到lex中:

  (1)保留字:

reservedWord [const|var|procedure|begin|end|if|then|while|do|read|call|write|writeln],由於在lex中不區分大小寫,所以將保留字寫成:

reservedWord [cC][oO][nN][sS][tT]|[vV][aA][rR]|[pP][rR][oO][cC][eE][dD][uU][rR][eE]|

[bB][eE][gG][iI][nN]|[eE][nN][dD]|[iI][fF]|[tT][hH][eE][nN]|[wW][hH][iI]

[lL][eE]|[dD][oO]|[rR][eE][aA][dD]|[cC][aA][lL][lL]|[wW][rR][iI][tT][eE]

[wW][rR][iI][tT][eE][lL][nN]

  (2)常數:

constant ([0-9])+  /*0---9這幾個數字可以重複*/

  (3)識別符號:

      identfier [A-Za-z]([A-Za-z][0-9])*

  (4)運算子:

      operator \+|-|\*|\/|:=|>=|<=|#|=  /*在lex中,有特殊意義的運算子要加轉義字元\,如+、*及*、/

  (5)分界符:

      delimiter [,\.;\(\)]

  3、PL/0的語言的詞法分析器要求跳過分隔符(如空格,回車,製表符),對應的lex定義為:

     delim [""\n\t]

whitespace{delim}+

  4、為lex制定一些規則:

     {reservedWord}{count++;printf("\t%d\t(1,‘%s’)\n",count,yytext);}   /*對保留字定規則,輸出設定*/

     {operator} {count++;printf("\t%d\t(2,‘%s’)\n",count,yytext); }

     {delimiter}{count++;printf("\t%d\t(3,‘%s’)\n",count,yytext);}

{constant}{count++;printf("\t%d\t(4,‘%s’)\n",count,yytext);}

{identfier}{count++;printf("\t%d\t(5,‘%s’)\n",count,yytext);}

{whitespace} {/* do    nothing*/ }         /*遇到空格,什麼都不做*/

  5、寫子程式:

   voidmain()

{

   printf("詞法分析器輸出型別說明:\n");

         printf("1:保留字\n");

         printf("2:運算子\n");

         printf("3:分界符\n");

         printf("4:常  數\n");

         printf("5:識別符號\n");

         printf("\n");

         yyin=fopen("example.txt","r");

             yylex(); /* start the analysis*/

         fclose(yyin);

         system("PAUSE");/*暫停*/

}

 intyywrap()

 {

        return 1;

}

當lex 讀完輸入檔案之後就會呼叫函式 yywrap 。如果返回1 表示程式的工作已經完成了,否則,返回 0。

四、實現的原始碼

  1. %{  
  2.     #include <stdio.h>
  3.     #include <stdlib.h> 
  4.     int count = 0;  
  5. %}   
  6. delim [" "\n\t]   
  7. whitespace {delim}+   
  8. operator \+|-|\*|\/|:=|>=|<=|#|=  
  9. reservedWord [cC][oO][nN][sS][tT]|[vV][aA][rR]|[pP][rR][oO][cC][eE][dD][uU][rR][eE]|[bB][eE][gG][iI][nN]|[eE][nN][dD]|[iI][fF]|[tT][hH][eE][nN]|[wW][hH][iI][lL][eE]|[dD][oO]|[rR][eE][aA][dD]|[cC][aA][lL][lL]|[wW][rR][iI][tT][eE]|[wW][rR][iI][tT][eE][lL][nN]  
  10. delimiter [,\.;\(\)]  
  11. constant ([0-9])+  
  12. identfier [A-Za-z]([A-Za-z][0-9])*  
  13. %%   
  14. {reservedWord} {count++;printf("%d\t(1,‘%s’)\n",count,yytext);}  
  15. {operator} { count++;printf("%d\t(2,‘%s’)\n",count,yytext); }  
  16. {delimiter} {count++;printf("%d\t(3,‘%s’)\n",count,yytext);}  
  17. {constant} {count++;printf("%d\t(4,‘%s’)\n",count,yytext);}  
  18. {identfier} {count++;printf("%d\t(5,‘%s’)\n",count,yytext);}   
  19. {whitespace} { /* do    nothing*/ }   
  20. %%   
  21. void main()   
  22. {  
  23.     printf("詞法分析器輸出型別說明:\n");  
  24.     printf("1:保留字\n");  
  25.     printf("2:運算子\n");  
  26.     printf("3:分界符\n");  
  27.     printf("4:常  數\n");  
  28.     printf("5:識別符號\n");  
  29.     printf("\n");  
  30.     yyin=fopen("example.txt","r");   
  31.         yylex(); /* start the analysis*/
  32.     fclose(yyin);  
  33.     system("PAUSE");/*暫停停,  使DOS視窗停住*/
  34. }   
  35.  int yywrap()   
  36.  {   
  37.     return 1;   
  38.  }   

五、實操作

將原始碼儲存為a.l,然後用flex(Lex)進行編譯,輸入flex a.l,編譯後生成lex.yy.c檔案,用c編譯器開啟(確保example.txt儲存在相同目錄下),編譯執行即可!

六、執行結果


Lex與yacc確實是很不錯的工具!如果單用程式語言,如C,C++,Java寫的話,程式碼就多了不少。