Shift/Reduce 银联冲突
Shift/Reduce conflict in CUP
我正在尝试使用 JFlex 和 Cup 为 javascript-ish 语言编写解析器,但我遇到了一些致命的 shift/reduce 问题和 reduce/reduce问题。
我已经彻底搜索并找到了大量示例,但我无法将这些示例推断到我的语法中。目前我的理解是,出现这些问题是因为解析器无法决定应该采用哪种方式,因为它无法区分。
我的语法如下:
从输入开始;
INPUT::= PROGRAM;
PROGRAM::= FUNCTION NEWLINE PROGRAM
| NEWLINE PROGRAM;
FUNCTION ::= function OPTIONAL id p_izq ARG p_der NEWLINE l_izq NEWLINE BODY l_der;
OPTIONAL ::=
| TYPE;
TYPE::= integer
| boolean
ARG ::=
| TYPE id MORE_ARGS;
MORE_ARGS ::=
| colon TYPE id MORE_ARGS;
NEWLINE ::= salto NEWLINE
| ;
BODY ::= ;
我遇到了几个冲突,但这两个只是一个例子:
Warning : *** Shift/Reduce conflict found in state #5
between NEWLINE ::= (*)
and NEWLINE ::= (*) salto NEWLINE
under symbol salto
Resolved in favor of shifting.
Warning : *** Shift/Reduce conflict found in state #0
between NEWLINE ::= (*)
and FUNCTION ::= (*) function OPTIONAL id p_izq ARG p_der NEWLINE l_izq NEWLINE BODY l_der
under symbol function
Resolved in favor of shifting.
PS: 语法要复杂得多,但我认为如果我看到这些 shift/reduce 问题是如何解决的,我就能解决其余的问题。
感谢您的回答。
PROGRAM
没用(in the technical sense)。也就是说,它不能产生任何句子,因为 in
PROGRAM::= FUNCTION NEWLINE PROGRAM
| NEWLINE PROGRAM;
PROGRAM
的两个产生式都是递归的。为了使非终端有用,它需要能够最终产生一些终端串,并且要做到这一点,它必须至少有一个非递归产生式;否则,递归永远不会终止。令我惊讶的是,CUP 没有向您提及此事。也可能确实如此,而您选择忽略该警告。
这是个问题——无用的非终端真的永远无法匹配任何东西,因此它们最终会导致解析错误——但这不是您报告的解析冲突。冲突来自同一个产品的另一个特征,这与你不能除以0有关。
关于空的事情是,任何数量的空仍然是空。所以,如果你一无所有,有人过来问你到底有多少一无所有,你就会遇到一些问题,因为要从“0 * 大量”返回 "plenty",你需要计算“0 / 0”,这不是一个明确定义的值。 (如果你有很多个二,有人问你有多少个二,那不是问题:假设很多个二的结果是 40;你可以很容易地计算出 40 / 2 = 20,结果是完全是因为 20 * 2 = 40。)
所以这里我们没有算术,我们有符号串。不幸的是,不包含任何符号的字符串实际上是不可见的,就像 0 代表所有这些千年,直到一些阿拉伯数学家注意到什么都不写的价值。
这一切发生的地方是你有
PROGRAM::= ... something ...
| NEWLINE PROGRAM;
但是NEWLINE
允许什么都不生产。
NEWLINE ::= salto NEWLINE
| ;
所以 PROGRAM
的第二个递归产生式可能什么都不加。而且它可能多次不添加任何东西,因为生产是递归的。但是解析器需要是确定性的:它需要确切地知道有多少个 nothings 存在,这样它就可以将每个 nothing 减少到一个 NEWLINE
非终结符,然后减少一个新的 PROGRAM
非终结符。而且真的不知道要加多少废话
简而言之,optional nothings和repeated nothings是有歧义的。如果你要在你的语言中插入一个空,你需要确保有一个固定的有限数量的空,因为这是解析器可以明确地剖析空的唯一方法。
现在,由于该特定递归的唯一要点(据我所知)是允许空的换行终止语句(由于换行而可见,但什么都不做)。你可以通过改变递归来避免什么:
PROGRAM ::= ...
| salto PROGRAM;
尽管它与您当前的问题无关,但我觉得有必要提及 CUP 是一个 LALR 解析器生成器以及您可能已经在 Internet 上学习或阅读的所有关于递归下降解析器无法处理左递归的内容不适用。 (我删除了关于解析技术教授方式的咆哮,所以你必须尝试从我留下的提示中恢复它。)自下而上的解析器生成器,如 CUP 和 yacc/bison love 左递归。当然,他们也可以处理右递归,但不情愿,因为除了左递归之外,他们需要为每个递归浪费堆栈space。所以没有必要扭曲你的语法来处理这个缺陷;自然地写语法并开心。 (所以你很少需要非终端代表 "the rest of" 东西。)
PD:Plenty of nothing 是对 1934 年歌剧 Porgy and Bess 中歌曲的特定文化引用。
我正在尝试使用 JFlex 和 Cup 为 javascript-ish 语言编写解析器,但我遇到了一些致命的 shift/reduce 问题和 reduce/reduce问题。
我已经彻底搜索并找到了大量示例,但我无法将这些示例推断到我的语法中。目前我的理解是,出现这些问题是因为解析器无法决定应该采用哪种方式,因为它无法区分。
我的语法如下: 从输入开始;
INPUT::= PROGRAM;
PROGRAM::= FUNCTION NEWLINE PROGRAM
| NEWLINE PROGRAM;
FUNCTION ::= function OPTIONAL id p_izq ARG p_der NEWLINE l_izq NEWLINE BODY l_der;
OPTIONAL ::=
| TYPE;
TYPE::= integer
| boolean
ARG ::=
| TYPE id MORE_ARGS;
MORE_ARGS ::=
| colon TYPE id MORE_ARGS;
NEWLINE ::= salto NEWLINE
| ;
BODY ::= ;
我遇到了几个冲突,但这两个只是一个例子:
Warning : *** Shift/Reduce conflict found in state #5
between NEWLINE ::= (*)
and NEWLINE ::= (*) salto NEWLINE
under symbol salto
Resolved in favor of shifting.
Warning : *** Shift/Reduce conflict found in state #0
between NEWLINE ::= (*)
and FUNCTION ::= (*) function OPTIONAL id p_izq ARG p_der NEWLINE l_izq NEWLINE BODY l_der
under symbol function
Resolved in favor of shifting.
PS: 语法要复杂得多,但我认为如果我看到这些 shift/reduce 问题是如何解决的,我就能解决其余的问题。
感谢您的回答。
PROGRAM
没用(in the technical sense)。也就是说,它不能产生任何句子,因为 in
PROGRAM::= FUNCTION NEWLINE PROGRAM
| NEWLINE PROGRAM;
PROGRAM
的两个产生式都是递归的。为了使非终端有用,它需要能够最终产生一些终端串,并且要做到这一点,它必须至少有一个非递归产生式;否则,递归永远不会终止。令我惊讶的是,CUP 没有向您提及此事。也可能确实如此,而您选择忽略该警告。
这是个问题——无用的非终端真的永远无法匹配任何东西,因此它们最终会导致解析错误——但这不是您报告的解析冲突。冲突来自同一个产品的另一个特征,这与你不能除以0有关。
关于空的事情是,任何数量的空仍然是空。所以,如果你一无所有,有人过来问你到底有多少一无所有,你就会遇到一些问题,因为要从“0 * 大量”返回 "plenty",你需要计算“0 / 0”,这不是一个明确定义的值。 (如果你有很多个二,有人问你有多少个二,那不是问题:假设很多个二的结果是 40;你可以很容易地计算出 40 / 2 = 20,结果是完全是因为 20 * 2 = 40。)
所以这里我们没有算术,我们有符号串。不幸的是,不包含任何符号的字符串实际上是不可见的,就像 0 代表所有这些千年,直到一些阿拉伯数学家注意到什么都不写的价值。
这一切发生的地方是你有
PROGRAM::= ... something ...
| NEWLINE PROGRAM;
但是NEWLINE
允许什么都不生产。
NEWLINE ::= salto NEWLINE
| ;
所以 PROGRAM
的第二个递归产生式可能什么都不加。而且它可能多次不添加任何东西,因为生产是递归的。但是解析器需要是确定性的:它需要确切地知道有多少个 nothings 存在,这样它就可以将每个 nothing 减少到一个 NEWLINE
非终结符,然后减少一个新的 PROGRAM
非终结符。而且真的不知道要加多少废话
简而言之,optional nothings和repeated nothings是有歧义的。如果你要在你的语言中插入一个空,你需要确保有一个固定的有限数量的空,因为这是解析器可以明确地剖析空的唯一方法。
现在,由于该特定递归的唯一要点(据我所知)是允许空的换行终止语句(由于换行而可见,但什么都不做)。你可以通过改变递归来避免什么:
PROGRAM ::= ...
| salto PROGRAM;
尽管它与您当前的问题无关,但我觉得有必要提及 CUP 是一个 LALR 解析器生成器以及您可能已经在 Internet 上学习或阅读的所有关于递归下降解析器无法处理左递归的内容不适用。 (我删除了关于解析技术教授方式的咆哮,所以你必须尝试从我留下的提示中恢复它。)自下而上的解析器生成器,如 CUP 和 yacc/bison love 左递归。当然,他们也可以处理右递归,但不情愿,因为除了左递归之外,他们需要为每个递归浪费堆栈space。所以没有必要扭曲你的语法来处理这个缺陷;自然地写语法并开心。 (所以你很少需要非终端代表 "the rest of" 东西。)
PD:Plenty of nothing 是对 1934 年歌剧 Porgy and Bess 中歌曲的特定文化引用。