【问题标题】:lemon parser reduce error柠檬解析器减少错误
【发布时间】:2016-11-07 03:38:09
【问题描述】:

我正在尝试编写一个语法来解析英文句子中的数字,并且我可以成功解析多达 999 个。一旦我添加了支持千位的逻辑,我就会遇到 reduce 解析冲突,我我很难理解是什么原因造成的。

我附上了由柠檬生成的 parser.out 文件的一部分,我希望有人能对这个问题有所了解。我还包含了大部分语法,其中行以下的所有内容都可以自行运行,但是一旦我添加了行以上数千的逻辑,我就会开始遇到问题。

我的想法是我遇到了类似于“dangling else”但我的分隔符的问题。但是,这通常表现为shift-reduce 错误,而看起来我只有一个reduce 错误。 Lemon 文档有点稀疏,我不确定如何读取 parser.out 文件的内容。比如HYPHEN reduce 15 ** Parsing conflict **这一行,15到底指的是什么?

任何帮助将不胜感激!


我的语法文件的一部分:

final_number(A) ::= one_to_999999(B).
final_number(A) ::= ZERO.

one_to_999999(A) ::= thousands(B) separator one_to_999(C).
one_to_999999(A) ::= thousands(B).
one_to_999999(A) ::= one_to_999(B).

thousands(A) ::= one_to_999(B) separator THOUSAND.
thousands(A) ::= THOUSAND.

/* -------------------------------------- */

one_to_999(A) ::= hundreds(B) separator one_to_99(C).
one_to_999(A) ::= hundreds(B).
one_to_999(A) ::= one_to_99(B).

one_to_99(A) ::= tens(B) separator one_to_9(C).
one_to_99(A) ::= tens(B).
one_to_99(A) ::= ten_to_19(B).
one_to_99(A) ::= one_to_9(B).

hundreds(A) ::= one_to_9(B) separator HUNDRED.
hundreds(A) ::= HUNDRED.

separator ::= WHITESPACE.
separator ::= HYPHEN.
separator ::= .

parser.out 的错误部分:

State 5:
          one_to_99 ::= tens * separator one_to_9
     (15) one_to_99 ::= tens *
          separator ::= * WHITESPACE
          separator ::= * HYPHEN
     (65) separator ::= *

                             $ reduce       15     one_to_99 ::= tens
                      THOUSAND reduce       15     one_to_99 ::= tens
                    WHITESPACE shift-reduce 63     separator ::= WHITESPACE
                    WHITESPACE reduce       15      ** Parsing conflict **
                        HYPHEN shift-reduce 64     separator ::= HYPHEN
                        HYPHEN reduce       15      ** Parsing conflict **
                     separator shift        4      
                     {default} reduce       65     separator ::=

【问题讨论】:

    标签: parsing grammar lemon


    【解决方案1】:

    这里实际上没有足够的信息来诊断完整的问题,但我想我可以填补空白。

    表明问题在于解析器已识别tens 的状态(应该是“二十”、“三十”、...、“九十”,对吗?)现在需要一个@987654322 @(可能是可选的)。如果前瞻标记是一个实际的分隔符,它必须决定是立即将tens 减少为one_to_99(作为完成one_to_999 没有尾随数字的前奏)还是将WHITESPACEHYPHEN 字符移入用separator 和一个数字(one_to_9)扩展tens

    解析器真的不能仅仅看分隔符就做出决定。它需要知道接下来会发生什么(例如,可能是THOUSANDONE,以及其他可能性)。

    在您向语法添加数千之前不会发生这种情况,因为没有THOUSAND 的可能性,如果数字末尾没有单个数字,则tens 标记后面也没有分隔符。因此,如果有明确的分隔符,则必须有一个数字,因此需要移位。一旦添加了THOUSAND 选项,分隔符标记的存在就不再是一个足够的指导。

    尝试在解析器中显式匹配空格类似于通常所说的“无扫描器解析”,但严格来说并非如此,因为您可能确实有扫描器。但是,扫描仪无法正常工作;它无法删除没有语法价值的标记。虽然有些人喜欢无扫描解析,但普遍认为它会增加前瞻要求。 [注 1] 由于您无法增加柠檬解析器的前瞻(也不能增加许多其他基于 yacc 的解析器生成器),因此此类工具的无扫描解析存在问题。

    在这种情况下,很难看出通过强制解析器处理分隔符可能会获得什么,而且很明显你失去了什么(LALR(1) 可解析性),所以我建议你放弃扫描仪地板上的空格和连字符,并将它们从解析器中删除。您可能会争辩说,这样做会允许使用不正确的句子,例如 three hundred forty---two。确实如此,但您当前的语法允许three hundred-forty two(这在我见过的任何样式指南中都不正确),并且可能禁止forty - two,具体取决于您的扫描仪用于识别连字符的模式。

    如果您想要“正确连字符”,请务必从扫描仪返回连字符(但不是空格),然后仅在有用的地方接受它们:

    one_to_99 ::= tens
                | tens one_to_9
                | tens HYPHEN one_to_9
                ;
    

    不会产生任何移位/减少冲突。

    注意事项

    1. 我不是喜欢无扫描仪解析的人之一,所以我什至不会试图解释为什么它被认为是一个好主意。

    【讨论】:

    • 我现在正在工作,但今晚我会阅读并回复您的评论。我确实注意到你一开始提到没有足够的信息可以继续,所以我提前道歉,我会尝试提供任何你可能觉得有帮助的东西。谢谢!
    • 不幸的是,数字可能出现在句子中间,例如I am thirty three years old,将输出为I am 33 years old。我试图保留空格,以便我可以重建句子。我想到了一个解决方法,即跟踪开始和结束令牌的位置,这样我就可以知道33 出现在位置 5 和 17 之间,然后我可以重建插入数字的句子。这听起来像是一个好方法,还是知道我正在尝试做什么会让您找到更好的解决方案?
    • 另外,parser.out 文件中的星号是否代表解析器的当前位置?我仍然对这些数字试图代表什么感到困惑。
    • @jesse,是的,星号表示每个产品中与当前输入位置相对应的“点”。这些数字要么是下一个状态(shift/goto),要么是生产编号(reduce)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多