編譯原理實驗1:詞法分析

URNOTJANET發表於2017-12-13

2017-3-6 實驗內容:用flex工具生成一個PL/0語言的詞法分析程式,對PL/0語言的源程式進行掃描,識別出單詞符號的類別,輸出各種符號的資訊 輸入:PL0源程式 輸出:把單詞符號分為下面五類,然後統計PL0源程式中各單詞符號出現的次數。 K類(關鍵字) I類(識別符號) C類(常量) P類(算符及界符) O類(其他)

實驗環境: 詞法分析器生成工具:flex 程式語言:C 除錯環境:VC

PL/0 語言簡介 PL/0語言是Pascal語言的子集 資料型別只有整型 識別符號的有效長度是10,以字母開始的字母數字串 數最多為14位 過程無參,可巢狀(最多三層),可遞迴呼叫 變數的作用域同PASCAL,常量為全域性的 語句型別: 賦值語句,if...then..., while...do..., read, write, call, 複合語句begin... end, 說明語句: const..., var..., procedure… 13個保留字: if, then, while, do, read, write, call, begin, end, const, var, procedure, odd

PL0語言的EBNF正規化 EBNF:可說明哪些符號序列是對於某給定語言在語法上有效的程式。

EBNF正規化的符號說明 < >:語法構造成分,為非終結符 ::= :該符號的左部由右部定義,讀作“定義為” | :或 { }:括號內的語法成分可重複 [ ]:括號內成分為任選項 ( ):圓括號內成分優先 <表示式> ::= [+|-]<項>{<加法運算子><項>} <項> ::= <因子>{<乘法運算子><因子>} <因子> ::= <識別符號>|<無符號整數>|’(‘<表示式>’)’ <加法運算子> ::= +|- <乘法運算子> ::= *|/ <關係運算子> ::= =|#|<|<=|>|>= <當型迴圈語句> ::= WHILE<條件>DO<語句> <過程呼叫語句> ::= CALL<識別符號> <讀語句> ::= READ’(‘<識別符號>{,<識別符號>}’)’ <寫語句> ::= WRITE’(‘<表示式>{,<表示式>}’)’ <字母> ::= a|b|...|X|Y|Z <數字> ::= 0|1|...|8|9

LEX源程式的格式 %{ 宣告 --可選 %} 輔助定義 --可選 %% 識別規則 --必須有 %% 使用者子程式 --可選

宣告 所有嵌在“%{”和“%}”之間的內容將被原樣拷貝到lex.yy.c檔案中。 在宣告中,可以引入標頭檔案、巨集定義以及全域性變數的定義。 例如: %{ #include <stdio.h> int num_ident, num_keyword; %}

輔助定義 輔助定義可以用一個名字代表一個正規式。 輔助定義的語法是:輔助定義名 正規式 注意:輔助定義必須從第一列寫起。 後面的輔助定義可以引用前面的輔助定義。 在正規式中,用“{輔助定義名}”可以引用相應的正規式。 例如: NEW_LINE (\n) INTEGER ([0-9]+) EXPONENT ([Ee][+-] {INTEGER})

識別規則 識別規則由兩部分組成:正規式和相應的動作。 正規式用於描述輸入串的詞法結構。 動作用於描述識別出某一個詞形時要完成的操作。 例如: %% void {return T_Void;}

LEX源程式舉例
%{ int num_lines = 0, num_chars = 0; %} %% \n {++num_lines; ++num_chars;} . {++num_chars;} %% main(){ yylex(); printf("# of lines = %d, # of chars = %d\n", num_lines, num_chars ); }

識別規則的二義性 有時輸入串中的字元可以與多條規則匹配,在這 種情況下,LEX有兩個處理原則: 能匹配最多字元的規則優先; 若各規則匹配的字元數目相同,先給出的規則優先。 例如,給定規則如下: void {return T_Void;} [A-Za-z]+ {return T_Identifier;} “void”將被識別為T_Void, “voida”將被識別為T_Identifier。

實驗結果: 編寫的C程式如下:

%{ #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX 20

/----對同類的不同內容建立結構體,方便統計和輸出---/ typedef struct { char* contest; int cnt; }Node;

/----對不同類的內容建立結構體---/ typedef struct { int num; Node seque[MAX]; }Type; Type Key,Ident,Const,Oper,Others; void insert(Type *p); void print_it(char name[],Type *p); void print(); %}

/-------輔助定義------/ whitespace ([ \t\n])+ keyword (if|then|while|do|read|write|call|begin|end|const|var|procedure|odd) id ([A-Za-z][A-Za-z0-9]*) numbers ([0-9])+ %% {whitespace} {} {keyword} { insert(&Key); } {id} { insert(&Ident); } {numbers} { insert(&Const); } "."|","|";"|":="|"="|"+"|"-"|"*"|"/"|"#"|">"|">="|"<"|"<="|"("|")"|"^" { insert(&Oper); } . { insert(&Others); } %%

//輸出 void print_it(char name[],Type *p) { int i; yyout = fopen("output.txt","a+"); fprintf(yyout,"Class %s:\n",name ); for(i=0;i<p->num;i++){ fprintf(yyout,"\t%d. %s\t %d\n",i+1,p->seque[i].contest,p->seque[i].cnt ); } fprintf(yyout,"\n" ); fclose(yyout); } void print() { print_it("keyword",&Key); print_it("id",&Ident); print_it("Const",&Const); print_it("Operation",&Oper); print_it("Others",&Others); } //判斷是否已經是該Type中的一員,並作相應操作

  int strange = 1;
  int i=0;
  if(p->num>=MAX) {
    printf("Error!\n");
    return;
  }
  if(strlen(yytext)>=MAX) {
    printf("Too long!\n");
    //return;
  }
  for( i=p->num-1;i>=0;--i ) {
    if(!strcmp(p->seque[i].contest,yytext)){
      ++p->seque[i].cnt;
      strange = 0;
      break;
    }
  if(strange){
    p->seque[p->num].contest = (char*)malloc(100*sizeof(char));
    strcpy(p->seque[p->num].contest,yytext);
    ++p->seque[p->num].cnt;
    ++p->num;
  }
  return;
}```
`int yywrap() {
  return 1;
}
`
`void main(int agrc, char* argv[])
{
  yyin = fopen(argv[1],"r");
  yylex();
  printf("-----Here is the output-------- \n");
  print();
  fclose(yyin);`
  `
  return ;
}`

複製程式碼

相關文章