首页 > 代码库 > 语法分析

语法分析

文档链接  源码

实验目的及实验环境

1)实验目的:熟悉语法分析的过程,编写代码实现判断LL(1)文法

并判断一个句子是否属于该文法。

2)实验环境:ubuntu14.04,使用的工具vim, gcc, gdb

 

实验内容

1)输入任意文法,消除左递归和公共左因子;

2)打印文法的FirstFollow;

3)判断是否是LL1)文法,如果是则打印其分析表;

4输入一个句子,如果该句子合法则输出与句子对应的语法树;

  能够输出分析过程中每一步符号栈的变化情况。

  如果该句子非法则进行相应的报错处理。

三.方案设计

重要的数据结构

产生式的数据结构

/* 表示一条产生式 */
struct productive {
  
    struct node *head;
    /* 表示产生式的长度,如上个产生式的长度是4 */
    unsigned    length;
};

文法的数据结构

/* 文法 */
struct sentence {
    
    struct productive   *prod;
    /* 产生式的实际长度 */
    unsigned    cnt;
    /* 产生式表的长度 */
    unsigned    size;
}

First 和 follow 集的数据结构

/* ==================================================================
 * 下面是非终结符的First follow 集合的数据结构
 * ================================================================== */
struct ff_node {
    
    /* 非终结符少的多,所以右移两位 */
    char    ne_sign[NODE_MAX>>2];
    char    first[NODE_MAX];
    char    follow[NODE_MAX];
    /* 分别用最低位,次低位表示first, follow 集合有没有求完(1完) */
    unsigned char   f;
};

struct nes_table {
    
    struct ff_node  *table;
    size_t  cnt;
    size_t  size;
};
/* ================================================================== */

分析表的数据结构

/* ==================================================================
 * 下面是分析表的结构
 * ================================================================== */
 /* 分析表中的一个节点类型 */
 #define SYNCH  1
 struct anat_node {
    
    struct node *row_add;   /* 产生式表中的第几条 */
    struct node *col_add;   /* 对应产生式中那的第几个 */
    unsigned char   err;    //
 };
 
 /* 分析表结构体 */
 struct ana_table {
    
    struct anat_node    *table;
    struct e_ne_signs   *signs;
 };
<p>/* ================================================================== */ </p><p><pre name="code" class="html"><span style="font-family: 'Times New Roman'; font-size: 12pt; background-color: rgb(255, 255, 255);">整个语法分析的函数调用关系图</span>

1)消除左递归

循环判断每条产生式(每条产生式的右部一一判断)是否含有左递归,

若含有调用下面代码段消除左递归。

主要代码段:清除一条产生式里的左递归

  void clean_direct_a_prod (struct sentence *s, size_t i)
  {
      int         len, flag = 0;
      struct node *head, *p, *q, *node;
      struct productive new;
  
      head = s->prod[i].head;
      len = strlen (head->val);
      q = head->next;
  
          
      /* 判断是否含有直接左递归 */
      if (0 == strncmp (head->val, q->val, len)) {
  
          /* 添加的产生式的头部 */
          memset (&new, 0, sizeof (struct productive));
          MALLOC_AND_fill (node, struct node, node->val, head->val, len);
          strncat (node->val, "\'", 1);
  
          new.head = node;
          node->next = head->next;
          s->prod[i].length = new.length = 1;
  
          for (q = head->next; q; q = q->next) {
  
              if (0 == strncmp (head->val, q->val, len)) {
              
                  memmove (q->val, q->val+len, strlen (q->val)-len+1);
                  strncat (q->val, new.head->val, len+1);
                  new.length++;
                  p = q;
              }else break;
          }
          MALLOC_AND_fill (node, struct node, node->val, EMPTY_SET, strlen (EMPTY_SET));
          p->next = node;     /* 给新的产生式添加空元素 */
          new.length++;
  
          head->next = q;
          if (NULL == q) {
  
              MALLOC_AND_fill (node, struct node, node->val, new.head->val, len+1);
              head->next = node;
              s->prod[i].length += 1;
          }
  
          for (q; q; q = q->next) {
              
              strncat (q->val, new.head->val, len+1);
              s->prod[i].length += 1;
          }
  
          /* 添加新的产生式到文法中 */
          add_P_2_S (s, &new);
      }
  }

(2)构造FirstFollow

按照First集得规则:

A)若X属于VT,则FIRSTX= {X};

B)若X属于VN,且右产生式X->a...,则把a加入到FIRSTX)中;

X->ε也是一条产生式,则把ε也加入到FIRSTX)中;

