【问题标题】:Is left-factoring of grammar necessary in Bison?在 Bison 中,语法的左分解是必要的吗?
【发布时间】:2014-12-26 02:16:46
【问题描述】:

我正在使用野牛制作解析器。我只是想问一下在野牛中使用语法是否仍然需要左因子。我尝试给野牛一个非左因子语法,它没有给出任何警告或错误,它也接受了我给解析器的示例语法,但我担心解析器在每个输入中可能都不准确.

【问题讨论】:

标签: parsing grammar bison bnf


【解决方案1】:

左分解是您在语法中删除 LL 冲突的方法。由于 Bison 使用 LALR,因此左递归或任何其他 LL 冲突都没有问题(事实上,左递归更可取,因为它最大限度地减少了堆栈需求),因此左分解既没有必要也不可取。

请注意,左因子不会破坏任何东西 - bison 可以处理左因子语法以及非左因子语法,但它可能需要更多资源(内存)来解析左因子语法,所以一般来说,不要。

编辑

您似乎对 LL-vs-LR 解析的工作原理以及语法结构如何影响它们感到困惑。

LL 解析是自上而下的——您只从解析堆栈上的开始符号开始,并且在每一步中,您将堆栈顶部的非终结符替换为某个规则右侧的符号非终端。当堆栈顶部有一个终端时,它必须匹配下一个输入标记,因此您将其弹出并使用输入。目标是消耗所有输入并最终得到一个空堆栈。

LR 解析是自下而上的——你从一个空堆栈开始,在每一步你要么将一个令牌从输入复制到堆栈(使用它),要么替换堆栈顶部的一系列符号对应于规则左侧的单个符号的某些规则的右侧。目标是消耗所有输入并在堆栈中只留下开始符号。

因此,对于以右侧相同符号开头的相同非终结符的不同规则对于 LL 解析来说是一个大问题——您可以用任一规则中的符号替换该非终结符并匹配接下来的几个标记输入,所以你需要更多的前瞻才能知道要做什么。但是对于 LR 解析,没有问题——您只需将令牌从输入转移(移动)到堆栈,当您到达后面的令牌时,您可以决定它匹配哪个右侧。

LR 解析往往会遇到结束右侧相同标记的规则,而不是以相同标记开头的规则。在 John Levine 书中的示例中,有规则“cart_animal ::= HORSE”和“work_animal ::= HORSE”,因此在移动 HORSE 符号后,它可以减少(替换为)“cart_animal”或“work_animal” .由于上下文允许后跟“AND”标记,因此当下一个标记为“AND”时,您最终会遇到 reduce/reduce (LR) 冲突。

【讨论】:

  • 我认为左分解和左递归是两个不同的主题,但无论如何,感谢您明确说明左分解不是必需的。我正在阅读 John Levine 的关于野牛的书 Flex & Bison,含蓄地说野牛无法处理非左因子语法(第 50 页),这让我感到困惑。
  • 我是一个解析新手,所以如果我错了,请纠正我,左分解和左递归是不同的主题。左递归是当 LHS 出现在最左边的 RHS 中时;像 A-> A b。左分解是消除在不同替代方案中相同的几个最左边的符号,因此需要一个前瞻标记的算法,如 LALR(1),可以有效地解析语法。例如,A -> B C x | B C xz;必须左分解为 A -> Dx | A -> Dxz; D -> B C; .这意味着左分解不会消除左递归,而是将前瞻减少到一个符号
  • 左分解和左递归密切相关——它们都与语法中的 LL 冲突有关。 LR 解析不关心 LL 冲突,只关心 LR 冲突,因此两者都与 LR 解析器无关。您的示例只需要 LR 解析的 1 令牌前瞻,而不需要任何左因子,因为 LR 解析处理同时规则集而不是单个规则。
  • 你的例子是什么意思“你的例子只需要 LR 解析的 1-token 前瞻,而不需要任何左因子,因为 LR 解析处理同时规则集而不是单个规则。”
  • 在约翰莱文的书中,他说这个语法不能被bison解析...短语:cart_animal AND CART |工作动物和犁; cart_animal: 马 |山羊; work_animal: 马 |牛
【解决方案2】:

事实上,恰恰相反。由 LALR(1) 解析器生成器生成的解析器不仅支持左递归,它们实际上更好地地使用左递归。具有讽刺意味的是,您可能必须从语法中重构 right 递归。

右递归有效;但是,它会延迟归约,导​​致解析堆栈空间与被解析的递归构造的大小成正比。

例如,像这样构建一个 Lisp 样式的列表:

list : item { $$ = cons($1, nil); }
     | item list { $$ = cons($1, $2); }

表示解析器堆栈与列表的长度成正比。在到达最右边的 item 之前不会进行归约,然后会进行级联归约,通过一系列 cons 调用从右到左构建列表。

在您开始解析数据而不是代码并且数据变大之前,您可能不会遇到此问题。

如果你为左递归修改这个,你可以在一个常量解析器堆栈中构建一个列表,因为这个动作将是“随手减少”:

list : item        { $$ = cons($1, nil); }
     | list item   { $$ = append($1, cons($2, nil)); }

(现在append搜索列表尾部存在性能问题;解决方案多种多样,与解析无关。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-24
    • 1970-01-01
    • 2012-10-03
    • 2012-01-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多