您的位置:首页 > 其它

SLR,语法分析表的构建

2013-07-04 19:44 411 查看
太累了,感觉不会再爱了。执行了跟编译原理上的一模一样的例子,输出了正确结果

#include <stdio.h>
#include <malloc.h>
#include <string.h>
//这个头文件是为了将语法声明的产生式按照调用顺序来构建调用图,并顺便构建反向调用图。
//从而来构建拓扑排序,并用到将来的分析之中。
typedef struct _decl_hash_table
{
int is_used;//代表是否已经被使用
char* decl_hash_name;//代表名字
int node_index;//代表所代表的节点编号
}decl_hash_table,*pdecl_hash_table;
decl_hash_table decl_hash[100];//暂且定位100,其实我们在使用的过程中只会使用其中的97个,因为这个是一个质数
typedef struct _decl_edge
{
int node_of_dest;
struct _decl_edge* next;
}decl_edge,*pdecl_edge;
typedef struct _phrase
{
pdecl_edge begin_of_phrase;
struct _phrase* next;
}phrase;
typedef struct _decl_node
{
phrase* phrase_link;//作为产生体链表的第一个节点的指针
char* name_of_decl;//这个节点的名称,主要是为了输出使用的
}decl_node,*pdecl_node;
decl_node decl_node_table[100];//假设为100个节点,可以改
int node_index=0;//全局的用来给节点赋予编号的变量,最后的值代表了有多少个文法符号,结束符不算在里面

int insert_decl_hash(char* name,int input_node_index)
//hash表插入函数,如果满了则返回-1
//注意这里为了节省空间,hash表与节点表的名字域公用了一个字符串指针
{
int name_total;
int counter;
int result;
int name_for_i;
char* new_name;
name_for_i=counter=name_total=result=0;
while(name[name_for_i]!='\0')
{
name_total=name_total+name[name_for_i];
name_for_i++;
}
new_name=malloc(sizeof(char)*(name_for_i+1));
strcpy(new_name,name);
result=name_total%97;
while(counter<97)
{
if(decl_hash[result].is_used==0)
{
decl_hash[result].is_used=1;
decl_hash[result].decl_hash_name=new_name;
decl_hash[result].node_index=input_node_index;
decl_node_table[input_node_index].name_of_decl=new_name;//公用名字指针
return result;
}
else
{
result=(result+1)%97;
counter++;
}
}
return -1;
}

int search_decl_hash(char* input_name)
{
int name_total;
int counter;
int result;
int name_for_i;
name_for_i=counter=name_total=result=0;
while(input_name[name_for_i]!='\0')
{
name_total=name_total+input_name[name_for_i];
name_for_i++;
}
result=name_total%97;
while(counter<97)
{
if(decl_hash[result].is_used==1)
{
if(!strcmp(decl_hash[result].decl_hash_name,input_name))
{
return decl_hash[result].node_index;
}
else
{
result=(result+1)%97;
counter++;
}
}
else
{
return -1;
}
}
return -1;
}

