語法分析

文檔鏈接  源碼

實驗目的及實驗環境

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);

}


發佈了65 篇原創文章 · 獲贊 9 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章