Bison 减少/减少野牛语法中的冲突

Bison 减少/减少野牛语法中的冲突,bison,Bison,以下bison语法产生reduce/reduce冲突: %token LEFT_PARENTHESIS %token RIGHT_PARENTHESIS %token NAME %token RELATION_INFIX %% formula: term RELATION_INFIX term | NAME | LEFT_PARENTHESIS formula RIGHT_PARENTHESIS ; term: NAME | LEFT_PARENTHESIS term RIGHT

以下bison语法产生reduce/reduce冲突:

%token LEFT_PARENTHESIS
%token RIGHT_PARENTHESIS
%token NAME
%token RELATION_INFIX

%%

formula:
  term RELATION_INFIX term 
| NAME
| LEFT_PARENTHESIS formula RIGHT_PARENTHESIS
;

term:
  NAME
| LEFT_PARENTHESIS term RIGHT_PARENTHESIS
;
我看不出模棱两可的地方在哪里。左括号名称右括号必须是公式,因为没有关系中缀

bison详细输出的开始是

State 0

    0 $accept: . formula $end

    LEFT_PARENTHESIS  shift, and go to state 1
    NAME              shift, and go to state 2

    formula  go to state 3
    term     go to state 4


State 1

    3 formula: LEFT_PARENTHESIS . formula RIGHT_PARENTHESIS
    5 term: LEFT_PARENTHESIS . term RIGHT_PARENTHESIS

    LEFT_PARENTHESIS  shift, and go to state 1
    NAME              shift, and go to state 2

    formula  go to state 5
    term     go to state 6


State 2

    2 formula: NAME .
    4 term: NAME .

    RIGHT_PARENTHESIS  reduce using rule 2 (formula)
    RIGHT_PARENTHESIS  [reduce using rule 4 (term)]
    RELATION_INFIX     reduce using rule 4 (term)
    $default           reduce using rule 2 (formula)
所以它似乎被右括号弄糊涂了。各州似乎接受右括号,尽管语法要求在前面加左括号

有什么想法吗

编辑

上面的语法经过简化,重点放在reduce/reduce冲突上。我的真正意图是解析一阶逻辑公式:术语是运算符的组合,公式是逻辑组合,而不是术语上关系的and或暗示

术语和公式有时是长表达式,因此我们给它们命名,以便更容易地调用它们。所有名称最终都会扩展到它们的连接器和运算符树,稍后在程序中,而不是在bison解析器中

在终端符号中,变量是由逻辑建模的对象;公式是关于这些对象的语句。它与C++中的类型和函数类似:所有的名称都可以使用相同的命名方案。p> 在语法后面左括号名称右括号可能与公式或术语匹配

由于它涉及到在特定状态下减少输入,因此不能保证成功,并且在名称右括号表达式的情况下也不会成功。国家进程说,这是唯一可能的明确方向。从语法的角度来看,对错误的输入不这样做并不是一个转折点。但是有几个reduce选项肯定是正确的。

遵循语法左括号名称右括号可能匹配公式或术语


由于它涉及到在特定状态下减少输入,因此不能保证成功,并且在名称右括号表达式的情况下也不会成功。国家进程说,这是唯一可能的明确方向。从语法的角度来看,对错误的输入不这样做并不是一个转折点。但是有几个减少的选择肯定是正确的。

没有歧义。这并不意味着没有解析冲突

不明确的语法不能用LRk解析器解析;尝试生成解析自动机必然会失败,因为同一个句子存在两个不同的派生。但事实并非如此。有冲突的语法可能是歧义的,也可能不是歧义的。[注1]

要通过LRk语法进行解析,在读取超过k个符号之前,需要解析每个非终端

所以,虽然你说得很对

左括号名称右括号必须是公式,因为没有关系中缀

这并不是全部。对于左括号NAME右括号要成为公式,NAME也必须是公式,并且名称到公式的缩减必须发生在1个符号内,因为在bison LR解析器中k始终是1。但是,只读取名称后的一个符号,就不可能知道中缀之间没有关系。从而减少冲突

如果我没弄错的话,你的笔记基本上是一个表达式语法,其中有两种类型的表达式公式和具有不同运算符的术语,标识符可以是基元变量,也可以是任一类型表达式的名称。限制条件是,项运算符的操作数只能是项,而公式运算符的操作数可以是公式或项。简化语法中没有反映这一点,它不允许使用术语和术语以及带或不带括号的术语;我只是假设这是需要的,因为否则公式就不可能复杂,但你的笔记说它们通常是复杂的

如果一个短语包含可见运算符,则语法类别公式或术语是明显的,但仅由标识符组成的语法短语(可能由多余的括号包围)可以是术语或公式。这使得在不知道每个标识符的语法类别的情况下,从语法上完全表达限制是不可能的

