LL(1)文法分析

第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();
}
相关推荐
计算机安禾15 小时前
【算法分析与设计】第6篇:动态规划的原理:最优子结构与重叠子问题
算法
Larcher15 小时前
数组去重算法:理论与实践深度解析
javascript·算法·代码规范
CS创新实验室15 小时前
数据结构和算法:摊还分析
java·数据结构·算法
curry____30315 小时前
邻接矩阵 和 领接表 和 链式前向星对比
数据结构·c++·算法
通信小呆呆15 小时前
维度分数傅里叶时频图 + 图神经网络:突破传统时频分析的目标识别与杂波抑制新框架
人工智能·神经网络·算法
csdn_aspnet15 小时前
C++ 算法 LeetCode 编号 70 - 爬楼梯
开发语言·c++·算法·leetcode
he___H15 小时前
leetcode100-合并区间
java·数据结构·算法
wuweijianlove16 小时前
算法性能优化中的数据流重构与依赖消解的技术6
算法
Agent手记16 小时前
智能财务对账Agent如何设计?2026金融大模型Agent架构设计与实战指引
人工智能·算法·ai·金融