算符优先分析法程序模拟
作者:李朝中
摘要:大家在学编译原理文法分析时都会先学习弱文法和算符文法,然后是LR系列的文法,算符文法做为一种简单易于理解的文法,可以帮助大家学习深刻复杂的文法。本文介绍算符分析程序实现方法,希望能帮助大家理解算符分析的实际分析流程。
关键词:算符分析、VC++、编译原理
一、简介
算符优先分析法的关键是比较两个相继出现的终结符号的优先级而决定应采取的动作。要完成算符间的优先级比较,就要先定义各种可能出相继出现的运算符的优先级,并将其表示成矩阵形式,在分析过程中通过查询矩阵元素而得算符间的优先关系。对于任何两个相继出现的终结符号a和b具有形式:“…ab…”或 “…aQb…”,Q为非终结符,定义a,b间的如下三种关系为:
1)a〈b a的优先级低于b
2)a=b a的优先级等于b
3)a>b a的优先级高于b
如果a和b在任何情况下不可能相继出现,则a,b之间无关系。i½(E)½E*E½E-E½E+E 已知文法G[E]:E 其终结符号的关系可用一个矩阵表示如下,称其为优先表。有了优先表我们就可根据算符的优先关系对符号串进行归约,从而求出其运算结果。
二、程序实现
下面来一起来计论一下算符文法分析的程序实现。所分析的文法是E->E*E|E/E|E-E|E+E|(E)|i分析表的建立过程大家可以自己做。大部教材上都有详细的说明。我们所要达到目的是写一个指定的表达式列出分析的过程并最终分析出它是否合法。
第一步:在VC++6.0中新建一个对话框工程命名为SyntaxAnalyse;
第二步:将取消按钮改成退出,确定的ID改为ID_ANALYSE,名字改为分析
第三步:添加一个文本框,一个LIST控件分别对应变量m_edit,m_list。注意LIST控件设置为REPORT模式,无排序。
第四步:要为产生式建立一个结构体把它放在stdafx.h中,这个结构体存储产生式的左部和右部,我们在具体分析时会用到它,具体如下:
typedef struct _tagProductFormula
{
char m_left;
char m_right[256];
}Formula,*PFormula;
第五步:在SyntaxAnalyseDlg.h中添加变量及声名函数其都为私有成员。下面列出几较为重要的变量和函数
void Analyse(CString param);//分析主函数
int CmpPRI(char param1,char param2);//分析时确定符号的优先级
bool TestISVT(CString string);
int PRITable[8][8];
Formula FormulaTable[6];
第六步:在OnInitDialog()函数中加入一个对m_list变量的初始化,下面的代码是用来设置控件的样式和初始化我们分析时所要用的到的数据。
m_list.SetExtendedStyle(LVS_EX_HEADERDRAGDROP|LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES|LVS_EX_TRACKSELECT);
///初始化数据表为分析方法做准备
InitList();//初始化list控件
InitTable();//初始化数据表
第七步:我们来实现在实际分析过程中需要的其他操作。其作用与上面第五步已经说明,下面是具体的实现码。
//测试串是否在规定的字符集中
bool CSyntaxAnalyseDlg::TestISVT(CString string)
{
int iLen=string.GetLength()-1;
char temp;
while(iLen>-1)
{
temp=string.GetAt(iLen);
if(GetItem(temp)==8)
return false;
}
return true;
}
//比较优先级
int CSyntaxAnalyseDlg::CmpPRI(char param1, char param2)
{
int pri_1=GetItem(param1);
int pri_2=GetItem(param2);
if(pri_1==8||pri_2==8)
{
return 3;
}
return PRITable[pri_1][pri_2];
}
第八步:我们前已经做很多的初化和准备工作下面的代码,就是我们的具体分析过程实现。
//语法分析
void CSyntaxAnalyseDlg::Analyse(CString param)
{
int k=0,p=0,j=0,iLen=param.GetLength(),PRI,l=0;
char temp_1,temp_2;
InserteList(-1,m_stack,3," ",param,"初始");
while(iLen>p)
{
temp_2=param.GetAt(p);
goto1: temp_1=m_stack.GetAt(k);
if(GetItem(temp_1)!=8)
j=k;
else
j=k-1;
temp_1=m_stack.GetAt(j);
PRI=CmpPRI(temp_1,temp_2);
if(PRI==1)
{
while(1)
{
//MessageBox("temp_2=param.GetAt(p);");
temp_1=m_stack.GetAt(j);
if(GetItem(m_stack.GetAt(j-1))!=8)
{
j=j-1;
}
else
{
j=j-2;
}//end if(GetItem(m_stack.GetAt(j-1))!=8)
PRI=CmpPRI(m_stack.GetAt(j),temp_1);
if(PRI==2)
break;
}
///完成一次归约/
j++;
CString str1,str2;
l=0;
while(l<6)
{
str1.Format("%s",m_stack.Mid(j,k-j+1));
str2.Format("%s",FormulaTable[l].m_right);
if(str1==str2)
{
m_stack.Delete(j,k-j+1);
m_stack.Insert(j,FormulaTable[l].m_left);
k=j;
InserteList(p,m_stack,1,temp_2,param.Right(iLen-p-1),"归约");
goto goto1;
}//end if(!strcmp(m_stack.Mid(j,k-j),FormulaTable[l].m_right))
l++;
}
}
else
{
if(PRI==2)
{
//如果优先低就移进栈
k++;
m_stack.Insert( k, temp_2 );
//temp_2=(char)p+48;
InserteList(p,m_stack,2,temp_2,param.Right(iLen-p-1),"移进");
p++;
}
else
{
if(PRI==0)
{
PRI=CmpPRI(m_stack.GetAt(j),'#');
if(PRI==0)
{
InserteList(p,m_stack,0,temp_2,param.Right(iLen-p-1),"接受");
break;
}
else
{
k++;
m_stack.Insert( k, temp_2 );
InserteList(p,m_stack,2,temp_2,param.Right(iLen-p-1),"移进");
p++;
}
}
else
{
InserteList(p,m_stack,3,temp_2,param.Right(iLen-p-1),"出错");
MessageBox("语法出错误!!!!");
break;
}//end if(PRI==0)
}//end if(PRI==2)
}//end if(PRI==1)
}
}
最后我们在分析按钮单击事件中实现下面代码:
CString token;
UpdateDate(true);
m_stack=”#”;
token.Format(“%s#”,m_edit);
m_list.DeleteAllItems();
nIndex=0;
Aanalyse(token);
编译运行出如下图:
三、结束语
上面我们一起讨论算符文法分析程序的实现的过程,希望能给大家学编译原理这门课带来一些帮助。