【问题标题】:Why is my parser generating reporting that this LALR(1) grammar is not LALR(1)?为什么我的解析器生成报告该 LALR(1) 语法不是 LALR(1)?
【发布时间】:2020-08-21 17:32:52
【问题描述】:

好的,我的解析器生成器的冒险继续。这次摸到了一个经典的语法,据说是LALR语法:

start -> a a
a -> "A" a
a -> "B"

当我将它放入我的解析器生成器时,它会给我输出:

LIST OF STATES:
-----------------------
<0: S' -> . start , $
start -> . a a , $
a -> . "A" a , "A" / "B"
a -> . "B" , "A" / "B"
{NTerm(start): 1, Term(A): 2, Term(B): 3, NTerm(a): 4}>(3104365621624877555)

--------------------
<1: S' -> start .  , $
{}>(3969511602615904846)

--------------------
<2: a -> "A" . a , "A" / "B"
a -> . "A" a , "A" / "B"
a -> . "B" , "A" / "B"
{Term(A): 2, Term(B): 3, NTerm(a): 5}>(5490562805113673592)

--------------------
<3: a -> "B" .  , "A" / "B"
{}>(-4845209343945471034)

--------------------
<4: start -> a . a , $
a -> . "A" a , $
a -> . "B" , $
{Term(A): 6, Term(B): 7, NTerm(a): 8}>(598157158659875896)

--------------------
<5: a -> "A" a .  , "A" / "B"
{}>(436327415052220213)

--------------------
<6: a -> "A" . a , $
a -> . "A" a , $
a -> . "B" , $
{Term(A): 6, Term(B): 7, NTerm(a): 9}>(5490562805113673592)

--------------------
<7: a -> "B" .  , $
{}>(-4845209343945471034)

--------------------
<8: start -> a a .  , $
{}>(5795088700656730485)

--------------------
<9: a -> "A" a .  , $
{}>(436327415052220213)

POSSIBLE STATES TO JOIN: (2, 6), (3, 7), (5, 9)
ATTEMPTING CONVERSION TO LALR GRAMMAR...FAILED
CONTINUING WITH CLR(1)...

这些状态与我在其他资源中读到的关于编译 LALR 语法的内容相匹配——这一步看起来不错,它会产生正确的状态,就像我手动完成一样。生成器建议——这也是其他消息来源所说的将 CLR(1) 语法转换为 LALR 的内容——声明 (2,6),(3,7),(5,9) 可以加入,但它不能这样做。 当我查看生成的 actiongoto 表时,我明白了原因:

如您所见,无法加入状态 2 和 6,因为存在不兼容的项目 s2 &lt;&gt; s6s3 &lt;&gt; s7 等。

但最让我吃惊的是生成器完成了它的工作并生成了一个运行程序。当我在测试数据上运行这个程序时,它会接受数据!所以,我的生成器生成了正确的表格。

这是否意味着这种经典的“LALR”语法只有在人工编译时才是 LALR?我的解析器生成器有什么不同?

【问题讨论】:

  • “据说是”和“在其他来源中阅读”仔细未能指定信息的实际来源。如果您怀疑您获得的建议不正确,最好实际引用来源,或者如果可能,提供链接。另外,从你的语法生成器的输出来看,那些看似任意的巨大数字贡献了什么价值?如果它们是指针,请考虑使用十六进制输出格式:至少在 C 中,void* 具有 %p 格式。
  • 你的生成器是 LR(1)——由于 LR(1) 是 LALR(1) 的超集,它可以很好地处理 LALR(1) 语法,它只是以额外的状态结束.

标签: parsing lalr


【解决方案1】:

我不知道您的解析器生成器是做什么的,但标准解析器生成器在该语法上没有问题。例如,这里是野牛的状态转换表,生成的:

bison --report-file=aa.output --report=all --graph=aa.dot --output=/dev/null \
      <(printf "%%%%\n%s" "start: a a; a: 'A' a | 'B'")
dot -o aa.png -Tpng aa.dot

(Bison 是一个非常方便的工具;即使您正在编写自己的解析器生成器,您也可以利用它的功能。另请参阅它的 XML 输出。)

这是稍微编辑的报告文件(我删除了终端和非终端的列表,以及一些空白行,以便占用更少的空间。)

Grammar

    0 $accept: start $end
    1 start: a a
    2 a: 'A' a
    3  | 'B'

State 0
    0 $accept: . start $end
    1 start: . a a
    2 a: . 'A' a
    3  | . 'B'

    'A'  shift, and go to state 1
    'B'  shift, and go to state 2

    start  go to state 3
    a      go to state 4

State 1
    2 a: . 'A' a
    2  | 'A' . a
    3  | . 'B'

    'A'  shift, and go to state 1
    'B'  shift, and go to state 2

    a  go to state 5

State 2
    3 a: 'B' .

    $default  reduce using rule 3 (a)

State 3
    0 $accept: start . $end

    $end  shift, and go to state 6

State 4
    1 start: a . a
    2 a: . 'A' a
    3  | . 'B'

    'A'  shift, and go to state 1
    'B'  shift, and go to state 2

    a  go to state 7

State 5
    2 a: 'A' a .

    $default  reduce using rule 2 (a)

State 6
    0 $accept: start $end .

    $default  accept

State 7
    1 start: a a .

    $default  reduce using rule 1 (start)

【讨论】:

    【解决方案2】:

    我认为问题出在这里:

    如您所见,状态 2 和 6 无法连接,因为存在不兼容的项目 s2 s6、s3 s7 等等。

    这实际上并不完全正确。请注意,在状态 2 中,轮班项目将您分别带到状态 2 和 3。在状态 6 中,轮班项目将您分别带到状态 6 和 7。但是那里没有不兼容,因为您正在尝试将状态 2 和 6 合并在一起,并将状态 3 和 7 合并在一起。换句话说,是的,这些转变确实当前说去不同的地方,但是在你将具有相同核心的所有状态折叠在一起之后,你最终会得到它们都说去相同的地方地点。

    更一般地说,我不认为合并两个 LR(1) 状态会引入“移位/移位”冲突。要了解这是为什么,请注意每个 LR(1) 状态对应于 LR(0) 解析器中的一个状态,除了每个 LR 项都已使用一组前瞻项进行注释。在从 LR(1) 到 LALR(1) 的过程中,您将 LR(1) 状态与相同的项目组合在一起,忽略了前瞻,这意味着您实际上是将对应于相同 LR( 0) 状态。

    因此,如果你有一个 LR(1) 状态 S 说“在符号 a 上转到状态 T”,那么对应于 S 的 LR(0) 状态会转换到对应的 LR(0) 状态到 T,对于任何将要与 S 合并的 LR(1) 状态也是如此。

    在构建 LALR(1) 解析器的过程中可能出现的唯一冲突是 reduce/reduce 冲突,这是您需要警惕的。

    【讨论】:

    • 龙书第 4.7 节,第237(我相信在第一版中):“因此,具有共同核心的状态的合并永远不会产生原始状态中不存在的移位/减少冲突,因为移位动作仅取决于核心,而不是向前看。”
    • 这样的事情让我捂着脸问自己“你在想什么?”。是的,我忘了考虑那些冲突的移位操作将是相同的操作加入之后。傻我...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多