You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
234 lines
6.3 KiB
234 lines
6.3 KiB
1 year ago
|
//
|
||
|
// Created by lenfrex on 2023/5/7.
|
||
|
//
|
||
|
|
||
|
#include <iomanip>
|
||
|
#include "Grammar.h"
|
||
|
|
||
|
using namespace std;
|
||
|
|
||
|
const set<char> &Grammar::getNonTerminals() const {
|
||
|
return nonTerminals;
|
||
|
}
|
||
|
|
||
|
const set<char> &Grammar::getTerminals() const {
|
||
|
return terminals;
|
||
|
}
|
||
|
|
||
|
#define isBlankSpace(c) (c == ' ')
|
||
|
|
||
|
Grammar::Grammar(const string &nonTerminalString, const string &terminalString,
|
||
|
const set<string> &productionTexts, char startChar) : startChar(startChar) {
|
||
|
for (char c: nonTerminalString) {
|
||
|
nonTerminals.insert(c);
|
||
|
}
|
||
|
|
||
|
for (char c: terminalString) {
|
||
|
terminals.insert(c);
|
||
|
}
|
||
|
|
||
|
// 解析每条产生式
|
||
|
for (const string &prodText: productionTexts) {
|
||
|
char nonTerminal = '\0';
|
||
|
for (char c: prodText) {
|
||
|
if (isBlankSpace(c)) {
|
||
|
continue;
|
||
|
} else {
|
||
|
nonTerminal = c;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
string::size_type start = prodText.find("->");
|
||
|
|
||
|
// 输入的产生式有误,跳过该条产生式
|
||
|
if (start == -1) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
for (string::size_type i = start + 2; i < prodText.length(); ++i) {
|
||
|
// 跳过空格
|
||
|
if (isBlankSpace(prodText[i])) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
productionsMap[nonTerminal].insert(prodText.substr(i));
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
generateFirstSet();
|
||
|
generateFollowSet();
|
||
|
generateSelectSet();
|
||
|
|
||
|
this->ll1Grammar = isLL1Grammar();
|
||
|
if (ll1Grammar) {
|
||
|
buildAnalysisTable();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const map<char, set<Express>> &Grammar::getGrammarExpresses() const {
|
||
|
return productionsMap;
|
||
|
}
|
||
|
|
||
|
const char emptyChar = '~';
|
||
|
|
||
|
#define isEmptyStr(c) (c == '~')
|
||
|
|
||
|
// 是否可以推导出空字符
|
||
|
bool Grammar::canDeducedEmpty(const set<string> &productions) {
|
||
|
for (const auto &production: productions) {
|
||
|
for (const auto &c: production) {
|
||
|
if (isEmptyStr(c)) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
ostream &alignPrint(ostream &os, const string &str) {
|
||
|
os << setw(8) << left << setfill(' ') << str;
|
||
|
return os;
|
||
|
}
|
||
|
|
||
|
ostream &alignPrint(ostream &os, char str) {
|
||
|
os << setw(8) << left << setfill(' ') << str;
|
||
|
return os;
|
||
|
}
|
||
|
|
||
|
ostream &operator<<(ostream &os, const Grammar &grammar) {
|
||
|
const auto &terminals = grammar.getTerminals();
|
||
|
const auto &nonTerminals = grammar.getNonTerminals();
|
||
|
|
||
|
os << "文法符号详情:" << endl;
|
||
|
|
||
|
os << "开始符号:" << grammar.getStartChar() << endl;
|
||
|
|
||
|
os << "终止符:Vt = {";
|
||
|
for (const auto &c: terminals) {
|
||
|
os << '\'' << c << '\'' << ", ";
|
||
|
}
|
||
|
os << "}" << endl;
|
||
|
|
||
|
os << "非终止符:Vn = {";
|
||
|
for (const auto &c: nonTerminals) {
|
||
|
os << '\'' << c << '\'' << ", ";
|
||
|
}
|
||
|
os << "}" << endl;
|
||
|
|
||
|
os << "================================" << endl;
|
||
|
os << " FirstSet " << endl;
|
||
|
os << "================================" << endl;
|
||
|
|
||
|
const auto &firstSet = grammar.getFirstSet();
|
||
|
for (const auto &nonTerminal: nonTerminals) {
|
||
|
os << "First(" << nonTerminal << ") = {";
|
||
|
const auto &firstChars = firstSet.at(nonTerminal);
|
||
|
for (const auto &firstChar: firstChars) {
|
||
|
os << '\'' << firstChar << '\'' << ", ";
|
||
|
}
|
||
|
os << "}" << endl;
|
||
|
}
|
||
|
|
||
|
os << "================================" << endl;
|
||
|
os << " FollowSet " << endl;
|
||
|
os << "================================" << endl;
|
||
|
|
||
|
const auto &followSet = grammar.getFollowSet();
|
||
|
for (const auto &nonTerminal: nonTerminals) {
|
||
|
os << "Follow(" << nonTerminal << ") = {";
|
||
|
const auto &followChars = followSet.at(nonTerminal);
|
||
|
for (const auto &followChar: followChars) {
|
||
|
os << '\'' << followChar << '\'' << ", ";
|
||
|
}
|
||
|
os << "}" << endl;
|
||
|
}
|
||
|
|
||
|
os << "================================" << endl;
|
||
|
os << " SelectSet " << endl;
|
||
|
os << "================================" << endl;
|
||
|
|
||
|
const auto &allSelectSet = grammar.getSelectSet();
|
||
|
for (const auto &nonTerminal: nonTerminals) {
|
||
|
const auto &nonTerminalSelectSet = allSelectSet.at(nonTerminal);
|
||
|
for (const auto &selectPair: nonTerminalSelectSet) {
|
||
|
os << "Select(" << nonTerminal << "->" << selectPair.first << ')' << "\t=\t" << "{";
|
||
|
for (const auto &selectChar: selectPair.second) {
|
||
|
os << '\'' << selectChar << '\'' << ", ";
|
||
|
}
|
||
|
os << "}" << endl;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
os << "================================" << endl;
|
||
|
os << " AnalysisTable " << endl;
|
||
|
os << "================================" << endl;
|
||
|
|
||
|
alignPrint(os, "Vn/Vt");
|
||
|
os << "| ";
|
||
|
for (const auto &terminal: terminals) {
|
||
|
if (terminal != '~') {
|
||
|
alignPrint(os, terminal);
|
||
|
}
|
||
|
}
|
||
|
alignPrint(os, '#');
|
||
|
os << endl;
|
||
|
|
||
|
for (int i = 0; i < terminals.size(); ++i) {
|
||
|
os << "----------";
|
||
|
}
|
||
|
os << endl;
|
||
|
|
||
|
const auto &analysisTable = grammar.getAnalysisTable();
|
||
|
for (const auto &nonTerminal: nonTerminals) {
|
||
|
alignPrint(os, nonTerminal);
|
||
|
os << "| ";
|
||
|
|
||
|
for (const auto &terminal: terminals) {
|
||
|
if (terminal == '~') {
|
||
|
break;
|
||
|
}
|
||
|
bool found = analysisTable.contains(nonTerminal) && analysisTable.at(nonTerminal).contains(terminal);
|
||
|
string printText = found ? ("->" + analysisTable.at(nonTerminal).at(terminal)): "[/]";
|
||
|
alignPrint(os, printText);
|
||
|
}
|
||
|
|
||
|
bool found = analysisTable.contains(nonTerminal) && analysisTable.at(nonTerminal).contains('#');
|
||
|
string printText = found ? ("->" + analysisTable.at(nonTerminal).at('#')): "[/]";
|
||
|
alignPrint(os, printText);
|
||
|
|
||
|
os << endl;
|
||
|
}
|
||
|
|
||
|
return os;
|
||
|
}
|
||
|
|
||
|
char Grammar::getStartChar() const {
|
||
|
return startChar;
|
||
|
}
|
||
|
|
||
|
const map<char, std::set<Express>> &Grammar::getProductionsMap() const {
|
||
|
return productionsMap;
|
||
|
}
|
||
|
|
||
|
const map<char, std::set<char>> &Grammar::getFirstSet() const {
|
||
|
return firstSet;
|
||
|
}
|
||
|
|
||
|
const map<char, std::set<char>> &Grammar::getFollowSet() const {
|
||
|
return followSet;
|
||
|
}
|
||
|
|
||
|
const map<char, SelectSet> &Grammar::getSelectSet() const {
|
||
|
return selectSet;
|
||
|
}
|
||
|
|
||
|
const map<char, TableRow> &Grammar::getAnalysisTable() const {
|
||
|
return analysisTable;
|
||
|
}
|
||
|
|
||
|
bool Grammar::isLl1Grammar() const {
|
||
|
return ll1Grammar;
|
||
|
}
|