编译原理——语法分析器(SLR)
识别语法结构:
- 变量声明(不含变量初始化)
- if单条件分支语句以及if else 双条件分支语句
- for循环和while循环语句
- 赋值语句 ,四则运算,逻辑判断复合语句
- 函数声明 函数调用
文法
//其中$作为标识符判断 @常数值判断
Z->S
S->while(E)S
S->E;S
S->;S
S->for(E;E;E)S
S->CM;S
S->C$(W){SreturnE;}S
S->$(H);S
M->M,$
M->$
H->$H
H->nH
H->,$H
H->,@H
H->#
C->char
C->int
C->double
S->if(E)S
S->SelseS
S->{S}S
S->#
E->EOE
E->(E)
E->@
E->$
W->#
W->C$,W
W->C$
O->+
O->-
O->/
O->*
O->>
O->=
O->>=
O-><
O-><=
O->!=
O->==
O->&
O->&&
O->|
O->||
算法描述及实验步骤
数据结构解析
- product—存放文法
typedef struct Product{
char left;
char right[N];
int length;
}Product;
- Follow—存放文法的follow集
typedef struct Follow{
char V;
vector<char> fset;
}Follow;
- I—存放状态闭包集
typedef struct I{
vector<Product> pros;
}I;
主要函数解析
-
SLR函数设计
-
该函数能够实现将文件中输入好的文法自动根据SLR状态转移算法形成Action表和Goto表
-
事先读入文法,并获取产生式中出现的终结符集合以及非终结符集合
-
修正文法,将产生式右部添加 . 号,便于算法模拟
Z->.S S->.while(E)S S->.E;S S->.;S S->.for(E;E;E)S S->.CM;S S->.C$(W){SreturnE;}S S->.$(H);S M->.M,$ M->.$ H->.$H H->.nH H->.,$H H->.,@H H->. C->.char C->.int C->.double S->.if(E)S S->.SelseS S->.{S}S S->. E->.EOE E->.(E) E->.@ E->.$ W->. W->.C$,W W->.C$ O->.+ O->.- O->./ O->.* O->.> O->.= O->.>= O->.< O->.<= O->.!= O->.== O->.& O->.&& O->.| O->.||
-
将第一个产生式放入闭包集I(0),并求出完整的闭包集
-
依次依据识别出的非终结符和终结符,对第一个闭包集进行状态转移
-
每次转移之后,得到新的闭包集I(i),并对I(i)求出完整的闭包集。求出之后在已有的闭包集中判断是否是重复的闭包集。
-
如果不重复添加Action表和Goto表信息,状态I(i-1)中的i-1为横坐标,识别的终结符或者是非终结符为纵坐标,i为表项的值。
-
如果重复则第一次出现该闭包集的状态下标作为表项的值
-
依次次做上一步,直到没有新的闭包集出现
-
对所有闭包集做判断,倘若闭包集中出现了规约性项目,查询该项目的左部的非终结符的Follow集,并将对应的闭包集状态下标以及Follow符号对应的Action表和Goto表表项进行修改。
-
-
close函数设计
- 该函数实现求状态转移后,完整闭包集的求法
- 遍历当前闭包集中的产生式,每个产生式寻找到 . 号,对 . 号后面的字符做判断
- 倘若是**\0**则代表是规约型项目,不做处理
- 倘若是终结符,不作处理
- 倘若是非终结符,则寻找,当前所有产生式左部含有该非终结符的产生式添加到该闭包集中
- 循环处理上一步,直至闭包集的大小不发生改变
-
SLR驱动函数设计
- 该函数实现对输入字符串的语法解析,采用的是SLR的算法策略
- 首先该函数调用之前应具有正确的Action表以及Goto表
- 根据SLR算法思想设立字符栈,状态栈,初始状态为0
- 读入所需判断的字符,根据状态栈顶和字符,查询Action表以及Goto表
- 如果是移路状态,则将该字符放入字符栈中
- 如果是规约状,则根据规约产生式,将字符(状态)从字符(状态)栈中弹出,并判断对应非终结符的状态,放入状态栈中
- 如果是接受状态,则判断该字符串语法正确
- 如果出现错误,将调用error函数,处理错误
-
error函数设计
- 该函数为出现错误时调用
- 判断当前字符栈中的字符串,与所有产生式进行比对,如果是该字符串是产生式的一部分,那么可以得到结论,原始需要判断的字符串的中缺少的是该产生式中未得到比对的字符
- 记录当前出错的代码位置,以及错误信息,将字符栈,状态栈,置空,重新判断后面的代码。
- 该函数处理的比较一般,无法处理较复杂的错误,暂时未能够想到更好的处理办法
-
init_Follow函数设计
- 该函数能获取非终结的follow集
- 该函数暂时未能用代码自动实现,主要靠文件读取。
-
main函数设计
- 先做词法分析,倘若出现词法错误,输出错误信息结束程序
- 读取文法,将文件中的文法读取,到程序中
- 初始化得到非终结集合,终结符集合,follow集合
- 初始化Action表以及Goto表
- SLR函数,求闭包集
- 更新Action表以及Goto表
- 调用SLR驱动程序,进行语法分析
- 输出错误信息
源代码
```c++
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <string.h>
#include <cmath>
#include <vector>
#include <stack>
//词法分析器
#include "CIFA.h"
using namespace std;
//const int N=100;
//const int MAXSIZE=1000;
//产生式存储
typedef struct Product{
char left;
char right[N];
int length;
}Product;
//状态闭包集存储
typedef struct I{
vector<Product> pros;
}I;
//follow集存储
typedef struct Follow{
char V;
vector<char> fset;
}Follow;
//闭包集 集合
vector<I> Is;
//follow集 集合
vector<Follow> FollowSet;
int prod_len;
//产生式集
Product products[MAXSIZE];
//非终结符集,终结符集
char V[MAXSIZE];int V_len=0;
char T[MAXSIZE];int T_len=0;
//action goto 表
char _action[MAXSIZE][MAXSIZE];
int _goto[MAXSIZE][MAXSIZE];
//action 表初始化
void init_action(){
for(int i=0;i<MAXSIZE;i++)
for(int j=0;j<MAXSIZE;j++)
_action[i][j]='e';
}
//goto 表初始化
void init_goto(){
for(int i=0;i<MAXSIZE;i++)
for(int j=0;j<MAXSIZE;j++)
_goto[i][j]=-1;
}
//字符串比对函数
bool mystrcmp(char *s,char *p){
int i=0,j=0;
while(s[i]!='\0' && p[j]!='\0' && s[i]==p[j]){++i;++j;}
if(s[i]=='\0' && p[j]=='\0')return true;
else return false;
}
//判断产生式是否相同
bool is_sameProduct(Product a,Product b){
if(a.length!=b.length)return false;
if(a.left==b.left && mystrcmp(a.right,b.right))return true;
else return false;
}
//判断是否为操作符
bool is_operate(char c){
char op[N]="+-*/=&|><";
int i=0;
while(op[i]!='\0'){
if(op[i]==c)return true;
i++;
}
return false;
}
//判断是否为非终结符
bool is_signV(char c){
if(c>='A' && c<='Z')return true;
else return false;
}
//判断状态闭包集是否相同
bool is_sameI(I a,I b){
if(a.pros.size()!=b.pros.size())return false;
int i,j;
int cnt=0;
for(i=0;i<a.pros.size();i++){
for(j=0;j<b.pros.size();j++){
if(is_sameProduct(a.pros[i],b.pros[j])){cnt++;break;}
}
}
if(cnt==a.pros.size()){
return true;
}
else return false;
}
//产生式拷贝
Product newProduct(Product a){
Product ans;
ans.left=a.left;
strcpy(ans.right,a.right);
ans.length=a.length;
return ans;
}
//下标索引转换
int changIndex(char c){
for(int i=0;i<T_len;i++){
if(T[i]==c)return i;
}
for(int i=0;i<V_len;i++){
if(V[i]==c)return T_len+i;
}
return -1;
}
//判断产生式是否在闭包集中
bool is_inI(I s,Product a){
for(int i=0;i<s.pros.size();i++){
if(is_sameProduct(s.pros[i],a))return true;
}
return false;
}
//输出产生式
void print_product(Product a){
printf("%c->%s\n",a.left,a.right);
}
//输出产生式 --- 保存文件
void print_I(I a,int ans){
char filename[10]="Is/I";
int t=0,cnt=ans;
string s="";
while(filename[t]!='\0')t++;
while(cnt){s+=(cnt%10)+'0';cnt/=10;}
for(int i=s.size()-1;i>=0;i--)filename[t++]=s[i];
filename[t++]='.';filename[t++]='t';filename[t++]='x';filename[t++]='t';
filename[t]='\0';
FILE *fp=fopen(filename,"w");
for(int i=0;i<a.pros.size();i++){
fprintf(fp,"%c->%s\n",a.pros[i].left,a.pros[i].right);
}
fclose(fp);
}
//判断是否是对应产生式
bool judge_product(Product a,Product b){
int i=1,j=0;
while(a.right[i]!='\0' && b.right[j]!='.' && a.right[i]==b.right[j]){++i;++j;}
if(a.left==b.left && a.right[i]=='\0' && b.right[j]=='.')return true;
else return false;
}
//闭包集求解
I close(I a){
for(int i=0;i<a.pros.size();i++){
char *s=a.pros[i].right;
int k=0;
while(s[k]!='\0' && s[k]!='.')k++;
char c=s[k+1];
if(c!='\0' && c>='A' && c<='Z'){
for(int j=0;j<prod_len;j++){
if(products[j].left==c){
if(!is_inI(a,products[j]))
a.pros.push_back(products[j]);
}
}
}
}
return a;
}
//寻找非终结符对应follow集
int findFollow(char v){
for(int i=0;i<FollowSet.size();i++){
if(FollowSet[i].V==v)return i;
}
return -1;
}
//寻找产生式
int findProduct(Product a){
for(int i=0;i<prod_len;i++){
if(judge_product(products[i],a))return i;
}
return -1;
}
//SLR
void SLR(){
I t;
t.pros.push_back(products[0]);
t=close(t);
Is.push_back(t);
int z=0;
do{
/*求状态闭包集*/
/*状态转移---非终结符*/
for(int m=0;m<V_len;m++){
I t;
for(int n=0;n<Is[z].pros.size();n++){
Product ans=newProduct(Is[z].pros[n]);
int k=0;
while(ans.right[k]!='\0' && ans.right[k]!='.')k++;
if(ans.right[k+1]=='\0')continue;
if(V[m]==ans.right[k+1]){
swap(ans.right[k],ans.right[k+1]);
t.pros.push_back(ans);
}
}
t=close(t);
if(t.pros.size()==0)continue;
int u;
for(u=0;u<Is.size();u++){
if(is_sameI(Is[u],t)){
int xx=z,yy=changIndex(V[m]);
_goto[xx][yy]=u;
break;
}
}
if(u==Is.size()){
Is.push_back(t);
int xx=z,yy=changIndex(V[m]);
_goto[xx][yy]=Is.size()-1;
}
}
for(int m=0;m<T_len;m++){
if(T[m]=='\0')continue;
I t;
for(int n=0;n<Is[z].pros.size();n++){
Product ans=newProduct(Is[z].pros[n]);
int k=0;
while(ans.right[k]!='\0' && ans.right[k]!='.')k++;
if(ans.right[k+1]=='\0')continue;
if(T[m]==ans.right[k+1]){
swap(ans.right[k],ans.right[k+1]);
t.pros.push_back(ans);
}
}
t=close(t);
if(t.pros.size()==0)continue;
int u;
for(u=0;u<Is.size();u++){
if(is_sameI(Is[u],t)){
int xx=z,yy=changIndex(T[m]);
_action[xx][yy]='s';
_goto[xx][yy]=u;
break;
}
}
if(u==Is.size()){
Is.push_back(t);
int xx=z,yy=changIndex(T[m]);
_action[xx][yy]='s';
_goto[xx][yy]=Is.size()-1;
}
}
z++;
}while(z<Is.size());
//printf("66\n");
int l,xx,yy,index,r;
for(int i=0;i<Is.size();i++){
for(int j=0;j<Is[i].pros.size();j++){
l=Is[i].pros[j].length;
if(Is[i].pros[j].right[l-1]=='.'){
xx=i;index=findFollow(Is[i].pros[j].left);
//printf("%d \n",index);
r=findProduct(Is[i].pros[j]);
for(int k=0;k<FollowSet[index].fset.size();k++){
yy=changIndex(FollowSet[index].fset[k]);
_action[xx][yy]='r';
_goto[xx][yy]=r;
}
}
}
}
_action[1][T_len-1]='a';
}
//初始化非终结符
void init_V(){
char c;
int i,j;
for(i=0;i<prod_len;i++){
c=products[i].left;
//printf("%c",c);
for(j=0;j<V_len;j++){
if(c==V[j]){break;}
}
if(j==V_len){V[V_len]=c;V_len++;}
}
//for(int i=0;i<V_len;i++){printf("%c ",V[i]);}
}
//初始化终结符
void init_T(){
char *s,c;
int i,j,k;
for(i=0;i<prod_len;i++){
s=products[i].right;
k=0;
while(s[k]!='\0'){
c=s[k];k++;
if((c>'Z' || c<'A') && c!='#'){
//printf("%c ",c);
for(j=0;j<T_len;j++){
if(c==T[j]){break;}
}
if(j==T_len){T[T_len]=c;T_len++;}
}
}
}
T[T_len++]='\0';
//for(int i=0;i<T_len;i++){printf("%c ",T[i]);}
//printf("\n");
}
//初始化follow集
/*暂时还无法用程序实现*/
void init_Follow(){
FILE * fp=fopen("Follow.txt","r");
char ans[N];
int v,k;
while(!feof(fp)){
fscanf(fp,"%s",ans);
k=0;
Follow t;
while(ans[k]!='\0' && ans[k]!=EOF){
if(ans[k]>='A' && ans[k]<='Z')t.V=ans[k];
else {
if(ans[k]=='#')t.fset.push_back('\0');
else t.fset.push_back(ans[k]);
}
k++;
}
FollowSet.push_back(t);
fgetc(fp);
}
fclose(fp);
}
//读取文件中的产生式
void GetProduct(char *filename){
FILE *fp=fopen(filename,"r");
int ans;
char c;
prod_len=0;
while(!feof(fp)){
fscanf(fp,"%c->%s",&products[prod_len].left,products[prod_len].right);
c=fgetc(fp);
ans=0;
while(products[prod_len].right[ans]!='\0')ans++;
products[prod_len].length=ans;
prod_len++;
}
fclose(fp);
}
//更新产生式
char* updateProduct(){
FILE * fp=fopen("t_WENFA.txt","w");
for(int i=0;i<prod_len;i++){
if(i==prod_len-1){
if(mystrcmp("#",products[i].right))fprintf(fp,"%c->.",products[i].left);
else fprintf(fp,"%c->.%s",products[i].left,products[i].right);
}
else{
if(mystrcmp("#",products[i].right))fprintf(fp,"%c->.\n",products[i].left);
else fprintf(fp,"%c->.%s\n",products[i].left,products[i].right);
}
}
fclose(fp);
return "t_WENFA.txt";
}
//错误处理
void error(int eline,stack<char> s,char c,FILE *fp){
if(s.empty())return ;
int i,j,index,t=0;
char tmp[20],ct;
vector<char> ans(s.size());
for(i=ans.size()-1;i>=0;i--){
ans[i]=s.top();s.pop();
}
if(is_operate(ans[ans.size()-1])){insert_error(eline,"操作数","语法错,此处缺少");}
bool judge=false;
for(i=0;i<ans.size();i++)
{
if(ans[i]=='(')judge=true;
if(ans[i]==')')judge=false;
}
if(judge){insert_error(eline,")","语法错,此处缺少");}
judge=false;
for(i=0;i<ans.size();i++)
{
if(ans[i]=='{')judge=true;
if(ans[i]=='}')judge=false;
}
if(judge){insert_error(eline,"}","语法错,此处缺少");}
// for(i=0;i<ans.size();i++){cout<<ans[i]<<" ";}
// cout<<endl;
for(i=0;i<prod_len;i++){
j=0;
while(j<products[i].length && j<ans.size() && products[i].right[j]==ans[j])j++;
//cout<<i<<endl;
if(j==ans.size()){
while(products[i].right[j]!='\0'){
if(products[i].right[j]<'A' || products[i].right[j]>'Z')tmp[t++]=products[i].right[j++];
else break;
//cout<<tmp[t-1]<<endl;
}
//print_product(products[i]);
tmp[t]='\0';
if(t==1){
if(tmp[0]=='@' || tmp[0]=='$')
{insert_error(eline,"操作数","语法错,此处缺少");return ;}
else{
if(is_operate(tmp[0])){insert_error(eline,tmp,"语法错,此处缺少");return ;}
else{
insert_error(eline,tmp,"语法错,非法字符");
return ;
}
}
}else if(t>1){
if(t>1)insert_error(eline,tmp,"语法错,此处缺少");
return ;
}else{
tmp[t++]=c;
while((ct=fgetc(fp))!=EOF){
if(ct>='a' && ct<='z')tmp[t++]=ct;
else break;
}
tmp[t]='\0';
insert_error(eline,tmp,"语法错,非法字符");
return ;
}
}
}
//for(int i=0;i<=Erlen;i++){printf("%d :%s %s\n",Errors[i].line,Errors[i].info,Errors[i].c);}
//exit(0);
}
//更新action表
void update_action(){
FILE *file_action=fopen("action.txt","w");
fprintf(file_action,"\t");
for(int i=0;i<T_len;i++){
if(T[i]!='\0')fprintf(file_action,"%c\t",T[i]);
else fprintf(file_action,"#\t");
}
fprintf(file_action,"\n");
for(int i=0;i<Is.size();i++)
{
fprintf(file_action,"%d\t",i);
for(int j=0;j<T_len;j++)
fprintf(file_action,"%c\t",_action[i][j]);
fprintf(file_action,"\n");
}
fclose(file_action);
}
//更新goto表
void update_goto(){
FILE *file_goto=fopen("goto.txt","w");
fprintf(file_goto,"\t");
for(int i=0;i<T_len;i++){
if(T[i]!='\0')fprintf(file_goto,"%c\t",T[i]);
else fprintf(file_goto,"#\t");
}
for(int i=0;i<V_len;i++){fprintf(file_goto,"%c\t",V[i]);}
fprintf(file_goto,"\n");
for(int i=0;i<Is.size();i++)
{
fprintf(file_goto,"%d\t",i);
for(int j=0;j<V_len+T_len;j++)
fprintf(file_goto,"%d\t",_goto[i][j]);
fprintf(file_goto,"\n");
}
fclose(file_goto);
}
//SLR驱动程序
void SLR_Driver(){
FILE *fp_Token=fopen("Token.txt","r");
FILE *fp_debug=fopen("debug.txt","w");
stack<int> s_state;
stack<char> s_char;
int i=0,pos,state,code_line=1,ju=10;
char ans,c,s[N];
s_state.push(0);
c=fgetc(fp_Token);
while(1){
if(c=='\n'){c=fgetc(fp_Token);code_line++;continue;}
if(c==EOF){c='\0';}
state=s_state.top();
pos=changIndex(c);
ans=_action[state][pos];
//cout<<"状态栈顶"<<" "<<"符号栈顶"<<endl;
fprintf(fp_debug,"状态栈顶 符号栈顶\n");
if(!s_state.empty()&&!s_char.empty()){
//cout<<s_state.top()<<" "<<s_char.top()<<endl;
fprintf(fp_debug,"%d %c\n",s_state.top(),s_char.top());
}
//cout<<c<<" "<<" "<<ans<<" "<<_goto[state][pos]<<endl;
fprintf(fp_debug,"%c %c %d\n",c,ans,_goto[state][pos]);
switch(ans){
case 's':{s_state.push(_goto[state][pos]);s_char.push(c);c=fgetc(fp_Token);break;}
case 'r':{
if(products[_goto[state][pos]].right[0]!='#')
for(int i=0;i<products[_goto[state][pos]].length;i++){s_state.pop();s_char.pop();}
s_char.push(products[_goto[state][pos]].left);
s_state.push(_goto[s_state.top()][changIndex(s_char.top())]);
break;
}
case 'a':{
if(Erlen<0)
printf("Accept,语法分析成功666\n");
return ;
}
default :{
//cout<<c<<endl;
error(code_line,s_char,c,fp_Token);
s_char=stack<char>();
s_state=stack<int>();s_state.push(0);
c=fgetc(fp_Token);
if(c==EOF)return ;
break;
}
}
}
fclose(fp_Token);
fclose(fp_debug);
}
int main()
{
ci_start();
if(Erlen!=-1){
printf("词法分析错误\n");
for(int i=0;i<=Erlen;i++){printf("%d :%s %s\n",Errors[i].line,Errors[i].info,Errors[i].c);}
exit(0);
}
GetProduct("WENFA.txt");
init_V();
init_T();
init_Follow();
char *filename=updateProduct();
GetProduct(filename);
init_action();init_goto();
SLR();
update_action();
update_goto();
for(int i=0;i<Is.size();i++){print_I(Is[i],i);}
GetProduct("WENFA.txt");
update_action();
update_goto();
SLR_Driver();
for(int i=0;i<=Erlen;i++){printf("%d :%s %s\n",Errors[i].line,Errors[i].info,Errors[i].c);}
return 0;
}
效果呈现
test.c
int max(int a,int b){
int m;
if(a>b)m=a;
else m=b;
return m;
}
int main(){
int sum,i,n;
int cnt;
double ave;
sum=0;
cnt=0;
for(i=1;i<=n;i=i+1){
sum=sum+i;
}
max(sum,n);
ave=sum/n;
for(i=1;i<=n;i=i+1){
if(i==ave)cnt=cnt+1;
}
i=0;
while(i<=n){
if(cnt/i==0){printf("YES\n");}
else{printf("NO\n");}
i=i+1;
}
return 0;
}
调用程序,进行解析(该程序未出现语法错)
test2.c
int main(){
int name;
name=;
for(name=0;name<10;name=name+name)name+1;
return 0;
}
调用程序,进行解析(显然该程序出现语法错),因为一个错,带动后面一起错,所以出错处理不是非常好。
该程序可能会有bug,请见谅…欢迎指正。