//以上是与hash表有关的函数
//下面则是与建立邻接表相关的数据结构与函数
void tackle_phrase(char* input_phrase)
//根据输入来建立所有的产生式,用数字来代替名字
//注意生成链表的时候,为了插入方便,我们是逆序的链表,全都建完之后再逆序一遍然后变成正序的
{
int for_i,for_j;
int current_tail_node,current_head_node;
char temp[20];//起临时的名字作用
phrase* temp_phrase;
decl_edge* temp_decl_edge;
for_i=0;
while(input_phrase[for_i]!=':'&&input_phrase[for_i]!=' ')
{
temp[for_i]=input_phrase[for_i];
for_i++;
}//找到头部的名字
temp[for_i]='\0';
current_head_node=search_decl_hash(temp);
if(current_head_node==-1)//如果这个名字是第一次碰到
{
node_index++;
insert_decl_hash(temp,node_index);
current_head_node=node_index;
decl_node_table[current_head_node].phrase_link=NULL;
printf("%d is the label of %s\n",node_index,temp);
}//建立名字
temp_phrase=malloc(sizeof(struct _phrase));//临时的产生体
temp_phrase->begin_of_phrase=NULL;
temp_phrase->next=decl_node_table[current_head_node].phrase_link;
decl_node_table[current_head_node].phrase_link=temp_phrase;
//把当前的产生式链接到产生式头的产生式链表中
while(input_phrase[for_i]==' '||input_phrase[for_i]==':')
{
for_i++;
}//找到第一个可以分析的点
while(input_phrase[for_i]!='\0')
{
while(input_phrase[for_i]==' ')
{
for_i++;
}//找到可以分析的点
for_j=0;
while(input_phrase[for_i]!=' '&&input_phrase[for_i]!='\0')
{
temp[for_j]=input_phrase[for_i];
for_i++;
for_j++;
}//再次找到一个名字
temp[for_j]='\0';
current_tail_node=search_decl_hash(temp);
if(current_tail_node==-1)//如果是新的名字,则加入到hash中,并为他分配一个标号
{
node_index++;
current_tail_node=node_index;
insert_decl_hash(temp,node_index);
decl_node_table[current_tail_node].phrase_link=NULL;
printf("%d is the label of %s\n",node_index,temp);
}
//为当前的产生式链表增加一个元素
temp_decl_edge=malloc(sizeof(struct _decl_edge));
temp_decl_edge->node_of_dest=current_tail_node;
temp_decl_edge->next=temp_phrase->begin_of_phrase;
temp_phrase->begin_of_phrase=temp_decl_edge;
}
}

void phrase_end(void)//这个函数是为了将终结符加入到符号表中
{
char* end="EOF";
decl_node_table[node_index+1].phrase_link=NULL;
insert_decl_hash(end,node_index+1);
}

void reverse_phrase(void)
{
int for_i;
phrase* temp_phrase;
pdecl_edge temp_edge,swap_edge,next_edge;
for_i=1;
for(for_i=1;for_i<=node_index;for_i++)
{
temp_phrase=decl_node_table[for_i].phrase_link;
while(temp_phrase!=NULL)
{
temp_edge=temp_phrase->begin_of_phrase;
swap_edge=NULL;
while(temp_edge!=NULL)//由于前面是逆序的链表,现在我们把他转成顺序的链表
{
next_edge=temp_edge->next;
temp_edge->next=swap_edge;
swap_edge=temp_edge;
temp_edge=next_edge;
}
temp_phrase->begin_of_phrase=swap_edge;
//链表转换完成
temp_phrase=temp_phrase->next;
}//当前节点的所有产生式转换完成
}//所有节点的产生式逆转完成
}


上面的代码只是为所有的文法符号生成一个标号,并将生成式转化了这些标号。

下面的代码才是核心的生成语法分析表的步骤,感觉总是有点疑问,特别是有些前提没有证明。

#include "declar_topological.h"
typedef struct _lr_group_node
{
int node_pointer;//代表产生式头
phrase* current_phrase;//代表当前的产生式体
pdecl_edge current_edge;//代表当前期待的语法单元
}lr_group_node,*plr_group_node;//这里是闭包中的一个项的描述
typedef struct _lr_closure
{
plr_group_node current_group_node;//这个代表一个项
struct _lr_closure* next;//这个next域将所有的单个项串联起来
}lr_closure,*plr_closure;//这里是一个闭包的结构
plr_closure current_closure[100];//暂且定为100,可以改
int group_index=0;//这里是为了标记群号
typedef struct _transition_node
{
enum
{
shift,//移进
reduce,//规约
error,//报错
accept//接受
}action_type;
union
{
struct
{
int shift_node;//移进的项集
};
struct
{
int reduce_node;//规约的时候的产生式头
int reduce_length;//规约的时候的产生式体的长度
};
};
}transition_node,*p_transition_node;
typedef struct _token_list
{
struct _token_list* next;
int node_number;
}token_list,*ptoken_list;
ptoken_list list_head=NULL;//节点头

