这学期的编译原理终于学完了,不愧是号称最难的科目。要用C++从头到尾实现一下小型编译器,还真不容易啊。不过总算是做完了。
首先上文法,这个文法是根据上一篇博客简化的,但还是有一点问题的,暂时发现有一个地方不符合LR1的规则(函数的返回类型如果是int就会报错)。
有了文法就可以生成LR1分析表了,如图所示,一共有187个项目集族,代码跑了2分50秒才出结果,要不是我优化了一个下午,剪掉了一些不必要的循环,以及把一个o(n)的改成了lg的,大概要跑10分钟吧。
具体的优化在代码中有注释。
生成的LR(1)表存到文件中,用err表示空格,便于从文件再读到程序中,如图所示:
接下来是输入程序:
转成Token,如果是ID或则NUM类型的,还需要存下对应的值。
下面的就是分析过程和“文本格式的语法树”了
同时dfs语法树生成了四元式:
代码:
1 #include <map> 2 #include <set> 3 #include <list> 4 #include <ctime> 5 #include <queue> 6 #include <stack> 7 #include <string> 8 #include <vector> 9 #include <iomanip> 10 #include <fstream> 11 #include <sstream> 12 #include <iostream> 13 using namespace std; 14 #define rep(i,a,n) for(int i=a;i<n;i++) 15 #define per(i,a,n) for(int i=n-1;i>=a;i--) 16 #define all(x) (x).begin(),(x).end() 17 typedef pair<string, vector<string>> Production; //产生式 18 const int WIDTH = 16; //setw(WIDTH) 19 // head 20 21 struct Project { //项目集 22 string left; 23 vector<string> right; 24 set<string> expect; 25 26 const bool operator<(const Project &p) const { 27 if (left < p.left) return true; 28 if (left > p.left) return false; 29 if (right < p.right) return true; 30 if (right > p.right) return false; 31 if (expect < p.expect) return true; 32 return false; 33 } 34 35 const bool operator==(const Project &p) const { 36 if (left == p.left && right == p.right && expect == p.expect) return true; 37 return false; 38 } 39 }; 40 41 namespace project { //产生项目集族 42 set<string> terminal; //终结符 43 set<string> not_terminal; //非终结符 44 set<string> all_symbol; //所有的符号 45 vector<string> sss; //所有的符号 46 map<string, int> Hash; //符号哈希 47 vector<Production> vp; //所有的产生式,加·前 48 set<Production> sp; //所有的项目集,加·后 49 50 void PROJECT() { 51 ifstream fin("C:\\Users\\Flowersea\\Desktop\\Grammar.txt"); 52 string a, b; 53 vector<string> c; 54 while (fin >> a >> b) { 55 not_terminal.insert(a); 56 string str; 57 getline(fin, str); 58 stringstream ss; 59 ss.str(str); 60 c.clear(); 61 while (ss >> str) { 62 c.push_back(str); 63 int fg = 0; 64 rep(i, 0, str.size()) { 65 if (!(str[i] >= 'a' && str[i] <= 'z' || str[i] == '_')) { //文法中非终结符都是小写字母和下划线组成的 66 fg = 1; 67 break; 68 } 69 } 70 if (fg) terminal.insert(str); //如果含有小写字母和下划线以外的符号就是终结符 71 else not_terminal.insert(str); //否则是非终结符 72 } 73 vp.push_back(Production(a, c)); 74 } 75 terminal.insert("#"); 76 not_terminal.erase("program'"); //删掉program' 77 all_symbol.insert(all(terminal)); 78 all_symbol.insert(all(not_terminal)); 79 for (auto it : terminal) sss.push_back(it); 80 for (auto it : not_terminal) sss.push_back(it); //sss其实是为了输出lr1表的表头 81 rep(i, 0, sss.size()) Hash[sss[i]] = i; 82 for (auto it : vp) { 83 a = it.first, c = it.second; 84 rep(i, 0, c.size() + 1) { 85 vector<string> d = c; 86 d.insert(d.begin() + i, "."); 87 sp.insert(Production(a, d)); 88 } 89 } 90 } 91 92 void main() { 93 PROJECT(); 94 } 95 } 96 97 namespace lr1 { //生成lr表 98 using namespace project; 99 100 vector<set<Project>> vsp(1); //项目集族 101 string lr1[1000][1000]; //lr1表 102 int n, m; //n行,m列 103 104 set<string> FIRST(vector<string> X) { //求FIRST集族 105 set<string> res; 106 if (terminal.find(X[0]) != terminal.end()) { 107 res.insert(X[0]); //如果是终结符,直接insert返回 108 return res; 109 } 110 else { 111 rep(j, 0, vp.size()) { //遍历所有的产生式 112 if (vp[j].first == X[0]) { 113 if (terminal.find(vp[j].second[0]) != terminal.end()) //如果第一个是终结符 114 res.insert(vp[j].second[0]); //插入到res中 115 else { 116 set<string> t = FIRST(vp[j].second); //否则递归求FIRST集 117 res.insert(all(t)); 118 } 119 } 120 } 121 } 122 return res; 123 } 124 125 set<Project> GO(set<Project> I, string X) { //GO函数 126 set<Project> J; 127 for (auto it : I) { 128 vector<string> vs = it.right; 129 auto pos = find(all(vs), "."); 130 if (pos == vs.end() - 1) continue; //如果·是最后一个,continue 131 if (*(pos + 1) == X) { 132 swap(*pos, *(pos + 1)); //交换·和后面的一个字符串 133 J.insert(Project{ it.left, vs, it.expect }); 134 } 135 } 136 return J; 137 } 138 139 set<Project> CLOSURE(set<Project> I) { //求closure 140 while (1) { 141 bool update = false; //判断此次循环是否有更新 142 for (auto it : I) { 143 vector<string> B = it.right; 144 auto pos = find(all(B), "."); //找到·的位置 145 if (pos == B.end() - 1) continue; //如果·是最后一个,continue 146 string c = *(pos + 1); //c等于·后面的字符 147 if (terminal.find(c) != terminal.end()) continue; //如果c是终结符,continue 148 B.erase(B.begin(), pos + 2); //删掉·后面的一个字符之前的所有字符,包括它自己 149 string last; //为了剪枝,记录上一次求FIRST集的第一个字符串 150 for (auto ite : it.expect) { 151 B.push_back(ite); //把expect插入到B的后面 152 if (last == B[0]) continue; //如果B[0]和上次的last一样,就不求了,因为文法中没有空产生式 153 else last = B[0]; 154 set<string> First = FIRST(B); //求B的FIRST集 155 B.pop_back();