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 ;
}`
複製程式碼