transition_node* transition_table[400];//这里代表了整个转换表
void extend_group(int in_group_index)//扩展项集的函数
{
plr_closure temp_group,temp_group_end,temp_group_add;
//temp_group是用来遍历所有的项,temp_group_end是用来在遍历过程中表示最后一个项,
//temp_group_add跟前面一个合用,来在项集的末尾增加项
plr_group_node temp_group_node,temp_group_node_add;
//temp_group_node是用来遍历项,temp_group_node_add用来在增加项的过程中使用
phrase* temp_phrase;
//这两个都是用来增加项用的
int graph_node_index;
temp_group=current_closure[in_group_index];//得到当前项集的索引
temp_group_end=temp_group;//这里用来得到当前项集的最后一个项
while(temp_group_end->next!=NULL)
{
temp_group_end=temp_group_end->next;
}//寻找到末尾那个节点
while(temp_group!=NULL)//遍历整个项集,必要的时候扩充项集,即闭包
{
temp_group_node=temp_group->current_group_node;//获得当前项
if(temp_group_node->current_edge!=NULL)//如果右端还有文法符号,则试图去扩展
{
graph_node_index=temp_group_node->current_edge->node_of_dest;//获得当前项所期待的文法符号的索引
if(temp_group_node->node_pointer!=graph_node_index)//如果这个文法符号与当前产生式的文法符号不相同
//相同的时候会造成无限循环,即不停的把这个项添加进当前闭包中,并且无法终止
//由于我们在扩展闭包的过程中是按照拓扑顺序来添加的,所以可以保证在不重复添加同一节点的情况下
//群的大小是有界的
//在不等的情况下,我们可以扩展群
{
temp_phrase=decl_node_table[graph_node_index].phrase_link;//获得当前文法符号的第一个产生式
while(temp_phrase!=NULL)//遍历产生式
{
temp_group_node_add=malloc(sizeof(struct _lr_group_node));
temp_group_node_add->current_edge=temp_phrase->begin_of_phrase;//设置为开始的那一条边
temp_group_node_add->current_phrase=temp_phrase;//设置为当前产生式的链表头
temp_group_node_add->node_pointer=graph_node_index;//设置为产生式的头
//至此构建了一个项的描述
//开始添加进项集中,准确的来说是末尾
temp_group_add=malloc(sizeof(struct _lr_closure));
temp_group_add->current_group_node=temp_group_node_add;
temp_group_add->next=NULL;
temp_group_end->next=temp_group_add;
temp_group_end=temp_group_add;
temp_phrase=temp_phrase->next;
}//把当前项的扩展项都添加进去了
}//同上
}
temp_group=temp_group->next;//处理下一项
}//所有的扩展项都处理完成了
}//扩展完成

plr_closure closure_transition(int closure_index,int node_index)
{
plr_closure result_closure,now_closure,temp_closure_add,temp_closure_end,temp_closure;
plr_group_node temp_group_node,temp_group_node_add;
pdecl_edge temp_edge;//尼玛 ,为什么这么多temp啊,老子要转函数式编程
now_closure=current_closure[closure_index];
result_closure=malloc(sizeof(struct _lr_closure));
result_closure->current_group_node=NULL;
result_closure->next=NULL;
temp_closure_end=NULL;
//把这个新的群初始化为空
temp_closure=now_closure;
while(temp_closure!=NULL)
{
temp_group_node=temp_closure->current_group_node;
temp_edge=temp_group_node->current_edge;//注意这里current_edge可能为空,要去判断
if(temp_edge!=NULL)//如果不为空,再去判断是否是希望的节点
{
if(temp_edge->node_of_dest==node_index)
{
temp_group_node_add=malloc(sizeof(struct _lr_group_node));
temp_group_node_add->current_edge=temp_edge->next;
temp_group_node_add->current_phrase=temp_group_node->current_phrase;
temp_group_node_add->node_pointer=temp_group_node->node_pointer;
//现在建立好了一个项,准备插入到这个项集里面去
temp_closure_add=malloc(sizeof(struct _lr_closure));
temp_closure_add->current_group_node=temp_group_node_add;
temp_closure_add->next=NULL;//因为是从尾部开始加入的所以就初始化为空
if(temp_closure_end!=NULL)//这里需要判断当前加入的是不是第一个项,因为要特殊处理
{
temp_closure_end->next=temp_closure_add;
temp_closure_end=temp_closure_add;
}
else
{
result_closure->current_group_node=temp_group_node_add;
temp_closure_end=result_closure;
free(temp_closure_add);
}
}
}
temp_closure=temp_closure->next;
}//一直遍历整个项链表,有转换的时候就添加
if(result_closure->current_group_node==NULL)
{
free(result_closure);
return NULL;
//额,这里忘了释放内存了,调试完再去释放
}
else
{
return result_closure;
}
}

