Cygwin環境的熟悉和lex的使用1
一. 目的:
熟悉cygwin環境的使用,學習使用lex寫簡單的詞法分析程式,會在cygwin環境下使用flex調試lex寫的程式。
二. 内容:
讀懂exam1.l和exam2.l兩個例子,使用cygwin下的flex工具将exam1.l和exam2.l編譯并調試通過。并且修改exam2.l,在其基礎上增加如下記号:
(1) 左右大小括号:{ } ( )
(2) 将關系算符改寫成C中的形式
(3) 分号、指派号:; =
(4) 關鍵字:if else
(5) 雙斜線表示的注釋://
(6) 算術運算符号:+ - * /
(7) 将辨別符改為可含有下劃線,并且可以以下劃線開頭
(8) 将注釋内容忽略
完善exam1.l中installID和installNum兩輔助函數的功能.
三.要求:
在cygwin下用flex和gcc工具将實驗調試通過,并寫出測試例測試正确性。
四.參考:
exam1.l 和exam2.l為測試例子。Test1.p和test2.p可分别作為兩個程式的測試用例。
五.源代碼:
1.l:
%{
#include <stdio.h>
#define LT 1
#define LE 2
#define GT 3
#define GE 4
#define EQ 5
#define NE 6
#define ADD 7
#define JIAN 8
#define CHEN 9
#define CHU 10
#define FUZHI 11
#define WHILE 18
#define DO 19
#define IF 11
#define ELSE 12
#define ID 20
#define NUMBER 21
#define RELOP 22
#define NEWLINE 23
#define ERRORCHAR 24
int yylval;
%}
delim [ \t \n]
string id
ws {delim}+
letter [A-Za-z]
digit [0-9]
id (_)*{letter}(_)*({letter}|{digit})*
number {digit}+(\.{digit}+)?(E[+-]?{digit}+)?
/* 狀态(或條件)定義可以定義在這裡
* INITIAL是一個預設的狀态,不需要定義
*/
%s COMMENT
%s ZHUSHI
%%
<INITIAL>"/*" {BEGIN COMMENT;ECHO;}
<COMMENT>"*/" {BEGIN INITIAL;ECHO;}
<COMMENT>.|\n {ECHO;}
<INITIAL>"\\" {BEGIN ZHUSHI;;}
<ZHUSHI>"\t" {BEGIN INITIAL;;}
<ZHUSHI>.|\n {;}
/* ECHO是一個宏,相當于 fprintf(yyout, "%s", yytext)*/
<INITIAL>{ws} {;}
<INITIAL>while {return (WHILE);}
<INITIAL>if {return (IF);}
<INITIAL>else {return (ELSE);}
<INITIAL>do {return (DO);}
<INITIAL>{id} {yylval = installID ();return (ID);}
<INITIAL>{number} {yylval = installNum ();return (NUMBER);}
<INITIAL>"<" {yylval = LT;return (RELOP);}
<INITIAL>"<=" {yylval = LE;return (RELOP);}
<INITIAL>"==" {yylval = EQ;return (RELOP);}
<INITIAL>"!=" {yylval = NE;return (RELOP);}
<INITIAL>">" {yylval = GT;return (RELOP);}
<INITIAL>">=" {yylval = GE;return (RELOP);}
<INITIAL>"{" {return (RELOP);}
<INITIAL>"}" {return (RELOP);}
<INITIAL>"(" {return (RELOP);}
<INITIAL>")" {return (RELOP);}
<INITIAL>";" {return (RELOP);}
<INITIAL>"+" {yylval = ADD;return (RELOP);}
<INITIAL>"-" {yylval = JIAN;return (RELOP);}
<INITIAL>"*" {yylval = CHEN;return (RELOP);}
<INITIAL>"/" {yylval = CHU;return (RELOP);}
<INITIAL>"=" {yylval = FUZHI;return (RELOP);}
<INITIAL>. {return ERRORCHAR;}
%%
int installID () {
/* 把詞法單元裝入符号表并傳回指針。*/
return ID;
}
int installNum () {
/* 類似上面的過程,但詞法單元不是辨別符而是數 */
return NUMBER;
}
int yywrap (){
return 1;
}
void writeout(int c){
switch(c){
case ERRORCHAR: fprintf(yyout, "(ERRORCHAR, \"%s\") ", yytext);break;
case RELOP: fprintf(yyout, "(RELOP, \"%s\") ", yytext);break;
case WHILE: fprintf(yyout, "(WHILE, \"%s\") ", yytext);break;
case DO: fprintf(yyout, "(DO, \"%s\") ", yytext);break;
case IF: fprintf(yyout, "(IF, \"%s\") ", yytext);break;
case ELSE: fprintf(yyout, "(ELSE, \"%s\") ", yytext);break;
case NUMBER: fprintf(yyout, "(NUM, \"%s\") ", yytext);break;
case ID: fprintf(yyout, "(ID, \"%s\") ", yytext);break;
case NEWLINE: fprintf(yyout, "\n");break;
default:break;
}
return;
}
int main (int argc, char ** argv){
int c,j=0;
if (argc>=2){
if ((yyin = fopen(argv[1], "r")) == NULL){
printf("Can't open file %s\n", argv[1]);
return 1;
}
if (argc>=3){
yyout=fopen(argv[2], "w");
}
}
while (c = yylex()){
writeout(c);
j++;
if (j%5 == 0) writeout(NEWLINE);
}
if(argc>=2){
fclose(yyin);
if (argc>=3) fclose(yyout);
}
return 0;
}<strong>
</strong>
Test.p:
while _a >= -1.2E-2 do b_x<=2 {if(c!=5)c==(d+e)*f; else c=(e-f)/2;}
六.結果及分析:
(WHILE,while),(ID,_a),( RELOP,>=),( NUM,-1.2E-2),(DO,do),(ID,b_x),( RELOP,<=), ( NUM,2), ( RELOP,{), ( RELOP,(), (ID,c), ( RELOP,!=), (NUM,5), ( RELOP,)), (ID,c), ( RELOP,==), ( RELOP,(), (ID,d), ( RELOP,+), (ID,e), ( RELOP,*), ( ID,f), ( RELOP,;), ( ELSE,else), (ID,c), ( RELOP,=), ( RELOP,(), (ID,e), ( RELOP,-), (ID,f), ( RELOP,)), ( RELOP,/), ( NUM,2), ( RELOP,;), (RELOP,})
詞法分析器從第一個字元開始掃描,将相應的字元與比對的類别輸出在螢幕上,忽略空格和換行字元,将注釋也忽略,能識别“;”和“=”。
七.附錄
exam1.l
/* 這是注釋的形式,與C中的/*...* /注釋相同。 */
/* 第一部分是定義、聲明部分。這部分内容可以為空。*/
%{
/* 寫在 %{...%}這對特殊括号内的内容會被直接拷貝到C檔案中。
*
* 這部分通常進行一些頭檔案聲明,變量(全局,外部)、常量
* 的定義,用C文法。
*
* %{和%}兩個符号都必須位于行首
*/
/* 下面定義了需要識别的記号名,如果和yacc聯合使用,這些記号名都應該在yacc中定義 */
#include <stdio.h>
#define LT 1
#define LE 2
#define GT 3
#define GE 4
#define EQ 5
#define NE 6
#define WHILE 18
#define DO 19
#define ID 20
#define NUMBER 21
#define RELOP 22
#define NEWLINE 23
#define ERRORCHAR 24
int yylval;
/* yylval 是yacc中定義的變量,用來儲存記号的屬性值,預設是int類型。
* 在用lex實作的詞法分析器中可以使用這個變量将記号的屬性傳遞給用
* yacc實作的文法分析器。
*
* 注意:該變量隻有在聯合使用lex和yacc編寫詞法和文法分析器時才可在lex
* 中使用,此時該變量不需要定義即可使用。
* 單獨使用lex時,編譯器找不到這個變量。這裡定義該變量為了“欺騙”編譯器。
*/
%}
/* 這裡進行正規定義和狀态定義。
* 下面就是正規定義,注意,正規定義和狀态定義都要頂着行首寫。
*/
delim [ \t \n]
/* \用來表示轉義,例如\t表示制表符,\n表示換行符。*/
ws {delim}+
letter [A-Za-z_]
digit [0-9]
id {letter}({letter}|{digit})*
/* 注意:上面正規定義中出現的小括号表示分組,而不是被比對的字元。
* 而大括号括起的部分表示正規定義名。
*/
number {digit}+(\.{digit}+)?(E[+-]?{digit}+)?
/* %%作為lex檔案三個部分的分割符,必須位于行首 */
/* 下面這個%%不能省略 */
%%
/* 第二部分是翻譯規則部分。 */
/* 寫在這一部分的注釋要有前導空格,否則lex編譯出錯。*/
/* 翻譯規則的形式是:正規式 {動作}
* 其中,正規式要頂行首寫,動作要以C文法寫(動作會被拷貝到yylex()函數中,),\
* 正規式和動作之間要用空白分割。
*/
{ws} {;/* 此時詞法分析器沒有動作,也不傳回,而是繼續分析。 */}
/* 正規式部分用大括号擴住的表示正規定義名,例如{ws}。
* 沒有擴住的直接表示正規式本身。
* 一些元字元沒辦法表示它本身,此時可以用轉義字元或
* 用雙引号括起來,例如"<"
*/
while {return (WHILE);}
do {return (DO);}
{id} {yylval = installID (); return (ID);}
{number} {yylval = installNum (); return (NUMBER);}
"<" {yylval = LT; return (RELOP);}
"<=" {yylval = LE; return (RELOP);}
"=" {yylval = EQ; return (RELOP);}
"<>" {yylval = NE; return (RELOP);}
">" {yylval = GT; return (RELOP);}
">=" {yylval = GE; return (RELOP);}
. {yylval = ERRORCHAR; return ERRORCHAR;}
/*.比對除換行之外的任何字元,一般可作為最後一條翻譯規則。*/
%%
/* 第三部分是輔助函數部分,這部分内容以及前面的%%都可以省略 */
/* 輔助函數可以定義“動作”中使用的一些函數。這些函數
* 使用C語言編寫,并會直接被拷貝到lex.yy.c中。
*/
int installID () {
/* 把詞法單元裝入符号表并傳回指針。*/
return ID;
}
int installNum () {
/* 類似上面的過程,但詞法單元不是辨別符而是數 */
return NUMBER;
}
/* yywrap這個輔助函數是詞法分析器遇到輸入檔案結尾時會調用的,用來決定下一步怎麼做:
* 若yywrap傳回0,則繼續掃描;傳回1,則詞法分析器傳回報告檔案已結束的0。
* lex庫中的标準yywrap程式就是傳回1,你也可以定義自己的yywrap。
*/
int yywrap (){
return 1;
}
void writeout(int c){
switch(c){
case ERRORCHAR: fprintf(yyout, "(ERRORCHAR, \"%s\") ", yytext);break;
case RELOP: fprintf(yyout, "(RELOP, \"%s\") ", yytext);break;
case WHILE: fprintf(yyout, "(WHILE, \"%s\") ", yytext);break;
case DO: fprintf(yyout, "(DO, \"%s\") ", yytext);break;
case NUMBER: fprintf(yyout, "(NUM, \"%s\") ", yytext);break;
case ID: fprintf(yyout, "(ID, \"%s\") ", yytext);break;
case NEWLINE: fprintf(yyout, "\n");break;
default:break;
}
return;
}
/* 輔助函數裡可以使用yytext和yyleng這些外部定義的變量。
* yytext指向輸入緩沖區目前詞法單元(lexeme)的第一個字元,
* yyleng給出該詞法單元的長度 */
/* 如果你的詞法分析器并不是作為文法分析器的子程式,
* 而是有自己的輸入輸出,你可以在這裡定義你的詞法
* 分析器的main函數,main函數裡可以調用yylex()
*/
int main (int argc, char ** argv){
int c,j=0;
if (argc>=2){
if ((yyin = fopen(argv[1], "r")) == NULL){
printf("Can't open file %s\n", argv[1]);
return 1;
}
if (argc>=3){
yyout=fopen(argv[2], "w");
}
}
/* yyin和yyout是lex中定義的輸入輸出檔案指針,它們指明了
* lex生成的詞法分析器從哪裡獲得輸入和輸出到哪裡。
* 預設:鍵盤輸入,螢幕輸出。
*/
while (c = yylex()){
writeout(c);
j++;
if (j%5 == 0) writeout(NEWLINE);
}
if(argc>=2){
fclose(yyin);
if (argc>=3) fclose(yyout);
}
return 0;
}
exam2.l
%{
#include <stdio.h>
#define LT 1
#define LE 2
#define GT 3
#define GE 4
#define EQ 5
#define NE 6
#define LLK 7
#define RLK 8
#define LBK 9
#define RBK 10
#define IF 11
#define ELSE 12
#define EQU 13
#define SEM 14
#define WHILE 18
#define DO 19
#define ID 20
#define NUMBER 21
#define RELOP 22
#define NEWLINE 23
#define ERRORCHAR 24
#define ADD 25
#define DEC 26
#define MUL 27
#define DIV 28
%}
delim [ \t \n]
ws {delim}+
letter [A-Za-z_]
digit [0-9]
id {letter}({letter}|{digit})*
number {digit}+(\.{digit}+)?(E[+-]?{digit}+)?
/* 狀态(或條件)定義可以定義在這裡
* INITIAL是一個預設的狀态,不需要定義
*/
%s COMMENT
%s COMMENT2
%%
<INITIAL>"/*" {BEGIN COMMENT;}
<COMMENT>"*/" {BEGIN INITIAL;}
<COMMENT>.|\n {;}
<INITIAL>"//" {BEGIN COMMENT2;}
<COMMENT2>\n {BEGIN INITIAL;}
<COMMENT2>. {;}
/* ECHO是一個宏,相當于 fprintf(yyout, "%s", yytext)*/
<INITIAL>{ws} {;}
<INITIAL>while {return (WHILE);}
<INITIAL>do {return (DO);}
<INITIAL>if {return (IF);}
<INITIAL>else {return (ELSE);}
<INITIAL>{id} {return (ID);}
<INITIAL>{number} {return (NUMBER);}
<INITIAL>"<" {return (RELOP);}
<INITIAL>"<=" {return (RELOP);}
<INITIAL>"=" {return (RELOP);}
<INITIAL>"!=" {return (RELOP);}
<INITIAL>">" {return (RELOP);}
<INITIAL>">=" {return (RELOP);}
<INITIAL>"(" {return (RELOP);}
<INITIAL>")" {return (RELOP);}
<INITIAL>"{" {return (RELOP);}
<INITIAL>"}" {return (RELOP);}
<INITIAL>"+" {return (RELOP);}
<INITIAL>"-" {return (RELOP);}
<INITIAL>"*" {return (RELOP);}
<INITIAL>"/" {return (RELOP);}
<INITIAL>";" {return (RELOP);}
<INITIAL>. {return ERRORCHAR;}
%%
int yywrap (){
return 1;
}
void writeout(int c){
switch(c){
case ERRORCHAR: fprintf(yyout, "(ERRORCHAR, \"%s\") ", yytext);break;
case RELOP: fprintf(yyout, "(RELOP, \"%s\") ", yytext);break;
case WHILE: fprintf(yyout, "(WHILE, \"%s\") ", yytext);break;
case DO: fprintf(yyout, "(DO, \"%s\") ", yytext);break;
case IF: fprintf(yyout, "(IF, \"%s\") ", yytext);break;
case ELSE: fprintf(yyout, "(ELSE, \"%s\") ", yytext);break;
case NUMBER: fprintf(yyout, "(NUM, \"%s\") ", yytext);break;
case ID: fprintf(yyout, "(ID, \"%s\") ", yytext);break;
case NEWLINE: fprintf(yyout, "\n");break;
default:break;
}
return;
}
int main (int argc, char ** argv){
int c,j=0;
if (argc>=2){
if ((yyin = fopen(argv[1], "r")) == NULL){
printf("Can't open file %s\n", argv[1]);
return 1;
}
if (argc>=3){
yyout=fopen(argv[2], "w");
}
}
while (c = yylex()){
writeout(c);
j++;
if (j%5 == 0) writeout(NEWLINE);
}
if(argc>=2){
fclose(yyin);
if (argc>=3) fclose(yyout);
}
return 0;
}