为什么bison不符合这个语法规则?

问题描述 投票:0回答:1

我有这些

lex
代币:

,[Yy] {
    return COMMAY;
}

(?xi:
    ADC|AND|ASL|BCC|BCS|BEQ|BIT|BMI|BNE|BPL|BRK|
    BVC|BVS|CLC|CLD|CLI|CLV|CMP|CPX|CPY|DEC|DEX|
    DEY|EOR|INC|INX|INY|JMP|JSR|LDA|LDX|LDY|LSR|
    NOP|ORA|PHA|PHP|PLA|PLP|ROL|ROR|RTI|RTS|SBC|
    SEC|SED|SEI|STA|STX|STY|TAX|TAY|TSX|TXA|TXS|
    TYA
) {
    yylval.str = strdup(yytext);
    for(char *ptr = yylval.str; *ptr = tolower(*ptr); *ptr++);

    return MNEMONIC;
}

\$[0-9a-fA-F]{2} {
    yylval.str = strdup(yytext);
    return ZEROPAGE;
}

[a-zA-Z_][a-zA-Z0-9_]+ {
    yylval.str = strdup(yytext);
    return IDENTIFIER;
}

以及

bison
中的这些语法规则:

expression:
    MNEMONIC '(' zp_identifier ')' COMMAY   { statement($1, $3,     "(zp),y"); }
|   MNEMONIC '(' zp_abs_identifier ')'      { statement($1, $3,     "(zp)"); }
;

zp_identifier:
    ZEROPAGE
|   IDENTIFIER
;

zp_abs_identifier:
    ZEROPAGE
|   ABSOLUTE
|   IDENTIFIER
;

由于某种原因这段代码:

; absolute indirect
lda ($9000)
lda (steps) 

; zp indirect
lda ($90)
lda (gold)

lda (steps)
处生成语法错误。我想说这符合
MNEMONIC '(' zp_abs_identifier ')'
规则,但看起来它试图寻找上面规则的
COMMAY
部分(并且失败)。

当我用

COMMAY
删除规则时,这部分会被正确解析。

bison flex-lexer
1个回答
0
投票

我已将您的词典和语法插入 lex 和 bison 并生成结果解析器。 当用 bison 生成语法时,bison 显示语法有 2 个归约/归约冲突。

技术含义:

Shift/Reduce 解析器是表驱动的,有两个操作:shift 和 reduce。 这些操作由一个表定义,该表考虑了当前状态和先行标记。 Bison 是一个自动生成这些表格的工具。

当解析器可以从单个状态下的多个操作中进行选择时,就会发生冲突。 有两种类型:

通常,Shift/Reduce 冲突会通过默认的 Shift 操作自动解决。这通常有效,但并非总是有效。因此,转移/减少冲突不一定是一个问题。

然而,Reduce/Reduce 冲突是一个关键问题,没有适当的方法来解决这种冲突。因此,如果您看到这些冲突,您可能会遇到意想不到的行为。

为什么会出现reduce/reduce冲突?

发生冲突是因为解析器需要决定它必须探索哪个产生式规则。规则一旦选定就不会再回头。 由于产生式规则有一个公共部分,解析器足够聪明,可以先解析公共部分,然后再决定产生式规则应该是什么。

但是,要解析标识符,它必须决定生成 zp_identifier 或 zp_abs_identifier 非终结符。当两个非终结符都工作时,它会猜测一些非终结符。 在这种情况下,它默认选择 zp_identifier,因为选择了这个非终结符,它强制生产规则成为第一条规则。

这就是它期望 COMMAY 的原因。

如何解决这个问题:

该问题是由于缺乏前瞻或使用不够强大的解析算法引起的。

一个简单的解决方案是使用 Bison 的 GLR 版本:https://www.gnu.org/software/bison/manual/html_node/GLR-Parsers.html, 这使得解析器分别探索不明确的选择,这将导致两个标识符都被探索,随后具有错误标识符的分支被销毁,仅保留有效分支。 请注意,如果语法本质上是不明确的,即使 GLR 也无法拯救您,语法重建是这种情况下唯一剩下的工具。

另一种方法是通过语法重构来消除reduce/reduce冲突。 这是一个相当复杂的方法,这肯定会降低你的语法的逻辑性。 在这种情况下,问题是由 zp_abs_identifier 和 zp_identifier 中的公共部分引起的。

我建议去掉 ABSOLUTE 的特殊情况,并使用可选的 COMMAY 终端来制作一般情况:

expression:
    MNEMONIC LEFT_PARENTHESIS ABSOLUTE RIGHT_PARENTHESIS
    | MNEMONIC LEFT_PARENTHESIS zp_identifier RIGHT_PARENTHESIS optional_commay
    ;

optional_commay:
    |
    COMMAY
    ;

zp_identifier:
    ZEROPAGE
    | IDENTIFIER
    ;

这样就不存在reduce/reduce冲突了,特殊情况还是解决了。

© www.soinside.com 2019 - 2024. All rights reserved.