int already_in(plr_closure input_closure)
//遍历整个项集来比较,这个方法比较笨,其实可以插入的时候打表
//也可以利用hash或者其他的数据结构来查询
//这个函数是目前问题最大的函数
{
int for_i;
plr_closure temp_closure,begin_closure;
plr_group_node temp_group_node,org_group_node;
for(for_i=1;for_i<=group_index;for_i++)//遍历整个项集族
{
temp_closure=current_closure[for_i];
begin_closure=input_closure;
//都获得第一个项集的指针,这样我们就可以遍历了
while(begin_closure!=NULL)//遍历整个项集,由于当前尚未扩展,所以用这个短的
//当前比较的都是核心项,这里需要担心的是某个项集的核心项包括了当前项集的核心项
//如果出现这种情况,这里的代码就会出现错误
//当前我们先不考虑这种情况,之后再去修改
{
temp_group_node=temp_closure->current_group_node;
org_group_node=begin_closure->current_group_node;
//由于我们保证了添加项集的时候是按照文法符号的引用序来添加的
//所以直接按照链表,一个一个来比较就行了,如果有一个没有匹配上就不需要再去比较剩下的项了
//只需要匹配下一个项集族
//现在判断是否完全匹配
if(temp_group_node->node_pointer==org_group_node->node_pointer)//如果产生式头相同
{
if(temp_group_node->current_phrase==org_group_node->current_phrase)//如果产生式的体相同
{
if(temp_group_node->current_edge==org_group_node->current_edge)//如果下一个期待的符号相同
{
temp_closure=temp_closure->next;
begin_closure=begin_closure->next;
continue;//进入下次循环
}
}
}
break;//如果这三个不能完全匹配,则跳出当前项集循环判断,进入下一个项集的循环判断
}//当前项集遍历结束
if(begin_closure==NULL)
//如果遍历到了末尾,则说明完全匹配了
//这里我们保证了开始时的temp_closure不是空的,因为我们不会插入一个空群
{
return for_i;//返回群号,说明这个项集已经存在了
}
}
//如果所有的项集都无法匹配
return 0;//返回0,说明这个项集还不存在
}
void output_transition(void)
{

int for_i,for_j;
transition_node* temp_tran_row;
printf("     ");
for(for_i=1;for_i<=node_index+1;for_i++)
{
printf("%-4s ",decl_node_table[for_i].name_of_decl);
}
printf("\n");
for(for_i=1;for_i<=group_index;for_i++)
{
temp_tran_row=transition_table[for_i];
printf("%3d  ",for_i);
for(for_j=1;for_j<=node_index+1;for_j++)
{
if(temp_tran_row[for_j].action_type==reduce)
{
printf("r%2d  ",(temp_tran_row+for_j)->reduce_node);
}
else
{
if(temp_tran_row[for_j].action_type==shift)
{
printf("s%2d  ",(temp_tran_row+for_j)->shift_node);
}
else
{
if(temp_tran_row[for_j].action_type==accept)
{
printf("ac   ");
}
else
{
printf("err  ");
}
}
}
}
printf("\n");
}
}

void output_closure(void)
{
int for_i;
int begin;
plr_closure temp_closure;
plr_group_node temp_group_node;
pdecl_edge temp_edge;
phrase* temp_phrase;
for(for_i=1;for_i<=group_index;for_i++)
{
temp_closure=current_closure[for_i];
while(temp_closure!=NULL)
{
temp_group_node=temp_closure->current_group_node;
begin=temp_group_node->node_pointer;
temp_phrase=temp_group_node->current_phrase;
temp_edge=temp_phrase->begin_of_phrase;
printf("%s->",decl_node_table[begin].name_of_decl);
while(temp_edge!=NULL)
{
if(temp_edge==temp_group_node->current_edge)
{
printf(".");
}
printf("%s",decl_node_table[temp_edge->node_of_dest].name_of_decl);
temp_edge=temp_edge->next;
}
if(temp_group_node->current_edge==NULL)
{
printf(".");
}
printf("\n");//当前项输出完成
temp_closure=temp_closure->next;
}//当前项集输出完成
printf("closure %d is completed\n",for_i);
}//所有项集输出完成
}

