我正在使用PLY(Python Lex / Yacc)作为解析器。我得到以下输出:
State : 52
Stack : <shortened preamble> primary_expr ARROW simple_name LPAREN ID COLON . LexToken(INTEGER,'Integer',13,362)
Action : Shift and goto state 116
State : 116
Stack : <shortened preamble> primary_expr ARROW simple_name LPAREN ID COLON INTEGER . LexToken(BAR,'|',13,370)
ERROR: Error : <shortened preamble> primary_expr ARROW simple_name LPAREN ID COLON INTEGER . LexToken(BAR,'|',13,370)
您可以看到在状态52中,它移动了INTEGER
令牌,然后转到状态116.状态116的状态机输出文件具有:
state 116
(56) simple_type -> INTEGER .
RBRACKET reduce using rule 56 (simple_type -> INTEGER .)
EQ reduce using rule 56 (simple_type -> INTEGER .)
SEMI reduce using rule 56 (simple_type -> INTEGER .)
这似乎只是将INTEGER
缩减为simple_type
的状态。然而,此处未列出BAR
令牌(显示在输入中的下一个)。
我期望匹配的规则是:
iterator_expr : primary_expr ARROW simple_name LPAREN uninitialized_variable BAR other_expr RPAREN
这些其他规则提供了最后三个令牌的减少以匹配规则。
simple_type : INTEGER
type_specifier : simple_type
declarator : ID COLON type_specifier
uninitialized_variable : declarator
所以,它在我看来状态机已全部设置为进入状态116,将INTEGER
减少到simple_type
,simple_type
减少到{{ 1}},type_specifier
到ID COLON type_specifier
,declarator
到declarator
,并从那里开始与uninitialized_variable
愉快地继续。然而,它似乎将进入一个只有转变行动并抱怨下一轮转变的状态。关于所提到的任何规则,没有转移/减少或减少/减少冲突。
接下来为什么需要考虑BAR
,为什么它不能在状态机中列出它?
编辑4/20/18:
我改变了我期望匹配的规则:
BAR
(即,将iterator_expr : primary_expr ARROW simple_name LPAREN declarator BAR other_expr RPAREN
更改为uninitialized_variable
),declarator
现在显示为状态116中的下一个标记。解析器现在选择减少BAR
而非转移simple_type -> INTEGER
,并且匹配正常。我想知道是否有必要匹配非终端(BAR
),然后匹配一些终端(ID,COLON)和一些非终端以某种方式阻止检测有效的下一个令牌。
我意识到没有人可以在没有完整语法(不幸的是专有)的情况下调试它。但是我有兴趣了解yacc可能涉及的原理,并且还希望记录这些奇怪问题的良好调试技术。我实际上遇到了其他规则的问题,其他规则包含其他规则中的unintialized_variable
非终端 - 像'+'这样的东西显示为无效的下一个令牌,显然expression
非终端应该覆盖它。如何 创建下一个有效令牌的列表,以及什么会阻止规则中如此明确列出的令牌出现?
答案 0 :(得分:0)
如果您的解析器出错,那就是因为语法错误 - 它没有一个有效的选项来移动或减少,所以它&#39 ; s得出结论,你的输入与语法不匹配。如果你不相信,一些观察可能有助于弄清楚为什么你们两个得出不同的结论。
请记住,LALR解析器会生成一个确定性状态机来解析输入。无论它处于什么状态,给定下一个标记,它将做出一个决定并继续前进。你的语法必须以这样的方式形成:对于任何有效的输入,状态机总会有一个有效的下一个动作。
语法错误是一个死胡同,但这并不意味着只有你所采取的最后一个回合是错误的。转换/减少和减少/减少冲突可能已存在于先前应用的规则中,并以某种方式得到解决。这样可能会为您的其余输入设置一个课程,使其不符合有效的下一步操作。
您的解析树中很可能早先存在歧义,解析器选择解决该歧义的方法是让您的特定输入的其余部分变得不可接受。您需要检查解析历史记录以找到发生这一点的位置,了解它似乎希望两个或更多选择同时有效的语法,并重新编写语法规则以防止这种情况发生。在历史中找到这一点的好建议:
重建输入字符串的状态历史记录和规则应用程序。然后浏览parser.out文件以查找任何这些规则的任何冲突。查看该冲突是否会影响解析输入字符串。
围绕您似乎涉及的语法部分创建一个简单示例。将简单的语法提供给JFLAP或JsMachines等工具。让工具解析代表性输入,看看它在每个步骤做出的决定。请记住,在这些工具中#&#39;意思是'转移'并转到#&#34;和#&#39; r#&#39;意味着&#34;根据规则#&#34;减少,只是&#39;#&#39;意味着&#34;进入州#。&#34;在堆栈减少之后,您将看到返回的状态,这将有助于了解解析器认为可能在哪些规则中完成。
对于像PLY这样的工具来说,生成一个状态机可能会非常好,在冲突时,该状态机将解析通过一个状态来记录这是发现冲突的点,然后自动进入所选的状态解决。您可以将其添加到PLY的yacc模块中。
如果您需要语法来处理一些可能性,直到语法将它们解析为单个选项,您可以添加它可以跟踪的非终端规则。你有效地试图迫使状态机选择&#34;转移&#34;对于一些令牌,直到遇到解析语法。然后减少的应用应该是独特的。为了让它选择&#34; shift,&#34;必须有多个适用的产品代表可能的选择,但为了使减少具有独特性,它们必须以某种独特的方式结束。您可以从歧义的角度覆盖语法元素,直到使用少量非终端产品解决问题,并获得您需要的选项。