一. 实验目的及实验环境
(1)实验目的:熟悉语法分析的过程,编写代码实现判断LL(1)文法
并判断一个句子是否属于该文法。
(2)实验环境:ubuntu14.04,使用的工具vim, gcc, gdb
二. 实验内容
(1)输入任意文法,消除左递归和公共左因子;
(2)打印文法的First和Follow集;
(3)判断是否是LL(1)文法,如果是则打印其分析表;
(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)构造First和Follow集
按照First集得规则:
A)若X属于VT,则FIRST(X)= {X};
B)若X属于VN,且右产生式X->a...,则把a加入到FIRST(X)中;
若X->ε也是一条产生式,则把ε也加入到FIRST(X)中;
C)若X->Y... 是一条产生式且Y属于VN,则把FIRST(Y)中的所有非ε
元素都加到FIRST(X)中; 若X->Y1Y2...YK是一个产生式,Y1...Yi-1
都是非终结符,而且,对于任何j,1<=j<=i-1, FIRST(Yj)都含有ε,
则把FIRST(Yi)中的所有非ε元素都加到FIRST(X)中;
构造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,置#到FOLLOW(S)中;
B)若A->αΒβ是一个产生式,则把FIRST(β)去除ε加至FOLLOW(B)中;
C)若A->αΒ是一个产生式,或A->αΒβ是一个产生式而ε属于FIRST(β),
则把FOLLOW(A)加至FOLLOW(B)中;
构造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则
FIRST(A)Π FOLLOW(A)= Φ
代码实现如下
/* 判断是否为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);
}
原文地址:http://blog.csdn.net/zhanglpql/article/details/32179403