編譯原理實驗一:詞法分析
阿新 • • 發佈:2019-01-10
實驗一:詞法分析程式
一、實驗目的
通過設計編制除錯一個具體的詞法分析程式,加深對詞法分析原理的理解。並掌握在對程式設計語言源程式進行掃描過程中將其分解為各類單詞的詞法分析方法。
編制一個讀單詞過程,從輸入的源程式中,識別出各個具有獨立意義的單詞,即基本保留字、識別符號、常數、運算子、分隔符五大類。並依次輸出各個單詞的型別碼及單詞符號的自身值。(遇到錯誤時可顯示“Error”,然後跳過錯誤部分繼續顯示)
二、實驗要求
用C或C++寫一個簡單的詞法分析程式,程式可以滿足下列要求:
1、能分析如下幾種簡單的語言詞法
(1) 識別符號: ID=letter(letter|digit)*
(2 ) 關鍵字(全部小寫)
main int float double char if then else switch case break continue while do for
(3)整型常量:NUM=digit digit*
(4)運算子
= + - * / < <= == != > >= ; ( )? :
(5)空格由空白、製表符和換行符組成,用以分隔ID、NUM、運算子等,字元分析時被忽略。
2、單詞符號和相應的類別碼
假定單詞符號和相應的類別碼如下:
單詞符號 種別碼
int 1
= 17
float 2
< 20
if 3
<= 21
switch 4
== 22
while 5
!= 23
Do 6
> 24
識別符號 10
>= 25
整型常量 11
; 26
+ 13
( 27
- 14
) 28
* 15
? 29
/ 16
: 30
3、詞法分析程式實現的功能
輸入:單詞序列(以檔案形式提供),輸出識別的單詞的二元組序列到檔案和螢幕
輸出:二元組構成: (syn,token或sum)
其中: syn 為單詞的種別碼
token 為存放的單詞自身符號串
sum 為整型常數
例:
源程式: int ab; float ef=20;
ab=10+ef;
輸出:
(保留字--1,int)
(識別符號--10,ab)
(分號--26,;)
(保留字--2,float)
(識別符號--10,ef)
(等號--17,=)
(整數--11,20)
(分號--26,;)
(識別符號--10,ab)
(等號--17,=)
(整數--11,10)
(加號--13,+)
(識別符號--10,ef)
(分號--26,;)
4、自己準備測試資料存放於TestData.txt檔案中,測試資料中應覆蓋有以上5種資料,測試結果要求以原資料與結果對照的形式輸出並儲存在Result.txt中,同時要把結果輸出到螢幕。
5、提前準備
① 實驗前,先編制好程式,上機時輸入並除錯程式。
② 準備好多組測試資料(存放於檔案TestData.txt中)。
本以為很快就能做出來的,沒想到前前後後共總共大概花了3個小時,慚愧,大體流程就是讀取檔案中的內容,先一行一行的讀,讀取一行後然後利用istringstream流讀取單詞,需要注意,比如一行輸入是”int a=1,bcd=4;”,這裡單詞就是”int”和”a=1,bcd=4”,因為讀取時遇到空格,製表符等就讀入一個單詞,所以還要對單詞進行分割,將識別符號,運算子(+,-,<=,>=,!=,==,逗號等等),整型數字(為了簡單起見,只用整形數字)分開,難點就在於怎麼分,分開後對每個分開的單詞然後再根據是識別符號還是運算子等等,對應輸出就行了
程式碼:
# include<iostream>
# include<string>
# include<fstream>
# include<sstream>//流物件
# include<vector>
# include<map>
using namespace std;
bool isIdentifier(string s);//識別符號
bool isKeywords(string s);//關鍵字
bool isDigit(string s);//整型數字
bool isOperator(string s);//運算子
bool isOperator(char c);//運算子
string result(string s);//根據傳入的引數s產生對應的輸出
int main()
{
//=====================測試函式是否正確=============================
/*if(isIdentifier("_s2ias"))
cout << "是識別符號" << endl;
if (isKeywords("for"))
cout << "是關鍵字" << endl;
if (isDigit("12a42"))
cout << "是數字" << endl;
if (isOperator(">"))
cout << "是運算子" << endl;
result("?");*/
//===================================================================
string file = ("TestData1.txt");
ifstream input(file);
//輸入檔案流,注意編碼,文字檔案編碼格式需和專案一直,否則亂碼
ofstream output("Result.txt",ofstream::app);
//先將TtestData.txt內容拷貝到Result.txt中
string copy;
getline(input, copy, '\0');
cout<< copy << endl;//測試是否正確
//此時input已經指到了檔案尾,為了後面的讀取,需要關閉再開啟
input.close();
input.open(file);
/*測試結果要求以原資料與結果對照的形式輸出並儲存在Result.txt中,
同時要把結果輸出到螢幕。
*/
output << "原資料:\n";
output << copy;
output << "處理後結果:\n";
string str;
string words;
cout << "處理後結果:\n";
while (getline(input, str)) //讀取檔案每一次讀取一行,遇到EOF結束
{
//從輸入流中獲取單詞,需要用到輸入流物件,即istringstream
istringstream strCin(str);
string s;
while (strCin >> words)
{
//注意處理逗號,比如int a,b;這裡有一個單詞"a,b;”,所以要處理一個字串裡面
//的各種運算子,但是這樣會很麻煩,發現沒有,用ide寫程式碼寫完一句輸入分號時,ide
//會自動加入空格,這樣就方便處理多了
//1.首先可以確定的是關鍵字肯定是單獨作為一個單詞的
if (isKeywords(words))
{
s = result(words);
cout << s << endl;
output << s << endl;
continue;
}
//2,對單詞進行掃描,肯定是識別符號,運算子,逗號分號,數字等等混合在一起的單詞
vector<int> index = {0};
vector<string> mulWords;//將words分解為多個單詞
for (int i = 0; i < words.length(); i++)
{
//運算子有兩位的,比如"<=",">=","==","!="
if ((i < words.length() - 1) && isOperator(words[i]) && isOperator(words[i + 1]))
{
//但是要注意只有以上四種兩位運算子,比如+-,))就不是,但是))還是要輸出),)
if (string(words.begin() + i, words.begin() + i + 2) == "<=" ||
string(words.begin() + i, words.begin() + i + 2) == ">=" ||
string(words.begin() + i, words.begin() + i + 2) == "==" ||
string(words.begin() + i, words.begin() + i + 2) == "!=")
{
index.push_back(i);
index.push_back(i + 2);
++i;
}
else if (isOperator(words[i]))
{
if (find(index.begin(), index.end(), i) == index.end())
index.push_back(i);
if (find(index.begin(), index.end(), i + 1) == index.end())
index.push_back(i + 1);
}
}
//逗號,運算子作為分隔
else if (isOperator(words[i]))
{
if (find(index.begin(), index.end(), i) == index.end())
//比如遇到"a,b"這裡下標0和1將a分開,1到2將逗號分開,2到3將b分開
index.push_back(i);
if (find(index.begin(), index.end(), i+1) == index.end())
index.push_back(i + 1);
//如果是a<=b這樣的呢?一樣,先0和1將a分開,1和2將<分開,2和3將=分開
//3和4將b分開,然後後面分隔單詞時,注意如果相鄰都是運算子,則忽略,比如
//後面判斷到1和2,2和3都是運算子,則忽略2
}
}
for (int i = 0; i < index.size()-1; i++)
{
string rel;
//比如遇到"<=",需要提取”<=“
/*if (isOperator(words[index[i]]) && isOperator(words[index[i + 1]]))
{
rel = result(string(words.begin() + index[i], words.begin() + index[i + 2]));
++i;
}
else*/
rel=result(string(words.begin() + index[i], words.begin() + index[i + 1]));
output << rel << endl;
cout << rel<<endl;
}
}
}
output << endl;
output.close();
input.close();
system("pause");
return 0;
}
bool isIdentifier(string s)//識別符號,試驗要求:ID=letter(letter|digit)*
{
if (!isKeywords(s))//識別符號不能是關鍵字
{
if ((s[0] >= 'a'&&s[0] <= 'z') || (s[0] >= 'A'&&s[0] <= 'Z'))//是字母
{
for (int i = 1; i < s.length(); i++)
{
if ((s[i] >= 'a'&&s[i] <= 'z') || (s[i] >= 'A'&&s[i] <= 'Z')
|| (s[i] >= '0'&&s[i] <= '9'))
continue;
else return false;
}
return true;
}
return false;
}
return false;
}
bool isKeywords(string s)//關鍵字
{
static vector<string> keyVec = { "main", "int", "float", "double", "char",
"if", "then","else", "switch", "case", "break", "continue", "while",
"do", "for" };
vector<string>::iterator result = find(keyVec.begin(), keyVec.end(),s);
if (result != keyVec.end())
return true;
else return false;
}
bool isDigit(string s)//整型數字,NUM=digit digit*
{
if (s[0] >= '0'&&s[0] <= '9')
{
for (int i = 1; i < s.length(); ++i)
if (s[i] >= '0'&&s[i] <= '9')
continue;
else return false;
return true;
}
return false;
}
bool isOperator(string s)//運算子
{
static vector<string> opeVec = { "=","+","-","*","/","<","<=","==","!=",
">",">=",";","(",")","?",":","," };
vector<string>::iterator result = find(opeVec.begin(), opeVec.end(), s);
if (result != opeVec.end())
return true;
else return false;
}
bool isOperator(char c)//運算子
{
static vector<char> opeVec = { '=','+','-','*','/','<',
//"<=","==","!=",
'>',
//">=",
';','(',')','?',':',',' };
vector<char>::iterator result = find(opeVec.begin(), opeVec.end(), c);
if (result != opeVec.end())
return true;
else return false;
}
string result(string s)//根據傳入的引數s產生對應的輸出
{
//種別碼
//1.識別符號
if (isIdentifier(s))
return "(識別符號--10,"+s+")";
//2.關鍵字
static map<string, string> keyMap;
keyMap["int"] = "1";
keyMap["float"] = "2";
keyMap["if"] = "3";
keyMap["switch"] = "4";
keyMap["while"] = "5";//只寫5個吧,沒必要全寫
if (isKeywords(s))
return "(關鍵字--"+keyMap[s]+"," +s+")";
//3.整型常量
if (isDigit(s))
return "(整型常量--11,"+s+")";
//4.運算子
static map<string, string> opeMap;
opeMap["="] = "(等號--17,=)";
opeMap["<"] = "(小於號--20,<)";
opeMap["<="] = "(小於等於號--21,<=)";
opeMap["=="] = "(賦值運算子--22,==)";
opeMap["!="] = "(不等於號--23,!=)";
opeMap[">"] = "(大於號--24,>)";
opeMap[">="] = "(大於等於號--25,>=)";
opeMap[";"] = "(分號--26,;)";
opeMap["+"] = "(加號--13,+)";
opeMap["("] = "( 左括號--27,( )";
opeMap["-"] = "(減號--14,-)";
opeMap[")"] = "(右括號--28,) )";
opeMap[">"] = "(大於號--24,>)";
opeMap["*"] = "(星號--15,*)";
opeMap["?"] = "(問號--29,?)";
opeMap["/"] = "(除號--16,/)";
opeMap[":"] = "(冒號--30,:)";
opeMap[","] = "(逗號--31,,)";
if (isOperator(s))
return opeMap[s];
return "Error";
}
測試結果
先來簡單點的
然後複雜一點的