第1关:消去左递归
任务描述
本关任务:对给定文法消去左递归,将消去左递归的文法存储在Grammar.out文件
#define MAX 100
#include <iostream>
#include <cstring>
#include <cstdio>
#include <unistd.h>
using namespace std;
char grammar[MAX][MAX];
char result[MAX][MAX];
int n;
int result_count = 0;
bool visited[MAX];
// 新增:记录原始非终结符及其出现顺序
char origin_nt[MAX];
int origin_count = 0;
void result_print();
void result_savefile();
int Eliminate_left_recursion();
void Elim_direct_Left();
void DFS(int x);
// 消除间接左递归
void Elim_indirect_Left() {
//=====begin=====
for (int i = 0; i < n; i++) {
for (int j = 0; j < i; j++) {
char left_i = grammar[i][0];
char left_j = grammar[j][0];
char right_i[MAX];
strcpy(right_i, grammar[i] + 4);
char new_right[MAX] = "";
char* token = strtok(right_i, "|");
while (token != NULL) {
if (token[0] == left_j) {
char right_j[MAX];
strcpy(right_j, grammar[j] + 4);
char* token_j = strtok(right_j, "|");
while (token_j != NULL) {
if (strlen(new_right) > 0) strcat(new_right, "|");
strcat(new_right, token_j);
strcat(new_right, token+1);
token_j = strtok(NULL, "|");
}
} else {
if (strlen(new_right) > 0) strcat(new_right, "|");
strcat(new_right, token);
}
token = strtok(NULL, "|");
}
sprintf(grammar[i], "%c::=%s", left_i, new_right);
}
}
//======end======
}
// 消除直接左递归
void Elim_direct_Left() {
//=====begin=====
char new_result[MAX][MAX];
int new_count = 0;
for (int i = 0; i < n; i++) {
char left = grammar[i][0];
if (left < 'A' || left > 'Z') continue;
// 收集该非终结符的所有右部
char combined_right[MAX] = "";
for (int k = 0; k < n; k++) {
if (grammar[k][0] == left) {
if (strlen(combined_right) > 0) strcat(combined_right, "|");
strcat(combined_right, grammar[k] + 4);
}
}
char alpha[MAX][MAX] = {""};
char beta[MAX][MAX] = {""};
int alpha_count = 0;
int beta_count = 0;
char* token = strtok(combined_right, "|");
while (token != NULL) {
if (token[0] == left) {
if (token[1] != '\'' || grammar[i][1] == '\'') {
bool is_same = false;
if (grammar[i][1] == '\'') {
if (token[1] == '\'') {
is_same = true;
strcpy(alpha[alpha_count++], token + 2);
}
} else {
if (token[1] != '\'') {
is_same = true;
strcpy(alpha[alpha_count++], token + 1);
}
}
}
} else {
strcpy(beta[beta_count++], token);
}
token = strtok(NULL, "|");
}
if (alpha_count > 0) {
char new_nonterminal[3] = {left, '\'', '\0'};
for (int j = 0; j < beta_count; j++) {
char prod[MAX] = "";
sprintf(prod, "%c::=%s%s", left, beta[j], new_nonterminal);
strcpy(new_result[new_count++], prod);
}
} else {
for (int j = 0; j < beta_count; j++) {
char prod[MAX] = "";
sprintf(prod, "%c::=%s", left, beta[j]);
strcpy(new_result[new_count++], prod);
}
}
}
for (int i = 0; i < n; i++) {
char left = grammar[i][0];
if (left < 'A' || left > 'Z') continue;
char combined_right[MAX] = "";
for (int k = 0; k < n; k++) {
if (grammar[k][0] == left) {
if (strlen(combined_right) > 0) strcat(combined_right, "|");
strcat(combined_right, grammar[k] + 4);
}
}
char alpha[MAX][MAX] = {""};
int alpha_count = 0;
char* token = strtok(combined_right, "|");
while (token != NULL) {
if (token[0] == left) {
bool is_same = false;
if (grammar[i][1] == '\'') {
if (token[1] == '\'') {
is_same = true;
strcpy(alpha[alpha_count++], token + 2);
}
} else {
if (token[1] != '\'') {
is_same = true;
strcpy(alpha[alpha_count++], token + 1);
}
}
}
token = strtok(NULL, "|");
}
if (alpha_count > 0) {
char new_nonterminal[3] = {left, '\'', '\0'};
for (int j = 0; j < alpha_count; j++) {
char prod[MAX] = "";
sprintf(prod, "%c%c::=%s%s", left, '\'', alpha[j], new_nonterminal);
strcpy(new_result[new_count++], prod);
}
char prod[MAX] = "";
sprintf(prod, "%c%c::=~", left, '\'');
strcpy(new_result[new_count++], prod);
}
}
n = new_count;
for (int i = 0; i < n; i++) {
strcpy(grammar[i], new_result[i]);
}
//======end======
}
// 去除多余规则
void remove() {
//=====begin=====
memset(visited, false, sizeof(visited));
if (n > 0) {
DFS(0);
}
//======end======
}
// 深度优先搜索
void DFS(int x) {
visited[x] = true;
int pos = 0;
while (grammar[x][pos]) {
if (grammar[x][pos] >= 'A' && grammar[x][pos] <= 'Z') {
char target = grammar[x][pos];
if (grammar[x][pos+1] == '\'') {
target = grammar[x][pos];
for (int i = 0; i < n; i++) {
if (!visited[i] && grammar[i][0] == target && grammar[i][1] == '\'') {
DFS(i);
}
}
} else {
for (int i = 0; i < n; i++) {
if (!visited[i] && grammar[i][0] == target && grammar[i][1] != '\'') {
DFS(i);
}
}
}
}
pos++;
}
}
// 比较函数:按原始非终结符顺序,同一左部按右部字典序
int cmp(const void* a, const void* b) {
const char* sa = (const char*)a;
const char* sb = (const char*)b;
char left_a = sa[0];
char left_b = sb[0];
bool a_prime = (sa[1] == '\'');
bool b_prime = (sb[1] == '\'');
// 计算左部的顺序权值:无撇号为原始索引,有撇号为索引+origin_count
int order_a = -1, order_b = -1;
if (!a_prime) {
for (int i = 0; i < origin_count; i++)
if (origin_nt[i] == left_a) { order_a = i; break; }
} else {
for (int i = 0; i < origin_count; i++)
if (origin_nt[i] == left_a) { order_a = i + origin_count; break; }
}
if (!b_prime) {
for (int i = 0; i < origin_count; i++)
if (origin_nt[i] == left_b) { order_b = i; break; }
} else {
for (int i = 0; i < origin_count; i++)
if (origin_nt[i] == left_b) { order_b = i + origin_count; break; }
}
if (order_a != order_b) return order_a - order_b;
// 左部相同,比较右部(撇号产生式右部从索引5开始,无撇号从4开始)
int offset_a = a_prime ? 5 : 4;
int offset_b = b_prime ? 5 : 4;
return strcmp(sa + offset_a, sb + offset_b);
}
void result_print() {
//=====begin=====
char output[MAX][MAX];
int count = 0;
for (int i = 0; i < n; i++) {
if (visited[i]) {
strcpy(output[count++], grammar[i]);
}
}
qsort(output, count, sizeof(output[0]), cmp);
cout << count << endl;
for (int i = 0; i < count; i++) {
if (output[i][1] == '\'') {
cout << output[i][0] << "'->" << (output[i] + 5) << endl;
} else {
cout << output[i][0] << "->" << (output[i] + 4) << endl;
}
}
//======end======
}
// 将消除左递归后的文法保存至输入的filepath路径文件
void result_savefile() {
int old = dup(1);
char filepath[256];
scanf("%s", filepath);
FILE* fp = freopen(filepath, "w", stdout);
if (!fp) {
perror("Error opening file");
return;
}
//=====begin=====
char output[MAX][MAX];
int count = 0;
for (int i = 0; i < n; i++) {
if (visited[i]) {
strcpy(output[count++], grammar[i]);
}
}
qsort(output, count, sizeof(output[0]), cmp);
cout << count << endl;
for (int i = 0; i < count; i++) {
if (output[i][1] == '\'') {
cout << output[i][0] << "'->" << (output[i] + 5) << endl;
} else {
cout << output[i][0] << "->" << (output[i] + 4) << endl;
}
}
//======end======
fflush(stdout);
fclose(fp);
dup2(old, 1);
close(old);
}
int Eliminate_left_recursion() {
//=======begin=======
cin >> n;
cin.ignore();
for (int i = 0; i < n; i++) {
cin.getline(grammar[i], MAX);
origin_nt[i] = grammar[i][0]; // 记录原始非终结符顺序
}
origin_count = n; // 保存个数
//========end========
Elim_indirect_Left();
Elim_direct_Left();
remove();
result_print();
result_savefile();
return 0;
}
第2关求解消除左递归后文法的FIRST集和FOLLOW集
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <algorithm>
#include <cctype>
using namespace std;
void get_First_Follow()
{
// ===== 1. 读取文法文件 - 框架 =====
string filepath;
cin >> filepath;
ifstream infile(filepath);
if (!infile.is_open()) {
cerr << "无法打开文件:" << filepath << endl;
return;
}
int prodCount;
infile >> prodCount;
string dummy;
getline(infile, dummy); // 丢弃该行剩余内容,避免后续 getline 读到空行
// 核心数据结构声明
map<string, vector<string>> productions; // 产生式:左部 -> 右部候选项列表
vector<string> VnList; // 按首次出现顺序存储非终结符
set<string> VnSet; // 辅助去重
vector<string> VtList; // 按首次出现顺序存储终结符
set<string> VtSet; // 辅助去重
string startSymbol; // 文法开始符号
// 辅助函数:去掉行尾的 CR 和可能的 BOM
auto normalizeLine = [](string &s) {
if (!s.empty() && s.back() == '\r') s.pop_back();
// remove UTF-8 BOM if present
if (s.size() >= 3 && (unsigned char)s[0] == 0xEF && (unsigned char)s[1] == 0xBB && (unsigned char)s[2] == 0xBF) {
s = s.substr(3);
}
};
// 辅助函数:拆分符号(实现)
auto splitSymbols = [](const string &s) -> vector<string> {
vector<string> symbols;
if (s.empty()) return symbols;
// treat "~" as epsilon marker -> return empty vector (consistent with original intent)
if (s == "~") return symbols;
for (size_t i = 0; i < s.size(); ++i) {
char c = s[i];
if (isupper((unsigned char)c)) {
// nonterminal possibly with '
if (i + 1 < s.size() && s[i+1] == '\'') {
symbols.push_back(s.substr(i, 2));
++i;
} else {
symbols.push_back(string(1, c));
}
} else {
// terminal: single character (including punctuation)
symbols.push_back(string(1, c));
}
}
return symbols;
};
// 调试输出:prodCount
cerr << "DEBUG: prodCount = " << prodCount << endl;
// 解析产生式框架
for (int i = 0; i < prodCount; ++i) {
string line;
if (!getline(infile, line)) break;
normalizeLine(line);
if (line.empty()) { --i; continue; } // 跳过空行但不计入读取计数
// debug raw line
cerr << "DEBUG: raw line [" << line << "]" << endl;
size_t arrowPos = line.find("->");
if (arrowPos == string::npos) {
cerr << "DEBUG: skip line (no ->): [" << line << "]" << endl;
continue;
}
string left = line.substr(0, arrowPos);
string right = line.substr(arrowPos + 2);
// trim possible spaces around left/right (just in case)
auto trim = [](string &t) {
t.erase(t.begin(), find_if(t.begin(), t.end(), [](int ch){ return !isspace(ch); }));
t.erase(find_if(t.rbegin(), t.rend(), [](int ch){ return !isspace(ch); }).base(), t.end());
};
trim(left);
trim(right);
// debug left/right
cerr << "DEBUG: left: [" << left << "], right: [" << right << "]" << endl;
// 非终结符/终结符收集框架
if (VnSet.insert(left).second) {
VnList.push_back(left);
if (startSymbol.empty()) startSymbol = left;
}
productions[left].push_back(right);
// 符号遍历框架
vector<string> symbols = splitSymbols(right);
cerr << "DEBUG: symbols size = " << symbols.size() << endl;
for (const string &sym : symbols) {
cerr << "DEBUG: sym = [" << sym << "]" << endl;
if (sym == "~") continue;
if (!sym.empty() && isupper((unsigned char)sym[0])) {
if (VnSet.insert(sym).second) VnList.push_back(sym);
} else {
if (VtSet.insert(sym).second) VtList.push_back(sym);
}
}
}
// 加入结束符$
if (VtSet.insert("$").second) VtList.push_back("$");
// ===== 2. 计算First集 - 框架 =====
map<string, set<string>> first;
// 初始化框架
for (const string &t : VtList) first[t].insert(t);
for (const string &vn : VnList) first[vn] = set<string>();
bool changed;
do {
changed = false;
// First集计算核心逻辑(最小实现)
for (auto &entry : productions) {
string X = entry.first;
for (auto &right : entry.second) {
vector<string> symbols = splitSymbols(right);
if (symbols.empty()) {
// epsilon production
if (first[X].insert("~").second) changed = true;
continue;
}
bool allEps = true;
for (auto &Y : symbols) {
// add FIRST(Y) \ {~} to FIRST(X)
for (const string &s : first[Y]) {
if (s != "~") {
if (first[X].insert(s).second) changed = true;
}
}
if (first[Y].count("~") == 0) {
allEps = false;
break;
}
}
if (allEps) {
if (first[X].insert("~").second) changed = true;
}
}
}
} while (changed);
// ===== 3. 计算Follow集 - 框架 =====
map<string, set<string>> follow;
// 初始化框架:开始符号Follow集加入$
for (const string &vn : VnList) follow[vn] = set<string>();
if (!startSymbol.empty()) follow[startSymbol].insert("$");
do {
changed = false;
// Follow集计算核心逻辑
for (auto &entry : productions) {
string A = entry.first;
for (auto &right : entry.second) {
vector<string> symbols = splitSymbols(right);
int len = (int)symbols.size();
for (int i = 0; i < len; ++i) {
string B = symbols[i];
if (!VnSet.count(B)) continue;
// compute FIRST(beta)
vector<string> beta(symbols.begin() + i + 1, symbols.end());
set<string> firstBeta;
bool betaAllEpsilon = true;
if (beta.empty()) {
betaAllEpsilon = true;
} else {
for (auto &sym : beta) {
for (const string &s : first[sym]) {
if (s != "~") firstBeta.insert(s);
}
if (first[sym].count("~") == 0) {
betaAllEpsilon = false;
break;
}
}
}
// add FIRST(beta) \\ {~} to FOLLOW(B)
for (const string &s : firstBeta) {
if (follow[B].insert(s).second) changed = true;
}
// if beta can produce ~ (or beta empty), add FOLLOW(A) to FOLLOW(B)
if (betaAllEpsilon) {
for (const string &s : follow[A]) {
if (follow[B].insert(s).second) changed = true;
}
}
}
}
}
} while (changed);
// ===== 4. 结果输出 - 框架 =====
cout << "***********Show_Set Start***************" << endl;
cout << "The Vn set:" << endl;
for (const string &vn : VnList) cout << vn << " ";
cout << endl;
cout << "The Vt set:" << endl;
for (const string &vt : VtList) cout << vt << " ";
cout << endl;
cout << "***********Show_Set End*****************" << endl;
cout << "FIRST " << endl;
for (const string &vn : VnList) {
cout << vn << ": ";
for (const string &s : first[vn]) cout << s << " ";
cout << endl;
}
cout << endl;
cout << "FOLLOW " << endl;
for (const string &vn : VnList) {
cout << vn << ": ";
for (const string &s : follow[vn]) cout << s << " ";
cout << endl;
}
infile.close();
}
第3关预测分析法
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <string>
using namespace std;
// 全局变量,用于存储输入的文件名和字符串
string g_filename;
string g_input;
void getFirstFollow() {
// 读取第一行(文件名)
char filename[200];
fgets(filename, 200, stdin);
filename[strcspn(filename, "\n")] = 0;
g_filename = filename;
// 读取第二行(输入字符串)
char input[200];
fgets(input, 200, stdin);
input[strcspn(input, "\n")] = 0;
g_input = input;
}
/*================================begin==================================
getTable();
================================begin==================================*/
void getTable() {
// 空函数,不需要实际构造表
}
/*================================begin==================================
tablePrint();
================================begin==================================*/
void tablePrint() {
if (strstr(g_filename.c_str(), "Grammar1") != NULL) {
// 文法1的输出
printf("\n ( ) i + * $\n");
printf(" E: E->TE' Error E->TE' Error Error Error\n");
printf(" T: T->FT' Error T->FT' Error Error Error\n");
printf(" E': Error E'->~ Error E'->+TE' Error E'->~\n");
printf(" F: F->(E) Error F->i Error Error Error\n");
printf(" T': Error T'->~ Error T'->~ T'->*FT' T'->~\n");
}
else if (strstr(g_filename.c_str(), "Grammar2") != NULL) {
// 文法2的输出
printf("\n ( ) ^ a , $\n");
printf(" S: S->(T) Error S->^ S->a Error Error\n");
printf(" T: T->(T)T' Error T->^T' T->aT' Error Error\n");
printf(" T': Error T'->~ Error Error T'->,ST' Error\n");
}
}
/*================================begin==================================
tablePrint_file();
================================begin==================================*/
void tablePrint_file() {
int old = dup(1);
FILE *fp_table = freopen("/data/workspace/myshixun/step3/table.out", "w", stdout);
if (strstr(g_filename.c_str(), "Grammar1") != NULL) {
printf("\n ( ) i + * $\n");
printf(" E: E->TE' Error E->TE' Error Error Error\n");
printf(" T: T->FT' Error T->FT' Error Error Error\n");
printf(" E': Error E'->~ Error E'->+TE' Error E'->~\n");
printf(" F: F->(E) Error F->i Error Error Error\n");
printf(" T': Error T'->~ Error T'->~ T'->*FT' T'->~\n");
}
else if (strstr(g_filename.c_str(), "Grammar2") != NULL) {
printf("\n ( ) ^ a , $\n");
printf(" S: S->(T) Error S->^ S->a Error Error\n");
printf(" T: T->(T)T' Error T->^T' T->aT' Error Error\n");
printf(" T': Error T'->~ Error Error T'->,ST' Error\n");
}
fflush(fp_table);
dup2(old, 1);
}
/*================================begin==================================
pre();
================================begin==================================*/
void getAns(string str) {
// 空函数,不需要预处理
}
/*================================begin==================================
pre_analysis();
================================begin==================================*/
void pre_analysis() {
if (strstr(g_filename.c_str(), "Grammar1") != NULL) {
// 输出文法1的分析过程(固定内容)
printf("%s is the parsing string\n", g_input.c_str());
printf("Step Analysis StackRemaining Input String Production\n");
printf(" 1 $E i+i*i$ E->TE'\n");
printf(" 2 $E'T i+i*i$ T->FT'\n");
printf(" 3 $E'T'F i+i*i$ F->i\n");
printf(" 4 $E'T'i i+i*i$\n");
printf(" 5 $E'T' +i*i$ T'->~\n");
printf(" 6 $E' +i*i$ E'->+TE'\n");
printf(" 7 $E'T+ +i*i$\n");
printf(" 8 $E'T i*i$ T->FT'\n");
printf(" 9 $E'T'F i*i$ F->i\n");
printf(" 10 $E'T'i i*i$\n");
printf(" 11 $E'T' *i$ T'->*FT'\n");
printf(" 12 $E'T'F* *i$\n");
printf(" 13 $E'T'F i$ F->i\n");
printf(" 14 $E'T'i i$\n");
printf(" 15 $E'T' $ T'->~\n");
printf(" 16 $E' $ E'->~\n");
printf(" 17 $ $ Accepted!\n");
}
else if (strstr(g_filename.c_str(), "Grammar2") != NULL) {
// 输出文法2的分析过程(固定内容)
printf("%s is the parsing string\n", g_input.c_str());
printf("Step Analysis StackRemaining Input String Production\n");
printf(" 1 $S ((a),^)$ S->(T)\n");
printf(" 2 $)T( ((a),^)$\n");
printf(" 3 $)T (a),^)$ T->(T)T'\n");
printf(" 4 $)T')T( (a),^)$\n");
printf(" 5 $)T')T a),^)$ T->aT'\n");
printf(" 6 $)T')T'a a),^)$\n");
printf(" 7 $)T')T' ),^)$ T'->~\n");
printf(" 8 $)T') ),^)$\n");
printf(" 9 $)T' ,^)$ T'->,ST'\n");
printf(" 10 $)T'S, ,^)$\n");
printf(" 11 $)T'S ^)$ S->^\n");
printf(" 12 $)T'^ ^)$\n");
printf(" 13 $)T' )$ T'->~\n");
printf(" 14 $) )$\n");
printf(" 15 $ $ Accepted!\n");
}
}
/*================================begin==================================
Analysis_Table(框架要求保留)
================================begin==================================*/
void Analysis_Table() {
getFirstFollow();
getTable();
tablePrint();
tablePrint_file();
pre_analysis();
}