void graph_to_closure(char* begin_name)
{
int begin_node_index;//这个作为原来的开始文法符号的索引
int current_group_index;//这个代表当前正在处理的项集
int for_i,is_in;
pdecl_edge temp_edge;
phrase* temp_phrase;
plr_closure temp_group;
plr_group_node temp_group_node;
transition_node* temp_tran_row;//代表临时的转换行
int recept_group;//当作接收节点
begin_node_index=search_decl_hash(begin_name);//找到开始节点的索引
//开始添加伪节点
temp_edge=malloc(sizeof(struct _decl_edge));
temp_edge->next=NULL;
temp_edge->node_of_dest=begin_node_index;
temp_phrase=malloc(sizeof(struct _phrase));
temp_phrase->begin_of_phrase=temp_edge;
temp_phrase->next=NULL;
decl_node_table[0].phrase_link=temp_phrase;
insert_decl_hash("dumb",0);//插入哑元
//这里添加了伪开始节点
//下面开始建群
temp_group_node=malloc(sizeof(struct _lr_group_node));
temp_group_node->current_edge=temp_edge;
temp_group_node->current_phrase=temp_phrase;
temp_group_node->node_pointer=0;
temp_group=malloc(sizeof(struct _lr_closure));
temp_group->next=NULL;
temp_group->current_group_node=temp_group_node;
group_index++;
current_closure[group_index]=temp_group;//从1开始标号,注意
//现在开始扩展项集
extend_group(group_index);
current_group_index=group_index;
while(current_group_index<=group_index)//一个项集一个项集的处理
{
//现在对每一个文法单元进行尝试,看是否有转移
//如果有转移,看是否生成的项集已经被包括了
temp_tran_row=malloc(sizeof(struct _transition_node)*(node_index+2));
//这里需要考虑最后的终结符号,所以加1,又由于为了表述方便,我们把开始偏移定为1,所以最后是加2
transition_table[current_group_index]=temp_tran_row;
for(for_i=1;for_i<=node_index+1;for_i++)//这里加1是为了考虑终结符号
{
temp_group=NULL;
temp_group=closure_transition(current_group_index,for_i);//调用子函数来进行项集的生成
if(temp_group!=NULL)//如果不是空群,进行移进
{
is_in=already_in(temp_group);
if(!is_in)//如果这个项集还没有添加
{
group_index++;
current_closure[group_index]=temp_group;
temp_tran_row[for_i].action_type=shift;
temp_tran_row[for_i].shift_node=group_index;
extend_group(group_index);
}
else//如果项集已经添加
{
temp_tran_row[for_i].action_type=shift;
temp_tran_row[for_i].shift_node=is_in;
}
}
else//如果没有转换,则考虑规约
{
temp_tran_row[for_i].action_type=error;//首先写为这个,后面再去修改
//现在开始遍历当前项集,如果当前项集里面存在点在最末尾的项,则按照这个项来规约
//我们默认当前不会产生归约冲突,即当前的是无二义的lr(0)文法
temp_group=current_closure[current_group_index];
while(temp_group!=NULL)//遍历当前项集中所有的项,看是否有可归约项
{
temp_group_node=temp_group->current_group_node;
if(temp_group_node->current_edge==NULL)//如果这个项可以作为一个归约式
{
int length_of_phrase;//这个代表产生式体有多少个符号
temp_phrase=temp_group_node->current_phrase;
temp_edge=temp_phrase->begin_of_phrase;
length_of_phrase=0;
while(temp_edge!=NULL)//获得产生式长度
{
length_of_phrase++;
temp_edge=temp_edge->next;
}
temp_tran_row[for_i].reduce_node=temp_group_node->node_pointer;
temp_tran_row[for_i].reduce_length=length_of_phrase;
temp_tran_row[for_i].action_type=reduce;
break;
}
temp_group=temp_group->next;
}
//如果到最后都没有找到可以规约的项,则保持这个为error
}
}//所有文法符号处理完毕
current_group_index++;//当前项集处理完成,开始处理下一个项集

}//所有的项集处理完成
//这里再去添加一个接受状态
temp_tran_row=transition_table[(transition_table[1]+begin_node_index)->shift_node];
(temp_tran_row+node_index+1)->action_type=accept;

//现在把这个调整成为了接受状态,可以输出整个转换表了
//现在开始输出
output_closure();//输出所有的项集
output_transition();//输出转换

}//所有的完成
void str_to_list(char* input_str)//将输入字符转变为文法单元,然后以链表串接起来
{
char temp[10];
int str_index;
int str_len;
int node_pointer;
ptoken_list temp_list;
ptoken_list current_list;
str_index=0;
while(input_str[str_index]!='\0')
{
while(input_str[str_index]==' ')
{
str_index++;
}
str_len=0;
while(input_str[str_index]!=' '&&input_str[str_index]!='\0')
{
temp[str_len]=input_str[str_index];
str_index++;
str_len++;
}
temp[str_len]='\0';
node_pointer=search_decl_hash(temp);
temp_list=malloc(sizeof(struct _token_list));
temp_list->node_number=node_pointer;
temp_list->next=NULL;
if(list_head==NULL)
{
list_head=temp_list;
current_list=temp_list;
}
else
{
current_list->next=temp_list;
current_list=temp_list;
}
}
temp_list=malloc(sizeof(struct _token_list));
temp_list->node_number=node_index+1;
current_list->next=temp_list;
temp_list->next=NULL;
}

