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

NO IMAGE

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 ;
}`

相關文章

編譯原理實驗2:語法分析

計算機網絡實驗4:鏈路層分析與組網

IntroductiontoAlgorithm

計算機網絡實驗五:虛擬局域網技術