?? 使用lex分析java源程序.txt
字號:
使用lex分析java源程序
一般編譯程序在進行詞法分析的過程中都要將源程序中的無用符號及注釋刪除。雖然這個步驟并不復雜,用c語言就可以直接實現,但是如果用詞法分析程序生成工具lex實現起來卻可以更加方便。
Lex是美國Bell實驗室用C語言研制的一個詞法分析程序自生成工具
。它的基本原理就是使用正則表達式掃描匹配文本,并為每一個匹配模式定義一些操作,當用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也可。
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -