如何找到第一套?

时间:2018-11-19 10:40:07

标签: c++ compiler-construction

我正在尝试使用此功能列出给定语法的第一组:

注意:
    char c-查找第一个字符集的字符;
    first_set-存储对应的第一组元素;
    q1q2-前一个位置;
    rule-逐行存储下面列出的所有语法规则;
    第一次将参数设为('S', 0, 0)

void findfirst(char c, int q1, int q2){
    if(!(isupper(c)) || c=='$'){
         first_set[n++] = c;
    }
    for(int j=0;j<rule_number;j++){
        if(rule[j][0]==c){
            if(rule[j][2]==';'){
                if(rule[q1][q2]=='\0')
                    first_set[n++] = ';';
                else if(rule[q1][q2]!='\0' &&(q1!=0||q2!=0))
                    findfirst(rule[q1][q2], q1, (q2+1));
                else
                    first_set[n++] = ';';
            }
            else if(!isupper(rule[j][2]) || rule[j][2]=='$')
                first_set[n++] = rule[j][2];
            else
                findfirst(rule[j][2],j,3);
        }
    }
}

但是发现如果给定的语法看起来像这样:

S AC$
C c
C ;
A aBCd
A BQ
B bB
B ;
Q q
Q ;

(其中左侧或右侧的任何大写字母均为非终止符,而任何小写字母均为终止符) 该函数无法正确输出S的第一个集合,因为它会停止查找Q的第一集合并将';'存储到第一集合,并且不会继续执行找到C的第一组。

有人知道吗?提前致谢。

2 个答案:

答案 0 :(得分:1)

一次计算一个FIRST集是非常低效的,因为它们是相互依赖的。例如,为了计算A的FIRST集,还需要计算B的FIRST集,然后由于B可以得出情感字符串,因此需要FIRST Q集。

大多数算法使用传递闭包算法的某些变体来并行计算所有算法。您可以通过深度优先搜索来执行此操作,这似乎是您要尝试的方法,但是实现Dragon书(和Wikipedia中描述的最小不动点算法可能会更容易。

无论哪种方式,您都可能会发现首先计算NULLABLE(即,哪些非终端派生空集)会更容易。有一个简单的线性时间算法(语法大小呈线性),又很容易找到。

如果您是在课堂上完成这项工作的,则您可能会在课程资料中找到相关的算法。或者,您可以寻找Dragon book或其他类似教科书的副本。

答案 1 :(得分:0)

您可以喜欢以下code

used[i]表示rule[i]是否已使用

方法为Depth-first search,请参见https://en.wikipedia.org/wiki/Depth-first_search

#include <iostream>

#define MAX_SIZE 1024

char rule[][10] = {
  "S AC$",
  "C c",
  "C ;",
  "A aBCd",
  "A BQ",
  "B bB",
  "B ;",
  "Q q",
  "Q ;"
};

constexpr int rule_number = sizeof(rule) / sizeof(rule[0]);

char first_set[MAX_SIZE];

bool findfirst(int row, int col, int *n, bool* used) {
  for (;;) {
    char ch = rule[row][col];
    if (ch == '$' || ch == ';' || ch == '\0') {
      first_set[*n] = '\0';
      break;
    }
    if (islower(ch)) {
      first_set[(*n)++] = ch;
      ++col;
      continue;
    }
    int i;
    for (i = 0; i != rule_number; ++i) {
      if (used[i] == true || rule[i][0] != ch)
        continue;
      used[i] = true;
      int k = *n;
      if (findfirst(i, 2, n, used) == true)
        break;
      used[i] = false;
      *n = k;
    }
    if (i == rule_number)
      return false;
    ++col;
  }
  return true;
}

int main() {
  bool used[rule_number];
  int n = 0;
  for (int i = 2; rule[0][i] != '$' && rule[0][i] != '\0'; ++i) {
    for (int j = 0; j != rule_number; ++j)
      used[j] = false;
    used[0] = true;
    findfirst(0, i, &n, used);
  }
  std::cout << first_set << std::endl;
  return 0;
}