% q$ R/ e& W. P. S2 l# V1:将输入的指针Input复制出一个副本,叫Current;给出一个同类型的指针Last,将其赋值为NULL;使用一个变量Status来记录当前的状态。初始化状态,一般为了方便我们把初始状态编号成0。 1 I4 N$ k$ u3 J& E+ Z" [& C7 a7 q u, O
2:做一个死循环不断的计算新Status。对于某个Status我们总是能够知道输入什么字符跳转到什么新的Status上去。不同的人写出来的DFA可能会有所区别。我们首先判断当前的Status是不是终结状态,如果是的话将Current赋值给Last,然后继续往下走。我们从Current指针拿出一个字符,然后计算新Status。如果Current不满足要求那么结束循环,如果Current满足要求那么改变Status并让Current指向新的位置。% ?! ~+ a/ V4 [. N a
& C' Q" q* W) T* z" {3:因为字符串总是有限的,所以这个循环总是会结束。结束了之后,我们检查Last。如果Last仍然是NULL,那么代表输入的字符串是有问题的。如果不是,那么我们所需要的一个记号就从Input开始到Last结束了。如果记号的类型有需要保留的话,那么我们只需要添加一个新的代表类型的变量,在每一次修改Last的时候修改这个保存类型的变量就行了。因为一个终结状态只能代表一种类型的结束(反过来不成立,一种类型可能有好几个终结状态)。3 Q& \) [$ _, B+ n. E
Y+ w. @8 D5 S, j" `) ]* w- k然后是语法分析。一般来说,使用《如何手写语法分析器》中描述的方法实现一个语法分析器的话是很容易的,但是一个主要问题就是如果一门语言很复杂,特别是操作符特别多的话,这些函数写起来会很乱,因此每一个文法产生式的处理函数的命名和注释就变得相当重要了。为了简化这件事情,我们还有另一种专门用来处理操作符的方法,而且是高度可配置的。为了简化,我仅给出二元操作符和前缀操作符的处理方法。后缀操作符不常见,需要的话自己想办法吧,在上一篇文章中的语法定义中并没有出现后缀操作符。 [) G( R+ r% `4 A* M a " O0 u5 v# E, n2 N! t在这种方法中,我们把重点放在不包含修改优先级的括号的表达式中。遇到一个用于修改优先级的括号的时候,只要递归一下就好了。现在,我们通过词法分析,已经得到了很多记号,然后就使用以下的方法来生成一颗正确的语法树: * P( ^) S5 F6 Q1 y0 } 1 k/ A/ o4 @$ }4 z) i3 U1:我们需要定义两个指针,第一个用于保存根节点,第二个用于保存当前节点。在分析的过程中,根节点会经常变化,当前节点也是。 % e3 Z) p8 }+ v, u ' _" d& ~! O8 p7 J$ ]! }, E2:取出一个单元。一个单元指的是一个用括号包括起来的完整的表达式、一个函数调用、一个常量或变量和仅由前缀操作符与单元组成的整体。举个例子,1是单元,a是单元,function(param1,param2+param3)是单元,(a*b+c*d)是单元,-(a+b)也是单元。但是-a+b就不是单元了。单元内部可能有表达式,我们可以递归下去。取出单元以后,就把根节点和当前节点指向这个单元。7 a9 g6 \6 w& U1 w
& b, H1 B' K; Z" S7 p: [
3:一个正确的表达式总是由单元和二元操作组成的,如果在以下的步骤中出错的话,那么可以直接确定输入的表达式的语法不正确。我们做一个死循环一直到遇到右括号、逗号等这些结束表达式的记号为止,对于每一个输入执行第4步。- E6 |2 w1 R6 N* D8 z$ m# w- X
5 u0 k X8 R, `$ E5 d; o& z
4:取出一个二元操作符和一个单元。然后从当前节点往父节点找,一直到根节点或者父节点优先级比当前的二元操作符小的二元操作符为止。如果找到根节点,那么整个根节点将作为二元操作符的左操作数,单元作为右操作数,根节点更新,当前节点指向单元。如果不是的话,将找到的节点(这个节点的父节点的优先级比自己小)从父节点脱离,整个节点作为操作符的左操作数,单元作为右操作数,然后用这个二元操作符接上父节点。1 e. z1 U" T. h: W6 ~7 ~1 q
5 r! z# @/ _# ^. W8 L* A$ n6 a/ _
5:当3与4进行不下去的时候,我们就得到了一棵完整的表达式语法树了。当然,如果中间出错的话,我们应当输出错误信息。这个时候要不要继续往下走就自己看着办吧,因为进行错误恢复的话,接下去的错误信息会很难看,就像VC++一样。6 Y& N3 J; b' V4 T
4 q7 ~. ^0 i( S1 T我给一个例子来说明如何处理这些事情。现在我们要分析1+2*3+4。这个算法将会产生一个正确的语法树”1”,然后修改为正确的语法树”1+2”,然后修改为正确的语法树”1+2*3”,最后产生完整的正确的语法树。% K" \- W# J5 Y$ E6 l+ @
( B9 m3 V z7 k1 `3 x4 R" S
第一步,产生一个单元的正确的语法树:( j. O; ^' O9 B
第二步,获得一个二元操作符,并产生一个单元的语法树”2”。因为当前节点往上就没有了,所以执行4中的第一种情况:& i/ `9 x; r0 v* ?! d
第三步,获得操作符”*”和一个单元的语法树”3”。因为2的父节点的优先级比”*”小,因此执行4的第二种情况: - B+ q& Y. Y. L2 Z7 I# s4 I0 u7 j/ F