如果在第一次使用标识符之前就知道每个标识符的语法类别,那么可以使用词汇反馈来允许在解析期间完全强制执行限制。否则,或者如果认为不需要词汇反馈,则有必要在后续语义分析过程中验证包含标识符的任何子表达式,可能是扩展宏标识符的子表达式

由于无论如何都需要进行语义检查,因此也很容易检查公式是否永远不是术语运算符的操作数。在这种情况下,可以使用普通表达式语法,包括正常使用优先级声明来简化pr 陈述。该语法将直接为任何正确的输入生成正确的AST;语义检查仅用于拒绝不正确的输入。以这种方式进行检测也有助于生成信息性错误消息。IMHO,这个解决方案实际上没有什么不好的一面

但写一个语法来表达除名称以外的其他情况的句法限制也是相当直接的。诀窍是使用三种表达式类型,因为实际上有三种语法类别,如上面的讨论所示:

明显公式

明显术语

可能是公式或术语的短语,即可能是括号中的名称

正确使用括号中的子表达式有点烦人,会给语法增加混乱。在我看来,没有什么真正的好处。但是,如果您想尝试此操作,下面是一个简单的示例,使用一个术语运算符和一个公式运算符,以及将显式检查/转换操作插入AST的示例缩减操作:

formula: formula_not_term
       | term_not_name     { $$ = mkform("TERM->FORM", $1, NULL); }
       | name              { $$ = mkform("NAME->FORM", $1, NULL); }
formula_not_term
       : formula FORMULA_OPERATOR formula
                           { $$ = mkform("FORM_OP", $1, $3); }
       | '(' formula_not_term ')'
                           { $$ = $2; }
term   : term_not_name
       | name              { $$ = mkterm("NAME->TERM", $1, NULL); }
term_not_name
       : term TERM_OPERATOR term
                           { $$ = mkterm("TERM_OP", $1, $3); }
       | '(' term_not_name ')'
                           { $$ = $2; }
name   : NAME
       | '(' name ')'      { $$ = $2; }
笔记 确定一个语法歧义是否是不可判定的,这意味着不存在任何算法可以肯定地回答任何语法的问题。如果所有歧义语法都产生了LR1解析冲突,那么可以使用LR1解析器构造来检测歧义,这将与不可判定性相矛盾。因此,我们可以预期会产生解析冲突的明确语法。
没有含糊不清的地方。这并不意味着没有解析冲突

不明确的语法不能用LRk解析器解析;尝试生成解析自动机必然会失败,因为同一个句子存在两个不同的派生。但事实并非如此。有冲突的语法可能是歧义的,也可能不是歧义的。[注1]

要通过LRk语法进行解析,在读取超过k个符号之前,需要解析每个非终端

所以,虽然你说得很对

左括号名称右括号必须是公式,因为没有关系中缀

这并不是全部。对于左括号NAME右括号要成为公式,NAME也必须是公式,并且名称到公式的缩减必须发生在1个符号内,因为在bison LR解析器中k始终是1。但是,只读取名称后的一个符号,就不可能知道中缀之间没有关系。从而减少冲突

如果我没弄错的话,你的笔记基本上是一个表达式语法,其中有两种类型的表达式公式和具有不同运算符的术语,标识符可以是基元变量,也可以是任一类型表达式的名称。限制条件是,项运算符的操作数只能是项,而公式运算符的操作数可以是公式或项。简化语法中没有反映这一点,它不允许使用术语和术语以及带或不带括号的术语;我只是假设这是需要的,因为否则公式就不可能复杂,但你的笔记说它们通常是复杂的

如果一个短语包含可见运算符,则语法类别公式或术语是明显的,但仅由标识符组成的语法短语(可能由多余的括号包围)可以是术语或公式。这使得在不知道每个标识符的语法类别的情况下,从语法上完全表达限制是不可能的

如果在第一次使用标识符之前就知道每个标识符的语法类别,那么可以使用词汇反馈来允许在解析期间完全强制执行限制。否则,或者如果认为不需要词汇反馈,则有必要在后续语义分析过程中验证包含标识符的任何子表达式,可能是扩展宏标识符的子表达式

由于无论如何都需要进行语义检查,因此也很容易检查公式是否永远不是术语运算符的操作数。在这种情况下,可以使用普通表达式语法,包括正常使用优先级声明来简化表示。该语法将直接为任何正确的输入生成正确的AST;语义检查仅用于拒绝不正确的输入。以这种方式进行检测也有助于生成信息性错误消息。IMHO,这个解决方案实际上没有什么不好的一面

但写一个语法来表达除名称以外的其他情况的句法限制也是相当直接的。诀窍是使用三种表达式类型,因为实际上有三种语法类别,如上面的讨论所示:

