我最近一直在为基于 C 的语言编写解析器。我正在使用 CUP(Java 的 Yacc)。
我想实施“词法分析器黑客”(http://eli.thegreenplace.net/2011/05/02/the-context-sensitive-of-c%E2%80%99s-grammar-revisited/ 或 https:// /en.wikipedia.org/wiki/The_lexer_hack),以区分 typedef 名称和变量/函数名称等。要启用与之前声明的类型同名的声明变量(来自第一个链接的示例):
typedef int AA;
void foo() {
AA aa; /* OK - define variable aa of type AA */
float AA; /* OK - define variable AA of type float */
}
我们必须介绍一些新的产品,其中变量/函数名称可以是IDENTIFIER
or TYPENAME
。这就是困难发生的时刻——语法冲突。
我试图不将这个凌乱的 Yacc 语法用于 gcc 3.4 ( http://yaxx.googlecode.com/svn-history/r2/trunk/gcc-3.4.0/gcc/c-parse.y ),但这次我不知道如何自己解决冲突。我看了一下 Yacc 语法:
declarator:
after_type_declarator
| notype_declarator
;
after_type_declarator:
...
| TYPENAME
;
notype_declarator:
...
| IDENTIFIER
;
fndef:
declspecs_ts setspecs declarator
// some action code
// the rest of production
...
setspecs: /* empty */
// some action code
declspecs_ts
表示声明说明符,其中“是否已看到类型说明符;在类型说明符之后,typedef 名称是要重新声明的标识符(_ts 或 _nots)。”
从 declspecs_ts 我们可以到达
typespec_nonreserved_nonattr:
TYPENAME
...
;
乍一看,我不敢相信 shift/reduce 冲突没有出现!
setspecs
是空的,所以我们declspecs_ts
后面跟着declarator
,所以我们可以预期解析器应该混淆TYPENAME
是 fromdeclspecs_ts
还是 from declarator
。
任何人都可以简要(甚至准确地)解释这一点。提前致谢!
编辑:有用的链接:http ://www.gnu.org/software/bison/manual/bison.html#Semantic-Tokens