26 |
27 | QT_BEGIN_NAMESPACE
28 |
29 | class Ui_MainWindow
30 | {
31 | public:
32 | QWidget *centralwidget;
33 | QGridLayout *gridLayout;
34 | QWidget *browserWidget;
35 | QVBoxLayout *verticalLayout_5;
36 | QTextBrowser *textBrowser;
37 | QWidget *titleWidget;
38 | QVBoxLayout *verticalLayout_4;
39 | QLabel *title;
40 | QWidget *buttonWidget;
41 | QVBoxLayout *verticalLayout_2;
42 | QPushButton *analysisPushButton;
43 | QPushButton *nfaPushButton;
44 | QPushButton *dfaPushButton;
45 | QPushButton *minimumDfaPushButton;
46 | QPushButton *codePushButton;
47 | QPushButton *resetPushButton;
48 | QWidget *textWidget;
49 | QVBoxLayout *verticalLayout;
50 | QLabel *label;
51 | QTextEdit *textEdit;
52 | QWidget *mainWidget;
53 | QVBoxLayout *verticalLayout_3;
54 | QLabel *labelOfTable;
55 | QTableWidget *tableWidget;
56 | QMenuBar *menubar;
57 | QStatusBar *statusbar;
58 |
59 | void setupUi(QMainWindow *MainWindow)
60 | {
61 | if (MainWindow->objectName().isEmpty())
62 | MainWindow->setObjectName(QString::fromUtf8("MainWindow"));
63 | MainWindow->resize(800, 600);
64 | centralwidget = new QWidget(MainWindow);
65 | centralwidget->setObjectName(QString::fromUtf8("centralwidget"));
66 | gridLayout = new QGridLayout(centralwidget);
67 | gridLayout->setObjectName(QString::fromUtf8("gridLayout"));
68 | browserWidget = new QWidget(centralwidget);
69 | browserWidget->setObjectName(QString::fromUtf8("browserWidget"));
70 | verticalLayout_5 = new QVBoxLayout(browserWidget);
71 | verticalLayout_5->setObjectName(QString::fromUtf8("verticalLayout_5"));
72 | textBrowser = new QTextBrowser(browserWidget);
73 | textBrowser->setObjectName(QString::fromUtf8("textBrowser"));
74 | QFont font;
75 | font.setFamily(QString::fromUtf8("\345\256\213\344\275\223"));
76 | font.setPointSize(10);
77 | textBrowser->setFont(font);
78 | textBrowser->setLineWrapColumnOrWidth(1);
79 |
80 | verticalLayout_5->addWidget(textBrowser);
81 |
82 |
83 | gridLayout->addWidget(browserWidget, 2, 1, 1, 1);
84 |
85 | titleWidget = new QWidget(centralwidget);
86 | titleWidget->setObjectName(QString::fromUtf8("titleWidget"));
87 | verticalLayout_4 = new QVBoxLayout(titleWidget);
88 | verticalLayout_4->setObjectName(QString::fromUtf8("verticalLayout_4"));
89 | title = new QLabel(titleWidget);
90 | title->setObjectName(QString::fromUtf8("title"));
91 | QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
92 | sizePolicy.setHorizontalStretch(0);
93 | sizePolicy.setVerticalStretch(0);
94 | sizePolicy.setHeightForWidth(title->sizePolicy().hasHeightForWidth());
95 | title->setSizePolicy(sizePolicy);
96 | QFont font1;
97 | font1.setFamily(QString::fromUtf8("Times New Roman"));
98 | font1.setPointSize(18);
99 | title->setFont(font1);
100 | title->setAlignment(Qt::AlignCenter);
101 |
102 | verticalLayout_4->addWidget(title);
103 |
104 | buttonWidget = new QWidget(titleWidget);
105 | buttonWidget->setObjectName(QString::fromUtf8("buttonWidget"));
106 | buttonWidget->setMaximumSize(QSize(16777215, 16777215));
107 | verticalLayout_2 = new QVBoxLayout(buttonWidget);
108 | verticalLayout_2->setObjectName(QString::fromUtf8("verticalLayout_2"));
109 | analysisPushButton = new QPushButton(buttonWidget);
110 | analysisPushButton->setObjectName(QString::fromUtf8("analysisPushButton"));
111 |
112 | verticalLayout_2->addWidget(analysisPushButton);
113 |
114 | nfaPushButton = new QPushButton(buttonWidget);
115 | nfaPushButton->setObjectName(QString::fromUtf8("nfaPushButton"));
116 |
117 | verticalLayout_2->addWidget(nfaPushButton);
118 |
119 | dfaPushButton = new QPushButton(buttonWidget);
120 | dfaPushButton->setObjectName(QString::fromUtf8("dfaPushButton"));
121 |
122 | verticalLayout_2->addWidget(dfaPushButton);
123 |
124 | minimumDfaPushButton = new QPushButton(buttonWidget);
125 | minimumDfaPushButton->setObjectName(QString::fromUtf8("minimumDfaPushButton"));
126 |
127 | verticalLayout_2->addWidget(minimumDfaPushButton);
128 |
129 | codePushButton = new QPushButton(buttonWidget);
130 | codePushButton->setObjectName(QString::fromUtf8("codePushButton"));
131 |
132 | verticalLayout_2->addWidget(codePushButton);
133 |
134 | resetPushButton = new QPushButton(buttonWidget);
135 | resetPushButton->setObjectName(QString::fromUtf8("resetPushButton"));
136 |
137 | verticalLayout_2->addWidget(resetPushButton);
138 |
139 |
140 | verticalLayout_4->addWidget(buttonWidget);
141 |
142 |
143 | gridLayout->addWidget(titleWidget, 0, 0, 1, 1);
144 |
145 | textWidget = new QWidget(centralwidget);
146 | textWidget->setObjectName(QString::fromUtf8("textWidget"));
147 | textWidget->setMinimumSize(QSize(0, 0));
148 | verticalLayout = new QVBoxLayout(textWidget);
149 | verticalLayout->setObjectName(QString::fromUtf8("verticalLayout"));
150 | label = new QLabel(textWidget);
151 | label->setObjectName(QString::fromUtf8("label"));
152 | label->setAlignment(Qt::AlignCenter);
153 |
154 | verticalLayout->addWidget(label);
155 |
156 | textEdit = new QTextEdit(textWidget);
157 | textEdit->setObjectName(QString::fromUtf8("textEdit"));
158 |
159 | verticalLayout->addWidget(textEdit);
160 |
161 |
162 | gridLayout->addWidget(textWidget, 2, 0, 1, 1);
163 |
164 | mainWidget = new QWidget(centralwidget);
165 | mainWidget->setObjectName(QString::fromUtf8("mainWidget"));
166 | mainWidget->setMinimumSize(QSize(0, 0));
167 | mainWidget->setMaximumSize(QSize(16777215, 16777215));
168 | verticalLayout_3 = new QVBoxLayout(mainWidget);
169 | verticalLayout_3->setObjectName(QString::fromUtf8("verticalLayout_3"));
170 | labelOfTable = new QLabel(mainWidget);
171 | labelOfTable->setObjectName(QString::fromUtf8("labelOfTable"));
172 | labelOfTable->setAlignment(Qt::AlignCenter);
173 |
174 | verticalLayout_3->addWidget(labelOfTable);
175 |
176 | tableWidget = new QTableWidget(mainWidget);
177 | tableWidget->setObjectName(QString::fromUtf8("tableWidget"));
178 |
179 | verticalLayout_3->addWidget(tableWidget);
180 |
181 |
182 | gridLayout->addWidget(mainWidget, 0, 1, 1, 1);
183 |
184 | MainWindow->setCentralWidget(centralwidget);
185 | menubar = new QMenuBar(MainWindow);
186 | menubar->setObjectName(QString::fromUtf8("menubar"));
187 | menubar->setGeometry(QRect(0, 0, 800, 21));
188 | MainWindow->setMenuBar(menubar);
189 | statusbar = new QStatusBar(MainWindow);
190 | statusbar->setObjectName(QString::fromUtf8("statusbar"));
191 | MainWindow->setStatusBar(statusbar);
192 |
193 | retranslateUi(MainWindow);
194 |
195 | QMetaObject::connectSlotsByName(MainWindow);
196 | } // setupUi
197 |
198 | void retranslateUi(QMainWindow *MainWindow)
199 | {
200 | MainWindow->setWindowTitle(QApplication::translate("MainWindow", "MainWindow", nullptr));
201 | textBrowser->setHtml(QApplication::translate("MainWindow", "\n"
202 | "\n"
205 | "Regex2DFA\346\230\257\351\200\232\350\277\207\350\276\223\345\205\245\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217\347\273\204\357\274\214\345\260\206\344\273\245"_"\345\274\200\345\244\264\347\232\204\345\217\230\351\207\217\346\211\200\344\273\243\350\241\250\347\232\204\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217\350\275\254\346\215\242\344\270\272NFA\343\200\201DFA\343\200"
206 | "\201\346\234\200\345\260\217\345\214\226DFA\347\232\204\350\275\257\344\273\266\343\200\202
\n"
207 | "\357\274\210\344\273\245\347\273\277\350\211\262\346\240\207\350\256\260\345\247\213\346\200\201\357\274\214\344\273\245\347\272\242\350\211\262\346\240\207\350\256\260\347\273\210\346\200\201\357\274\211
\n"
208 | "
\n"
209 | "\345\205\266\344\270\255\357\274\214\350\275\257\344\273\266\345\256\236\347\216\260\344\272\206\357\274\232
\n"
211 | "1. \345\210\206\346\236\220\357\274\210\345\267\246\344\276\247\347\254\254\344\270\200\344\270\252\346\214\211\351\222\256\357\274\214\346\214\211\344\270\213\350\257\245\346\214\211\351\222\256\345\220\216\345\217\257\344\273\245\351\200\232\350\277\207\347\202\271\345\207\273\345\267\246\344\276\2472\343\200\2013\343\200\2014\346\214\211\351\222\256\346\230\276\347\244\272\344\270\215\345\220\214\345\206\205\345\256\271\357\274\211
\n"
212 | ""
213 | "2. \346\255\243\345\210\231\350\241\250\350\276\276\345\274\217\350\275\254\344\270\272NFA\357\274\210\345\267\246\344\276\247\347\254\254\344\272\214\344\270\252\346\214\211\351\222\256\357\274\211
\n"
214 | "3. NFA\350\275\254\344\270\272DFA\357\274\210\345\267\246\344\276\247\347\254\254\344\270\211\344\270\252\346\214\211\351\222\256\357\274\211
\n"
215 | "4. DFA\346\234\200\345\260\217\345\214\226\357\274\210\345\267\246\344\276\247\347\254\254\345\233\233\344\270\252\346\214\211\351\222\256\357\274\211
\n"
217 | "5. \351\207\215\347\275\256\357\274\210\345\217\257\344\273\245\351\207\215\346\226\260\350\276\223\345\205\245\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217\357\274\211
\n"
218 | "\347\202\271\345\207\273\345\210\206\346\236\220\346\214\211\351\222\256\357\274\214\345\256\236\347\216\260\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217->NFA->DFA->\346\234\200\345\260\217\345\214\226DFA
\n"
220 | "
\n"
221 | "\346\263\250\357\274\232
1. \350\276\223\345\205\245\345\217\252\350\203\275\345\214\205\345\220\253\344\270\200\344\270\252\344\273\245"_"\345\274\200\345\244\264\347\232\204\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217\357\274\214\345\220\246\345"
222 | "\210\231\350\275\257\344\273\266\345\217\257\350\203\275\344\274\232\345\207\272\347\216\260\350\257\257\350\247\243\357\274\214\344\272\247\347\224\237\344\270\215\346\255\243\347\241\256\347\232\204NFA\343\200\201DFA\347\255\211\357\274\233
2. \347\255\211\345\217\267\345\267\246\344\276\247\347\232\204\346\240\207\350\257\206\347\254\246\344\273\205\345\214\205\345\220\253\345\255\227\346\257\215\343\200\201\346\225\260\345\255\227\344\270\216\344\270\213\345\210\222\347\272\277
\n"
223 | "3. \350\275\257\344\273\266\344\273\245#\345\255\227\347\254\246\344\275\234\344\270\272NFA\350\275\254\346\215\242\347\232\204epsilon\346\240\207\350\257\206\357\274\214\345\233\240\346\255\244\357\274\214\345\270\214\346\234\233\350\276\223\345\205\245#\345\255\227\347\254\246\357\274\214\345\272\224\345\275\223\344\275\277\347\224\250\350"
224 | "\275\254\344\271\211\347\254\246\345\217\267\357\274\214\350\276\223\345\205\245\\#\346\211\215\345\217\257\344\273\245\350\257\206\345\210\253\343\200\202
\n"
225 | "
\n"
226 | "\350\276\203\351\225\277\347\232\204\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217\345\217\257\350\203\275\344\274\232\345\210\206\346\236\220\350\276\203\344\271\205\357\274\214\350\257\267\346\202\250\350\200\220\345\277\203\347\255\211\345\276\205\357\274\214\350\260\242\350\260\242\343\200\202
", nullptr));
227 | title->setText(QApplication::translate("MainWindow", "Regex2DFA", nullptr));
228 | analysisPushButton->setText(QApplication::translate("MainWindow", "\345\210\206\346\236\220", nullptr));
229 | nfaPushButton->setText(QApplication::translate("MainWindow", "\346\230\276\347\244\272NFA", nullptr));
230 | dfaPushButton->setText(QApplication::translate("MainWindow", "\346\230\276\347\244\272DFA", nullptr));
231 | minimumDfaPushButton->setText(QApplication::translate("MainWindow", "\346\234\200\345\260\217\345\214\226DFA", nullptr));
232 | codePushButton->setText(QApplication::translate("MainWindow", "\347\224\237\346\210\220\345\214\271\351\205\215\344\273\243\347\240\201", nullptr));
233 | resetPushButton->setText(QApplication::translate("MainWindow", "\351\207\215\347\275\256\347\250\213\345\272\217", nullptr));
234 | label->setText(QApplication::translate("MainWindow", "\350\257\267\345\234\250\344\270\213\346\226\271\346\241\206\344\270\255\345\206\231\345\205\245\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217", nullptr));
235 | labelOfTable->setText(QApplication::translate("MainWindow", "NFA\343\200\201DFA\345\261\225\347\244\272\350\241\250", nullptr));
236 | } // retranslateUi
237 |
238 | };
239 |
240 | namespace Ui {
241 | class MainWindow: public Ui_MainWindow {};
242 | } // namespace Ui
243 |
244 | QT_END_NAMESPACE
245 |
246 | #endif // UI_MAINWINDOW_H
247 |
--------------------------------------------------------------------------------
/第二次实验/regex2dfa/codedialog.cpp:
--------------------------------------------------------------------------------
1 | #include "codedialog.h"
2 | #include "ui_codedialog.h"
3 |
4 | #include
5 | #include
6 |
7 | CodeDialog::CodeDialog(QWidget *parent, QString code) :
8 | QDialog(parent),
9 | ui(new Ui::CodeDialog)
10 | {
11 | ui->setupUi(this);
12 | ui->codeBrowser->setText(code);
13 |
14 | this->setWindowTitle("匹配代码");
15 | QScreen *screen = QGuiApplication::primaryScreen();
16 | QRect screenGeometry = screen->geometry();
17 | int screenWidth = screenGeometry.width();
18 | int screenHeight = screenGeometry.height();
19 | int newWidth = screenWidth * 0.6;
20 | int newHeight = screenHeight * 0.6;
21 | this->resize(newWidth, newHeight);
22 | }
23 |
24 | CodeDialog::~CodeDialog()
25 | {
26 | delete ui;
27 | }
28 |
--------------------------------------------------------------------------------
/第二次实验/regex2dfa/codedialog.h:
--------------------------------------------------------------------------------
1 | #ifndef CODEDIALOG_H
2 | #define CODEDIALOG_H
3 |
4 | #include
5 |
6 | namespace Ui {
7 | class CodeDialog;
8 | }
9 |
10 | class CodeDialog : public QDialog
11 | {
12 | Q_OBJECT
13 |
14 | public:
15 | explicit CodeDialog(QWidget *parent = nullptr, QString code = "");
16 | ~CodeDialog();
17 |
18 | private:
19 | Ui::CodeDialog *ui;
20 | };
21 |
22 | #endif // CODEDIALOG_H
23 |
--------------------------------------------------------------------------------
/第二次实验/regex2dfa/codedialog.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | CodeDialog
4 |
5 |
6 |
7 | 0
8 | 0
9 | 648
10 | 496
11 |
12 |
13 |
14 | Dialog
15 |
16 |
17 | -
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/第二次实验/regex2dfa/dfa.cpp:
--------------------------------------------------------------------------------
1 | #include "dfa.h"
2 |
3 | #include
4 | #include
5 |
6 | DFA::DFA()
7 | {
8 |
9 | }
10 |
11 | void DFA::fromNFA(NFA nfa)
12 | {
13 | QHash, int> revMapping;
14 | // 获取始态
15 | QSet startSet;
16 | startSet.insert(nfa.startState);
17 | startSet = nfa.epsilonClosure(startSet);
18 | mapping[startState] = startSet;
19 | revMapping[startSet] = startState;
20 | if (startSet.contains(nfa.endState)) {
21 | endStates.insert(startState);
22 | }
23 | stateNum++;
24 |
25 | QSet> vis; // 判断某个状态是否被找到
26 | QQueue q; // 循环队列
27 | q.push_back(startState);
28 | while (!q.empty()) {
29 |
30 | // 取出队头
31 | int stateItem = q.front();
32 | q.pop_front();
33 | QSet nfaStateSet = mapping[stateItem];
34 |
35 | // 未曾出现过的状态集合才需要查找
36 | if (!vis.contains(nfaStateSet)) {
37 |
38 | // 查询当前状态集合的转移
39 | for (QString changeItem: nfa.stateSet) {
40 | if (changeItem != "epsilon") {
41 |
42 | // 获取对应转移到的epsilon闭包
43 | QSet changeClosure = nfa.valueClosure(nfaStateSet, changeItem);
44 | QSet changeEpsilon = nfa.epsilonClosure(changeClosure);
45 |
46 | // 找不到集合
47 | if (changeEpsilon.empty()) continue;
48 |
49 | // 之前找到过这个状态
50 | if (revMapping.contains(changeEpsilon)) {
51 | int nextItem = revMapping[changeEpsilon];
52 |
53 | G[stateItem][changeItem] = nextItem;
54 | if (changeEpsilon.contains(nfa.endState)) {
55 | endStates.insert(nextItem);
56 | }
57 |
58 | if (!vis.contains(changeEpsilon)) {
59 | q.push_back(nextItem);
60 | }
61 | } else {
62 | int nextItem = stateNum;
63 | mapping[nextItem] = changeEpsilon;
64 | revMapping[changeEpsilon] = nextItem;
65 |
66 | G[stateItem][changeItem] = nextItem;
67 | if (changeEpsilon.contains(nfa.endState)) {
68 | endStates.insert(nextItem);
69 | }
70 | stateNum++;
71 |
72 | if (!vis.contains(changeEpsilon)) {
73 | q.push_back(nextItem);
74 | }
75 | }
76 | }
77 | }
78 | vis.insert(nfaStateSet);
79 | }
80 | }
81 | }
82 |
83 | void DFA::fromDFA(DFA dfa)
84 | {
85 | QSet notEndStates;
86 | for (int i = 0; i < dfa.stateNum; i++) {
87 | if (!dfa.endStates.contains(i)) {
88 | notEndStates.insert(i);
89 | }
90 | }
91 |
92 | // 找到原始DFA的转移类型
93 | QSet stateSet;
94 | for (int i = 0; i < dfa.stateNum; i++) {
95 | for (QString changeItem: dfa.G[i].keys()) {
96 | stateSet.insert(changeItem);
97 | }
98 | }
99 |
100 | QQueue> q;
101 | if (!notEndStates.empty()) q.push_back(notEndStates);
102 | if (!dfa.endStates.empty()) q.push_back(dfa.endStates);
103 |
104 | int lastQueLen = 0, cnt = 0;
105 | if (!notEndStates.empty()) lastQueLen++;
106 | if (!dfa.endStates.empty()) lastQueLen++;
107 |
108 | while (true) {
109 |
110 | QSet stateItem = q.front();
111 |
112 | // 划分集合
113 | bool flag = true;
114 | for (QString changeItem: stateSet) {
115 | QSet> vis;
116 | QHash, QSet> setHash; // 当前选出
117 | for (int beginItem: stateItem) {
118 | if (!dfa.G.contains(beginItem)) continue;
119 | if (!dfa.G[beginItem].contains(changeItem)) {
120 | vis.insert(QSet());
121 | setHash[QSet()].insert(beginItem);
122 | continue;
123 | }
124 | for (QSet queueItem: q) {
125 | if (queueItem.contains(dfa.G[beginItem][changeItem])){
126 | vis.insert(queueItem);
127 | setHash[queueItem].insert(beginItem);
128 | break;
129 | }
130 | }
131 | }
132 | if (vis.size() <= 1) {
133 | continue;
134 | } else {
135 | for (QSet visItem: vis) {
136 | q.push_back(setHash[visItem]);
137 | }
138 | flag = false;
139 | break;
140 | }
141 | }
142 |
143 | q.pop_front();
144 | if (flag) q.push_back(stateItem);
145 |
146 | // 判断“上一个”队列是否全被扫过一次
147 | cnt++;
148 | if (lastQueLen == cnt) {
149 | if (lastQueLen == q.size()) {
150 | break;
151 | }
152 | lastQueLen = q.size();
153 | cnt = 0;
154 | }
155 | }
156 |
157 | int idx = 0;
158 | // 设置新编号
159 | QHash, int> revMapping;
160 | for (QSet queueItem: q) {
161 | if (queueItem.empty()) continue;
162 | mapping[idx] = queueItem;
163 | revMapping[queueItem] = idx;
164 | idx++;
165 | }
166 | stateNum = idx;
167 |
168 | // 找到始态和终态
169 | for (QSet queueItem: q) {
170 | if (queueItem.empty()) continue;
171 | // 找始态
172 | if (queueItem.contains(dfa.startState)) {
173 | startState = revMapping[queueItem];
174 | }
175 | // 找终态
176 | for (int endState: dfa.endStates) {
177 | if (queueItem.contains(endState)) {
178 | endStates.insert(revMapping[queueItem]);
179 | break;
180 | }
181 | }
182 | }
183 |
184 | // 构建新的G
185 | for (QSet queueItem: q) {
186 | if (queueItem.empty()) continue;
187 | int stateItem = *queueItem.begin();
188 | for (QString changeItem: stateSet) {
189 | if (!dfa.G[stateItem].contains(changeItem)) continue;
190 | int endItem = dfa.G[stateItem][changeItem];
191 | for (QSet endQueueItem: q) {
192 | if (endQueueItem.contains(endItem)) {
193 | G[revMapping[queueItem]][changeItem] = revMapping[endQueueItem];
194 | break;
195 | }
196 | }
197 | }
198 | }
199 | }
200 |
201 | void DFA::clear()
202 | {
203 | mapping.clear();
204 | G.clear();
205 | endStates.clear();
206 | stateNum = 0;
207 | startState = 0;
208 | }
209 |
210 | QString DFA::toCode()
211 | {
212 | QString code = "bool matchFunction(string s) {\n";
213 | code += "\tint idx = 0, state = ";
214 | code += QString::number(startState) + ";\n";
215 | code += "\twhile (state < " + QString::number(stateNum) + " && idx < s.size()) {\n";
216 | code += "\t\tswitch (state) {\n";
217 | for (int i = 0; i < stateNum; i++) {
218 | if (G[i].keys().empty()) continue;
219 | code += "\t\tcase " + QString::number(i) + ":\n";
220 | code += "\t\t\tswitch (s[idx]) {\n";
221 | for (QString changeItem: G[i].keys()) {
222 | code += "\t\t\tcase \'" + changeItem + "\':\n";
223 | code += "\t\t\t\tstate = " + QString::number(G[i][changeItem]) + ";\n";
224 | code += "\t\t\t\tbreak;\n";
225 | }
226 | code += "\t\t\tdefault:\n";
227 | code += "\t\t\t\tstate = " + QString::number(stateNum) + ";\n";
228 | code += "\t\t\t\tbreak;\n";
229 | code += "\t\t\t}\n";
230 | code += "\t\t\tbreak;\n";
231 | }
232 | code += "\t\tdefault:\n";
233 | code += "\t\t\tstate = " + QString::number(stateNum) + ";\n";
234 | code += "\t\t\tbreak;\n";
235 | code += "\t\t}\n";
236 | code += "\t\tidx++\n";
237 | code += "\t}\n";
238 | code += "\tif (";
239 | int cnt = 0;
240 | for (int i: endStates) {
241 | code += "state == " + QString::number(i);
242 | cnt++;
243 | if (cnt != endStates.size()) code += "||";
244 | }
245 | code += ") return true;\n";
246 | code += "\telse return false;\n";
247 | code += "}";
248 | return code;
249 | }
250 |
--------------------------------------------------------------------------------
/第二次实验/regex2dfa/dfa.h:
--------------------------------------------------------------------------------
1 | #ifndef DFA_H
2 | #define DFA_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | #include "nfa.h"
10 |
11 | class DFA
12 | {
13 | public:
14 | DFA();
15 | void fromNFA(NFA nfa);
16 | void fromDFA(DFA dfa);
17 | void clear();
18 | QString toCode();
19 |
20 | QHash> mapping;
21 | QHash> G;
22 | int startState;
23 | QSet endStates;
24 | int stateNum; // 表示状态数量
25 |
26 | };
27 |
28 | #endif // DFA_H
29 |
--------------------------------------------------------------------------------
/第二次实验/regex2dfa/main.cpp:
--------------------------------------------------------------------------------
1 | #include "mainwindow.h"
2 |
3 | #include
4 |
5 | int main(int argc, char *argv[])
6 | {
7 | QApplication a(argc, argv);
8 | MainWindow w;
9 | w.show();
10 | return a.exec();
11 | }
12 |
--------------------------------------------------------------------------------
/第二次实验/regex2dfa/mainwindow.cpp:
--------------------------------------------------------------------------------
1 | #include "mainwindow.h"
2 | #include "ui_mainwindow.h"
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | #include "utils.h"
15 | #include "nfa.h"
16 | #include "dfa.h"
17 | #include "codedialog.h"
18 |
19 | MainWindow::MainWindow(QWidget *parent)
20 | : QMainWindow(parent)
21 | , ui(new Ui::MainWindow)
22 | , nfa()
23 | {
24 | ui->setupUi(this);
25 |
26 | // 页面基本布局
27 | this->setWindowTitle("编译原理实验二:正则表达式转NFA、DFA");
28 | QScreen *screen = QGuiApplication::primaryScreen();
29 | QRect screenGeometry = screen->geometry();
30 | int screenWidth = screenGeometry.width();
31 | int screenHeight = screenGeometry.height();
32 | int newWidth = screenWidth * 0.9;
33 | int newHeight = screenHeight * 0.9;
34 | this->resize(newWidth, newHeight);
35 |
36 | // 分析并产生NFA、DFA、最小化DFA
37 | connect(ui->analysisPushButton, &QPushButton::clicked, this, [=]() {
38 | this->nfa.clear();
39 | this->dfa.clear();
40 | this->miniDFA.clear();
41 | this->code = "";
42 | tableRemoveAll();
43 |
44 | QString allText = ui->textEdit->toPlainText();
45 | if (allText != "") {
46 |
47 | QStringList lines = allText.split('\n', QString::SkipEmptyParts);
48 |
49 | // 预处理正则表达式组
50 | QString mainRegex = regexListPreprocessing(lines);
51 |
52 | // 正则表达式转后缀表达式
53 | QString postFixRegex = regexToPostFix(mainRegex);
54 |
55 | // 后缀表达式转NFA
56 | this->nfa.fromRegex(postFixRegex);
57 |
58 | // NFA转DFA
59 | this->dfa.fromNFA(this->nfa);
60 |
61 | // DFA最小化
62 | this->miniDFA.fromDFA(this->dfa);
63 |
64 | // 转换为代码
65 | this->code = this->miniDFA.toCode();
66 | }
67 |
68 | });
69 |
70 | // 显示NFA
71 | connect(ui->nfaPushButton, &QPushButton::clicked, this, [=]() {
72 | // 清空原来的表格
73 | tableRemoveAll();
74 |
75 | ui->tableWidget->setRowCount(nfa.stateNum);
76 | ui->tableWidget->setColumnCount(nfa.stateSet.size());
77 | QStringList strListColumnHander;
78 | for (QString item : nfa.stateSet) {
79 | strListColumnHander << tr(item.toStdString().c_str());
80 | }
81 | ui->tableWidget->setHorizontalHeaderLabels(strListColumnHander);
82 | QStringList strListRowHander;
83 | for (int i = 0; i < nfa.stateNum; i++) {
84 | strListRowHander << tr(QString::number(i).toStdString().c_str());
85 | }
86 | ui->tableWidget->setVerticalHeaderLabels(strListRowHander);
87 | for (int i = 0; i < nfa.stateNum; i++) {
88 | int j = 0;
89 | for (QString stateChange: nfa.stateSet) {
90 | QString itemString = "";
91 | for (int k = 0; k < nfa.stateNum; k++) {
92 | if (nfa.G[i][k] == stateChange) {
93 | itemString += QString::number(k);
94 | itemString += ",";
95 | }
96 | }
97 | ui->tableWidget->setItem(i, j, new QTableWidgetItem(itemString.left(itemString.size() - 1)));
98 | j++;
99 | }
100 | }
101 |
102 | // 添加始态、终态颜色
103 | QTableWidgetItem *beginItem = ui->tableWidget->verticalHeaderItem(nfa.startState);
104 | QTableWidgetItem *endItem = ui->tableWidget->verticalHeaderItem(nfa.endState);
105 | beginItem->setTextColor(QColor(0, 255, 0));
106 | endItem->setTextColor(QColor(255, 0, 0));
107 | });
108 |
109 | // 显示DFA
110 | connect(ui->dfaPushButton, &QPushButton::clicked, this, [=]() {
111 | // 清空原来的表格
112 | tableRemoveAll();
113 |
114 | ui->tableWidget->setRowCount(dfa.stateNum);
115 | ui->tableWidget->setColumnCount(nfa.stateSet.size() - (nfa.stateSet.contains("epsilon") ? 1 : 0));
116 |
117 | QStringList strListColumnHander;
118 | for (QString item : nfa.stateSet) {
119 | if (item == "epsilon") continue;
120 | strListColumnHander << tr(item.toStdString().c_str());
121 | }
122 | ui->tableWidget->setHorizontalHeaderLabels(strListColumnHander);
123 |
124 | QStringList strListRowHander;
125 | for (int i = 0; i < dfa.stateNum; i++) {
126 | strListRowHander << tr(QString::number(i).toStdString().c_str());
127 | }
128 | ui->tableWidget->setVerticalHeaderLabels(strListRowHander);
129 |
130 | for (int i = 0; i < dfa.stateNum; i++) {
131 | if (!dfa.G.contains(i)) continue;
132 | int j = 0;
133 | for (QString stateChange: nfa.stateSet) {
134 | if (stateChange == "epsilon") continue;
135 | if (dfa.G[i].contains(stateChange)) {
136 |
137 | int k = dfa.G[i][stateChange];
138 | QString titleTr = QString::number(k) + ":{";
139 | for (int item: dfa.mapping[k]) {
140 | titleTr += QString::number(item);
141 | titleTr += ",";
142 | }
143 | titleTr = titleTr.left(titleTr.size() - 1);
144 | titleTr += "}";
145 |
146 | ui->tableWidget->setItem(i, j, new QTableWidgetItem(titleTr));
147 | }
148 | j++;
149 | }
150 | }
151 |
152 | // 添加始态、终态颜色
153 | QTableWidgetItem *beginItem = ui->tableWidget->verticalHeaderItem(dfa.startState);
154 | beginItem->setTextColor(QColor(0, 255, 0));
155 | for (int endState: dfa.endStates) {
156 | QTableWidgetItem *endItem = ui->tableWidget->verticalHeaderItem(endState);
157 | endItem->setTextColor(QColor(255, 0, 0));
158 | }
159 |
160 | });
161 |
162 | // 显示最小化的DFA
163 | connect(ui->minimumDfaPushButton, &QPushButton::clicked, this, [=]() {
164 | // 清空原来的表格
165 | tableRemoveAll();
166 |
167 | ui->tableWidget->setRowCount(miniDFA.stateNum);
168 | ui->tableWidget->setColumnCount(nfa.stateSet.size() - (nfa.stateSet.contains("epsilon") ? 1 : 0));
169 |
170 | QStringList strListColumnHander;
171 | for (QString item : nfa.stateSet) {
172 | if (item == "epsilon") continue;
173 | strListColumnHander << tr(item.toStdString().c_str());
174 | }
175 | ui->tableWidget->setHorizontalHeaderLabels(strListColumnHander);
176 |
177 | QStringList strListRowHander;
178 | for (int i = 0; i < miniDFA.stateNum; i++) {
179 | strListRowHander << tr(QString::number(i).toStdString().c_str());
180 | }
181 | ui->tableWidget->setVerticalHeaderLabels(strListRowHander);
182 |
183 | for (int i = 0; i < miniDFA.stateNum; i++) {
184 | if (!miniDFA.G.contains(i)) continue;
185 | int j = 0;
186 | for (QString stateChange: nfa.stateSet) {
187 | if (stateChange == "epsilon") continue;
188 | if (miniDFA.G[i].contains(stateChange)) {
189 |
190 | int k = miniDFA.G[i][stateChange];
191 | QString titleTr = QString::number(k) + ":{";
192 | for (int item: miniDFA.mapping[k]) {
193 | titleTr += QString::number(item);
194 | titleTr += ",";
195 | }
196 | titleTr = titleTr.left(titleTr.size() - 1);
197 | titleTr += "}";
198 |
199 | ui->tableWidget->setItem(i, j, new QTableWidgetItem(titleTr));
200 | }
201 | j++;
202 | }
203 | }
204 |
205 | // 添加始态、终态颜色
206 | QTableWidgetItem *beginItem = ui->tableWidget->verticalHeaderItem(miniDFA.startState);
207 | beginItem->setTextColor(QColor(0, 255, 0));
208 | for (int endState: miniDFA.endStates) {
209 | QTableWidgetItem *endItem = ui->tableWidget->verticalHeaderItem(endState);
210 | endItem->setTextColor(QColor(255, 0, 0));
211 | }
212 | });
213 |
214 | // 生成匹配代码
215 | connect(ui->codePushButton, &QPushButton::clicked, this, [=]() {
216 | CodeDialog* codeDialog = new CodeDialog(this, code);
217 | codeDialog->exec();
218 | });
219 |
220 | // 重置程序
221 | connect(ui->resetPushButton, &QPushButton::clicked, this, [=]() {
222 | this->nfa.clear();
223 | this->dfa.clear();
224 | this->miniDFA.clear();
225 | this->code = "";
226 | tableRemoveAll();
227 | });
228 |
229 | }
230 |
231 | MainWindow::~MainWindow()
232 | {
233 | delete ui;
234 | }
235 |
236 | QString MainWindow::regexListPreprocessing(QStringList regexList) {
237 | int lineSize = regexList.size();
238 |
239 | // 去除所有空格
240 | for (int i = 0; i < lineSize; i++) {
241 | regexList[i].replace(" ", "");
242 | }
243 |
244 | // 替换[]
245 | for (int i = 0; i < lineSize; i++) {
246 | int leftBracket = -1; // 左右括号位置
247 | for (int j = 0; j < regexList[i].size(); j++) {
248 | if (regexList[i][j] == '\\') { // 转义字符
249 | j++;
250 | } else if (regexList[i][j] == '[') { // 匹配左括号
251 | leftBracket = j;
252 | } else if (regexList[i][j] == ']') {
253 | if (leftBracket == -1) { // 出现了右括号,但没有出现左括号
254 | continue;
255 | } else {
256 | if (leftBracket == j - 1) continue; // 特判一下
257 | QString bracketString = regexList[i].mid(leftBracket + 1, j - leftBracket - 1);
258 | QString newBracketString = "";
259 | int bracketLen = bracketString.size();
260 | int groupNum = bracketLen / 3;
261 | for (int k = 0; k < groupNum; k++) {
262 | int beginIndex = 3 * k;
263 | int endIndex = 3 * k + 2;
264 | for (char l = bracketString[beginIndex].unicode(); l <= bracketString[endIndex].unicode(); l++) {
265 | newBracketString.append(l);
266 | newBracketString.append('|');
267 | }
268 | }
269 | if (groupNum != 0) {
270 | // 替换字符串
271 | int newBracketStringLen = newBracketString.size();
272 | newBracketString = newBracketString.mid(0, newBracketStringLen - 1);
273 | regexList[i] = regexList[i].left(leftBracket) + "(" + newBracketString + ")" + regexList[i].right(regexList[i].size() - j - 1);
274 | j = leftBracket + newBracketString.size() + 2 - 1;
275 | }
276 | leftBracket = -1; // 后续需要重新匹配中括号
277 | }
278 | }
279 | }
280 | }
281 |
282 | // 替换正闭包+
283 | for (int i = 0; i < lineSize; i++) {
284 | int leftBracket = -1;
285 | for (int j = 0; j < regexList[i].size(); j++) {
286 | if (regexList[i][j] == '\\') { // 转义字符
287 | j++;
288 | } else if (regexList[i][j] == '(') { // 匹配左括号
289 | leftBracket = j;
290 | } else if (regexList[i][j] == '+') { // 遇到加号
291 | if (j > 0) {
292 | regexList[i][j] = '*';
293 | if (regexList[i][j - 1] == ')') {
294 | regexList[i].insert(leftBracket, regexList[i].mid(leftBracket, j - leftBracket));
295 | j += j - leftBracket;
296 | } else {
297 | regexList[i].insert(j - 1, regexList[i][j - 1]);
298 | j += 1;
299 | }
300 | }
301 | }
302 | }
303 | }
304 |
305 | // 替换可选?
306 | for (int i = 0; i < lineSize; i++) {
307 | int leftBracket = -1;
308 | for (int j = 0; j < regexList[i].size(); j++) {
309 | if (regexList[i][j] == '\\') { // 转义字符
310 | j++;
311 | } else if (regexList[i][j] == '(') { // 匹配左括号
312 | leftBracket = j;
313 | } else if (regexList[i][j] == '?') { // 遇到加号
314 | if (j > 0) {
315 | regexList[i][j] = '|';
316 | if (regexList[i][j - 1] == ')') {
317 | regexList[i].insert(leftBracket, '(');
318 | regexList[i].insert(j + 2, "#)");
319 | j += 3;
320 | } else {
321 | regexList[i].insert(j - 1, '(');
322 | regexList[i].insert(j + 2, "#)");
323 | j += 3;
324 | }
325 | }
326 | }
327 | }
328 | }
329 |
330 | // 获取要转换成为NFA的主正则表达式
331 | QString mainRegex;
332 | QHash regexHash;
333 | for (int i = 0; i < lineSize; i++) {
334 | QString regexItem = regexList[i];
335 | if (regexItem[0] == '_') { // 主正则表达式 (仅考虑有一个主正则表达式的情况)
336 | mainRegex = regexItem;
337 | regexList.removeAt(i);
338 | } else { // 非主正则表达式,获取其等号左侧的标识符
339 | int regexLen = regexItem.size();
340 | for (int j = 0; j < regexLen; j++) {
341 | if (regexItem[j] == '=') {
342 | QString identifier = regexItem.left(j);
343 | QString regexBody = regexItem.right(regexLen - j - 1);
344 | regexHash.insert(identifier, regexBody);
345 | break; // 只捕获第一个等号(等号前的转义符不考虑)
346 | }
347 | }
348 | }
349 | }
350 |
351 | // 合并正则表达式
352 | for (auto key: regexHash.keys()) {
353 | QString replaceString = regexHash[key];
354 | if (replaceString[0] != '(' || replaceString[replaceString.size() - 1] != ')') {
355 | replaceString = "(" + replaceString + ")";
356 | }
357 | for (auto& value: regexHash.values()) {
358 | value.replace(key, replaceString);
359 | }
360 | mainRegex.replace(key, replaceString);
361 | }
362 |
363 | // 主正则表达式添加连接符
364 | mainRegex = mainRegex.right(mainRegex.size() - mainRegex.indexOf('=') - 1);
365 | for (int i = 0; i < mainRegex.size() - 1; i++) {
366 | if (mainRegex[i] == '\\') {
367 | i++;
368 | if (i < mainRegex.size() - 1) {
369 | if (mainRegex[i + 1] == '(' || !isOperator(mainRegex[i + 1].unicode())) {
370 | mainRegex.insert(i + 1, '.');
371 | i++;
372 | }
373 | }
374 | } else if (isOperator(mainRegex[i].unicode())) {
375 | if (mainRegex[i] == ')' || mainRegex[i] == '*') {
376 | if (!isOperator(mainRegex[i + 1].unicode()) || mainRegex[i + 1] == '(') {
377 | mainRegex.insert(i + 1, '.');
378 | i++;
379 | }
380 | }
381 | } else {
382 | if (!isOperator(mainRegex[i + 1].unicode()) || mainRegex[i + 1] == '(') {
383 | mainRegex.insert(i + 1, '.');
384 | i++;
385 | }
386 | }
387 | }
388 |
389 | return mainRegex;
390 | }
391 |
392 | QString MainWindow::regexToPostFix(QString re)
393 | {
394 | QStack s1;
395 | QStack s2;
396 | for (int i = 0; i < re.size(); i++) {
397 | if (re[i] == '\\') {
398 | if (i < re.size() - 1) {
399 | s2.push(re.mid(i, 2));
400 | }
401 | i++;
402 | }
403 | else if (isOperator(re[i].unicode())) {
404 | if (re[i] == '(') {
405 | s1.push(re[i]); // 左括号直接放入符号栈
406 | } else if (re[i] == ')') {
407 | while (!s1.empty() && s1.top() != '(') { // 右括号不断拿出符号栈内容,放入结果栈,直到遇到结果栈
408 | s2.push(s1.top());
409 | s1.pop();
410 | }
411 | if (!s1.empty() && s1.top() == '(') s1.pop();
412 | } else {
413 | int nowPriority = getPriority(re[i].unicode());
414 | while (!s1.empty() && getPriority(s1.top().unicode()) >= nowPriority) {
415 | s2.push(s1.top());
416 | s1.pop();
417 | }
418 | if (s1.empty() || getPriority(s1.top().unicode()) < nowPriority) {
419 | s1.push(re[i]);
420 | }
421 | }
422 | } else {
423 | s2.push(re.mid(i, 1));
424 | }
425 | }
426 | while (!s1.empty()) {
427 | s2.push(s1.top());
428 | s1.pop();
429 | }
430 | QStack s3;
431 | QString postFixRegex = "";
432 | while (!s2.empty()) {
433 | s3.push(s2.top());
434 | s2.pop();
435 | }
436 | while (!s3.empty()) {
437 | postFixRegex += s3.top();
438 | s3.pop();
439 | }
440 | return postFixRegex;
441 | }
442 |
443 | void MainWindow::tableRemoveAll()
444 | {
445 | ui->tableWidget->clear();
446 | ui->tableWidget->setHorizontalHeaderLabels(QStringList());
447 | ui->tableWidget->setRowCount(0);
448 | ui->tableWidget->setColumnCount(0);
449 | }
450 |
451 |
--------------------------------------------------------------------------------
/第二次实验/regex2dfa/mainwindow.h:
--------------------------------------------------------------------------------
1 | #ifndef MAINWINDOW_H
2 | #define MAINWINDOW_H
3 |
4 | #include
5 | #include
6 |
7 | #include "nfa.h"
8 | #include "dfa.h"
9 |
10 | QT_BEGIN_NAMESPACE
11 | namespace Ui { class MainWindow; }
12 | QT_END_NAMESPACE
13 |
14 | class MainWindow : public QMainWindow
15 | {
16 | Q_OBJECT
17 |
18 | public:
19 | MainWindow(QWidget *parent = nullptr);
20 | ~MainWindow();
21 |
22 | private:
23 | Ui::MainWindow *ui;
24 |
25 | NFA nfa;
26 | DFA dfa;
27 | DFA miniDFA;
28 | QString code;
29 |
30 | static QString regexListPreprocessing(QStringList allRegex);
31 | static QString regexToPostFix(QString re);
32 |
33 | void tableRemoveAll(void);
34 | };
35 | #endif // MAINWINDOW_H
36 |
--------------------------------------------------------------------------------
/第二次实验/regex2dfa/mainwindow.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | MainWindow
4 |
5 |
6 |
7 | 0
8 | 0
9 | 800
10 | 600
11 |
12 |
13 |
14 | MainWindow
15 |
16 |
17 |
18 | -
19 |
20 |
21 |
-
22 |
23 |
24 |
25 | 宋体
26 | 10
27 |
28 |
29 |
30 | 1
31 |
32 |
33 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
34 | <html><head><meta name="qrichtext" content="1" /><style type="text/css">
35 | p, li { white-space: pre-wrap; }
36 | </style></head><body style=" font-family:'宋体'; font-size:10pt; font-weight:400; font-style:normal;">
37 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun'; font-size:11pt; font-weight:600;">Regex2DFA</span><span style=" font-family:'SimSun'; font-size:11pt;">是通过输入正则表达式组,将以"_"开头的变量所代表的正则表达式转换为NFA、DFA、最小化DFA的软件。</span></p>
38 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun'; font-size:11pt;">(以</span><span style=" font-family:'SimSun'; font-size:11pt; color:#00ff00;">绿色</span><span style=" font-family:'SimSun'; font-size:11pt;">标记始态,以</span><span style=" font-family:'SimSun'; font-size:11pt; color:#ff0000;">红色</span><span style=" font-family:'SimSun'; font-size:11pt;">标记终态)</span></p>
39 | <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'SimSun'; font-size:11pt;"><br /></p>
40 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun'; font-size:11pt;">其中,软件实现了:</span></p>
41 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun'; font-size:11pt;">1. </span><span style=" font-family:'SimSun'; font-size:11pt; font-weight:600;">分析</span><span style=" font-family:'SimSun'; font-size:11pt;">(左侧第一个按钮,按下该按钮后可以通过点击左侧2、3、4按钮显示不同内容)</span></p>
42 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun'; font-size:11pt;">2. </span><span style=" font-family:'SimSun'; font-size:11pt; font-weight:600;">正则表达式转为NFA</span><span style=" font-family:'SimSun'; font-size:11pt;">(左侧第二个按钮)</span></p>
43 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun'; font-size:11pt;">3. </span><span style=" font-family:'SimSun'; font-size:11pt; font-weight:600;">NFA转为DFA</span><span style=" font-family:'SimSun'; font-size:11pt;">(左侧第三个按钮)</span></p>
44 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun'; font-size:11pt;">4. </span><span style=" font-family:'SimSun'; font-size:11pt; font-weight:600;">DFA最小化</span><span style=" font-family:'SimSun'; font-size:11pt;">(左侧第四个按钮)</span></p>
45 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun'; font-size:11pt;">5. </span><span style=" font-family:'SimSun'; font-size:11pt; font-weight:600;">重置</span><span style=" font-family:'SimSun'; font-size:11pt;">(可以重新输入正则表达式)</span></p>
46 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun'; font-size:11pt;">点击分析按钮,实现正则表达式->NFA->DFA->最小化DFA</span></p>
47 | <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'SimSun'; font-size:11pt;"><br /></p>
48 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun'; font-size:11pt;">注:<br />1. 输入只能包含一个以"_"开头的正则表达式,否则软件可能会出现误解,产生不正确的NFA、DFA等;<br />2. 等号左侧的标识符仅包含字母、数字与下划线</span></p>
49 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun'; font-size:11pt;">3. 软件以#字符作为NFA转换的epsilon标识,因此,希望输入#字符,应当使用转义符号,输入\#才可以识别。</span></p>
50 | <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'SimSun'; font-size:11pt;"><br /></p>
51 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun'; font-size:11pt; color:#aa0000;">较长的正则表达式可能会分析较久,请您耐心等待,谢谢。</span></p></body></html>
52 |
53 |
54 |
55 |
56 |
57 |
58 | -
59 |
60 |
61 |
-
62 |
63 |
64 |
65 | 0
66 | 0
67 |
68 |
69 |
70 |
71 | Times New Roman
72 | 18
73 |
74 |
75 |
76 | Regex2DFA
77 |
78 |
79 | Qt::AlignCenter
80 |
81 |
82 |
83 | -
84 |
85 |
86 |
87 | 16777215
88 | 16777215
89 |
90 |
91 |
92 |
-
93 |
94 |
95 | 分析
96 |
97 |
98 |
99 | -
100 |
101 |
102 | 显示NFA
103 |
104 |
105 |
106 | -
107 |
108 |
109 | 显示DFA
110 |
111 |
112 |
113 | -
114 |
115 |
116 | 最小化DFA
117 |
118 |
119 |
120 | -
121 |
122 |
123 | 生成匹配代码
124 |
125 |
126 |
127 | -
128 |
129 |
130 | 重置程序
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 | -
141 |
142 |
143 |
144 | 0
145 | 0
146 |
147 |
148 |
149 |
-
150 |
151 |
152 | 请在下方框中写入正则表达式
153 |
154 |
155 | Qt::AlignCenter
156 |
157 |
158 |
159 | -
160 |
161 |
162 |
163 |
164 |
165 | -
166 |
167 |
168 |
169 | 0
170 | 0
171 |
172 |
173 |
174 |
175 | 16777215
176 | 16777215
177 |
178 |
179 |
180 |
-
181 |
182 |
183 | NFA、DFA展示表
184 |
185 |
186 | Qt::AlignCenter
187 |
188 |
189 |
190 | -
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
208 |
209 |
210 |
211 |
212 |
213 |
--------------------------------------------------------------------------------
/第二次实验/regex2dfa/nfa.cpp:
--------------------------------------------------------------------------------
1 | #include "nfa.h"
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | #include "utils.h"
10 |
11 | NFA::NFA(int begin, int end): startState(begin), endState(end), stateNum(0), maxStateNum(100) {
12 | G.resize(maxStateNum);
13 | for (int i = 0; i < maxStateNum; i++) {
14 | G[i].resize(maxStateNum);
15 | }
16 | }
17 |
18 | void NFA::clear()
19 | {
20 | // 清空nfa
21 | for (int i = 0; i < maxStateNum; i++) {
22 | G[i].clear();
23 | }
24 | G.clear();
25 | maxStateNum = 100;
26 | stateNum = 0;
27 | startState = -1;
28 | endState = -1;
29 | G.resize(maxStateNum);
30 | for (int i = 0; i < maxStateNum; i++) {
31 | G[i].resize(maxStateNum);
32 | }
33 | stateSet.clear();
34 | }
35 |
36 | void NFA::fromRegex(QString re) // 后缀表达式转NFA
37 | {
38 | QStack> s;
39 | for (int i = 0; i < re.size(); i++) {
40 | if (re[i] == '\\') { // 转义字符
41 | if (i < re.size() - 1) {
42 | allocateMemory(2);
43 | G[stateNum][stateNum + 1] = re[i + 1]; // 转义符不再出现
44 | s.append({stateNum, stateNum + 1}); // 入栈
45 | stateNum += 2; // 状态数量 + 2
46 | stateSet.insert(QString(re[i + 1]));
47 | }
48 | i++;
49 | } else if (re[i] == '*' || re[i] == '|' || re[i] == '.') { // 运算符
50 | if (re[i] == '.') {
51 | if (s.size() >= 2) { // 取出两个元素相连重新入栈
52 | QPair tail = s.top();
53 | s.pop();
54 | QPair head = s.top();
55 | s.pop();
56 | G[head.second][tail.first] = "epsilon"; // 连接操作
57 | s.append({head.first, tail.second}); // 重新入栈
58 | stateSet.insert("epsilon");
59 | }
60 | } else if (re[i] == '|') {
61 | allocateMemory(2);
62 | if (s.size() >= 2) { // 取出两个元素相连重新入栈
63 | QPair left = s.top();
64 | s.pop();
65 | QPair right = s.top();
66 | s.pop();
67 | G[stateNum][left.first] = "epsilon";
68 | G[stateNum][right.first] = "epsilon";
69 | G[left.second][stateNum + 1] = "epsilon";
70 | G[right.second][stateNum + 1] = "epsilon";
71 | s.append({stateNum, stateNum + 1}); // 入栈
72 | stateNum += 2; // 状态数量 + 2
73 | stateSet.insert("epsilon");
74 | }
75 | } else {
76 | allocateMemory(2);
77 | if (!s.empty()) { // 闭包为单元运算符,因此只需要取出一个元素
78 | QPair item = s.top();
79 | s.pop();
80 | G[stateNum][stateNum + 1] = "epsilon";
81 | G[stateNum][item.first] = "epsilon";
82 | G[item.second][stateNum + 1] = "epsilon";
83 | G[item.second][item.first] = "epsilon";
84 | s.append({stateNum, stateNum + 1}); // 入栈
85 | stateNum += 2; // 状态数量 + 2
86 | stateSet.insert("epsilon");
87 | }
88 | }
89 | } else {
90 | if (re[i] == '#') { // epsilon
91 | allocateMemory(2);
92 | G[stateNum][stateNum + 1] = "epsilon";
93 | s.append({stateNum, stateNum + 1}); // 入栈
94 | stateNum += 2; // 状态数量 + 2
95 | stateSet.insert("epsilon");
96 | } else { // 其他非运算符
97 | allocateMemory(2);
98 | G[stateNum][stateNum + 1] = re[i];
99 | s.append({stateNum, stateNum + 1}); // 入栈
100 | stateNum += 2; // 状态数量 + 2
101 | stateSet.insert(QString(re[i]));
102 | }
103 | }
104 | }
105 | if (!s.empty()){
106 | QPair statePair = s.top();
107 | startState = statePair.first;
108 | endState = statePair.second;
109 | }
110 | }
111 |
112 | void NFA::allocateMemory(int num)
113 | {
114 | // 当状态数即将超过最大状态时,分配更大的空间
115 | while (stateNum + num >= maxStateNum) {
116 | QVector> newG(2 * maxStateNum);
117 | for (int i = 0; i < 2 * maxStateNum; i++) {
118 | newG[i].resize(2 * maxStateNum);
119 | }
120 | for (int i = 0; i < maxStateNum; i++) {
121 | for (int j = 0; j < maxStateNum; j++) {
122 | newG[i][j] = G[i][j];
123 | }
124 | }
125 | maxStateNum *= 2;
126 | G = newG;
127 | }
128 | }
129 |
130 | QSet NFA::epsilonClosure(QSet state)
131 | {
132 | while (true) {
133 | QSet tmpSet;
134 | for (int item: state) {
135 | for (int i = 0; i < maxStateNum; i++) {
136 | if (G[item][i] == "epsilon") {
137 | if (!state.contains(i)) {
138 | tmpSet.insert(i);
139 | }
140 | }
141 | }
142 | }
143 | if (tmpSet.empty()) break;
144 | state = state.unite(tmpSet);
145 | }
146 | return state;
147 | }
148 |
149 | QSet NFA::valueClosure(QSet state, QString value)
150 | {
151 | QSet result;
152 | for (int item: state) {
153 | for (int i = 0; i < maxStateNum; i++) {
154 | if (G[item][i] == value) {
155 | result.insert(i);
156 | }
157 | }
158 | }
159 | return result;
160 | }
161 |
--------------------------------------------------------------------------------
/第二次实验/regex2dfa/nfa.h:
--------------------------------------------------------------------------------
1 | #ifndef NFA_H
2 | #define NFA_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | class NFA
9 | {
10 | public:
11 | // 构造函数
12 | NFA(int begin = -1, int end = -1);
13 | void clear(); // 清空NFA
14 | void fromRegex(QString re); // 从后缀表达式构造NFA
15 | void allocateMemory(int num); // 分配G内存的函数
16 | QSet epsilonClosure(QSet state); // 计算epsilon闭包
17 | QSet valueClosure(QSet state, QString value); // 计算某个集合状态能通过value转移到的状态集合
18 |
19 | // 成员变量
20 | QVector> G; // 邻接矩阵
21 | QSet stateSet; // 存储所有状态类型
22 | int startState; // 表示起始状态
23 | int endState; // 表示终止状态
24 | int stateNum; // 表示状态数量
25 | int maxStateNum; // 表示当前最多存储的状态数量
26 | };
27 |
28 | #endif // NFA_H
29 |
--------------------------------------------------------------------------------
/第二次实验/regex2dfa/regex2dfa.pro:
--------------------------------------------------------------------------------
1 | QT += core gui
2 |
3 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
4 |
5 | CONFIG += c++11
6 |
7 | # You can make your code fail to compile if it uses deprecated APIs.
8 | # In order to do so, uncomment the following line.
9 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
10 |
11 | SOURCES += \
12 | codedialog.cpp \
13 | dfa.cpp \
14 | main.cpp \
15 | mainwindow.cpp \
16 | nfa.cpp \
17 | utils.cpp
18 |
19 | HEADERS += \
20 | codedialog.h \
21 | dfa.h \
22 | mainwindow.h \
23 | nfa.h \
24 | utils.h
25 |
26 | FORMS += \
27 | codedialog.ui \
28 | mainwindow.ui
29 |
30 | # Default rules for deployment.
31 | qnx: target.path = /tmp/$${TARGET}/bin
32 | else: unix:!android: target.path = /opt/$${TARGET}/bin
33 | !isEmpty(target.path): INSTALLS += target
34 |
--------------------------------------------------------------------------------
/第二次实验/regex2dfa/regex2dfa.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WA-automat/Compiler_Design_SCNU/0f3e92c521e9b785ae8af9784feade5078582a6f/第二次实验/regex2dfa/regex2dfa.zip
--------------------------------------------------------------------------------
/第二次实验/regex2dfa/utils.cpp:
--------------------------------------------------------------------------------
1 | #include "utils.h"
2 |
3 | int getPriority(const char &op) // 返回运算符优先级
4 | {
5 | switch (op) {
6 | case '(':
7 | case ')':
8 | return 0;
9 | case '*':
10 | return 4;
11 | case '|':
12 | return 1;
13 | case '.':
14 | return 2;
15 | default:
16 | return -1;
17 | }
18 | }
19 |
20 | bool isOperator(const char &ch)
21 | {
22 | switch (ch) {
23 | case '(':
24 | case ')':
25 | case '*':
26 | case '|':
27 | case '.':
28 | return true; // 运算符返回true
29 | default:
30 | return false; // 非运算符返回false
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/第二次实验/regex2dfa/utils.h:
--------------------------------------------------------------------------------
1 | #ifndef UTILS_H
2 | #define UTILS_H
3 |
4 | int getPriority(const char &op);
5 | bool isOperator(const char &ch);
6 |
7 | #endif // UTILS_H
8 |
--------------------------------------------------------------------------------
/第二次实验/编译原理实验二.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WA-automat/Compiler_Design_SCNU/0f3e92c521e9b785ae8af9784feade5078582a6f/第二次实验/编译原理实验二.zip
--------------------------------------------------------------------------------
/第二次实验/编译原理实验二/文档文件夹/编译原理实验二.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WA-automat/Compiler_Design_SCNU/0f3e92c521e9b785ae8af9784feade5078582a6f/第二次实验/编译原理实验二/文档文件夹/编译原理实验二.pdf
--------------------------------------------------------------------------------
/第二次实验/编译原理实验二/源代码文件夹/regex2dfa/codedialog.cpp:
--------------------------------------------------------------------------------
1 | #include "codedialog.h"
2 | #include "ui_codedialog.h"
3 |
4 | #include
5 | #include
6 |
7 | CodeDialog::CodeDialog(QWidget *parent, QString code) :
8 | QDialog(parent),
9 | ui(new Ui::CodeDialog)
10 | {
11 | ui->setupUi(this);
12 | ui->codeBrowser->setText(code);
13 |
14 | this->setWindowTitle("匹配代码");
15 | QScreen *screen = QGuiApplication::primaryScreen();
16 | QRect screenGeometry = screen->geometry();
17 | int screenWidth = screenGeometry.width();
18 | int screenHeight = screenGeometry.height();
19 | int newWidth = screenWidth * 0.6;
20 | int newHeight = screenHeight * 0.6;
21 | this->resize(newWidth, newHeight);
22 | }
23 |
24 | CodeDialog::~CodeDialog()
25 | {
26 | delete ui;
27 | }
28 |
--------------------------------------------------------------------------------
/第二次实验/编译原理实验二/源代码文件夹/regex2dfa/codedialog.h:
--------------------------------------------------------------------------------
1 | #ifndef CODEDIALOG_H
2 | #define CODEDIALOG_H
3 |
4 | #include
5 |
6 | namespace Ui {
7 | class CodeDialog;
8 | }
9 |
10 | class CodeDialog : public QDialog
11 | {
12 | Q_OBJECT
13 |
14 | public:
15 | explicit CodeDialog(QWidget *parent = nullptr, QString code = "");
16 | ~CodeDialog();
17 |
18 | private:
19 | Ui::CodeDialog *ui;
20 | };
21 |
22 | #endif // CODEDIALOG_H
23 |
--------------------------------------------------------------------------------
/第二次实验/编译原理实验二/源代码文件夹/regex2dfa/codedialog.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | CodeDialog
4 |
5 |
6 |
7 | 0
8 | 0
9 | 648
10 | 496
11 |
12 |
13 |
14 | Dialog
15 |
16 |
17 | -
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/第二次实验/编译原理实验二/源代码文件夹/regex2dfa/dfa.cpp:
--------------------------------------------------------------------------------
1 | #include "dfa.h"
2 |
3 | #include
4 | #include
5 |
6 | DFA::DFA()
7 | {
8 |
9 | }
10 |
11 | void DFA::fromNFA(NFA nfa)
12 | {
13 | QHash, int> revMapping;
14 | // 获取始态
15 | QSet startSet;
16 | startSet.insert(nfa.startState);
17 | startSet = nfa.epsilonClosure(startSet);
18 | mapping[startState] = startSet;
19 | revMapping[startSet] = startState;
20 | if (startSet.contains(nfa.endState)) {
21 | endStates.insert(startState);
22 | }
23 | stateNum++;
24 |
25 | QSet> vis; // 判断某个状态是否被找到
26 | QQueue q; // 循环队列
27 | q.push_back(startState);
28 | while (!q.empty()) {
29 |
30 | // 取出队头
31 | int stateItem = q.front();
32 | q.pop_front();
33 | QSet nfaStateSet = mapping[stateItem];
34 |
35 | // 未曾出现过的状态集合才需要查找
36 | if (!vis.contains(nfaStateSet)) {
37 |
38 | // 查询当前状态集合的转移
39 | for (QString changeItem: nfa.stateSet) {
40 | if (changeItem != "epsilon") {
41 |
42 | // 获取对应转移到的epsilon闭包
43 | QSet changeClosure = nfa.valueClosure(nfaStateSet, changeItem);
44 | QSet changeEpsilon = nfa.epsilonClosure(changeClosure);
45 |
46 | // 找不到集合
47 | if (changeEpsilon.empty()) continue;
48 |
49 | // 之前找到过这个状态
50 | if (revMapping.contains(changeEpsilon)) {
51 | int nextItem = revMapping[changeEpsilon];
52 |
53 | G[stateItem][changeItem] = nextItem;
54 | if (changeEpsilon.contains(nfa.endState)) {
55 | endStates.insert(nextItem);
56 | }
57 |
58 | if (!vis.contains(changeEpsilon)) {
59 | q.push_back(nextItem);
60 | }
61 | } else {
62 | int nextItem = stateNum;
63 | mapping[nextItem] = changeEpsilon;
64 | revMapping[changeEpsilon] = nextItem;
65 |
66 | G[stateItem][changeItem] = nextItem;
67 | if (changeEpsilon.contains(nfa.endState)) {
68 | endStates.insert(nextItem);
69 | }
70 | stateNum++;
71 |
72 | if (!vis.contains(changeEpsilon)) {
73 | q.push_back(nextItem);
74 | }
75 | }
76 | }
77 | }
78 | vis.insert(nfaStateSet);
79 | }
80 | }
81 | }
82 |
83 | void DFA::fromDFA(DFA dfa)
84 | {
85 | QSet notEndStates;
86 | for (int i = 0; i < dfa.stateNum; i++) {
87 | if (!dfa.endStates.contains(i)) {
88 | notEndStates.insert(i);
89 | }
90 | }
91 |
92 | // 找到原始DFA的转移类型
93 | QSet stateSet;
94 | for (int i = 0; i < dfa.stateNum; i++) {
95 | for (QString changeItem: dfa.G[i].keys()) {
96 | stateSet.insert(changeItem);
97 | }
98 | }
99 |
100 | QQueue> q;
101 | if (!notEndStates.empty()) q.push_back(notEndStates);
102 | if (!dfa.endStates.empty()) q.push_back(dfa.endStates);
103 |
104 | int lastQueLen = 0, cnt = 0;
105 | if (!notEndStates.empty()) lastQueLen++;
106 | if (!dfa.endStates.empty()) lastQueLen++;
107 |
108 | while (true) {
109 |
110 | QSet stateItem = q.front();
111 |
112 | // 划分集合
113 | bool flag = true;
114 | for (QString changeItem: stateSet) {
115 | QSet> vis;
116 | QHash, QSet> setHash; // 当前选出
117 | for (int beginItem: stateItem) {
118 | if (!dfa.G.contains(beginItem)) continue;
119 | if (!dfa.G[beginItem].contains(changeItem)) {
120 | vis.insert(QSet());
121 | setHash[QSet()].insert(beginItem);
122 | continue;
123 | }
124 | for (QSet queueItem: q) {
125 | if (queueItem.contains(dfa.G[beginItem][changeItem])){
126 | vis.insert(queueItem);
127 | setHash[queueItem].insert(beginItem);
128 | break;
129 | }
130 | }
131 | }
132 | if (vis.size() <= 1) {
133 | continue;
134 | } else {
135 | for (QSet visItem: vis) {
136 | q.push_back(setHash[visItem]);
137 | }
138 | flag = false;
139 | break;
140 | }
141 | }
142 |
143 | q.pop_front();
144 | if (flag) q.push_back(stateItem);
145 |
146 | // 判断“上一个”队列是否全被扫过一次
147 | cnt++;
148 | if (lastQueLen == cnt) {
149 | if (lastQueLen == q.size()) {
150 | break;
151 | }
152 | lastQueLen = q.size();
153 | cnt = 0;
154 | }
155 | }
156 |
157 | int idx = 0;
158 | // 设置新编号
159 | QHash, int> revMapping;
160 | for (QSet queueItem: q) {
161 | if (queueItem.empty()) continue;
162 | mapping[idx] = queueItem;
163 | revMapping[queueItem] = idx;
164 | idx++;
165 | }
166 | stateNum = idx;
167 |
168 | // 找到始态和终态
169 | for (QSet queueItem: q) {
170 | if (queueItem.empty()) continue;
171 | // 找始态
172 | if (queueItem.contains(dfa.startState)) {
173 | startState = revMapping[queueItem];
174 | }
175 | // 找终态
176 | for (int endState: dfa.endStates) {
177 | if (queueItem.contains(endState)) {
178 | endStates.insert(revMapping[queueItem]);
179 | break;
180 | }
181 | }
182 | }
183 |
184 | // 构建新的G
185 | for (QSet queueItem: q) {
186 | if (queueItem.empty()) continue;
187 | int stateItem = *queueItem.begin();
188 | for (QString changeItem: stateSet) {
189 | if (!dfa.G[stateItem].contains(changeItem)) continue;
190 | int endItem = dfa.G[stateItem][changeItem];
191 | for (QSet endQueueItem: q) {
192 | if (endQueueItem.contains(endItem)) {
193 | G[revMapping[queueItem]][changeItem] = revMapping[endQueueItem];
194 | break;
195 | }
196 | }
197 | }
198 | }
199 | }
200 |
201 | void DFA::clear()
202 | {
203 | mapping.clear();
204 | G.clear();
205 | endStates.clear();
206 | stateNum = 0;
207 | startState = 0;
208 | }
209 |
210 | QString DFA::toCode()
211 | {
212 | QString code = "bool matchFunction(string s) {\n";
213 | code += "\tint idx = 0, state = ";
214 | code += QString::number(startState) + ";\n";
215 | code += "\twhile (state < " + QString::number(stateNum) + " && idx < s.size()) {\n";
216 | code += "\t\tswitch (state) {\n";
217 | for (int i = 0; i < stateNum; i++) {
218 | if (G[i].keys().empty()) continue;
219 | code += "\t\tcase " + QString::number(i) + ":\n";
220 | code += "\t\t\tswitch (s[idx]) {\n";
221 | for (QString changeItem: G[i].keys()) {
222 | code += "\t\t\tcase \'" + changeItem + "\':\n";
223 | code += "\t\t\t\tstate = " + QString::number(G[i][changeItem]) + ";\n";
224 | code += "\t\t\t\tbreak;\n";
225 | }
226 | code += "\t\t\tdefault:\n";
227 | code += "\t\t\t\tstate = " + QString::number(stateNum) + ";\n";
228 | code += "\t\t\t\tbreak;\n";
229 | code += "\t\t\t}\n";
230 | code += "\t\t\tbreak;\n";
231 | }
232 | code += "\t\tdefault:\n";
233 | code += "\t\t\tstate = " + QString::number(stateNum) + ";\n";
234 | code += "\t\t\tbreak;\n";
235 | code += "\t\t}\n";
236 | code += "\t\tidx++\n";
237 | code += "\t}\n";
238 | code += "\tif (";
239 | int cnt = 0;
240 | for (int i: endStates) {
241 | code += "state == " + QString::number(i);
242 | cnt++;
243 | if (cnt != endStates.size()) code += "||";
244 | }
245 | code += ") return true;\n";
246 | code += "\telse return false;\n";
247 | code += "}";
248 | return code;
249 | }
250 |
--------------------------------------------------------------------------------
/第二次实验/编译原理实验二/源代码文件夹/regex2dfa/dfa.h:
--------------------------------------------------------------------------------
1 | #ifndef DFA_H
2 | #define DFA_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | #include "nfa.h"
10 |
11 | class DFA
12 | {
13 | public:
14 | DFA();
15 | void fromNFA(NFA nfa);
16 | void fromDFA(DFA dfa);
17 | void clear();
18 | QString toCode();
19 |
20 | QHash> mapping;
21 | QHash> G;
22 | int startState;
23 | QSet endStates;
24 | int stateNum; // 表示状态数量
25 |
26 | };
27 |
28 | #endif // DFA_H
29 |
--------------------------------------------------------------------------------
/第二次实验/编译原理实验二/源代码文件夹/regex2dfa/main.cpp:
--------------------------------------------------------------------------------
1 | #include "mainwindow.h"
2 |
3 | #include
4 |
5 | int main(int argc, char *argv[])
6 | {
7 | QApplication a(argc, argv);
8 | MainWindow w;
9 | w.show();
10 | return a.exec();
11 | }
12 |
--------------------------------------------------------------------------------
/第二次实验/编译原理实验二/源代码文件夹/regex2dfa/mainwindow.cpp:
--------------------------------------------------------------------------------
1 | #include "mainwindow.h"
2 | #include "ui_mainwindow.h"
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | #include "utils.h"
15 | #include "nfa.h"
16 | #include "dfa.h"
17 | #include "codedialog.h"
18 |
19 | MainWindow::MainWindow(QWidget *parent)
20 | : QMainWindow(parent)
21 | , ui(new Ui::MainWindow)
22 | , nfa()
23 | {
24 | ui->setupUi(this);
25 |
26 | // 页面基本布局
27 | this->setWindowTitle("编译原理实验二:正则表达式转NFA、DFA");
28 | QScreen *screen = QGuiApplication::primaryScreen();
29 | QRect screenGeometry = screen->geometry();
30 | int screenWidth = screenGeometry.width();
31 | int screenHeight = screenGeometry.height();
32 | int newWidth = screenWidth * 0.9;
33 | int newHeight = screenHeight * 0.9;
34 | this->resize(newWidth, newHeight);
35 |
36 | // 分析并产生NFA、DFA、最小化DFA
37 | connect(ui->analysisPushButton, &QPushButton::clicked, this, [=]() {
38 | this->nfa.clear();
39 | this->dfa.clear();
40 | this->miniDFA.clear();
41 | this->code = "";
42 | tableRemoveAll();
43 |
44 | QString allText = ui->textEdit->toPlainText();
45 | if (allText != "") {
46 |
47 | QStringList lines = allText.split('\n', QString::SkipEmptyParts);
48 |
49 | // 预处理正则表达式组
50 | QString mainRegex = regexListPreprocessing(lines);
51 |
52 | // 正则表达式转后缀表达式
53 | QString postFixRegex = regexToPostFix(mainRegex);
54 |
55 | // 后缀表达式转NFA
56 | this->nfa.fromRegex(postFixRegex);
57 |
58 | // NFA转DFA
59 | this->dfa.fromNFA(this->nfa);
60 |
61 | // DFA最小化
62 | this->miniDFA.fromDFA(this->dfa);
63 |
64 | // 转换为代码
65 | this->code = this->miniDFA.toCode();
66 | }
67 |
68 | });
69 |
70 | // 显示NFA
71 | connect(ui->nfaPushButton, &QPushButton::clicked, this, [=]() {
72 | // 清空原来的表格
73 | tableRemoveAll();
74 |
75 | ui->tableWidget->setRowCount(nfa.stateNum);
76 | ui->tableWidget->setColumnCount(nfa.stateSet.size());
77 | QStringList strListColumnHander;
78 | for (QString item : nfa.stateSet) {
79 | strListColumnHander << tr(item.toStdString().c_str());
80 | }
81 | ui->tableWidget->setHorizontalHeaderLabels(strListColumnHander);
82 | QStringList strListRowHander;
83 | for (int i = 0; i < nfa.stateNum; i++) {
84 | strListRowHander << tr(QString::number(i).toStdString().c_str());
85 | }
86 | ui->tableWidget->setVerticalHeaderLabels(strListRowHander);
87 | for (int i = 0; i < nfa.stateNum; i++) {
88 | int j = 0;
89 | for (QString stateChange: nfa.stateSet) {
90 | QString itemString = "";
91 | for (int k = 0; k < nfa.stateNum; k++) {
92 | if (nfa.G[i][k] == stateChange) {
93 | itemString += QString::number(k);
94 | itemString += ",";
95 | }
96 | }
97 | ui->tableWidget->setItem(i, j, new QTableWidgetItem(itemString.left(itemString.size() - 1)));
98 | j++;
99 | }
100 | }
101 |
102 | // 添加始态、终态颜色
103 | QTableWidgetItem *beginItem = ui->tableWidget->verticalHeaderItem(nfa.startState);
104 | QTableWidgetItem *endItem = ui->tableWidget->verticalHeaderItem(nfa.endState);
105 | beginItem->setTextColor(QColor(0, 255, 0));
106 | endItem->setTextColor(QColor(255, 0, 0));
107 | });
108 |
109 | // 显示DFA
110 | connect(ui->dfaPushButton, &QPushButton::clicked, this, [=]() {
111 | // 清空原来的表格
112 | tableRemoveAll();
113 |
114 | ui->tableWidget->setRowCount(dfa.stateNum);
115 | ui->tableWidget->setColumnCount(nfa.stateSet.size() - (nfa.stateSet.contains("epsilon") ? 1 : 0));
116 |
117 | QStringList strListColumnHander;
118 | for (QString item : nfa.stateSet) {
119 | if (item == "epsilon") continue;
120 | strListColumnHander << tr(item.toStdString().c_str());
121 | }
122 | ui->tableWidget->setHorizontalHeaderLabels(strListColumnHander);
123 |
124 | QStringList strListRowHander;
125 | for (int i = 0; i < dfa.stateNum; i++) {
126 | strListRowHander << tr(QString::number(i).toStdString().c_str());
127 | }
128 | ui->tableWidget->setVerticalHeaderLabels(strListRowHander);
129 |
130 | for (int i = 0; i < dfa.stateNum; i++) {
131 | if (!dfa.G.contains(i)) continue;
132 | int j = 0;
133 | for (QString stateChange: nfa.stateSet) {
134 | if (stateChange == "epsilon") continue;
135 | if (dfa.G[i].contains(stateChange)) {
136 |
137 | int k = dfa.G[i][stateChange];
138 | QString titleTr = QString::number(k) + ":{";
139 | for (int item: dfa.mapping[k]) {
140 | titleTr += QString::number(item);
141 | titleTr += ",";
142 | }
143 | titleTr = titleTr.left(titleTr.size() - 1);
144 | titleTr += "}";
145 |
146 | ui->tableWidget->setItem(i, j, new QTableWidgetItem(titleTr));
147 | }
148 | j++;
149 | }
150 | }
151 |
152 | // 添加始态、终态颜色
153 | QTableWidgetItem *beginItem = ui->tableWidget->verticalHeaderItem(dfa.startState);
154 | beginItem->setTextColor(QColor(0, 255, 0));
155 | for (int endState: dfa.endStates) {
156 | QTableWidgetItem *endItem = ui->tableWidget->verticalHeaderItem(endState);
157 | endItem->setTextColor(QColor(255, 0, 0));
158 | }
159 |
160 | });
161 |
162 | // 显示最小化的DFA
163 | connect(ui->minimumDfaPushButton, &QPushButton::clicked, this, [=]() {
164 | // 清空原来的表格
165 | tableRemoveAll();
166 |
167 | ui->tableWidget->setRowCount(miniDFA.stateNum);
168 | ui->tableWidget->setColumnCount(nfa.stateSet.size() - (nfa.stateSet.contains("epsilon") ? 1 : 0));
169 |
170 | QStringList strListColumnHander;
171 | for (QString item : nfa.stateSet) {
172 | if (item == "epsilon") continue;
173 | strListColumnHander << tr(item.toStdString().c_str());
174 | }
175 | ui->tableWidget->setHorizontalHeaderLabels(strListColumnHander);
176 |
177 | QStringList strListRowHander;
178 | for (int i = 0; i < miniDFA.stateNum; i++) {
179 | strListRowHander << tr(QString::number(i).toStdString().c_str());
180 | }
181 | ui->tableWidget->setVerticalHeaderLabels(strListRowHander);
182 |
183 | for (int i = 0; i < miniDFA.stateNum; i++) {
184 | if (!miniDFA.G.contains(i)) continue;
185 | int j = 0;
186 | for (QString stateChange: nfa.stateSet) {
187 | if (stateChange == "epsilon") continue;
188 | if (miniDFA.G[i].contains(stateChange)) {
189 |
190 | int k = miniDFA.G[i][stateChange];
191 | QString titleTr = QString::number(k) + ":{";
192 | for (int item: miniDFA.mapping[k]) {
193 | titleTr += QString::number(item);
194 | titleTr += ",";
195 | }
196 | titleTr = titleTr.left(titleTr.size() - 1);
197 | titleTr += "}";
198 |
199 | ui->tableWidget->setItem(i, j, new QTableWidgetItem(titleTr));
200 | }
201 | j++;
202 | }
203 | }
204 |
205 | // 添加始态、终态颜色
206 | QTableWidgetItem *beginItem = ui->tableWidget->verticalHeaderItem(miniDFA.startState);
207 | beginItem->setTextColor(QColor(0, 255, 0));
208 | for (int endState: miniDFA.endStates) {
209 | QTableWidgetItem *endItem = ui->tableWidget->verticalHeaderItem(endState);
210 | endItem->setTextColor(QColor(255, 0, 0));
211 | }
212 | });
213 |
214 | // 生成匹配代码
215 | connect(ui->codePushButton, &QPushButton::clicked, this, [=]() {
216 | CodeDialog* codeDialog = new CodeDialog(this, code);
217 | codeDialog->exec();
218 | });
219 |
220 | // 重置程序
221 | connect(ui->resetPushButton, &QPushButton::clicked, this, [=]() {
222 | this->nfa.clear();
223 | this->dfa.clear();
224 | this->miniDFA.clear();
225 | this->code = "";
226 | tableRemoveAll();
227 | });
228 |
229 | }
230 |
231 | MainWindow::~MainWindow()
232 | {
233 | delete ui;
234 | }
235 |
236 | QString MainWindow::regexListPreprocessing(QStringList regexList) {
237 | int lineSize = regexList.size();
238 |
239 | // 去除所有空格
240 | for (int i = 0; i < lineSize; i++) {
241 | regexList[i].replace(" ", "");
242 | }
243 |
244 | // 替换[]
245 | for (int i = 0; i < lineSize; i++) {
246 | int leftBracket = -1; // 左右括号位置
247 | for (int j = 0; j < regexList[i].size(); j++) {
248 | if (regexList[i][j] == '\\') { // 转义字符
249 | j++;
250 | } else if (regexList[i][j] == '[') { // 匹配左括号
251 | leftBracket = j;
252 | } else if (regexList[i][j] == ']') {
253 | if (leftBracket == -1) { // 出现了右括号,但没有出现左括号
254 | continue;
255 | } else {
256 | if (leftBracket == j - 1) continue; // 特判一下
257 | QString bracketString = regexList[i].mid(leftBracket + 1, j - leftBracket - 1);
258 | QString newBracketString = "";
259 | int bracketLen = bracketString.size();
260 | int groupNum = bracketLen / 3;
261 | for (int k = 0; k < groupNum; k++) {
262 | int beginIndex = 3 * k;
263 | int endIndex = 3 * k + 2;
264 | for (char l = bracketString[beginIndex].unicode(); l <= bracketString[endIndex].unicode(); l++) {
265 | newBracketString.append(l);
266 | newBracketString.append('|');
267 | }
268 | }
269 | if (groupNum != 0) {
270 | // 替换字符串
271 | int newBracketStringLen = newBracketString.size();
272 | newBracketString = newBracketString.mid(0, newBracketStringLen - 1);
273 | regexList[i] = regexList[i].left(leftBracket) + "(" + newBracketString + ")" + regexList[i].right(regexList[i].size() - j - 1);
274 | j = leftBracket + newBracketString.size() + 2 - 1;
275 | }
276 | leftBracket = -1; // 后续需要重新匹配中括号
277 | }
278 | }
279 | }
280 | }
281 |
282 | // 替换正闭包+
283 | for (int i = 0; i < lineSize; i++) {
284 | int leftBracket = -1;
285 | for (int j = 0; j < regexList[i].size(); j++) {
286 | if (regexList[i][j] == '\\') { // 转义字符
287 | j++;
288 | } else if (regexList[i][j] == '(') { // 匹配左括号
289 | leftBracket = j;
290 | } else if (regexList[i][j] == '+') { // 遇到加号
291 | if (j > 0) {
292 | regexList[i][j] = '*';
293 | if (regexList[i][j - 1] == ')') {
294 | regexList[i].insert(leftBracket, regexList[i].mid(leftBracket, j - leftBracket));
295 | j += j - leftBracket;
296 | } else {
297 | regexList[i].insert(j - 1, regexList[i][j - 1]);
298 | j += 1;
299 | }
300 | }
301 | }
302 | }
303 | }
304 |
305 | // 替换可选?
306 | for (int i = 0; i < lineSize; i++) {
307 | int leftBracket = -1;
308 | for (int j = 0; j < regexList[i].size(); j++) {
309 | if (regexList[i][j] == '\\') { // 转义字符
310 | j++;
311 | } else if (regexList[i][j] == '(') { // 匹配左括号
312 | leftBracket = j;
313 | } else if (regexList[i][j] == '?') { // 遇到加号
314 | if (j > 0) {
315 | regexList[i][j] = '|';
316 | if (regexList[i][j - 1] == ')') {
317 | regexList[i].insert(leftBracket, '(');
318 | regexList[i].insert(j + 2, "#)");
319 | j += 3;
320 | } else {
321 | regexList[i].insert(j - 1, '(');
322 | regexList[i].insert(j + 2, "#)");
323 | j += 3;
324 | }
325 | }
326 | }
327 | }
328 | }
329 |
330 | // 获取要转换成为NFA的主正则表达式
331 | QString mainRegex;
332 | QHash regexHash;
333 | for (int i = 0; i < lineSize; i++) {
334 | QString regexItem = regexList[i];
335 | if (regexItem[0] == '_') { // 主正则表达式 (仅考虑有一个主正则表达式的情况)
336 | mainRegex = regexItem;
337 | regexList.removeAt(i);
338 | } else { // 非主正则表达式,获取其等号左侧的标识符
339 | int regexLen = regexItem.size();
340 | for (int j = 0; j < regexLen; j++) {
341 | if (regexItem[j] == '=') {
342 | QString identifier = regexItem.left(j);
343 | QString regexBody = regexItem.right(regexLen - j - 1);
344 | regexHash.insert(identifier, regexBody);
345 | break; // 只捕获第一个等号(等号前的转义符不考虑)
346 | }
347 | }
348 | }
349 | }
350 |
351 | // 合并正则表达式
352 | for (auto key: regexHash.keys()) {
353 | QString replaceString = regexHash[key];
354 | if (replaceString[0] != '(' || replaceString[replaceString.size() - 1] != ')') {
355 | replaceString = "(" + replaceString + ")";
356 | }
357 | for (auto& value: regexHash.values()) {
358 | value.replace(key, replaceString);
359 | }
360 | mainRegex.replace(key, replaceString);
361 | }
362 |
363 | // 主正则表达式添加连接符
364 | mainRegex = mainRegex.right(mainRegex.size() - mainRegex.indexOf('=') - 1);
365 | for (int i = 0; i < mainRegex.size() - 1; i++) {
366 | if (mainRegex[i] == '\\') {
367 | i++;
368 | if (i < mainRegex.size() - 1) {
369 | if (mainRegex[i + 1] == '(' || !isOperator(mainRegex[i + 1].unicode())) {
370 | mainRegex.insert(i + 1, '.');
371 | i++;
372 | }
373 | }
374 | } else if (isOperator(mainRegex[i].unicode())) {
375 | if (mainRegex[i] == ')' || mainRegex[i] == '*') {
376 | if (!isOperator(mainRegex[i + 1].unicode()) || mainRegex[i + 1] == '(') {
377 | mainRegex.insert(i + 1, '.');
378 | i++;
379 | }
380 | }
381 | } else {
382 | if (!isOperator(mainRegex[i + 1].unicode()) || mainRegex[i + 1] == '(') {
383 | mainRegex.insert(i + 1, '.');
384 | i++;
385 | }
386 | }
387 | }
388 |
389 | return mainRegex;
390 | }
391 |
392 | QString MainWindow::regexToPostFix(QString re)
393 | {
394 | QStack s1;
395 | QStack s2;
396 | for (int i = 0; i < re.size(); i++) {
397 | if (re[i] == '\\') {
398 | if (i < re.size() - 1) {
399 | s2.push(re.mid(i, 2));
400 | }
401 | i++;
402 | }
403 | else if (isOperator(re[i].unicode())) {
404 | if (re[i] == '(') {
405 | s1.push(re[i]); // 左括号直接放入符号栈
406 | } else if (re[i] == ')') {
407 | while (!s1.empty() && s1.top() != '(') { // 右括号不断拿出符号栈内容,放入结果栈,直到遇到结果栈
408 | s2.push(s1.top());
409 | s1.pop();
410 | }
411 | if (!s1.empty() && s1.top() == '(') s1.pop();
412 | } else {
413 | int nowPriority = getPriority(re[i].unicode());
414 | while (!s1.empty() && getPriority(s1.top().unicode()) >= nowPriority) {
415 | s2.push(s1.top());
416 | s1.pop();
417 | }
418 | if (s1.empty() || getPriority(s1.top().unicode()) < nowPriority) {
419 | s1.push(re[i]);
420 | }
421 | }
422 | } else {
423 | s2.push(re.mid(i, 1));
424 | }
425 | }
426 | while (!s1.empty()) {
427 | s2.push(s1.top());
428 | s1.pop();
429 | }
430 | QStack s3;
431 | QString postFixRegex = "";
432 | while (!s2.empty()) {
433 | s3.push(s2.top());
434 | s2.pop();
435 | }
436 | while (!s3.empty()) {
437 | postFixRegex += s3.top();
438 | s3.pop();
439 | }
440 | return postFixRegex;
441 | }
442 |
443 | void MainWindow::tableRemoveAll()
444 | {
445 | ui->tableWidget->clear();
446 | ui->tableWidget->setHorizontalHeaderLabels(QStringList());
447 | ui->tableWidget->setRowCount(0);
448 | ui->tableWidget->setColumnCount(0);
449 | }
450 |
451 |
--------------------------------------------------------------------------------
/第二次实验/编译原理实验二/源代码文件夹/regex2dfa/mainwindow.h:
--------------------------------------------------------------------------------
1 | #ifndef MAINWINDOW_H
2 | #define MAINWINDOW_H
3 |
4 | #include
5 | #include
6 |
7 | #include "nfa.h"
8 | #include "dfa.h"
9 |
10 | QT_BEGIN_NAMESPACE
11 | namespace Ui { class MainWindow; }
12 | QT_END_NAMESPACE
13 |
14 | class MainWindow : public QMainWindow
15 | {
16 | Q_OBJECT
17 |
18 | public:
19 | MainWindow(QWidget *parent = nullptr);
20 | ~MainWindow();
21 |
22 | private:
23 | Ui::MainWindow *ui;
24 |
25 | NFA nfa;
26 | DFA dfa;
27 | DFA miniDFA;
28 | QString code;
29 |
30 | static QString regexListPreprocessing(QStringList allRegex);
31 | static QString regexToPostFix(QString re);
32 |
33 | void tableRemoveAll(void);
34 | };
35 | #endif // MAINWINDOW_H
36 |
--------------------------------------------------------------------------------
/第二次实验/编译原理实验二/源代码文件夹/regex2dfa/mainwindow.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | MainWindow
4 |
5 |
6 |
7 | 0
8 | 0
9 | 800
10 | 600
11 |
12 |
13 |
14 | MainWindow
15 |
16 |
17 |
18 | -
19 |
20 |
21 |
-
22 |
23 |
24 |
25 | 宋体
26 | 10
27 |
28 |
29 |
30 | 1
31 |
32 |
33 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
34 | <html><head><meta name="qrichtext" content="1" /><style type="text/css">
35 | p, li { white-space: pre-wrap; }
36 | </style></head><body style=" font-family:'宋体'; font-size:10pt; font-weight:400; font-style:normal;">
37 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun'; font-size:11pt; font-weight:600;">Regex2DFA</span><span style=" font-family:'SimSun'; font-size:11pt;">是通过输入正则表达式组,将以"_"开头的变量所代表的正则表达式转换为NFA、DFA、最小化DFA的软件。</span></p>
38 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun'; font-size:11pt;">(以</span><span style=" font-family:'SimSun'; font-size:11pt; color:#00ff00;">绿色</span><span style=" font-family:'SimSun'; font-size:11pt;">标记始态,以</span><span style=" font-family:'SimSun'; font-size:11pt; color:#ff0000;">红色</span><span style=" font-family:'SimSun'; font-size:11pt;">标记终态)</span></p>
39 | <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'SimSun'; font-size:11pt;"><br /></p>
40 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun'; font-size:11pt;">其中,软件实现了:</span></p>
41 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun'; font-size:11pt;">1. </span><span style=" font-family:'SimSun'; font-size:11pt; font-weight:600;">分析</span><span style=" font-family:'SimSun'; font-size:11pt;">(左侧第一个按钮,按下该按钮后可以通过点击左侧2、3、4按钮显示不同内容)</span></p>
42 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun'; font-size:11pt;">2. </span><span style=" font-family:'SimSun'; font-size:11pt; font-weight:600;">正则表达式转为NFA</span><span style=" font-family:'SimSun'; font-size:11pt;">(左侧第二个按钮)</span></p>
43 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun'; font-size:11pt;">3. </span><span style=" font-family:'SimSun'; font-size:11pt; font-weight:600;">NFA转为DFA</span><span style=" font-family:'SimSun'; font-size:11pt;">(左侧第三个按钮)</span></p>
44 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun'; font-size:11pt;">4. </span><span style=" font-family:'SimSun'; font-size:11pt; font-weight:600;">DFA最小化</span><span style=" font-family:'SimSun'; font-size:11pt;">(左侧第四个按钮)</span></p>
45 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun'; font-size:11pt;">5. </span><span style=" font-family:'SimSun'; font-size:11pt; font-weight:600;">重置</span><span style=" font-family:'SimSun'; font-size:11pt;">(可以重新输入正则表达式)</span></p>
46 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun'; font-size:11pt;">点击分析按钮,实现正则表达式->NFA->DFA->最小化DFA</span></p>
47 | <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'SimSun'; font-size:11pt;"><br /></p>
48 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun'; font-size:11pt;">注:<br />1. 输入只能包含一个以"_"开头的正则表达式,否则软件可能会出现误解,产生不正确的NFA、DFA等;<br />2. 等号左侧的标识符仅包含字母、数字与下划线</span></p>
49 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun'; font-size:11pt;">3. 软件以#字符作为NFA转换的epsilon标识,因此,希望输入#字符,应当使用转义符号,输入\#才可以识别。</span></p>
50 | <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'SimSun'; font-size:11pt;"><br /></p>
51 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun'; font-size:11pt; color:#aa0000;">较长的正则表达式可能会分析较久,请您耐心等待,谢谢。</span></p></body></html>
52 |
53 |
54 |
55 |
56 |
57 |
58 | -
59 |
60 |
61 |
-
62 |
63 |
64 |
65 | 0
66 | 0
67 |
68 |
69 |
70 |
71 | Times New Roman
72 | 18
73 |
74 |
75 |
76 | Regex2DFA
77 |
78 |
79 | Qt::AlignCenter
80 |
81 |
82 |
83 | -
84 |
85 |
86 |
87 | 16777215
88 | 16777215
89 |
90 |
91 |
92 |
-
93 |
94 |
95 | 分析
96 |
97 |
98 |
99 | -
100 |
101 |
102 | 显示NFA
103 |
104 |
105 |
106 | -
107 |
108 |
109 | 显示DFA
110 |
111 |
112 |
113 | -
114 |
115 |
116 | 最小化DFA
117 |
118 |
119 |
120 | -
121 |
122 |
123 | 生成匹配代码
124 |
125 |
126 |
127 | -
128 |
129 |
130 | 重置程序
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 | -
141 |
142 |
143 |
144 | 0
145 | 0
146 |
147 |
148 |
149 |
-
150 |
151 |
152 | 请在下方框中写入正则表达式
153 |
154 |
155 | Qt::AlignCenter
156 |
157 |
158 |
159 | -
160 |
161 |
162 |
163 |
164 |
165 | -
166 |
167 |
168 |
169 | 0
170 | 0
171 |
172 |
173 |
174 |
175 | 16777215
176 | 16777215
177 |
178 |
179 |
180 |
-
181 |
182 |
183 | NFA、DFA展示表
184 |
185 |
186 | Qt::AlignCenter
187 |
188 |
189 |
190 | -
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
208 |
209 |
210 |
211 |
212 |
213 |
--------------------------------------------------------------------------------
/第二次实验/编译原理实验二/源代码文件夹/regex2dfa/nfa.cpp:
--------------------------------------------------------------------------------
1 | #include "nfa.h"
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | #include "utils.h"
10 |
11 | NFA::NFA(int begin, int end): startState(begin), endState(end), stateNum(0), maxStateNum(100) {
12 | G.resize(maxStateNum);
13 | for (int i = 0; i < maxStateNum; i++) {
14 | G[i].resize(maxStateNum);
15 | }
16 | }
17 |
18 | void NFA::clear()
19 | {
20 | // 清空nfa
21 | for (int i = 0; i < maxStateNum; i++) {
22 | G[i].clear();
23 | }
24 | G.clear();
25 | maxStateNum = 100;
26 | stateNum = 0;
27 | startState = -1;
28 | endState = -1;
29 | G.resize(maxStateNum);
30 | for (int i = 0; i < maxStateNum; i++) {
31 | G[i].resize(maxStateNum);
32 | }
33 | stateSet.clear();
34 | }
35 |
36 | void NFA::fromRegex(QString re) // 后缀表达式转NFA
37 | {
38 | QStack> s;
39 | for (int i = 0; i < re.size(); i++) {
40 | if (re[i] == '\\') { // 转义字符
41 | if (i < re.size() - 1) {
42 | allocateMemory(2);
43 | G[stateNum][stateNum + 1] = re[i + 1]; // 转义符不再出现
44 | s.append({stateNum, stateNum + 1}); // 入栈
45 | stateNum += 2; // 状态数量 + 2
46 | stateSet.insert(QString(re[i + 1]));
47 | }
48 | i++;
49 | } else if (re[i] == '*' || re[i] == '|' || re[i] == '.') { // 运算符
50 | if (re[i] == '.') {
51 | if (s.size() >= 2) { // 取出两个元素相连重新入栈
52 | QPair tail = s.top();
53 | s.pop();
54 | QPair head = s.top();
55 | s.pop();
56 | G[head.second][tail.first] = "epsilon"; // 连接操作
57 | s.append({head.first, tail.second}); // 重新入栈
58 | stateSet.insert("epsilon");
59 | }
60 | } else if (re[i] == '|') {
61 | allocateMemory(2);
62 | if (s.size() >= 2) { // 取出两个元素相连重新入栈
63 | QPair left = s.top();
64 | s.pop();
65 | QPair right = s.top();
66 | s.pop();
67 | G[stateNum][left.first] = "epsilon";
68 | G[stateNum][right.first] = "epsilon";
69 | G[left.second][stateNum + 1] = "epsilon";
70 | G[right.second][stateNum + 1] = "epsilon";
71 | s.append({stateNum, stateNum + 1}); // 入栈
72 | stateNum += 2; // 状态数量 + 2
73 | stateSet.insert("epsilon");
74 | }
75 | } else {
76 | allocateMemory(2);
77 | if (!s.empty()) { // 闭包为单元运算符,因此只需要取出一个元素
78 | QPair item = s.top();
79 | s.pop();
80 | G[stateNum][stateNum + 1] = "epsilon";
81 | G[stateNum][item.first] = "epsilon";
82 | G[item.second][stateNum + 1] = "epsilon";
83 | G[item.second][item.first] = "epsilon";
84 | s.append({stateNum, stateNum + 1}); // 入栈
85 | stateNum += 2; // 状态数量 + 2
86 | stateSet.insert("epsilon");
87 | }
88 | }
89 | } else {
90 | if (re[i] == '#') { // epsilon
91 | allocateMemory(2);
92 | G[stateNum][stateNum + 1] = "epsilon";
93 | s.append({stateNum, stateNum + 1}); // 入栈
94 | stateNum += 2; // 状态数量 + 2
95 | stateSet.insert("epsilon");
96 | } else { // 其他非运算符
97 | allocateMemory(2);
98 | G[stateNum][stateNum + 1] = re[i];
99 | s.append({stateNum, stateNum + 1}); // 入栈
100 | stateNum += 2; // 状态数量 + 2
101 | stateSet.insert(QString(re[i]));
102 | }
103 | }
104 | }
105 | if (!s.empty()){
106 | QPair statePair = s.top();
107 | startState = statePair.first;
108 | endState = statePair.second;
109 | }
110 | }
111 |
112 | void NFA::allocateMemory(int num)
113 | {
114 | // 当状态数即将超过最大状态时,分配更大的空间
115 | while (stateNum + num >= maxStateNum) {
116 | QVector> newG(2 * maxStateNum);
117 | for (int i = 0; i < 2 * maxStateNum; i++) {
118 | newG[i].resize(2 * maxStateNum);
119 | }
120 | for (int i = 0; i < maxStateNum; i++) {
121 | for (int j = 0; j < maxStateNum; j++) {
122 | newG[i][j] = G[i][j];
123 | }
124 | }
125 | maxStateNum *= 2;
126 | G = newG;
127 | }
128 | }
129 |
130 | QSet NFA::epsilonClosure(QSet state)
131 | {
132 | while (true) {
133 | QSet tmpSet;
134 | for (int item: state) {
135 | for (int i = 0; i < maxStateNum; i++) {
136 | if (G[item][i] == "epsilon") {
137 | if (!state.contains(i)) {
138 | tmpSet.insert(i);
139 | }
140 | }
141 | }
142 | }
143 | if (tmpSet.empty()) break;
144 | state = state.unite(tmpSet);
145 | }
146 | return state;
147 | }
148 |
149 | QSet NFA::valueClosure(QSet state, QString value)
150 | {
151 | QSet result;
152 | for (int item: state) {
153 | for (int i = 0; i < maxStateNum; i++) {
154 | if (G[item][i] == value) {
155 | result.insert(i);
156 | }
157 | }
158 | }
159 | return result;
160 | }
161 |
--------------------------------------------------------------------------------
/第二次实验/编译原理实验二/源代码文件夹/regex2dfa/nfa.h:
--------------------------------------------------------------------------------
1 | #ifndef NFA_H
2 | #define NFA_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | class NFA
9 | {
10 | public:
11 | // 构造函数
12 | NFA(int begin = -1, int end = -1);
13 | void clear(); // 清空NFA
14 | void fromRegex(QString re); // 从后缀表达式构造NFA
15 | void allocateMemory(int num); // 分配G内存的函数
16 | QSet epsilonClosure(QSet state); // 计算epsilon闭包
17 | QSet valueClosure(QSet state, QString value); // 计算某个集合状态能通过value转移到的状态集合
18 |
19 | // 成员变量
20 | QVector> G; // 邻接矩阵
21 | QSet stateSet; // 存储所有状态类型
22 | int startState; // 表示起始状态
23 | int endState; // 表示终止状态
24 | int stateNum; // 表示状态数量
25 | int maxStateNum; // 表示当前最多存储的状态数量
26 | };
27 |
28 | #endif // NFA_H
29 |
--------------------------------------------------------------------------------
/第二次实验/编译原理实验二/源代码文件夹/regex2dfa/regex2dfa.pro:
--------------------------------------------------------------------------------
1 | QT += core gui
2 |
3 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
4 |
5 | CONFIG += c++11
6 |
7 | # You can make your code fail to compile if it uses deprecated APIs.
8 | # In order to do so, uncomment the following line.
9 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
10 |
11 | SOURCES += \
12 | codedialog.cpp \
13 | dfa.cpp \
14 | main.cpp \
15 | mainwindow.cpp \
16 | nfa.cpp \
17 | utils.cpp
18 |
19 | HEADERS += \
20 | codedialog.h \
21 | dfa.h \
22 | mainwindow.h \
23 | nfa.h \
24 | utils.h
25 |
26 | FORMS += \
27 | codedialog.ui \
28 | mainwindow.ui
29 |
30 | # Default rules for deployment.
31 | qnx: target.path = /tmp/$${TARGET}/bin
32 | else: unix:!android: target.path = /opt/$${TARGET}/bin
33 | !isEmpty(target.path): INSTALLS += target
34 |
--------------------------------------------------------------------------------
/第二次实验/编译原理实验二/源代码文件夹/regex2dfa/utils.cpp:
--------------------------------------------------------------------------------
1 | #include "utils.h"
2 |
3 | int getPriority(const char &op) // 返回运算符优先级
4 | {
5 | switch (op) {
6 | case '(':
7 | case ')':
8 | return 0;
9 | case '*':
10 | return 4;
11 | case '|':
12 | return 1;
13 | case '.':
14 | return 2;
15 | default:
16 | return -1;
17 | }
18 | }
19 |
20 | bool isOperator(const char &ch)
21 | {
22 | switch (ch) {
23 | case '(':
24 | case ')':
25 | case '*':
26 | case '|':
27 | case '.':
28 | return true; // 运算符返回true
29 | default:
30 | return false; // 非运算符返回false
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/第二次实验/编译原理实验二/源代码文件夹/regex2dfa/utils.h:
--------------------------------------------------------------------------------
1 | #ifndef UTILS_H
2 | #define UTILS_H
3 |
4 | int getPriority(const char &op);
5 | bool isOperator(const char &ch);
6 |
7 | #endif // UTILS_H
8 |
--------------------------------------------------------------------------------
/第四次实验/编译原理实验四/文档文件夹/编译原理实验四.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WA-automat/Compiler_Design_SCNU/0f3e92c521e9b785ae8af9784feade5078582a6f/第四次实验/编译原理实验四/文档文件夹/编译原理实验四.pdf
--------------------------------------------------------------------------------
/第四次实验/编译原理实验四/源程序文件夹/LALR.pro:
--------------------------------------------------------------------------------
1 | QT += core gui
2 |
3 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
4 |
5 | CONFIG += c++11
6 |
7 | # You can make your code fail to compile if it uses deprecated APIs.
8 | # In order to do so, uncomment the following line.
9 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
10 |
11 | SOURCES += \
12 | lalr.cpp \
13 | main.cpp \
14 | mainwindow.cpp
15 |
16 | HEADERS += \
17 | lalr.h \
18 | mainwindow.h
19 |
20 | FORMS += \
21 | mainwindow.ui
22 |
23 | # Default rules for deployment.
24 | qnx: target.path = /tmp/$${TARGET}/bin
25 | else: unix:!android: target.path = /opt/$${TARGET}/bin
26 | !isEmpty(target.path): INSTALLS += target
27 |
--------------------------------------------------------------------------------
/第四次实验/编译原理实验四/源程序文件夹/lalr.cpp:
--------------------------------------------------------------------------------
1 | #include "lalr.h"
2 |
3 | #include
4 |
5 | LALR::LALR(): size(0)
6 | {
7 |
8 | }
9 |
10 | void LALR::buildLR1(State faState, QHash> grammars, QVector nonFinalizers,
11 | QHash > firstSet, QHash > followSet)
12 | {
13 | int faStateId = stateHash[faState];
14 |
15 | // 找到可能的转移方法
16 | QStringList changeMethods;
17 | for (auto faItem: faState.st) {
18 | if (faItem.pos < faItem.rule.size()) {
19 | changeMethods.append(faItem.rule[faItem.pos]);
20 | }
21 | }
22 |
23 | // 深搜每个转移
24 | for (QString changeMethod: changeMethods) {
25 | State sonState = State::change(faState, changeMethod, grammars, nonFinalizers, firstSet);
26 | if (sonState.st.empty()) continue;
27 | if (stateHash.contains(sonState)) {
28 | // 该状态已经存在
29 | int sonStateId = stateHash[sonState];
30 | changeHash[faStateId].insert(changeMethod, sonStateId);
31 | } else {
32 | // 该状态不存在
33 | stateHash[sonState] = size++;
34 | changeHash[faStateId].insert(changeMethod, stateHash[sonState]);
35 | buildLR1(sonState, grammars, nonFinalizers, firstSet, followSet);
36 | }
37 |
38 | qDebug() << faStateId << ' ' << changeMethod << ' ' << stateHash[sonState];
39 | }
40 |
41 | }
42 |
43 | void LALR::buildLALR1(LALR lr1)
44 | {
45 | // 并查集(合并同心项)
46 | int* p = new int[lr1.size];
47 | for (int i = 0; i < lr1.size; i++) {
48 | p[i] = i;
49 | }
50 | std::function find = [&](int x) {
51 | if (p[x] != x) p[x] = find(p[x]);
52 | return p[x];
53 | };
54 | QHash revlr1StateHash;
55 | for (auto state: lr1.stateHash.keys()) {
56 | revlr1StateHash[lr1.stateHash[state]] = state;
57 | }
58 |
59 | // 状态合并
60 | for (int i = 0; i < lr1.size - 1; i++) {
61 | for (int j = i + 1; j < lr1.size; j++) {
62 | if (State::haveSameCore(revlr1StateHash[i], revlr1StateHash[j])) {
63 | p[find(j)] = find(i);
64 | }
65 | }
66 | }
67 |
68 | // LR1到LALR1状态映射
69 | QHash cntChangeSet;
70 | int idx = 0;
71 | for (int i = 0; i < lr1.size; i++) {
72 | if (!cntChangeSet.contains(find(i))) {
73 | cntChangeSet[find(i)] = idx++;
74 | }
75 | }
76 | size = cntChangeSet.size();
77 |
78 | // 建立LALR状态集
79 | QHash revlalr1StateHash;
80 | for (int i = 0; i < lr1.size; i++) {
81 | if (!revlalr1StateHash.contains(cntChangeSet[find(i)])) {
82 | revlalr1StateHash[cntChangeSet[find(i)]] = revlr1StateHash[i];
83 | } else {
84 | for (auto item: revlr1StateHash[i].st) {
85 | for (auto lalrItem: revlalr1StateHash[cntChangeSet[find(i)]].st) {
86 | auto tmpItem = lalrItem;
87 | if (Item::haveSameCore(item, lalrItem)) {
88 | lalrItem.next.unite(item.next);
89 | }
90 | revlalr1StateHash[cntChangeSet[find(i)]].st.remove(tmpItem);
91 | revlalr1StateHash[cntChangeSet[find(i)]].st.insert(lalrItem);
92 | }
93 | }
94 | }
95 | }
96 | // for (auto state: revlalr1StateHash) {
97 | // for (auto item: state.st) {
98 | // qDebug() << item.name << ' ' << item.rule << ' ' << item.next << ' ' << item.pos;
99 | // }
100 | // qDebug();
101 | // }
102 | for (auto idx: revlalr1StateHash.keys()) {
103 | stateHash[revlalr1StateHash[idx]] = idx;
104 | }
105 |
106 | // 建立LALR转移集
107 | for (int i = 0; i < lr1.size; i++) {
108 | for (auto changeMethod: lr1.changeHash[i].keys()) {
109 | if (lr1.changeHash[i].contains(changeMethod)) {
110 | changeHash[cntChangeSet[find(i)]].insert(changeMethod, cntChangeSet[find(lr1.changeHash[i][changeMethod])]);
111 | }
112 | }
113 | }
114 | }
115 |
116 | State State::closure(State I, QHash > grammars, QVector nonFinalizers, QHash> firstSet)
117 | {
118 | State J = I;
119 | while (true) {
120 | for (Item item: J.st) {
121 | qDebug() << item.name << ' ' << item.rule << ' ' << item.next << ' ' << item.pos;
122 | // 下一个字符是非终结符
123 | if (item.pos < item.rule.size() && nonFinalizers.contains(item.rule[item.pos])) {
124 | for (auto grammar: grammars[item.rule[item.pos]]) {
125 | qDebug() << grammar;
126 |
127 | for (auto nextItem: item.next) {
128 | qDebug() << nextItem;
129 |
130 | QStringList betaRule = item.rule.mid(item.pos + 1);
131 | qDebug() << betaRule;
132 |
133 | if (nextItem != "@") betaRule.push_back(nextItem);
134 |
135 | // 计算first集合
136 | int k = 0;
137 | QSet tmpSet;
138 | while (k < betaRule.size()) {
139 | bool sign = false;
140 | if (firstSet[betaRule[k]].contains("@")) sign = true;
141 | tmpSet.unite(firstSet[betaRule[k]].subtract(QSet({"@"})));
142 | if (sign) firstSet[betaRule[k]].insert("@");
143 | else break;
144 | k++;
145 | }
146 | if (k == betaRule.size()) tmpSet.insert("@");
147 |
148 | qDebug() << k << ' ' << tmpSet;
149 |
150 | // 遍历first集合中的每个元素
151 | for (auto b: tmpSet) {
152 |
153 | Item newItem;
154 | newItem.name = item.rule[item.pos];
155 | newItem.rule = grammar;
156 | newItem.next.insert(b);
157 | newItem.pos = 0;
158 |
159 | if (!J.st.contains(newItem)) {
160 | bool mark = true;
161 | for (auto it: J.st) {
162 | if (Item::haveSameCore(it, newItem)) {
163 | J.st.remove(it);
164 | it.next.unite(newItem.next);
165 | J.st.insert(it);
166 | mark = false;
167 | break;
168 | }
169 | }
170 | if (mark) {
171 | J.st.insert(newItem);
172 | }
173 | }
174 | }
175 |
176 | }
177 |
178 | }
179 | }
180 | }
181 | if (I == J) break;
182 | I = J;
183 | }
184 | qDebug();
185 | for (auto item: J.st) {
186 | qDebug() << item.name << ' ' << item.rule << ' ' << item.next << ' ' << item.pos;
187 | }
188 | return J;
189 | }
190 |
191 | State State::change(State I, QString X, QHash > grammars, QVector nonFinalizers, QHash> firstSet)
192 | {
193 | State I_new;
194 | for (auto item: I.st) {
195 | if (item.pos >= item.rule.size() || item.rule[item.pos] != X) continue;
196 |
197 | Item newItem;
198 | newItem.name = item.name;
199 | newItem.rule = item.rule;
200 | newItem.next = item.next;
201 | newItem.pos = item.pos + 1;
202 |
203 | // 判断I_new中是否已经出现了newItem
204 | if (!I_new.st.contains(newItem)) {
205 | bool mark = true;
206 | for (auto it: I_new.st) {
207 | if (Item::haveSameCore(it, newItem)) {
208 | I_new.st.remove(it);
209 | it.next.unite(newItem.next);
210 | I_new.st.insert(it);
211 | mark = false;
212 | break;
213 | }
214 | }
215 | if (mark) {
216 | I_new.st.insert(newItem);
217 | }
218 | }
219 | }
220 | if (I_new.st.empty()) return I_new;
221 | else return closure(I_new, grammars, nonFinalizers, firstSet);
222 | }
223 |
224 | bool State::haveSameCore(State i, State j)
225 | {
226 | if (i.st.size() != j.st.size()) return false;
227 | auto iList = i.st.toList();
228 | auto jList = j.st.toList();
229 | qSort(iList.begin(), iList.end());
230 | qSort(jList.begin(), jList.end());
231 | for (int cnt = 0; cnt < i.st.size(); cnt++) {
232 | if (!Item::haveSameCore(iList[cnt], jList[cnt])) {
233 | return false;
234 | }
235 | }
236 | return true;
237 | }
238 |
239 | bool Item::haveSameCore(Item i, Item j)
240 | {
241 | if (i.name == j.name && i.rule == j.rule && i.pos == j.pos) return true;
242 | else return false;
243 | }
244 |
245 | uint qHash(const Item& key)
246 | {
247 | return qHash(key.name) + qHash(key.rule) + qHash(key.next) + qHash(key.pos);
248 | }
249 |
250 | uint qHash(const State &key)
251 | {
252 | return qHash(key.st);
253 | }
254 |
--------------------------------------------------------------------------------
/第四次实验/编译原理实验四/源程序文件夹/lalr.h:
--------------------------------------------------------------------------------
1 | #ifndef LALR_H
2 | #define LALR_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | // 单条规则
11 | typedef struct Item {
12 | QString name;
13 | QStringList rule;
14 | QSet next;
15 | int pos;
16 |
17 | static bool haveSameCore(Item i, Item j);
18 |
19 | friend bool operator==(const Item a, const Item b) {
20 | return a.name == b.name && a.rule == b.rule && a.next == b.next && a.pos == b.pos;
21 | }
22 |
23 | friend bool operator<(const Item a, const Item b) {
24 | if (a.name == b.name) {
25 | if (a.rule == b.rule) {
26 | if (a.pos == b.pos) {
27 | return a.pos < b.pos;
28 | }
29 | return a.pos < b.pos;
30 | }
31 | return a.rule < b.rule;
32 | }
33 | return a.name < b.name;
34 | }
35 | }Item;
36 |
37 | uint qHash(const Item& key);
38 |
39 | // 单个状态
40 | typedef struct State {
41 | QSet- st;
42 | static State closure(State I, QHash> grammars, QVector nonFinalizers, QHash> firstSet);
43 | static State change(State I, QString X, QHash