1.試驗目的
輸入:一組任意的規則。
輸出:相應的Chomsky 文法的類型。
2.實驗原理
1).0型文法(短語文法)
如果對於某文法G,P中的每個規則具有下列形式:
u:: = v
其中u∈V+,v∈V*,則稱該文法G為0型文法或短語文法,簡寫為PSG。
0型文法或短語結構文法的相應語言稱為0型語言或短語結構語言L0。這種文法由於沒有其他任何限制,因此0型文法也稱為無限制文法,其相應的語言稱為無限制性語言。任何0型語言都是遞歸可枚舉的,故0型語言又稱遞歸可枚舉集。這種語言可由圖靈機(Turning)來識別。
2).1型文法(上下文有關文法)
如果對於某文法G,P中的每個規則具有下列形式:
xUy:: = xuy
其中U∈VN;u∈V+;x,y∈V*,則稱該文法G為1型文法或上下文有關文法,也稱上下文敏感文法,簡寫為CSG。
1型文法的規則左部的U和右部的u具有相同的上文x和下文y,利用該規則進行推導時,要用u替換U,必須在前面有x和后面有y的情況下才能進行,顯示了上下文有關的特性。
1型文法所確定的語言為1型語言L1,1型語言可由線性有界自動機來識別。
3).2型文法(上下文無關文法)
如果對於某文法G,P中的每個規則具有下列形式:
U :: = u
其中U∈VN;u∈V+,則稱該文法G為2型文法或上下文無關文法,簡寫為CFG。
按照這條規則,對於上下文無關文法,利用該規則進行推導時,無需考慮非終結符U所在的上下文,總能用u替換U,或者將u歸約為U,顯示了上下文無關的特點。
2型文法所確定的語言為2型語言L2,2型語言可由非確定的下推自動機來識別。
一般定義程序設計語言的文法是上下文無關的。如C語言便是如此。因此,上下文無關文法及相應語言引起了人們較大的興趣與重視。
4).3型文法(正則文法,線性文法)
如果對於某文法G,P中的每個規則具有下列形式:
U :: = T 或 U :: = WT
其中T∈VT;U,W∈VN,則稱該文法G為左線性文法。
如果對於某文法G,P中的每個規則具有下列形式:
U :: = T 或 U :: = TW
其中T∈VT;U, W∈VN,則稱該文法G為右線性文法。
左線性文法和右線性文法通稱為3型文法或正則文法,有時又稱為有窮狀態文法,簡寫為RG。
按照定義,對於正則文法應用規則時,單個非終結符號只能被替換為單個終結符號,或被替換為單個非終結符號加上單個終結符號,或者被替換為單個終結符號加上單個非終結符號。
3型文法所確定的語言為3型語言L3,3型語言可由確定的有限狀態自動機來識別。
在常見的程序設計語言中,多數與詞法有關的文法屬於3型文法。
可以看出,上述4類文法,從0型到3型,產生式限制越來越強,其后一類都是前一類的子集,而描述語言的功能越來越弱,四類文法及其表示的語言之間的關系可表示為:
0型1型2型3型;即L0 L1 L2 L3
3..實驗內容
1)輸入一組文法規則,以S為開始符,大寫字母為非終結符,非大寫字母為終結符。
2)判斷文法類型並生成文法的四元組形式。
3)輸出文法類型及文法。
4.實驗心得
這次實驗讓我體會到了判別文法的復雜性,我的算法是先默認是正則文法,再依次判斷,而許多同學是每次對一條規則進行相同的判斷,我認為這是可以改進的,例如若發現上一條規則是符合1型文法,則當前的規則就只需從1型文法開始判斷,詳見源代碼。
5.實驗代碼與結果
1)源代碼
1 /******************************************************************** 2 created: 2013/05/28 3 created: 28:5:2013 20:15 4 file base: main 5 file ext: cpp 6 author: Justme0 (http://blog.csdn.net/Justme0) 7 8 purpose: 9 判斷文法類型。 10 輸入約定以S為開始符,大寫字母為非終結符,非大寫字母為終結符。 11 12 *********************************************************************/ 13 14 #define _CRT_SECURE_NO_WARNINGS 15 #include <iostream> 16 #include <string> 17 #include <vector> 18 #include <set> 19 #include <algorithm> 20 #include <cassert> 21 using namespace std; 22 23 const int STRING_MAX_LENGTH = 10; 24 25 /* 26 ** 一條規則 27 */ 28 struct Principle { 29 string left; 30 string right; 31 32 Principle(const char *l, const char *r) : left(l), right(r) {} 33 }; 34 35 /* 36 ** 文法的四元組形式,同時將該文法類型也作為其屬性 37 */ 38 struct Grammer { 39 set<char> Vn; 40 set<char> Vt; 41 vector<Principle> P; 42 char S; 43 44 int flag; // 文法類型 45 46 Grammer(void) : flag(-1) {} 47 }; 48 49 50 /* 51 ** 輸入規則 52 */ 53 void input(vector<Principle> &principleSet) { 54 char left[STRING_MAX_LENGTH]; 55 char right[STRING_MAX_LENGTH]; 56 while (EOF != scanf("%[^-]->%s", left, right)) { 57 getchar(); 58 principleSet.push_back(Principle(left, right)); 59 } 60 } 61 62 /* 63 ** 得到S, Vn, Vt 64 */ 65 void getGrammer(Grammer &G) { 66 G.S = G.P.front().left.front(); 67 G.Vn.clear(); 68 G.Vt.clear(); 69 70 for (unsigned i = 0; i < G.P.size(); ++i) { 71 const Principle &prcp = G.P[i]; 72 73 for (unsigned j = 0; j < prcp.left.size(); ++j) { 74 char v = prcp.left[j]; 75 !isupper(v) ? G.Vt.insert(v) : G.Vn.insert(v); 76 } 77 for (unsigned j = 0; j < prcp.right.size(); ++j) { 78 char v = prcp.right[j]; 79 !isupper(v) ? G.Vt.insert(v) : G.Vn.insert(v); 80 } 81 } 82 } 83 84 /* 85 ** 判斷字符串(文法的左部)中是否存在一個非終結符。 86 */ 87 bool hasUpper(string s) { 88 return s.end() != find_if(s.begin(), s.end(), isupper); 89 } 90 91 /* 92 ** 判斷文法類型。 93 */ 94 void check(int &flag, const Principle &prcp) 95 { 96 switch (flag) { 97 case 3: 98 if ( 99 1 == prcp.left.size() && isupper(prcp.left.front()) && 100 (1 == prcp.right.size() && !isupper(prcp.right.front()) || 101 2 == prcp.right.size() && !isupper(prcp.right.front()) && isupper(prcp.right.back())) 102 ) { 103 break; 104 } 105 106 case 2: 107 if (1 == prcp.left.size() && isupper(prcp.left.front())) { 108 flag = 2; 109 break; 110 } 111 112 case 1: 113 if (hasUpper(prcp.left) && prcp.left.size() <= prcp.right.size()) { 114 flag = 1; 115 break; 116 } 117 118 default : 119 // 所有的文法規則左部都必須至少含有一個非終結符,是否應放到最前面判斷? 120 // 用異常機制exit是否合適? 121 try { 122 if (!hasUpper(prcp.left)) { 123 throw exception("輸入的文法錯誤!"); 124 } 125 } catch (exception e) { 126 cout << e.what() << endl; 127 exit(-1); 128 } 129 130 flag = 0; 131 break; 132 } 133 } 134 135 136 /* 137 ** 判別文法並生成四元組形式。 138 */ 139 void solve(Grammer &G) { 140 G.flag = 3; // 從正則文法開始判斷 141 for (unsigned i = 0; i < G.P.size(); ++i) { 142 check(G.flag, G.P[i]); 143 } 144 getGrammer(G); 145 } 146 147 /* 148 ** 輸出文法 149 */ 150 void output(const Grammer &G) { 151 cout << "G = (Vn, Vt, P, S)是" << G.flag << "型文法\n" << endl; 152 153 cout << "Vn:" << endl; 154 for (auto ite = G.Vn.begin(); ite != G.Vn.end(); ++ite) { 155 cout << *ite << endl; 156 } 157 cout << endl; 158 159 cout << "Vt:" << endl; 160 for (auto ite = G.Vt.begin(); ite != G.Vt.end(); ++ite) { 161 cout << *ite << endl; 162 } 163 cout << endl; 164 165 cout << "P:" << endl; 166 for (auto ite = G.P.begin(); ite != G.P.end(); ++ite) { 167 cout << (*ite).left << "->" << (*ite).right << endl; 168 } 169 cout << endl; 170 171 cout << "S:\n" << G.S << endl; 172 }; 173 174 int main(int argc, char **argv) { 175 freopen("cin.txt", "r", stdin); 176 177 Grammer G; 178 input(G.P); 179 solve(G); 180 output(G); 181 182 return 0; 183 }
2)實驗結果
(1)
輸入:
S->aSBE
S->aBE
EB->BE
aB->ab
bB->bb
bE->be
eE->ee
輸出:
(2)
輸入:
S->aB
S->bA
A->a
A->aS
A->bAA
B->b
B->bS
B->aBB
輸出: