算符优先分析过程模拟.docx
《算符优先分析过程模拟.docx》由会员分享,可在线阅读,更多相关《算符优先分析过程模拟.docx(10页珍藏版)》请在冰豆网上搜索。
算符优先分析过程模拟
算符优先分析过程模拟
一、需求分析......................................................................1
二、概要设计......................................................................2
三、详细设计......................................................................5
四、测试结果....................................................................10
五、总结............................................................................12
华东交通大学课程设计报告
算符优先分析是自底而上分析方法的一种,这种方法是对输入符号
串自左向右进行扫描,并将输入符逐个移入一个后进先出栈中,边移入边
分析,一旦栈顶符号串形成某个句型的句柄或可规约串时(该句柄或可归
约串对应产生式的右部),就用该产生式的左部非终结符代替相应右部的文
法符号串,重复这样的归约过程,一直到只剩文法的开始符号时则为分析
成功,也就是确认输入串是该文法的句子。
而算符优先分析的基本思想则是只规定算符之间的优先关系,也就
是只考虑终结符之间的优先关系,由于算符优先分析不考虑非终结符之间
的优先关系,在归约过程中只要找到可归约的串就归约,并不考虑归约到
那个非终结符名。
在定义具体的由于算符优先关系之前,先说明由于算符优先关系符
的定义,有三中由于算符优先关系符,分别是>,<,=
设G是一个不含ε产生式的算符文法,a,b是任意两个终结符,A,
B,C是非终结符,算符优先关系=,<,>定义如下:
(1)a=b当且仅当G中含有形如A->„ab„或A->„aBb„的产生式
(2)a„aB„的产生式,且B可以多
步推导出b„或B可以多步推倒出Cb„
(3)a>b当且仅当G中有型如A->„Bb„的产生式,且B可以多
步推导出„a或B可以多步推导出„Ac
因为算符优先关系运算的次序只与运算符有关,而与运算对象无关,
明显,对于算符优先文法的设计成为了关键。
第1页共13页
华东交通大学课程设计报告
算符优先分析只考虑算符(终结符)之间的优先关系,先设计文法G为:
(1)S->v=E
(2)E->E+E
(3)E->E-E
(4)E->E*E
(5)E->E/E
(6)E->(E)
(7)E->v
(8)E->i
通常在算术表达式求值过程中,运算次序是先乘除后加减,这说明了
乘除运算先级别高于加减运算的优先级,乘除为同一优先级但运算符在前
边的先做,这叫作左结合,同样加减运算也是如此,这也说明了运算的次
序只与运算符有关,而与运算对象无关,因而我根据这样的基本原理开始
设计运算符之间的优先关系。
(1)*,/的优先级别较高,高于+,-,遵循左结合。
相当于*>*,*>/,
/>/,/>*。
(2)+,-的优先级别较低,遵循左结合相当于+>+,+>-,->-,->+。
(3)对‘(’‘)’规定括号的优先性大于括号外的运算符,小于括号内
的运算符。
(4)对于句子括号‘#’号规定与它相邻的任何运算符号的优先性都比
它高。
、
(5)运算对象的终结符i,它优先级别最高。
第2页共13页
华东交通大学课程设计报告
算符优先关系表
+-*/()i#+>><<<><>->><<<><>*>>>><><>/>>>><><>(<<<<<=<)>>>>>>I>>>>>>#<<<<<<=
对于算符优先分析方法,在文法G下,预计总控程序的输出结果为:
对输入串i+i*i的归约过程
步骤栈S当前输入符剩余串动作
1#i+i*i#移进
2#i+i*i#归约E->i
3#E+i*i#移进
4#E+i*i#移进
5#E+i*i#归约E->i
6#E+E*i#移进
7#E+E*i#移进
8#E+E*i#归约E->i
9#E+E*E#归约E->E*E
10#E+E#归约E->E+E
11#E#接受
第3页共13页
华东交通大学课程设计报告
分析过程:
1、S栈中的“#”优先级小于当前输入符号i,移进。
2、栈顶符号“i”优先级大于当前输入符号+,对终结符i进行归约。
3、栈顶符号“#”优先级小于当前输入符号+,移进。
4、栈顶符号“+”优先级小于当前输入符号i,移进。
5、栈顶符号“i”优先级大于当前输入符号*,对终结符i进行归约。
6、栈顶符号“+”优先级小于当前输入符号*,移进。
7、栈顶符号“*”优先级小于当前输入符号i,移进。
8、栈顶符号“i”优先级大于当前输入符号#,对终结符i进行归约。
9、栈顶符号“*”优先级大于当前输入符号#,归约,E->E*E。
10、栈顶符号“+”优先级大于当前输入符号#,归约,E->E+E。
11、接受。
第4页共13页
华东交通大学课程设计报告
大多数对于算符优先分析方法都是设计两个栈,通过比较符号栈顶元素
和邻近的运算符之间的优先关系。
进行归约,移进的控制。
而我用的却是
另一种方法。
我将所输入的句型放在一个字符串Mystr中,然后对字符串进行扫描,
当扫描到的当前字符是“+,-,*,/,()”时分别调用与之对应的五种不同
的方法,当当前字符(token)是“+”调用方法evalExp2(),
因为*/法和()的有线级高于“+”法,所以在evalExp2()中要调用evalExp3()扫描后续的句子,后续的句子如果有“*,/”法,用evalExp3()完成对“*,/”法的归约。
同理括号中的算法的优先级别高于括号外的,所以evalExp3()的方法中要先调用evalExp4()对于括号的归约方法。
在完成后依次回溯到前一个方法,很明显我是通过递归的方法来实现算
符优先关系。
当然对于加减法evalExp2(),乘除法evalExp3(),括号evalExp4(),它们都可以单独调用,这由当前输入字符Taken确定。
归约的实现实际上是对字符串的处理,在下面的“模块设计”中将具体
解释。
第5页共13页
华东交通大学课程设计报告
1
privatecharevalExp2()throwsParserException
{
charresult;
charpartialResult;
result=evalExp3();//调用乘除法的归约方法
if(result!
='E'&&result!
='v'&&result!
='i')
return'Q';//非法字符的判断,退出。
else{
while(token=='+'||token=='-'){
charabc=token;
my[expIdx-1]="终结符";//读入当前运算符号
getToken();//读取下一个字符
if(token=='Q'){
error="语法错误";
handleErr(error);
return'Q';
}
else{//mystr用于保存输入的句子,mydel用于"动作"的输出
partialResult=evalExp3();
mystr[ind]=mystr[ind-1];mydel[ind]=mydel[ind-1];
ind++;
if("+-*/)".indexOf(token)==-1||!
mycmp)
{
mystr[ind-1]=mystr[ind-2].substring(0,mystr[ind-2].indexOf(abc,2))+mystr[ind-2
].substring(mystr[ind-2].indexOf(abc,2)+2);
mydel[ind-1]="归约"+"E?
E"+abc+"E";
}
else{
if(mystr[ind-3].indexOf(abc,mystr[ind-3].indexOf(abc)+1)!
=-1){
mystr[ind-2]=mystr[ind-3].substring(0,mystr[ind-3].indexOf(abc,mystr[ind-3].indexOf(abc)
+1))+mystr[ind-3].substring(mystr[ind-3].indexOf(abc,mystr[ind-3].indexOf(abc)+1)+2);
第6页共13页
华东交通大学课程设计报告mystr[ind-1]=mystr[ind-1].substring(0,mystr[ind-1].indexOf(abc,mystr[ind-1].indexOf(abc)
+1))+mystr[ind-1].substring(mystr[ind-3].indexOf(abc,mystr[ind-1].indexOf(abc)+1)+2);
}
else{//对于字符串的归约
mystr[ind-2]=mystr[ind-3].substring(0,mystr[ind-3].indexOf(abc,2))+mystr[ind-3].substring
(mystr[ind-3].indexOf(abc,2)+2);
mystr[ind-1]=mystr[ind-1].substring(0,mystr[ind-1].indexOf(abc,2))+mystr[ind-1].substring
(mystr[ind-1].indexOf(abc,2)+2);
}
mydel[ind-2]="归约"+"E?
E"+abc+"E";
}
if(partialResult!
='E'&&partialResult!
='v'&&partialResult!
='i')
return'Q';
}
}
return'E';
}
}
对于加减法归约的方法实际上是对存储句子的字符串mystr的操作,
给定一个句子,程序从逐个读入字符,当出现“+,-”号时调用evalExp2(),在一
系列的合法性判断之后,进行规约。
这里主要用到了(intstart,intend),方法,这是读取指定位置的字串。
例如,我们现在有一个句子v=E+E*E,要对加减法归约,先通过INDEX方
法读取到加号的位置,然后对字符串的前后求子串,
我们可以把所有的句子假设为„E+E„的形式,并假设当前算符位置为
taken通过读出前一个“„”,然后连接上规约的结果E(E->E+E),最后再连接上后面的子串直接用
输出后面的子串。
最终将„E+E„的形式规约为„E„的形式。
对于乘除法和括号的规约方法和加法的归约方法基本类似,也是对字符串的处理,
由此实现对运算符的归约。
第7页共13页
华东交通大学课程设计报告
2E->i
privatevoidgetToken()
{
if(expIdx==exp.length()){
mycmp=false;
token='Q';
return;
}
//
while(expIdxCharacter.isWhitespace(exp.charAt(expIdx)))++expIdx;
if(expIdx==exp.length()){
mycmp=false;
token='Q';
return;
}
//
token=exp.charAt(expIdx);
mystr[ind]=mystr[ind-1]+token;
mydel[ind]="移进";
wyq=wyq+token+"";
ind++;
if(cmp){
if(("+-/*()Evi=".indexOf(token)!
=-1)){
expIdx++;
}
else{
cmp=false;
handleErr("非法字符");
return;
}
cmp=false;
}
//E->i
else{
if(("+-/*()Evi".indexOf(token)!
=-1)){
第8页共13页
华东交通大学课程设计报告
expIdx++;
if(("vi".indexOf(token)!
=-1)&&ind!
=2){
mystr[ind]=mystr[ind-2]+"E";
mydel[ind]="归约"+"E?
"+token;
ind++;
}
}
else{
handleErr("非法字符");
return;
}
}
return;
}
由算符优先分析表我们知道终结符i的优先级别是最高的,也就是说当读入到i时立即进行归约,于是我在读取到下一个字符的时候先进行判断,当这
个字符是i时,立即归约。
第9页共13页
华东交通大学课程设计报告
第10页共13页
华东交通大学课程设计报告
第11页共13页
华东交通大学课程设计报告
一个星期的课程设计结束了,但真正的学习才刚刚开始。
就像古语说的一样,事情都是看起来容易做起来难,说实话算符优
先在我看来,是编译原理中比较简单的一个算法,但也就是这样一个简单
的算法摧残了我近一个星期,不停的有新的问题,不停的需要修改,不断
的翻书,很可惜还是没能用书上的方法实现。
虽然最后用递归的方法模拟
了这个算法,却觉得还是不那么舒服,写报告的这两天我又去看了些别人
写的代码,算是加深了对栈的理解吧。
其实,出现了问题并不可怕,可怕的是你逃避已经出现了的问题,
或许我们在大学学到的就是解决问题的方法。
谢谢黄老师一个学期以来对我的帮助和指导,黄老师将会是给我影
像较深的一个老师,我依然记得您在上课时,当听到学生不认真听讲或者
睡觉时,那种恨铁不成钢的焦急心态,不知道我为什么始终记得这样的一
件事,或许是能这样做的人太少了吧,呵呵。
最后祝您在以后的日子里,一切顺心。
第12页共13页