C)若X->Y... 是一条产生式且Y属于VN,则把FIRSTY)中的所有非ε

元素都加到FIRSTX)中X->Y1Y2...YK是一个产生式,Y1...Yi-1

都是非终结符,而且,对于任何j1<=j<=i-1, FIRSTYj)都含有ε,

则把FIRSTYi)中的所有非ε元素都加到FIRSTX)中;

构造FIRST集的重要代码段

  void first (const struct sentence *s, struct nes_table *ff, size_t i)
  {
      if (get_first (ff->table[i])) 
          return ;
  
      struct node     *p = s->prod[i].head->next;
      int     index, k;
  
      for (p; p; p = p->next) {
      
          index = get_index (ff, p->val);
          if (-1 == index) {
  
              /* 不加如重复的终结符 */
              for (k = 0; ff->table[i].first[k] && ff->table[i].first[k] != p->val[0]; k++) ;
              if (0 == ff->table[i].first[k]) 
                  ff->table[i].first[k] = p->val[0];
          }else {
              
              first (s, ff, index);
              strncpy (ff->table[i].first, ff->table[index].first,                        strlen (ff->table[index].first));
          }
      }
      set_first_1 (ff->table[i]);
  }

按照Follow集得规则:

A)对于文法的开始符号S,置#FOLLOWS)中;

B)若A->αΒβ是一个产生式,则把FIRSTβ)去除ε加至FOLLOW(B)中;

C)若A->αΒ是一个产生式,或A->αΒβ是一个产生式而ε属于FIRSTβ),

则把FOLLOWA)加至FOLLOWB)中;

构造FOLLOW集的重要代码段

	/* 判断的是一条产生式 */
void follow (const struct sentence *s, struct nes_table *ff, size_t i)
{
    struct node *head = s->prod[i].head;
    struct node *p = head->next;
    int     pos, index, cur, len;

    for (p; p; p = p->next) {
        
        for (pos = 0; p->val[pos]; ) {

            /* 当前判断的非终结符,B */
            cur = index = get_index (ff, p->val+pos); 
            /* 是非终结符 */
            if (-1 != index) {

                /* 非终结符后的下一个字符的位置 */
                pos += strlen (ff->table[index].ne_sign);
                /* 如B的后边没有字符,则加入follow A 到 follow B */
                if (0 == p->val[pos]) {

                    int i = get_index (ff, head->val);
                    strncat (ff->table[cur].follow, ff->table[i].follow, strlen (ff->table[i].follow));
                }else {
                    
                    /* 当前非终结符后的符号 */
                    index = get_index (ff, p->val + pos);
                    /* 是终结符 */
                    if (-1 == index) {

                        strncat (ff->table[cur].follow, p->val+pos, 1);
                        pos++;
                    }else {
                        
                        /* 添加出去空元素的 first 集 */
                        char    *buf = ff->table[index].first;
                        int     pos = 0, k = strlen (ff->table[cur].first);

                        for (pos = 0; buf[pos]; pos++) {
                            
                            if (EMPTY_ALP != ff->table[index].first[pos])

                                ff->table[cur].follow[k++] = ff->table[index].first[pos];
                        }
                        
                        /* 添加 follow */
                        if (1 == is_get_null (s, ff, index)) {

                            int i = get_index (ff, head->val);
                            strncat (ff->table[cur].follow, ff->table[i].follow, strlen (ff->table[i].follow));
                        }
                    }
                }
            }else pos++;
        }
    }
}

(3)构造分析表

按照规则先判断消除左递归后的文法是否为LL(1)文法

A)文法不含左递归

B)对于文法的每个非终结符A的各个产生式的候选首符集两两不相交

C)对于文法的每个非终结符A,若它存在某个候选首符集包含ε,z

FIRSTAΠ FOLLOWA= Φ

