算符优先分析过程模拟.docx
- 文档编号:5622550
- 上传时间:2022-12-29
- 格式:DOCX
- 页数:10
- 大小:76.34KB
算符优先分析过程模拟.docx
《算符优先分析过程模拟.docx》由会员分享,可在线阅读,更多相关《算符优先分析过程模拟.docx(10页珍藏版)》请在冰豆网上搜索。
算符优先分析过程模拟
目 录
一、需求分析
算符优先分析是自底而上分析方法的一种,这种方法是对输入符号串自左向右进行扫描,并将输入符逐个移入一个后进先出栈中,边移入边分析,一旦栈顶符号串形成某个句型的句柄或可规约串时(该句柄或可归约串对应产生式的右部),就用该产生式的左部非终结符代替相应右部的文法符号串,重复这样的归约过程,一直到只剩文法的开始符号时则为分析成功,也就是确认输入串是该文法的句子。
而算符优先分析的基本思想则是只规定算符之间的优先关系,也就是只考虑终结符之间的优先关系,由于算符优先分析不考虑非终结符之间的优先关系,在归约过程中只要找到可归约的串就归约,并不考虑归约到那个非终结符名。
在定义具体的由于算符优先关系之前,先说明由于算符优先关系符的定义,有三中由于算符优先关系符,分别是>,<,=
设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
因为算符优先关系运算的次序只与运算符有关,而与运算对象无关,明显,对于算符优先文法的设计成为了关键。
二、概要设计
(一)、文法设计
算符优先分析只考虑算符(终结符)之间的优先关系,先设计文法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,它优先级别最高。
算符优先关系表
+
-
*
/
(
)
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
#
接受
分析过程:
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、接受。
三、详细设计
(一)、设计思路
大多数对于算符优先分析方法都是设计两个栈,通过比较符号栈顶元素和邻近的运算符之间的优先关系。
进行归约,移进的控制。
而我用的却是另一种方法。
我将所输入的句型放在一个字符串Mystr中,然后对字符串进行扫描,当扫描到的当前字符是“+,-,*,/,()”时分别调用与之对应的五种不同的方法,当当前字符(token)是“+”调用方法evalExp2(),
因为*/法和()的有线级高于“+”法,所以在evalExp2()中要调用evalExp3()扫描后续的句子,后续的句子如果有“*,/”法,用evalExp3()完成对“*,/”法的归约。
同理括号中的算法的优先级别高于括号外的,所以evalExp3()的方法中要先调用evalExp4()对于括号的归约方法。
在完成后依次回溯到前一个方法,很明显我是通过递归的方法来实现算符优先关系。
当然对于加减法evalExp2(),乘除法evalExp3(),括号evalExp4(),它们都可以单独调用,这由当前输入字符Taken确定。
归约的实现实际上是对字符串的处理,在下面的“模块设计”中将具体解释。
(二)、模块设计
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);
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(),在一系列的合法性判断之后,进行规约。
这里主要用到了substring(int start,int end),方法,这是读取指定位置的字串。
例如,我们现在有一个句子v=E+E*E,要对加减法归约,先通过INDEX方法读取到加号的位置,然后对字符串的前后求子串,
我们可以把所有的句子假设为…E+E…的形式,并假设当前算符位置为taken通过substring(0,taken-1)读出前一个“…”,
然后连接上规约的结果E(E->E+E),最后再连接上后面的子串直接用substring(taken+1)输出后面的子串。
最终将…E+E…的形式规约为…E…的形式。
对于乘除法和括号的规约方法和加法的归约方法基本类似,也是对字符串的处理,由此实现对运算符的归约。
2、读取下一个字符,对E->i的归约。
privatevoidgetToken()
{
if(expIdx==exp.length()){
mycmp=false;
token='Q';
return;
}
//滤去空格
while(expIdx Character.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)){ 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时,立即归约。 四、测试结果 (一)、规定测试用例 (二)、对于有括号句型的分析 (三)、无法接受情况 (四)、接受到非法字符 五、总结 一个星期的课程设计结束了,但真正的学习才刚刚开始。 就像古语说的一样,事情都是看起来容易做起来难,说实话算符优先在我看来,是编译原理中比较简单的一个算法,但也就是这样一个简单的算法摧残了我近一个星期,不停的有新的问题,不停的需要修改,不断的翻书,很可惜还是没能用书上的方法实现。 虽然最后用递归的方法模拟了这个算法,却觉得还是不那么舒服,写报告的这两天我又去看了些别人写的代码,算是加深了对栈的理解吧。 其实,出现了问题并不可怕,可怕的是你逃避已经出现了的问题,或许我们在大学学到的就是解决问题的方法。 谢谢黄老师一个学期以来对我的帮助和指导,黄老师将会是给我影像较深的一个老师,我依然记得您在上课时,当听到学生不认真听讲或者睡觉时,那种恨铁不成钢的焦急心态,不知道我为什么始终记得这样的一件事,或许是能这样做的人太少了吧,呵呵。 最后祝您在以后的日子里,一切顺心。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 优先 分析 过程 模拟