让我们采用以下无上下文语法:
G = ( {Sum, Product, Number}, {decimal representations of numbers, +, *}, P, Sum)
P:
Sum → Sum + Product
Sum → Product
Product → Product * Number
Product → Number
Number → decimal representation of a number
我试图用一个自下而上的解析器和一个长度为1的预测缓冲区(LAB)来解析这个语法产生的表达式(假设它应该没有猜测和反向跟踪)。
现在,给定一个堆栈和一个LAB,通常有几种可能性来减少堆栈或者是否要减少它或者推送另一个令牌。
目前我使用此决策树:
如果堆栈加上LAB的任何前n个令牌都是开头的 在规则的右侧,我将下一个令牌推入堆栈。
否则,我减少堆栈顶部的最大令牌数。 即如果可以同时减少最顶层的项目 可以减少三个最重要的项目,我做后者。
如果没有可能的减少,我将另一个令牌推入堆栈。
冲洗并重复。
这似乎(!)工作,但它需要大量的规则搜索,找到匹配的前缀等。这不能在O(NM)中运行。
决定是否减少或推动(转移)的标准(可能只是明智的)方法是什么?如果减少,则适用哪种减少?
提前感谢您的意见和答案。
答案 0 :(得分:1)
像你这样的语法最简单的自下而上解析方法(基本上是表达式语法)是运算符优先解析。
回想一下,自下而上的解析涉及从底部从左到右构建解析树。换句话说,在解析过程中的任何给定时间,我们都有一个部分组装的树,在我们正在阅读的位置右边只有终端符号,左边是终端和非终端的组合(“前缀”) 。唯一可能的减少是适用于前缀后缀的减少;如果不适用减少,我们必须能够将终端从输入转移到前缀。
运算符语法的特点是在任何生产中都不会有两个连续的非终端。因此,在运算符语法的自下而上解析中,前缀中的最后一个符号是终端,或倒数第二个符号是一个。 (两者都可以。)
运算符优先级解析器对非终端基本上是盲目的;它根本不区分它们。所以你不能有两个右侧包含完全相同的终端序列的产品,因为op-prec解析器不知道应用这两个产品中的哪一个。 (这是传统观点。实际上可以扩展一点,以便你可以有两个具有相同终端的产品,只要非终端位于不同的地方。这允许具有一元-
运算符的语法,例如,因为可以在不知道非终端的名称的情况下区分右侧<non-terminal> - <non-terminal>
和- <non-terminal>
;只有它们的存在。
另一个要求是您必须能够在终端之间建立优先关系。更准确地说,我们定义了三个优先级关系,通常是<·
,·>
和·=·
(或主题的一些排版变体),并且坚持认为对于任何两个终端x
和y
,最多只有x ·> y
,x ·=· y
和x <· y
中的一个是真的。
粗略地说,关系中的<
和>
对应于制作的边缘。换句话说,如果x <· y
,这意味着x
可以跟一个非终端,其终端的第一个终端是y
。同样,x ·> y
表示y
可以跟随非最终终端为x
的生产终端。并且x ·=· y
表示存在一些右侧,其中x
和y
是连续的终端,按此顺序(可能具有插入的非终端)。
如果单关系限制为真,那么我们可以解析如下:
让
x
成为前缀中的最后一个终端(即最后一个或倒数第二个符号),让y
成为前瞻符号,它必须是终端。如果x ·> y
,我们会减少并重复该规则。否则,我们将y
转移到前缀。
为了减少,我们需要找到生产的开始。我们在前缀上向后移动,比较连续的终端(所有终端必须具有<·
或·=·
关系),直到找到具有<·
关系的终端。然后<·
和·>
之间的终端是我们正在寻找的生产的右侧,我们可以将非终端插入右侧,如图所示。 / p>
无法保证会有适当的产品;如果没有,则解析失败。但是如果输入是有效的句子,并且如果语法是运算符优先语法,那么我们将能够找到正确的生成来减少。
请注意,通常很容易找到制作,因为大多数制作只有一个(<non-terminal> * <non-terminal>
)或两个(( <non-terminal> )
)终端。一个简单的实现可能只是将终端一起运行到一个字符串中,并将其用作哈希表中的密钥。
运算符优先级解析的经典实现是由Edsger Dijkstra设计的所谓的“Shunting Yard算法”。在该算法中,通过提供两个函数(左优先和右优先)来建模优先关系,这两个函数将终端映射到整数,使得x <· y
仅在right-precedence(x) < left-precedence(y)
时为真(并且对于其他运算符类似) )。并不总是能够找到这样的映射,并且映射是实际优先关系的覆盖,因为通常情况是存在没有优先关系的终端对。尽管如此,通常情况下可以找到这些映射,而且对于简单的表达式语法几乎总是如此。
我希望这足以让你入门。我鼓励你实际阅读一些关于自下而上解析的文章,因为我认为我已经写了太多的SO答案,我还没有包含一行代码。 :)