语法规范解决Shift / Reduce冲突

时间:2012-10-03 20:01:33

标签: bison context-free-grammar shift-reduce-conflict jison

我正在使用Jison(Bison)来创建一个简单的标记语言。我显然对此很陌生,但略有不同的变化非常好。我只是不明白S / R冲突的根源。

“文本”由两个词法分析器操作(具有不同的“开始条件”)返回似乎并不重要,我喜欢这样,因为它似乎允许语法具有更少的规则,并且因为给用户的错误消息是一致的。我已经尝试将“文本”规则放在一起,不管上下文如何,我也尝试给每个标记一个不同的名称,但它似乎对S / R冲突没有任何影响,当它们全部在一起时。

解析器是SUPPOSED用于创建具有纯文本,子数组和各种特殊节点的json对象。

规格:

/* lexical grammar */
%lex

%s bracketed

%%

<bracketed>(\\.|[^\\\,\[\]])+       { yytext = yytext.replace(/\\(.)/g, '$1'); return 'Text'; }
<INITIAL>(\\.|[^\\\[])+             { yytext = yytext.replace(/\\(.)/g, '$1'); return 'Text'; }
"["                                 { this.begin('bracketed'); return '['; }
"]"                                 { this.popState(); return ']'; }
","                                 return ','
<<EOF>>                             return 'END'

/lex

%start template

%%    

template
    : sentence END
    ;

sentence
    : /* empty */
    | sentence Text
    | sentence '[' ']'
    | sentence '[' dynamic ']'
    ;

dynamic
    : sentence
    /*| dynamic ',' sentence*/
    ;

警告:

Conflict in grammar: multiple actions possible when lookahead token is ] in state 5
- reduce by rule: sentence ->
- shift token (then go to state 6)

States with conflicts:
State 5
  sentence -> sentence [ .] #lookaheads= END Text [ ]
  sentence -> sentence [ .dynamic ] #lookaheads= END Text [ ]
  dynamic -> .sentence #lookaheads= ]
  sentence -> . #lookaheads= ] Text [
  sentence -> .sentence Text
  sentence -> .sentence [ ]
  sentence -> .sentence [ dynamic ]

不同的生成器算法或多或少有麻烦,但它们似乎都有问题。

谢谢!

1 个答案:

答案 0 :(得分:14)

冲突基本上来自这两条规则:

sentence: sentence '[' Text ']'
        | sentence '[' sentenceList ']'

原因是,在看到sentence[并查看下一个令牌Text后,解析器不知道是否要转移Text ,匹配第一条规则,或将Text视为sentenceList的开头,以匹配第二条规则。

现在,如果你有一个使用2令牌前瞻的解析器生成器,这不会有问题,但是bison是LALR(1)(1是一个令牌前瞻)。

您可以尝试以下几种方法:

  • 在词法分析器中做额外的预测以区分Text-follow-by-]和Text-not-follow-by-]作为两个不同的标记,然后重写规则以使用这两个标记。

  • 使用bison的%glr-parser功能来使用GLR解析器。这将以两种方式解析句子,然后丢弃与

  • 不匹配的句子
  • 重构语法,不需要2个令牌前瞻。

在您的情况下有效的一个重构是重写sentence规则以使它们正确递归而不是左递归:

sentence: /* empty */
        | Text sentence 
        | '[' ']' sentence
        | '[' Text ']' sentence
        | '[' sentenceList ']' sentence
        ;

这可以避免让sentence(或以sentence开头的任何其他规则,例如sentenceList)以sentence: /*empty*/规则的空值减少开头。因此解析器可以在有问题的情况下自由地移动Text,推迟减少直到它看到下一个令牌。但它确实有内存使用的影响,因为它会导致一个解析器,它将把整个输入转移到解析器堆栈上,然后一次减少一个句子。

您可以做的另一个重构是将[Text][]结构包含在[sentenceList]中:

sentence: /* empty */
        | sentence Text 
        | sentence '[' sentenceList ']'
        ;

sentenceList: sentence
            | sentenceList ',' sentence

现在,sentenceList是一个或多个以逗号分隔的句子(而不是两个或更多),在sentence '[' sentenceList ']'规则的操作中,您需要检查sentenceList看是否是两个或更多的句子,并采取适当的行动。