一、實驗目的
通過設計編制調試一個具體的詞法分析程序,加深對詞法分析原理的理解。並掌握在對程序設計語言源程序進行掃描過程中將其分解爲各類單詞的詞法分析方法。
編制一個讀單詞過程,從輸入的源程序中,識別出各個具有獨立意義的單詞,即基本保留字、標識符、常數、運算符、分隔符五大類。並依次輸出各個單詞的內部編碼及單詞符號自身值。(遇到錯誤時可顯示“Error”,然後跳過錯誤部分繼續顯示)
二、實驗內容
程序輸入/輸出示例:
如源程序爲C語言。輸入如下一段:
main()
{
int a,b;
a = 10;
b = a + 20;
}
要求輸出如下:
(2,”main”)
(5,”(“)
(5,”)“)
(5,”{“)
(1,”int”)
(2,”a”)
(5,”,”)
(2,”b”)
(5,”;”)
(2,”a”)
(4,”=”)
(3,”10”)
(5,”;”)
(2,”b”)
(4,”=”)
(2,”a”)
(4,”+”)
(3,”20”)
(5,”;”)
(5,”}“)
要求:
識別保留字:if、int、for、while、do、return、break、continue;
單詞種別碼爲1。
其他的都識別爲標識符;單詞種別碼爲2。
常數爲無符號整形數;單詞種別碼爲3。
運算符包括:+、-、*、/、=、>、<、>=、<=、!= ;
單詞種別碼爲4。
分隔符包括:,、;、{、}、(、); 單詞種別碼爲5。
三、實驗大致過程
四、實驗步驟
c語言的源程序存放在code.txt中,只要用到三個函數來實現詞法分析:預處理函數、掃描判斷單詞類型的函數以及主函數.
1、在預處理函數process()中,使用文檔操作函數打開源程序文件code.txt,去除兩種類型(“//”,“/…/”)的註釋、多餘的空格合併爲一個、換行符、回車符等,然後將處理後的保存在另一個新的文件result.txt中,最後關閉文檔.
2、打開result.txt文件,調用掃描函數,從文件裏讀取一個單詞調用判斷單詞類型的函數與之前建立的符號表進行對比判斷,最後格式化輸出。
五、實驗代碼
#include<iostream>
#include<fstream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
using namespace std;
int aa;// fseek的時候用來接着的
string word="";
string reserved_word[20];//保留
char buffer;//每次讀進來的一個字符
int num=0;//每個單詞中當前字符的位置
int line=1; //行數
int row=1; //列數,就是每行的第幾個
bool flag; //文件是否結束了
int flag2;//單詞的類型
//預處理函數
int process(){
FILE *p;
int falg=0,len,i=0,j=0;
char str[1000],str1[1000],c;
if((p=fopen("code.txt","rt"))==NULL){
printf("無法打開要編譯的源程序");
return 0;
}else{
while((c=getc(p))!=EOF){
str[i++] = c;
}
fclose(p);
str[i] = '\0';
for(i=0;i<strlen(str);i++){
if(str[i]=='/'&&str[i+1]=='/'){ //單行註釋必然以換行符結束
while(str[i++]!='\n'){}
}else if(str[i]=='/'&&str[i+1]=='*'){ //多行註釋
while(!(str[i]=='*'&&str[i+1]=='/')){i++;}
i+=2;
}else if(str[i]==' '&&str[i+1]==' '){ //多個空格,去除空格
while(str[i]==' '){i++;}
i--;
if(str1[j-1]!=' ')
str1[j++]=' ';
}else if(str[i]=='\n') { //換行符處理
if(str1[j-1]!=' ')
str1[j++]=' ';
}else if(str[i]==9){ //tab鍵處理
while(str[i]==9){
i++;
}
if(str1[j-1]!=' ')
str1[j++]=' ';
i--;
}else str1[j++] = str[i]; //其他字符處理
}
str1[j] = '\0';
if((p = fopen("result.txt","w"))==NULL){
printf("can not find it!");
return 0;
}else{
if(fputs(str1,p)!=0){
printf("預處理失敗!");
}else
printf("預處理成功!");
}
fclose(p);
}
return 0;
}
//設置保留字
void set_reserve()
{
reserved_word[1]="return";
reserved_word[2]="def";
reserved_word[3]="if";
reserved_word[4]="else";
reserved_word[5]="while";
reserved_word[6]="return";
reserved_word[7]="char";
reserved_word[8]="for";
reserved_word[9]="and";
reserved_word[10]="or";
reserved_word[11]="int";
reserved_word[12]="bool";
}
//看這個字是不是字母
bool judge_word(char x)
{
if(x>='a' && x<='z' || x>='A' && x<='Z' ){
return true;
}
else return false;
}
//看這個字是不是數字
bool judge_number(char x)
{
if(x>='0' && x<='9'){
return true;
}
else return false;
}
//看這個字符是不是界符
bool judge_jiefu(char x)
{
if(x=='('||x==')'||x==','||x==';'||x=='{'||x=='}'){
return true;
}
else return false;
}
//加減乘
bool judge_yunsuanfu1(char x)
{
if(x=='+'||x=='-'||x=='*')
{
return true;
}
else return false;
}
//等於 賦值,大於小於 大於等於,小於等於,大於小於
bool judge_yunsuannfu2(char x)
{
if(x=='='|| x=='>'||x=='<'||x=='&'||x=='||'){
return true;
}
else return false;
}
//從文件裏讀一個單詞
int scan(FILE *fp)
{
buffer=fgetc(fp); //讀取一個字符
if(feof(fp)){ //檢測結束符
flag=0;return 0;
}
else if(buffer==' ')
{
row++;
return 0;
}
else if(buffer=='\n')
{
row=1;
return 0;
}
else if(judge_word(buffer) || buffer=='_') //如果是字母開頭或'_' 看關鍵字還是普通單詞
{
word+=buffer;
row++;
while((buffer=fgetc(fp)) && (judge_word(buffer) || judge_number(buffer) || buffer=='_'))
{
word+=buffer;
row++;
}
if(feof(fp)){
flag=0;
return 2;
}
for(int i=1;i<=12;i++){
if(word==reserved_word[i]){
aa=fseek(fp,-1,SEEK_CUR);//如果執行成功,stream將指向以fromwhere爲基準,偏移offset(指針偏移量)個字節的位置,函數返回0。
return 1;
}
}
aa=fseek(fp,-1,SEEK_CUR);
return 2;
}
else if(judge_yunsuanfu1(buffer)) //開始是加減乘 一定是類型4
{
word+=buffer;
row++;
return 4;
}
else if(judge_jiefu(buffer)) //檢驗界符
{
word+=buffer;
row++;
return 5;
}
else if(judge_number(buffer)) //開始是數字就一定是數字
{
word+=buffer;
row++;
while((buffer=fgetc(fp)) && judge_number(buffer))
{
word+=buffer;
row++;
}
if(feof(fp)){
flag=0;
return 3;
}
aa=fseek(fp,-1,SEEK_CUR);
return 3;
}
else if(judge_yunsuannfu2(buffer)) //檢驗 <=、 >=、 <>、 == =、 <、>
{
row++;
word+=buffer;
if(buffer=='<') //爲了檢驗題目中的<> <=
{
buffer=fgetc(fp);
if(buffer=='>' || buffer=='=')
{
word+=buffer;
row++;
return 4;
}
}
else{ //檢驗 >= ==
buffer=fgetc(fp);
if(buffer=='=')
{
word+=buffer;
row++;
return 4;
}
}
if(feof(fp)){
flag=0;
}
aa=fseek(fp,-1,SEEK_CUR);
return 4;
}
else if(buffer=='/') //首字符是/ 有可能是除號 也有可能是註釋
{
row++;
word+=buffer;
buffer=fgetc(fp);
aa=fseek(fp,-1,SEEK_CUR);
return 4;
}
else {
word+=buffer;
row++;
return -1;
}
}
int main()
{
set_reserve();
process();
cout<<"open "<<"result.txt"<<endl;
flag=1;
FILE *fp;
if(!(fp=fopen("result.txt","r")))
{
cout<<"not found the file or other error "<<endl;
flag=0;
}
while(flag==1){
flag2=scan(fp); //反覆調用函數提取單詞並返回類型
if(flag2==1)
{
cout<<"type:1 reserved word "<<word<<endl;
word.erase(word.begin(),word.end()); //將word字符串清空
}
else if(flag2==2)
{
cout<<"type:2 identifier "<<word<<endl;
if(word.length()>20)
cout<<"ERROR Identifier length cannot exceed 20 characters"<<endl;
word.erase(word.begin(),word.end());
}
else if(flag2==3)
{
cout<<"type:3 positive number "<<word<<endl;
word.erase(word.begin(),word.end());
}
else if(flag2==4)
{
cout<<"type:4 unary_operator "<<word<<endl;
word.erase(word.begin(),word.end());
}
else if(flag2==5)
{
cout<<"type:5 double_operator "<<word<<endl;
word.erase(word.begin(),word.end());
}
else if(flag2==-1) //非法字符
{
cout<<"Illegal character "<<word<<endl;
word.erase(word.begin(),word.end());
}
}
int a=fclose(fp);
cout<<"press e to close"<<endl;
char end;
while(cin>>end && end!='e'){
cout<<"只有e可以關閉"<<endl;
}
return 0;
}
六、實驗結果與討論
七、總結
本次實驗讓我瞭解如何設計編制並調試詞法分析程序,加深了我對詞法分析器原理的理解。詞法分析是編譯的第一階段。詞法分析器的主要任務是讀入源程序的輸入字符,將它們組成詞素,生成並輸出一個詞法單元序列,這個詞法單元序列被輸出到語法分析器進行語法分析。另外,由於詞法分析器在編譯器中負責讀取源程序,因此除了識別詞素之外,它還會完成一些其他任務,比如過濾掉源程序中的註釋和空白,將編譯器生成的錯誤消息與源程序的位置關聯起來等。