代码实现如下

  /* 判断是否为LL1文法, 是LL1文法返回1,否则返回0 */
  int decide_LL1 (const struct sentence s, const struct nes_table ff)
  {
      int     i;
      struct node *p, *q;
  
      /* 判断每个产生式中的候选首集是否有交集 */
      for (i = 0; i < s.cnt; i++) {
  
          for (p = s.prod[i].head->next; p; p = p->next) {
              
              /* 判断包含空集的非终结符的first follow 集是否有交集 */
              if ( !strncmp (p->val, EMPTY_SET, strlen (EMPTY_SET)) ) 
                  if ( is_strs_intersect (ff.table[i].first, ff.table[i].follow) )
                      return 0;
  
              for (q = p->next; q; q = q->next) {
  
                  /* 判断包含空集的非终结符的first follow 集是否有交集 */
                  if ( !strncmp (q->val, EMPTY_SET, strlen (EMPTY_SET)) ) 
                      if ( is_strs_intersect (ff.table[i].first, ff.table[i].follow) )
                          return 0;
  
                  int index1 = get_first (ff, p->val);
                  int index2 = get_first (ff, q->val);
  
                  if (index1 == index2) 
                      return 0;
  
                  else {
                      
                      char buf1[NODE_MAX] = {0}, *f1 = ff.table[index1].first;
                      char buf2[NODE_MAX] = {0}, *f2 = ff.table[index2].first;
  
                      0 > index1 ? snprintf (buf1, 1, "%c", (char)(-index1)) :
                                   snprintf (buf1, strlen (f1), "%s", f1);
                      0 > index2 ? snprintf (buf2, 1, "%c", (char)(-index2)) :
                                   snprintf (buf2, strlen (f2), "%s", f2);
  
                      /* 判断候选首符集是否相交 */
                      if ( is_strs_intersect (buf1, buf2) )
                          return 0;
                  }
              }
          }
      }
  
      return 1;
  }
  
  按照分析表的规则构造分析表
  重要代码实现如下
  void show_ana_table (const struct ana_table an) 
  {
      int     i, j;
      char    *buf;
  
      printf ("分析表 \n");
      print_line (10, an.signs->cols + 1);
      buf = an.signs->e_sign;
      printf ("| %-10s", "");
      while ( *buf ) printf ("| %-10c", *buf++);
      printf ("|\n");
      print_line (10, an.signs->cols + 1);
  
      for (i = 0; i < an.signs->rows; i++) {
  
          printf ("| %-10s", an.signs->ne_sign[i].val);
          for (j = 0; j < an.signs->cols; j++) {
      
              char temp[NODE_MAX] = "";
              char *buf1 = an.table[i * an.signs->cols + j].row_add->val;
              char *buf2 = an.table[i * an.signs->cols + j].col_add->val;
  
              if (buf1 && buf2)
                  sprintf (temp, "%s->%s", buf1, buf2);
              else if (SYNCH == an.table[i * an.signs->cols + j].err)
                  sprintf (temp, "%s", "synch");
              else 
                  sprintf (temp, "%s", "");
  
              printf ("| %-10s", temp);
          }
          printf ("|\n");
          print_line (10, an.signs->cols + 1);
      }
  }
  
  /* 添加一条记录到分析表中 */ 
  static void add_ana_table (const struct ana_table *an,int row,char e,const void *row_add,const void *col_add,struct nes_table ff)
  {
      /* 若e为空字符,则吧e属于follow(A)把产生式加入至M[A, e]中*/
      if (EMPTY_ALP == e) {
  
          char *buf = ff.table[row].follow;
          while ( *buf ) 
              add_ana_table (an, row, *buf++, row_add, col_add, ff);
          return ;
      }
  
      /* 若e终结符,则把产生式加入至M[A, e]中*/
      int     col;
      for (col = 0; col < an->signs->cols; col++) {
  
          if (e == an->signs->e_sign[col])
              break ;
      }
  
      /* 添加 sentence 表中的地址 */
      an->table[row * an->signs->cols + col].row_add = (struct node*)row_add;
      an->table[row * an->signs->cols + col].col_add = (struct node*)col_add;
  }

(4)构造分析栈检错并打印语法树

/* 分析表和输入串,同时构造语法树 */

void analyse_stack (const struct ana_table an, const char *str)

{

    struct ana_stack ana_stack = {0};

    /* #,和文法的开始符号加入栈低 */

    push_stack (&ana_stack, "#");

    push_stack (&ana_stack, an.signs->ne_sign[0].val);

 

    /* 语法树根 */

    struct tree_node *tree = NULL, *tree_pos;

    if (0 > add_node (&tree, 0, an.signs->ne_sign[0].val))

        printf ("tree error ! \n");

    tree_pos = tree;

 

    /* 表示扫描串的位置 */

    int pos = 0, len = strlen (str);

    /* 栈为空或字符串匹配完则结束 */

    display (ana_stack, str, "");

    while (0 < ana_stack.cnt && pos < len && strncmp (get_top (ana_stack), "#", 2)) {

 

        const char *buf = analyse_str (&ana_stack, an, str[pos], &pos, &tree_pos);

        display (ana_stack, str+pos, buf);

    }

 

    display_tree (tree);

    destroy_tree (tree);

}