Parsing Bison Reduce/Reduce与强制转换和表达式括号的冲突
我正在bison中构建语法,并且我已经将上一个reduce/reduce错误缩小到以下测试用例中:Parsing Bison Reduce/Reduce与强制转换和表达式括号的冲突,parsing,compiler-construction,bison,yacc,Parsing,Compiler Construction,Bison,Yacc,我正在bison中构建语法,并且我已经将上一个reduce/reduce错误缩小到以下测试用例中: %{ #include <stdio.h> #include <string.h> extern yydebug; void yyerror(const char *str) { fprintf(stderr, "Error: %s\n", str); } main() { yydebug = 1; yyparse(); } %} %right '='
%{
#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
;
如何解决此冲突?如果语法仅限于OP中显示的产品,则删除冲突相对容易,因为语法是明确的。唯一的问题是它是LR(2)而不是LR(1) OP中的分析完全正确。当解析器看到时,例如:
( identifier1 · )
(其中,·
标记当前点,因此前瞻标记为)),不可能知道这是否是的前缀
( identifier1 · ) ;
( identifier1 · ) = ...
( identifier1 · ) identifier2
( identifier1 · ) ( ...
在前两种情况下,标识符1
必须减少到表达式
,以便(表达式)
随后可以减少到表达式
,而在后两种情况下,
标识符1
必须缩减为类型
,以便(类型)表达式
随后可以缩减为表达式
。如果解析器能够看到一个令牌,那么就可以做出决定
因为对于任何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
;
(除了从类型
的产品中删除标识符
之外,其余语法相同)
对于没有符号可以同时作为前缀和中缀运算符(如-
)的语法,以及没有运算符可以省略的语法(有效地,如在函数调用中)。特别是,它对C不起作用,因为它会留下歧义:
( t ) * a // product or cast?
( t ) ( 3 ) // function-call or cast?
这些是语法中真正的歧义,只有知道t
是类型名还是变量/函数名才能解决
C解析器的“常用”解决方案是通过在扫描器和解析器之间共享符号表来解决歧义;由于typedef
类型别名声明必须在适用范围内首次将符号用作类型名之前出现,因此可以在扫描令牌之前知道令牌是否已使用typedef
声明。更准确地说,如果没有看到typedef,可以假定符号不是类型,尽管它可能完全没有声明
通过使用GLR语法和语义谓词,可以将逻辑限制到解析器。有些人觉得这更优雅
( t ) * a // product or cast?
( t ) ( 3 ) // function-call or cast?