以下野牛语法产生减少/减少冲突:
%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
;
我看不出歧义在哪里。 LEFT_PARENTHESIS NAME RIGHT_PARENTHESIS
必须是公式,因为没有RELATION_INFIX
。
野牛的详细输出的开头是
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)
所以它似乎被RIGHT_PARENTHESIS
搞糊涂了。状态似乎接受NAME RIGHT_PARENTHESIS
,即使语法之前需要LEFT_PARENTHESIS
。
有什么想法吗?
修改
上面的语法被简化为专注于减少/减少冲突。我的真正意图是解析一阶逻辑公式:术语是运算符和公式的组合是术语关系的逻辑组合(不,和,或暗示)。
术语和公式有时是长表达式,因此我们给它们命名以便更容易地调用它们。所有名称最终都扩展到连接器和运算符的树,稍后在程序中(不在bison解析器中)。
终端符号中有变量(由逻辑建模的对象);公式是关于那些对象的陈述。它类似于C ++中的类型和函数:可以为所有类型和函数使用相同的命名方案。
答案 0 :(得分:3)
没有歧义。这并不意味着没有解析冲突。
无法使用LR(k)解析器解析模糊语法;尝试生成解析自动机必然会失败,因为同一句子存在两个不同的派生。但事实恰恰相反。有冲突的语法可能有歧义,也可能没有歧义。 [注1]
要通过LR(k)语法进行解析,需要在超过 k 读取更多符号之前解析每个非终端。
所以,虽然你说的很正确
LEFT_PARENTHESIS NAME RIGHT_PARENTHESIS
必须是formula
,因为没有RELATION_INFIX
。
这并不能说明整个故事。要使LEFT_PARENTHESIS NAME RIGHT_PARENTHESIS
成为formula
,NAME
也必须是formula
,NAME
减少到formula
必须在1个符号内(因为 k 总是在野牛LR解析器中的一个)。但只能在NAME
之后阅读一个符号,就不可能知道"没有RELATION_INFIX
"。因此减少 - 减少冲突。
如果我理解你的笔记,你所拥有的基本上是一种表达式语法,其中有两种类型的表达式("公式"和#34;术语"),具有不同的运算符,其中标识符可以是原始变量或任一类型的表达式的名称。一个限制是"术语"的操作数。运算符只能是" term",而" formula"的操作数。运算符可以是"公式"或者"术语"。 (这没有反映在简化的语法中,它不允许term AND (term AND term)
(有或没有括号);我只是假设它是理想的,因为否则公式不可能复杂,但你的笔记说它们经常是。)
如果一个短语包含一个可见的运算符,那么句法类别("公式"或" term")是显而易见的,但是一个只包含一个标识符的句法短语,可能被冗余所包围括号,可以是" term"或者"公式"。这使得在不知道每个标识符的语法类别的情况下,基本上不可能在语法上完全表达限制。
如果在第一次使用标识符之前知道每个标识符的语法类别,则可以使用词汇反馈来允许在解析期间完全实施限制。否则,或者如果词汇反馈被认为是不合需要的,则有必要在随后的语义分析过程中验证包含标识符的任何子表达式,也许是扩展宏标识符的同一个子表达式。
由于无论如何都需要进行语义检查,因此很容易检查formula
永远不是term
运算符的操作数。在这种情况下,可以使用普通的表达式语法,包括正常使用优先级声明来简化表示。这个语法将直接为任何正确的输入产生正确的AST;语义检查只需要拒绝不正确的输入。以这种方式进行检测也有助于产生信息性错误消息。这个解决方案真的很少,恕我直言。
但是编写一个语法来表达对名称以外的其他案例的句法限制也是相当直截了当的。诀窍是使用三种表达式类型,因为实际上有三种语法类别,如上面的讨论所示:
明显的公式
明显的条款
可能是公式或术语的短语(即可能带有括号的名称)
获得带括号的子表达式是有点烦人的,并且会增加语法的混乱。在我看来,没有真正的好处。但是如果你想尝试这个,这里有一个简单的例子,只使用一个术语运算符和一个公式运算符以及将显式检查/转换操作插入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; }
答案 1 :(得分:1)
根据您的语法LEFT_PARENTHESIS NAME RIGHT_PARENTHESIS
可以匹配公式或术语。
因为它涉及减少特定状态的输入,所以不能保证成功(并且不会在NAME RIGHT_PARENTHESIS
表达式的情况下)。州的进展表明,这是唯一可行的明确方向。如果不正确输入,那么从语法角度来看,这不是一个突破点。但肯定有几种减少替代方案。