Parsing 如何展开此语法的项目集?

Parsing 如何展开此语法的项目集?,parsing,compiler-construction,dfa,lr,lars,Parsing,Compiler Construction,Dfa,Lr,Lars,我有这个语法 E -> E + i E -> i 增广语法 E' -> E E -> E + i E -> i 现在,我尝试展开项目集0 I0) E' -> .E +E -> .E + i +E -> .i 然后,由于我在I0中有.E,我会将其展开,但随后我会得到另一个E规则,依此类推,这是我的第一个疑问 假设这是正确的,下一个项目集是 I0) E' -> .E +E -> .E + i +E -> .i I1

我有这个语法

E -> E + i
E -> i
增广语法

E' -> E
E -> E + i
E -> i
现在,我尝试展开项目集0

I0)
 E' -> .E
+E  -> .E + i
+E  -> .i
然后,由于我在
I0
中有
.E
,我会将其展开,但随后我会得到另一个
E
规则,依此类推,这是我的第一个疑问

假设这是正确的,下一个项目集是

I0)
 E' -> .E
+E  -> .E + i
+E  -> .i

I1) (I moved the dot from I0, no variables at rhs of dot)
E' -> E.
E -> E. + i
E -> i.

I2) (I moved the dot from I1, no vars at rhs of dot)
E -> E +. i

I3) (I moved the dot from I2, also no vars)
E -> E + i.
那我就要这个DFA

I0 -(E, i)-> I1 -(+)-> I2 -(i)-> I3
              |                   |
              +-(∅)-> acpt <-(∅)--+
I0-(E,i)->I1-(+)->I2-(i)->I3
|                   |
+-(∅)-> acpt你所说的项目集的“扩展”实际上是一个闭包;我所看到的所有算法描述(至少在教科书中)都是这样描述的。与任何闭包操作一样,您只需继续进行转换,直到达到一个固定点;一旦您包含了
E
的产品,它们就会被包含在内

但关键的一点是,您不是在构建DFA。您是在构建下推自动机,而DFA只是其中的一部分。DFA用于移位操作;当您移位新终端时(因为当前解析堆栈不是句柄),您可以根据DFA进行状态转换。但您也可以将当前状态推送到PDA的堆栈上

有趣的是,当自动机决定执行缩减时会发生什么情况,该缩减将生产的右侧替换为其左侧非终端(堆栈顶部的右侧称为“句柄”)。要进行缩减,请展开堆栈,弹出每个右侧符号(以及相应的DFA状态)直到您到达生产的开始。这样做是将DFA倒带到它从右侧移动第一个符号之前的状态。(注意,只有在这一点上,您才能确定使用了哪个生产。)通过这样重置DFA,您现在可以移动遇到的非终端,执行相应的DFA转换,并继续解析

此过程的基础是解析器堆栈始终是一个“可行前缀”;也就是说,一系列符号是某些正确句子形式的前缀,可以从起始符号派生。上下文无关语法的一组可行前缀有趣的是,它是一种规则语言,因此可以被DFA识别。上面给出的简化过程正好代表了这一点“修剪”句柄时的识别过程(使用Knuth的原始词汇表)

从这个意义上说,使用什么过程来确定要修剪哪个句柄并不重要,只要它提供了一个有效的答案。例如,您可以在每次发现堆栈顶部有一个潜在句柄时进行fork分析,并与两个fork并行进行。通过巧妙的堆栈管理,这种并行搜索可以对于任何上下文无关语法,都可以在最坏情况下的O(n3)时间内完成(如果语法没有歧义,这可以减少)。这是对Earley解析器的一个非常粗略的描述

但是在LR(k)解析器的情况下,我们要求语法是明确的,并且我们还要求我们可以通过查看输入流中不超过
k
的符号来识别缩减,这是一个O(1)因为
k
的操作是固定的。如果在解析过程中的每一点我们都知道是否要缩减,如果知道要选择哪一个缩减,那么这些缩减可以如我上面所述的那样实现。对于固定语法,每个缩减都可以在O(1)时间内执行(因为特定语法中右手边的最大大小是固定的),并且由于解析中的缩减次数与输入大小成线性关系,因此整个解析可以在线性时间内完成

这有点不正式,但我希望它能作为一个直观的解释。如果你对正式证明感兴趣,那么Donald Knuth 1965年的原著(关于语言从左到右的翻译)很容易找到,而且随着这些东西的发展,可读性也很高。

你所说的“扩展”项目集的一部分实际上是一个闭包;在我所看到的算法的所有描述中(至少在教科书中)都是这样描述的。与任何闭包操作一样,您只需继续进行转换,直到达到一个固定点;一旦您包含了
E
的产品,它们就会被包含在内

但关键的一点是,您不是在构建DFA。您是在构建下推自动机,而DFA只是其中的一部分。DFA用于移位操作;当您移位新终端时(因为当前解析堆栈不是句柄),您可以根据DFA进行状态转换。但您也可以将当前状态推送到PDA的堆栈上

有趣的是,当自动机决定执行缩减时会发生什么情况,该缩减将生产的右侧替换为其左侧非终端(堆栈顶部的右侧称为“句柄”)。要进行缩减,请展开堆栈,弹出每个右侧符号(以及相应的DFA状态)直到您到达生产的开始。这样做是将DFA倒带到它从右侧移动第一个符号之前的状态。(注意,只有在这一点上,您才能确定使用了哪个生产。)通过这样重置DFA,您现在可以移动遇到的非终端,执行相应的DFA转换,并继续解析

此过程的基础是解析器堆栈始终是一个“可行前缀”;也就是说,一系列符号是某些正确句子形式的前缀,可以从起始符号派生。上下文无关语法的一组可行前缀有趣的是,它是一种规则语言,因此可以被DFA识别。上面给出的简化过程正好代表了这一点汉时的认知程序