我建立在野牛语法,我已经收窄我的最后一个减少/降低错误以下测试用例:
%{
#include <stdio.h>
#include <string.h>
extern yydebug;
void yyerror(const char *str)
{
fprintf(stderr, "Error: %s\n", str);
}
main()
{
yydebug = 1;
yyparse();
}
%}
%right '='
%precedence CAST
%left '('
%token AUTO BOOL BYTE DOUBLE FLOAT INT LONG SHORT SIGNED STRING UNSIGNED VOID
%token IDENTIFIER
%start file
%debug
%%
file
: %empty
| statement file
;
statement
: expression ';'
;
expression
: expression '=' expression
| '(' type ')' expression %prec CAST
| '(' expression ')'
| IDENTIFIER
;
type
: VOID
| AUTO
| BOOL
| BYTE
| SHORT
| INT
| LONG
| FLOAT
| DOUBLE
| SIGNED
| UNSIGNED
| STRING
| IDENTIFIER
;
想必问题是,它不能告诉类型和表达之间的差异,当它看到(IDENTIFIER)
的表达式中。
输出:
fail.y: warning: 1 reduce/reduce conflict [-Wconflicts-rr]
fail.y:64.5-14: warning: rule useless in parser due to conflicts [-Wother]
| IDENTIFIER
^^^^^^^^^^
我能做些什么来解决这个矛盾呢?
如果语法仅限于在OP显示的生产,这将是比较容易消除该冲突,因为语法是毫不含糊的。 唯一的问题是,它是LR(2),而不是LR(1)。
在OP分析是完全正确的。 当解析器看到,例如:
( identifier1 · )
(其中·
标志着当前点,因此超前记号是)),它是不可能知道这是否是一个前缀
( identifier1 · ) ;
( identifier1 · ) = ...
( identifier1 · ) identifier2
( identifier1 · ) ( ...
在前两种情况下, identifier1
必须减少到expression
,使得( expression )
随后可被还原为expression
,而在后两种情况下, identifier1
必须减少到type
,使得( type ) expression
可以随后降低到expression
。 如果解析器可以看到一个令牌还未来,决定可以作出。
因为对于任何LR(k)文法,有一个LR(1)文法识别相同的语言,有清楚的是溶液; 一般的方法是推迟还原,直到一个令牌先行是足以区分。 这样做的一个方法是:
cast_or_expr : '(' IDENTIFIER ')'
;
cast : cast_or_expr
| '(' type ')'
;
expr_except_id : cast_or_expr
| cast expression %prec CAST
| '(' expr_except_id ')'
| expression '=' expression
;
expression : IDENTIFIER
| expr_except_id
;
(语法的其余部分是除了用于去除的相同IDENTIFIER
从制作用于type
。)
这工作正常语法在没有符号既可以是一个前缀和中缀运算符(如-
并在没有操作员可以被省略(实际上,在函数调用)。 特别是,它不会对C的工作,因为它会留下模棱两可:
( t ) * a // product or cast?
( t ) ( 3 ) // function-call or cast?
那些是在语法真实模糊度只能由知道是否可以解决t
是类型名或变量/函数名。
对于C解析器的“通常”的解决方案是通过共享扫描器和解析器之间的符号表来解决多义性; 自typedef
类型别名声明必须在适用范围在第一次使用该符号的作为类型名称之前出现,它可以预先令牌是否已被声明为有令牌的扫描的是已知typedef
。 更准确地,如果类型定义尚未看到的那样,它可以假设该符号不是一个类型,虽然它可能是完全未声明的。
通过使用GLR语法和语义谓词,可以向逻辑限制到解析器。 一些人发现,更优雅。