C++編譯原理之求解First集合
1、上機(jī)要求
目的:熟練掌握自上而下的語法分析方法,并能用程序?qū)崿F(xiàn)。
要求:
例如,使用的文法如下:
編寫First
函數(shù),實(shí)現(xiàn)其求解過程。
E -> TE'
E' -> +TE' | #
T -> FT'
T' -> *FT' | #
F -> (E) | id
end
提示:
- 非終結(jié)符為 大寫字母;或 后面帶'的大寫字母
- 終結(jié)符為 小寫字母和符號(+、*)
- 推導(dǎo)符號→為或->
- 用end結(jié)束文法。
不針對特定文法,編寫求first函數(shù)。
2、原理
A -> a
, 則將 a 加入 First(A)
中
A -> Y1Y2···Yn
將 First(Y1)
除空串外的字符加入到First(A)中,若 1 =< i < n - 1,Y1,Y2, Yi
中均含有空串,則將First(Yi + 1)
加入到First(A)
中,若Y1
,Y2
,···,Yn都有空串,則將空串加入到First(A)
中
First(a) = {a}
3、一點(diǎn)思路及優(yōu)化
將輸入格式化(掃描輸入)
將產(chǎn)生式轉(zhuǎn)換為哈希map
:
- 對任一產(chǎn)生式:
A -> body_1 | body_2 | ··· | body_n
, - 將 A 作為
map
的key
, - map的
value
為一個string
類的向量(vector<string>
), - 將
body_1
,body_2
,···,body_n
都加入value
中。
- 求解First(str)
- 特殊情況處理,str為空或str不在產(chǎn)生式的
key
中,返回空;str的首個字符是終結(jié)符,返回首個字符構(gòu)成的集合。 - 一般情況,獲取str推導(dǎo)產(chǎn)生的產(chǎn)生體集
bodys
(其中的每個產(chǎn)生體為body
),遍歷產(chǎn)生體集合求解First集 - 針對空串,我們加入標(biāo)記
hasBlank = true
,往下遍歷body的字符 body
的首個字符為終結(jié)符,直接將該字符加入first集,記hasBlank = false
以便遍歷下一body
(如果有的話)。body
的首個字符為非終結(jié)符,遞歸求解該非終結(jié)符first集,記為temp
,同時(shí)將空串標(biāo)記記為false
,將temp
的中除空串外的字符加入first集;若temp
中有空串,記空串標(biāo)記為true
,繼續(xù)遍歷當(dāng)前body
的字符,理解上可以將body
后面的字符串視為一個新的body
繼續(xù)進(jìn)行求解步驟。- body的字符遍歷結(jié)束后若空串標(biāo)記
hasBlank
仍然為true
,則將空串加入first集。 - 優(yōu)化:遞歸求解的中間結(jié)果可以放在全局哈希First(或者換個名字避免沖突)中,避免重復(fù)的迭代(本代碼沒實(shí)現(xiàn),下次一定)。
4、代碼
/** * @brief Function for generating set of First(a) * @author 立秋小豬 * @time: 2021/10/13 * @notice: 要求產(chǎn)生體句型不得有空格 * 左遞歸的產(chǎn)生體中必須有空串(必須能夠終結(jié)) * char '#' act as varepsilon * **/ #include <iostream> #include <unordered_map> #include <vector> #include <string> #include <fstream> #include <unordered_set> using namespace std; unordered_map<string, vector<string>> P; //產(chǎn)生式P的集合 void scan(){ //scan函數(shù)實(shí)現(xiàn)從文件掃描文法,將對應(yīng)的產(chǎn)生式加入到映射P中 fstream fs; string input; fs.open("lan.txt"); if(!fs.is_open()){ // 文件打開失敗 cout << "Error: Could not open the file" << endl; exit(-1); } fs >> input; while(input != "end"){ string VN = input; // 產(chǎn)生式的非終結(jié)符 fs >> input; //跳過推導(dǎo)符號 if (input != "->" && input != "→"){ cout << "Error: undefined symbol [" << input << "]" << endl; exit(-2); } fs >> input; //產(chǎn)生體拆開后加入到set集合中,默認(rèn)推導(dǎo)符號后必有一個產(chǎn)生體 P[VN].emplace_back(input); while( fs >> input && input == "|"){ fs >> input; P[VN].emplace_back(input); } } } // void generate(){ // } unordered_set<char> First(const string& str){ // 終結(jié)符以及空串情況下, whether has the VN or not if(str == "" || str == "#" || P.find(str) == P.end()) return {}; if(!(str[0] >= 'A' && str[0] <= 'Z')) return {str[0]}; vector<string> bodys = P[str]; // str -> bodys unordered_set<char> res = {}; for(auto &s: bodys){ bool hasBlank = true;//是否含有空串,是否繼續(xù)讀產(chǎn)生體 for (int i = 0; i < s.size() && hasBlank; ++i){ if(s[i] >= 'A' && s[i] <= 'Z'){//是否為終結(jié)符 unordered_set<char> temp = {};//遞歸的臨時(shí)集 string next; if(i < s.size() - 1 && s[i + 1] == '\''){ // 大寫字母 + ' 的非終結(jié)符 next = s.substr(i, 2); ++i; }else{ //僅僅是大寫字母的終結(jié)符 next = s[i]; } if(next != str){ //避免無限遞歸,默認(rèn)自身是含有空串(hasBlank為True) temp = First(next); //遞歸求解 hasBlank = false; //先默認(rèn)temp中沒有空串 for(auto &c : temp) if(c == '#') hasBlank = true;//temp中發(fā)現(xiàn)了空串 else res.emplace(c); } }else{ res.emplace(s[i]); hasBlank = false;//默認(rèn)連接的終結(jié)符不為空,故此終結(jié)符后不會再有新元素加入First集 } } if(hasBlank) //產(chǎn)生體中所有非終結(jié)符都包含空串,則將空串加入first集中 res.emplace('#'); } return res; } int main(){ // unordered_map<string, vector<char>> First; //First集合 scan(); cout << "輸入的產(chǎn)生式如下:\n" << "********************************\n"; for(auto &[vn, bodys]: P){ cout << vn << " -> " << bodys[0]; for (int i = 1; i < bodys.size(); ++i) cout << " | " << bodys[i]; cout << endl; } cout << "********************************\n"; for(auto &[vn,_]: P){ unordered_set<char> f = First(vn); cout << "First(" << vn << ") : "; auto iter = f.begin(); if(iter != f.end()){ cout << *iter; while(++iter != f.end()){ cout << " , " << *iter; } } cout << endl; } return 0; }
4.1 lan.txt文件內(nèi)容
E -> TE' E' -> +TE' | # T -> FT' T' -> *FT' | # F -> (E) | id end
運(yùn)行結(jié)果
4.2 lan.txt文件內(nèi)容
S -> SaRb | # R -> RSQ | # Q -> e end
運(yùn)行結(jié)果
到此這篇關(guān)于C++/編譯原理之求解First集合的文章就介紹到這了,更多相關(guān)C++ 求解First集合內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++實(shí)現(xiàn)高校教室管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)高校教室管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03C語言中fchdir()函數(shù)和rewinddir()函數(shù)的使用詳解
這篇文章主要介紹了C語言中fchdir()函數(shù)和rewinddir()函數(shù)的使用詳解,是C語言入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下2015-09-09C語言實(shí)現(xiàn)九大排序算法的實(shí)例代碼
這篇文章主要給大家介紹了關(guān)于C語言實(shí)現(xiàn)九大排序算法的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01SublimeText編譯C開發(fā)環(huán)境設(shè)置
這篇文章主要介紹了使用SublimeText編譯C代碼的開發(fā)環(huán)境設(shè)置,大家參考使用2013-11-11