算符优先分析求中缀表达式
算符优先分析法:它只考虑算符(终结符)之间的优先关系,分析扫描每个规约式的算符间优先关系。本文将使用算符优先分析法实现求中缀表达式的值。
算符优先分析求中缀表达式
定义一个表达式的二义性文法
\[ E\rightarrow E+E \, | \, E-E \, | \, E*E \, | \, E/E \, | \, (E) \, | \, i \]
定义运算规则
对以上表达式的文法按公认的计算顺序规定优先级和结合性如下:
对于运算对象的终结符 \(i\) ,其优先级最高
- \(*,/\) 优先级其次,服从左结合。相当于 \(*>*、*>/、/>/、/>*\)
- \(+,-\) 优先级最低,服从左结合。相当于 \(+>+、+>-、->-、->+\)
- 对于 \(“(”,“)”\) 规定括号的优先性大于括号外的运算符,小于括号内的运算符,内括号的优先性大于外括号。
对于句子括号 "#" 规定与它相邻的任何运算符的优先性都比它大。
算符优先关系表
根据定义的优先规则可以构造出如下优先关系表:
+ | - | * | / | ( | ) | i | # | |
---|---|---|---|---|---|---|---|---|
+ | > | > | < | < | < | > | < | > |
- | > | > | < | < | < | > | < | > |
* | > | > | > | > | < | > | < | > |
/ | > | > | > | > | < | > | < | > |
( | < | < | < | < | < | = | < | |
) | > | > | > | > | > | > | ||
i | > | > | > | > | > | > | ||
# | < | < | < | < | < | < | = |
所给表达式文法虽然是二义性的,但我们人为直观地给出运算符之间的优先关系且这种优先关系是唯一的,有了这个优先关系表我们对与符合文法的表达式的归约过程就是唯一确定的了。
算符优先归约过程
以分析输入串 \(i_1+i_2*i_3\) 为例:
步骤 | 栈 | 优先关系 | 当前符号 | 剩余输入串 | 移进或归约 |
---|---|---|---|---|---|
(1) | # | < | \(i_1\) | +\(i_2\)*\(i_3\)# | 移进 |
(2) | #\(i_1\) | > | + | \(i_2\)*\(i_3\)# | 归约\(E \rightarrow i\) |
(3) | #E | < | + | \(i_2\)*\(i_3\)# | 移进 |
(4) | #E+ | < | \(i_2\) | *\(i_3\)# | 移进 |
(5) | #E+\(i_2\) | > | * | \(i_3\)# | 归约\(E \rightarrow i\) |
(6) | #E+E | < | * | \(i_3\)# | 移进 |
(7) | #E+E* | < | \(i_3\) | # | 移进 |
(8) | #E+E*\(i_3\) | > | # | 归约\(E \rightarrow i\) | |
(9) | #E+E*E | > | # | 归约\(E \rightarrow E*E\) | |
(10) | #E+E | > | # | 归约\(E \rightarrow E+E\) | |
(11) | #E | = | # | 接受 |
计算中缀表达式的值
创建两个辅助栈 stackOper 和 stackData ,扫描中缀表达式时,用 stackOper 存储表达式中的运算符,用 stackData 存储操作数。
stackOper 栈顶运算符对应着算符优先关系表第一纵列的运算符,每次从表达式中扫描到的运算符对应着算符优先关系表第一横行的运算符。
基本思想:
首先置 stackOper 和 stackData 为空栈
向 stackOper 压入运算符 # ,在表达式后面加上 # (作为结束标志)
依次扫描表达式中的每一个字符,若是操作数,则将操作数压入 stackData 中;
- 若是运算符,则将运算符和 stackOper 栈顶的运算符比较优先级后做相应操作 (若栈顶运算符优先级高,则 stackData 连续弹出两个数, stackOper 弹出运算符,按弹出的运算符计算出结果后,将结果压入 stackData ;否则,直接将新扫描的运算符压入 stackOper );
- 否则,表达式文法错误,抛异常
- 直至整个表达式扫描完毕, stackData 栈顶的数值就是运算结果
1 | import java.util.LinkedList; |