ANTLR4.7:规则XXX包含一个闭包,该闭包中至少有一个可以匹配空字符串的选项'

smw*_*dia 5 compiler-construction antlr compiler-errors antlr4

我正在尝试创建一种语法以匹配如下内容:

(有关此问题的简单语法,请参见添加1

[Defines]
  INF_VERSION                    = 0x00010005
  BASE_NAME                      = WebServer
  FILE_GUID                      = 99E87DCF-6162-40c5-9FA1-32111F5197F7
  MODULE_TYPE                    = SEC
  UEFI_SPECIFICATION_VERSION     = 0x00010005
Run Code Online (Sandbox Code Playgroud)

UEFI_SPECIFICATION_VERSION = 0x00010005部分是可选的。

(为简洁起见,我省略了一些语法)。

我的语法1看起来像这样:

defines : '[Defines]'
         define_statement+
         ;

define_statement  : 'INF_VERSION' EQ SpecVersion_VersionVal 
                  | 'BASE_NAME' EQ BaseName
                  | 'FILE_GUID' EQ RegistryFormatGUID
                  | 'MODULE_TYPE' EQ Edk2ModuleType
                  | ('UEFI_SPECIFICATION_VERSION' EQ SpecVersion_VersionVal)?
                  ;
Run Code Online (Sandbox Code Playgroud)

ANTLR 4.7报告此错误:

消息:“规则定义包含一个闭包,该闭包至少具有一个可以匹配空字符串的选项”

但是如果我这样改变语法:

defines : '[Defines]'
         define_statement+
         | ('UEFI_SPECIFICATION_VERSION' EQ SpecVersion_VersionVal)? // <<< HERE
         ;

define_statement  : 'INF_VERSION' EQ SpecVersion_VersionVal
                  | 'BASE_NAME' EQ BaseName
                  | 'FILE_GUID' EQ RegistryFormatGUID
                  | 'MODULE_TYPE' EQ Edk2ModuleType
Run Code Online (Sandbox Code Playgroud)

错误消失了。

我的问题是什么closure意思?是哪一部分closure?的define_statement

移动可能为空的替代项后,defines规则可以在'[Defines]' define_statement+和之间交替('UEFI_SPECIFICATION_VERSION' EQ SpecVersion_VersionVal)?,这意味着defines可以匹配空字符串。错误如何消失?

加1

为了使事情更清楚,我使用简化的语法来再现此错误:

grammar test;

rule : alternate+; // <<<<< HERE
alternate : '1'?;
Run Code Online (Sandbox Code Playgroud)

如果我使用+*HERE,ANTLR会报告错误

“规则包含一个至少包含一个可以匹配空字符串的替代项的闭包”

如果?在上使用HERE,ANTLR将报告警告

“规则包含一个可选块,其中至少有一个可以匹配空字符串的可选块”

我仍然不确定为什么。

加2

每个alternateWILL都是的子节点rule,因此如果alternate可以为空字符串,则在逻辑上有可能导致的子节点无穷rule。因此,我想这可以解释为什么ANTLR禁止我使用alternate+或这样做alternate*。但是如果使用alternate?,最多将只有一个子节点。这只是性能问题。因此,ANTLR只会生成警告。

Tom*_*rvo 4

让我们从警告开始。该应用程序只是提醒您空字符串可以匹配某些内容。这是一个警告,因为大多数时候,您不希望标记与空字符串匹配。

defines : '[Defines]'
         define_statement+
         ;

define_statement  : 'INF_VERSION' EQ SpecVersion_VersionVal 
                  | 'BASE_NAME' EQ BaseName
                  | 'FILE_GUID' EQ RegistryFormatGUID
                  | 'MODULE_TYPE' EQ Edk2ModuleType
                  | ('UEFI_SPECIFICATION_VERSION' EQ SpecVersion_VersionVal)?
                  ;
Run Code Online (Sandbox Code Playgroud)

由于 ('UEFI_SPECIFICATION_VERSION' EQ SpecVersion_VersionVal) 是可选的(它后面跟着?,所以它可以不替换,如下所示:

define_statement  : 'INF_VERSION' EQ SpecVersion_VersionVal 
                  | 'BASE_NAME' EQ BaseName
                  | 'FILE_GUID' EQ RegistryFormatGUID
                  | 'MODULE_TYPE' EQ Edk2ModuleType
                  | 
                  ;
Run Code Online (Sandbox Code Playgroud)

最后一个|本身意味着规则不能匹配任何内容或空字符串。这样,关于警告的谜团就解开了。他们称之为闭包,但您可以将其视为“令牌绑定”或“匹配”。我认为术语在实际意义上并不那么重要。

如果删除替代方案,错误就会消失,因为为了清楚起见,再次重写,我们有:

define_statement  : 'INF_VERSION' EQ SpecVersion_VersionVal 
                  | 'BASE_NAME' EQ BaseName
                  | 'FILE_GUID' EQ RegistryFormatGUID
                  | 'MODULE_TYPE' EQ Edk2ModuleType
                  ;
Run Code Online (Sandbox Code Playgroud)

那里没有什么可选的。其中之一必须匹配。

您已经在评论中提到,您明白为什么将规则移动到它自己的规则(可能会匹配无限数量的空字符串)是一个坏主意,所以我不会在这里详细说明。

但为什么当你这样做时错误就消失了呢?因为

defines : '[Defines]'
         define_statement+
         | ('UEFI_SPECIFICATION_VERSION' EQ SpecVersion_VersionVal)? // <<< HERE
         ;
Run Code Online (Sandbox Code Playgroud)

保证匹配某些东西,即使它只是标记[Defines],这是一个隐式词法分析器标记。因此,即使 UEFI 是空字符串,仍然有一些东西需要解析。在我们检查的第一个版本中情况并非如此;事实上,整个define_statement规则可能是一个空字符串。从解析的角度来看,这是一个很大的区别。

现在的大问题是:该[Defines]部分真的是可选的吗?只有你能回答这个问题。但如果是的话,也许你应该将其重新编码为:

defines : ('[Defines]' define_statement+)?

define_statement  : 'INF_VERSION' EQ SpecVersion_VersionVal 
                  | 'BASE_NAME' EQ BaseName
                  | 'FILE_GUID' EQ RegistryFormatGUID
                  | 'MODULE_TYPE' EQ Edk2ModuleType
                  | 'UEFI_SPECIFICATION_VERSION' EQ SpecVersion_VersionVal
Run Code Online (Sandbox Code Playgroud)

这使得它完全是可选的。同样,只有您可以决定这对于您的语法和预期输入是否有效。

合理?我希望我对你有帮助!

编辑1

要缓解错误,请尝试以下语法(我为测试值创建了显式标记以使其运行):

grammar Uefi;
defines : '[Defines]' statement+ ;
statement : define_statement | uefi_statement ;      
uefi_statement : 'UEFI_SPECIFICATION_VERSION' EQ SpecVersion_VersionVal ;
define_statement  : 'INF_VERSION' EQ SpecVersion_VersionVal 
                  | 'BASE_NAME' EQ BaseName
                  | 'FILE_GUID' EQ RegistryFormatGUID
                  | 'MODULE_TYPE' EQ Edk2ModuleType
                  ;
// DUMMY VALUES               
SpecVersion_VersionVal : '0x00010005';
BaseName : 'WebServer';
RegistryFormatGUID : '99E87DCF-6162-40c5-9FA1-32111F5197F7';
Edk2ModuleType : 'SEC';
EQ : '=';
WS : [ \t\r\n]+ -> skip;
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述