明显公式

明显术语

可能是公式或术语的短语,即可能是括号中的名称

正确使用括号中的子表达式有点不太可能 这使语法更加混乱。在我看来,没有什么真正的好处。但是,如果您想尝试此操作,下面是一个简单的示例,使用一个术语运算符和一个公式运算符,以及将显式检查/转换操作插入AST的示例缩减操作:

formula: formula_not_term
       | term_not_name     { $$ = mkform("TERM->FORM", $1, NULL); }
       | name              { $$ = mkform("NAME->FORM", $1, NULL); }
formula_not_term
       : formula FORMULA_OPERATOR formula
                           { $$ = mkform("FORM_OP", $1, $3); }
       | '(' formula_not_term ')'
                           { $$ = $2; }
term   : term_not_name
       | name              { $$ = mkterm("NAME->TERM", $1, NULL); }
term_not_name
       : term TERM_OPERATOR term
                           { $$ = mkterm("TERM_OP", $1, $3); }
       | '(' term_not_name ')'
                           { $$ = $2; }
name   : NAME
       | '(' name ')'      { $$ = $2; }
笔记 确定一个语法歧义是否是不可判定的,这意味着不存在任何算法可以肯定地回答任何语法的问题。如果所有歧义语法都产生了LR1解析冲突,那么可以使用LR1解析器构造来检测歧义,这将与不可判定性相矛盾。因此,我们可以预期会产生解析冲突的明确语法。

语法的第一条规则是公式,那么左括号名称右括号不应该被解析为公式吗?您是否有一个语法的有效输入示例,该语法有2个可能的解析路径?此外,如果删除2个括号规则,reduce/reduce冲突将消失。即使名称可以是term和formula,Bison也是一个自底向上的LALR解析器,所以它首先收集令牌,并尝试将它们匹配为可用的符号。这些符号在语法中的定义顺序并不重要,我对此表示怀疑。第一条语法规则是特殊的:它是解析器的入口点。尝试解析与简单语法中的第二条规则匹配的内容,bison将报告语法错误。单个名称跟随状态0-状态2-默认路径,因为这是最终移位,公式是可以用一个令牌先行规则标识的起始符号。符号项后面可能是左侧的关系_中缀,这是可以预见的,但可能是右侧的最后一个类似公式的公式,由于没有进一步的提示,因此会出现冲突。因此,使用类似于term RELATION\u INFIX的子句可能也可以修复语法。语法的第一条规则是公式,那么左括号名称右括号不应该被解析为公式吗?您是否有一个语法的有效输入示例,该语法有2个可能的解析路径?此外,如果删除2个括号规则,reduce/reduce冲突将消失。即使名称可以是term和formula,Bison也是一个自底向上的LALR解析器,所以它首先收集令牌,并尝试将它们匹配为可用的符号。这些符号在语法中的定义顺序并不重要,我对此表示怀疑。第一条语法规则是特殊的:它是解析器的入口点。尝试解析与简单语法中的第二条规则匹配的内容,bison将报告语法错误。单个名称跟随状态0-状态2-默认路径,因为这是最终移位,公式是可以用一个令牌先行规则标识的起始符号。符号项后面可能是左侧的关系_中缀,这是可以预见的,但可能是右侧的最后一个类似公式的公式,由于没有进一步的提示,因此会出现冲突。因此,使用类似于term RELATION_INFIX的子句可能也可以修复语法。@v.semeria不会在注释中添加基本信息。编辑问题。你应该考虑评论是短暂的;它们可以随时被删除或移动。@v.semeria这基本上就是我要建议的,因为您无论如何都需要在语义处理中键入检查变量,除非您实现词汇反馈。让我试着找出一个类似的答案……。@v.semeria:也许这个答案很有用:粗糙的语法为所有正确的输入生成准确的解析;它只是不检测所有类型错误。使用简单的深度优先树遍历,语义类型检查几乎是微不足道的。那么,为什么要使语法复杂化呢?@V.Semeria:在答案中添加上述注释和语法大纲。@V.Semeria不会在注释中添加基本信息。编辑问题。你应该考虑评论是短暂的;它们可以随时被删除或移动。@v.semeria这基本上就是我要建议的,因为您无论如何都需要在语义处理中键入检查变量,除非您实现词汇反馈。让我试着找出一个类似的答案……。@v.semeria:也许这个答案很有用:粗糙的语法为所有正确的输入生成准确的解析;它只是不检测所有类型错误。使用简单的深度优先树遍历,语义类型检查几乎是微不足道的。那么,为什么要把语法复杂化呢?@V.Semeria:在答案中添加上述注释和语法大纲草图。