为什么Antlr4无法解析C语法中FunctionDefinitionContext的声明符
为什么C语法中的Antlr4无法解析“FunctionDefinitionContext”的“declarator”,而“declarationList”只包含一个作为指针的参数 因此,如果我分析以下函数: int打印队列(int idx,void*数据) 解析器作为声明器检索打印队列 解析时: int销毁队列(无效*数据) 解析器将(void*data)作为声明器进行检索(我认为它将其视为函数指针) 如何解决此问题?这是中的一个错误。这个bug也被发现了 问题是C的语法在类型转换方面存在歧义。例如,表达式为什么Antlr4无法解析C语法中FunctionDefinitionContext的声明符,antlr4,Antlr4,为什么C语法中的Antlr4无法解析“FunctionDefinitionContext”的“declarator”,而“declarationList”只包含一个作为指针的参数 因此,如果我分析以下函数: int打印队列(int idx,void*数据) 解析器作为声明器检索打印队列 解析时: int销毁队列(无效*数据) 解析器将(void*data)作为声明器进行检索(我认为它将其视为函数指针) 如何解决此问题?这是中的一个错误。这个bug也被发现了 问题是C的语法在类型转换方面存在歧义。
(a)(b)
既匹配函数调用的规则((a)
将是计算函数指针的主表达式,(b)
将是包含单个参数的参数列表:变量b
)也匹配强制转换的规则(将变量b
的值强制转换为类型a
(其中a
将被识别为typedef name
)
C通过说明typedef name
规则应仅应用于实际已typedef
ed的标识符来解决此歧义。也就是说,当且仅当文件中以前确实存在typedef someType a;
时,应将上述示例解析为类型转换,否则应进行解析作为函数调用。这是无法在上下文无关语法中表达的。对于ANTLR,这意味着需要语义谓词来实现此规则
然而,这并不是所讨论的语法所做的(可能是为了保持语法语言的不可知性,或者保持它的简单性,或者可能是因为作者不知道这对于正确解析所有C代码是必要的).相反,原始版本解决了歧义,将标识符视为变量名-这仅仅是因为语法中的替代顺序。在某个点上,有人注意到这无法正确分析类型转换并“修复”这是通过更改语法中替代项的顺序来实现的。现在,歧义得到了解决,有利于将标识符视为类型名。这修复了类型转换的大小写,但打破了示例,因为现在代码中的print\u queue
被解释为类型名
如何解决这个问题
您可以还原到固定类型转换的提交之前的语法版本。然后,您的代码应该可以工作,但将类型转换为typedef
ed类型将不起作用。如果您希望语法在所有情况下都生成正确的解析,则需要向语法中添加操作和谓词
要做到这一点,您需要向解析器添加一组typedef
ed名称,如下所示(如果您使用的是不同的语言,则必须相应地调整以下代码):
现在,以前提到标识符
的所有其他地方应该改为引用标识符
。这样,标识符只有在typedef集合中才会被视为类型,如果不在typedef集合中,则只会被视为变量或函数名
现在剩下的就是实际填充集合。为此,我们需要在声明
规则中添加一个操作,如果声明是类型定义
,则将所有声明的标识符添加到集合中。我们可以这样做:
declaration
: declarationSpecifiers initDeclaratorList ';' {
if ($declarationSpecifiers.ctx.specifiers.stream().anyMatch(specifier -> specifier.getText().equals("typedef"))) {
ParseTreeWalker.DEFAULT.walk(new CBaseListener() {
@Override
public void exitIdentifier(IdentifierContext id) {
typedefs.add(id.getText());
}
}, $initDeclaratorList.ctx);
}
}
| declarationSpecifiers ';'
| staticAssertDeclaration
;
declarationSpecifiers
: specifiers+=declarationSpecifier+
;
通过这些更改,语法现在应该可以同时用于类型转换(如果类型转换中使用的类型已正确地
typedef
ed)还有你的例子。你指的是哪种语法?grammars-v4存储库中的C语法?是的,grammars-v4存储库中的C语法我认为语法可能只是被破坏了。看起来作者可能试图通过尽可能频繁地将标识符视为类型名来实现类型转换,并破坏了其他一些情况过程中。相关错误报告:
typedefName
: {typedefs.contains(getCurrentToken().getText())}? IdentifierOrTypedefName
;
identifier
: {!typedefs.contains(getCurrentToken().getText())}? IdentifierOrTypedefName
;
declaration
: declarationSpecifiers initDeclaratorList ';' {
if ($declarationSpecifiers.ctx.specifiers.stream().anyMatch(specifier -> specifier.getText().equals("typedef"))) {
ParseTreeWalker.DEFAULT.walk(new CBaseListener() {
@Override
public void exitIdentifier(IdentifierContext id) {
typedefs.add(id.getText());
}
}, $initDeclaratorList.ctx);
}
}
| declarationSpecifiers ';'
| staticAssertDeclaration
;
declarationSpecifiers
: specifiers+=declarationSpecifier+
;