。它的基本原理就是使用正則表達式掃描匹配文本,並為每壹個匹配模式定義壹些操作,當用C語言作宿主語言時,這些操作都由C語言實現。
壹種匹配的正則表達式可能會包含相關的動作。這壹動作可能還包括返回壹個標記。當 Lex 接收到文件或文本形式的輸入時,它試圖將文本與正則表達式進行匹配。它壹次讀入壹個輸入字符,直到找到壹個匹配的模式。如果能夠找到壹個匹配的模式,Lex 就執行相關的動作(可能包括返回壹個標記)。另壹方面,如果沒有可以匹配的正則表達式,將會停止進壹步的處理,Lex 將顯示壹個錯誤消息。
Lex 和 C 是強耦合的。壹個 .l 文件(Lex 文件具有 .l 的擴展名)通過 lex 公用程序來傳遞,並生成 C 的輸出文件。這些文件被編譯為詞法分析器的可執行版本。
本程序對java源程序進行分析,主要實現以下兩個功能:
(1)、清除註釋。java源程序有三種註釋方法:1、單行註釋,以//開頭直到行結束;2、多行註釋,以/*為開始,*/為結束,可以註釋多行;3、java文檔註釋,這也是壹種多行註釋,但它可以通過java文檔生成工具寫入java程序文檔中。它以/**為開始,*/為結束。
(2)、通過程序行數計算工作量。
(3)、計算程序中類的個數,並判斷有沒有兩個public類,如果存在則報錯:There is an error:One java file cannot includes two public class。
單行註釋的清除。由於單行註釋以//開頭直到行結束,首先要匹配的就是//,然後清除從匹配處到行結束的所有字符。具體實現如下:
"//" {
int c;
while ( (c = input()) != '/n' &&
c != EOF )
{
;
}
code = add(code,'/n');
}
多行註釋的清除。多行註釋有兩種,壹種是普通多行註釋,另壹種是java文檔註釋。這兩種註釋都以*/結束,普通多行註釋以/*開始,java文檔註釋以/**開始。可以先匹配/*,然後向後搜索*/。要區別這兩種註釋就要看/*後面是否緊跟壹個*字符,如果不是則為普通多行註釋;如果是還要看下壹字符是否為/字符,如果是也為普通註釋,如果不是則為java文檔註釋。具體lex程序實現如下:
"/*" {
int c,ct=0;
char * javadoc = "/*there is a Java Doc Comment*/";
for ( ; ; )
{
while ( (c = input()) != '*' &&
c != EOF )
{
ct++;
}
if ( c == '*' )
{
c = input();
if ( c == '/' )
{
ct = 0;
break; /* found the end */
}
else
{
if(ct==0)
code = strcat(code,javadoc);
}
}
if ( c == EOF )
{
printf( "EOF in comment" );
break;
}
}
}
my.l即為lex程序。輸入壹段帶有註釋的java源程序,然後打入結束標誌$號,回車就可以看到在輸出的程序中所有註釋都已經刪除,在含有java文檔註釋的地方加上了壹句註釋:/*there is a Java Doc Comment*/。
經過仔細研究發現,上面的實現過程還是過分依賴C語言,沒有真正發揮Lex模式匹配的強大功能。單行註釋、普通多行註釋、Java文檔註釋可分別由下列模式匹配:
////.*/n
///*[^/*//]*/*//
///*/*[^/*//]*/*//
本程序還提供了識別類定義的功能,匹配模式如下:
public[ /n/t]+class[ /n/t]+[a-zA-Z][_a-zA-z0-9]*/[ /n/t]*/{[^/]}*/}
(public|protected|private)[ /n/t]+class[ /n/t]+[a-zA-Z][_a-zA-z0-9]*/[ /n/t]*/{[^/]}*/}
[ /n/t]*class[ /n/t]+[a-zA-Z][_a-zA-z0-9]*/[ /n/t]*/{[^/]}*/}
經完善後的lex程序如下所示:
%{
#include <string.h>
char * code = "";
int codelines = 0;
int classnum = 0;
int pubclass = 0;
char * classes[4]={"","","",""};
/*add a char c to the string code*/
char * add(char * code,char c)
{
char * temp;
if(code==NULL)
return "";
temp = (char*)malloc(sizeof(char)*2);
temp[0] = c;
temp[1] = '/0';
temp = strcat(code,temp);
return temp;
}
%}
%%
///*[^/*//]*/*// code = add(code,'');
///*/*[^/*//]*/*// code = strcat(code,"/*there is a Java Doc Comment*//n");
////.*/n code = add(code,'/n');
public[ /n/t]+class[ /n/t]+[a-zA-Z][_a-zA-z0-9]*/[ /n/t]*/{[^/}]*/} {
classes[classnum] = (char*)malloc(100);
classes[classnum] = strcpy(classes[classnum],yytext);
classnum++;
code = strcat(code,yytext);
pubclass++;
}
(public|protected|private)[ /n/t]+class[ /n/t]+[a-zA-Z][_a-zA-z0-9]*/[ /n/t]*/{[^/}]*/} {
classes[classnum] = (char*)malloc(100);
classes[classnum] = strcpy(classes[classnum],yytext);
classnum++;
code = strcat(code,yytext);
}
[ /n/t]*class[ /n/t]+[a-zA-Z][_a-zA-z0-9]*/[ /n/t]*/{[^/}]*/} {
classes[classnum] = (char*)malloc(100);
classes[classnum] = strcpy(classes[classnum],yytext);
classnum++;
code = strcat(code,yytext);
}
/n code = add(code,'/n');
. {
if(yytext[0] == ';')
codelines++;
code = add(code,yytext[0]);
}
%%
yywrap()
{
int i=0;
printf("/nBelow is the code without comment:/n/n");
printf(code);
printf("/n/nConclude:/nThis code weights %d lines/n",codelines);
printf("This code includes %d classes/n",classnum);
printf("classes:/n");
for(i=0;i<classnum;i++)
{
printf(classes[i]);
printf("/n");
}
if(pubclass>1)
printf("/nThere is an error: a java file cannot have two public class/n");
code = (char*)malloc(1);
code[0]='/0';
}
main()
{
yylex();
system("pause");
return 1;
}
這個程序還有另外兩個其他功能:1、根據程序的行數來確定程序工作量,行數等於分號的個數。在結果的末尾將會顯示行數;2、我們知道壹個java文件中不能存在兩個public類,本程序可以檢查壹個文件中存在幾個類,並判斷是否存在兩個或兩個以上的public類,如果存在就報錯。
註:my.l文件是改進前的lex程序,改進後的程序保存在my1.l文件中,Java.txt內含有壹個java源程序可以用來測試。運行lexyy.exe,復制java.txt裏面的內容粘貼到程序裏,加上輸入結束符Ctrl+z,然後回車即可看到結果;或者在dos下把java.txt作為lexyy.exe的參數運行lexyy.exe也可。