为了理解算法中的文法分析,用一个简单的公式解析器来说明。
公式计算是电子表格中的常用功能。主要是实现了些简单的公式计算:
比如:=Sum(A1,A2)
假设我们要实现简单的公式解析,我们从头开始:
分析:
首先,确认采用标准的递归下降法,采用自上而下(预测式)的语法分析(LL方式)。
上下文无关文法(BNF)采用左结合推导树,
下面,也采用由上而下式的进行分析:
1:顶层的文法:
expression -> term expression'
expression' -> add_op term expression'
| minus_op term expression'
| and_op term expression'
add_op -> +
minus_op -> -
and_op -> &
说明:
对于+,-运算,优先级最低,总是最后执行,所以,将其提取为最顶文法。
而 & ,指的是字串连接,作为一个基本补充。如: ="abcd" & "efgh" 结果是: abcdefgh
问题:
将 &与 +,-做为同一优先级,似乎有些不对? 先不管它,后面再看。
规定左右操作数:简化操作。
+,-的处理,左右操作数可以只支持数值型(可转为数字的值)。
&的处理,左右操作数只支持字符串。
所以,再看问题:
&与 +,-应该是不能并存的,所以,不用考虑上面的问题。如果并存,一定报错。
类似 = 2 + 3 & 4 先计算 2+3,然后计算 5 & 4,报错。所以,虽然同优先级,
也没啥错误。
嗯,对于我们能处理的公式,我们有了一些限定,也是简化处理,如果要进一步处理,可以据此深一层探索。
2:term 文法
term -> factor term'
term' -> mult_op factor term'
|divide_op factor term'
mult_op -> *
divide_op -> /
说明:
这种做法,也是常用算法书中一种技巧,将 优先级高于 +,-的* / 运算提取出来,放入term中,简化问题。
看看左右操作数,*,/ 只支持 可以只支持数值型
3:factor文法
factor -> [PLUS | MINUS] factor'
factor' -> NUM
|STRING
|FUNC LP args RP
|FUNC LP RP
|[Cell | Cell COLON Cell]
|LP expression RP
args -> expression
|expression COMMA args
COMMA -> ,
LP -> (
RP -> )
COLON -> :
好了,这是最关键的部分了。
3.1: [PLUS | MINUS]:表示 正,负号,对于负号,你应该进行处理。比如结果是数值,则取反值。
3.2: NUM:数值处理:
3.3: STRING: 字串处理。由配对双引号来识别。
3.4: FUNC LP args RP | FUNC LP RP:函数,带参数或不带参数。
此处,函数应该做判定,将已知函数转向相应的函数指针,如果是未知函数,则抛出语法错误。
注意:如果公式解析器处理底层,而做为上层应用的调用模块,那么,还可以将未知公式向上抛出,
将控制权暂交于上层,由上层计算结果后返回。
看看函数的详细处理:
解析参数(args):
多个参数用,隔开。
参数可能有两种,一是表达式方式,另一种是字串(其实,由双引号包含的也可以当表达式处理)。
字串可直接处理,表达式可递归调用expresion
参数压栈。以便后续的函数调用。
3.5: Cell[Range): 引用单元格(或者是范围),范围引的格式,如 A1:B2
3.6: LP Expresion RP:左右括号必须配对,中间递归表达式。
4:好了,上面已经把文法解释清楚了,可以用以下的公式来说明:
= SUM(A1,("abcd & "efgh"),(3+4*5)) + SUM_USER()
以上,也差不多就是本公式解析器的解析复杂度了。
看一下未处理的标记,如果需要,可以很方便的加进去。
%:应该定位于[PLUS | MINUS]同层。
!:如果做为非运算,可以定位于[PLUS | MINUS]同层。
$:看实际的含义而定。
@:看实际的含义而定。
#:这个比较特珠,看具体应用
|:如果是位运算的话,应该是与 * / 同层
/:如果是整除的话,可以与 * / 同层
= , < , >:这个是逻辑运算,这个暂时不讨论
[ , ] :看实际的含义而定。
`:视情况而定
^:如果是异或操作,可以与 * / 同层
;:视情况而定,
':单引号,视情况而定
?:视情况而定,一般会做为三目逻辑运算。
_:视情况而定
5:最后,不能漏了最关键的预测器程序(Advance程序)。
文法的解析,都是通过探测器来判定标识,并进行递推的。
一般会有以下几种处理;
5.1:返回本公式解析支持标识。正常情况。
5.2:返回本公式暂不支持的标识。一般会在文法解释时抛出语法无法解析的错误。
5.3:忽略的标识,比如 空格 /n /t ,跳动它,视其不存在。
6:其它:
上面的公式解析器缺少逻辑运算,其实呢,方法都是一样,只不过文法有些区别,并没有实际的复杂度。
以上差不多就是一个简单的公式解析器的处理方法。