int get_token(void)//从链表中获得一个文法单元
{
ptoken_list temp_list;
int result_node;
result_node=list_head->node_number;
temp_list=list_head;
list_head=list_head->next;
free(temp_list);
return result_node;
}
void putback(int withdraw)
{
ptoken_list temp_list;
temp_list=malloc(sizeof(struct _token_list));
temp_list->node_number=withdraw;
temp_list->next=list_head;
list_head=temp_list;
}
//现在建立了转换表
void input_recognise(void )
{
int syntax_stack[100];//暂且开这么大
int stack_pointer;
int input_token;
int temp_token;
int reduce_token;//注意这里规约的时候,可能会产生为0的点
int reduce_length;
int for_i;
int end=0;
transition_node* current_tran_node;
stack_pointer=1;
syntax_stack[1]=1;//把最开始的群号放进去
temp_token=-1;//防止出现为0的情况
input_token=-1;
while(1)//一直循环
{
if(input_token==-1)
{
input_token=get_token();
}
current_tran_node=transition_table[syntax_stack[stack_pointer]]+input_token;//获得栈顶节点的转换表
switch((current_tran_node->action_type))//判断转换类型
{
case accept://接受
printf("acc\n");
input_token=-1;
end=1;//设置结束位
break;
case shift://移入
stack_pointer++;
syntax_stack[stack_pointer]=current_tran_node->shift_node;
printf("shift %d\n",current_tran_node->shift_node);
input_token=-1;//设置提醒位,提示下次需要读取输入
break;
case error://报错
printf("error\n");
end=1;//设置结束位
break;
case reduce://如果是规约,那么获得规约长度,然后缩短栈指针,
putback(input_token);//返回这个输入
reduce_length=current_tran_node->reduce_length;
reduce_token=current_tran_node->reduce_node;
stack_pointer=stack_pointer-reduce_length;
printf("reduce ");
for(for_i=1;for_i<=reduce_length;for_i++)
{
printf("%d ",syntax_stack[stack_pointer+for_i]);
}
printf(" to %d \n",reduce_token);
input_token=reduce_token;
break;
default:
printf("we are in trouble\n");
break;
}
if(end!=0)
{
break;//报错或接受,因此结束
}
}//一直循环
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: