ANTLR4.7:规则XXX包含一个闭包,其中至少有一个替代方法可以匹配空字符串'

时间:2017-07-31 08:44:33

标签: compiler-errors compiler-construction antlr antlr4

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

(有关重复此问题的简单语法,请参阅 ADD 1

[Defines]
  INF_VERSION                    = 0x00010005
  BASE_NAME                      = WebServer
  FILE_GUID                      = 99E87DCF-6162-40c5-9FA1-32111F5197F7
  MODULE_TYPE                    = SEC
  UEFI_SPECIFICATION_VERSION     = 0x00010005

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)?
                  ;

ANTLR 4.7报告此错误:

  

message:'rule definitions包含一个至少有一个的闭包   可以匹配空字符串'

的替代方案

但如果我改变了这样的语法:

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

错误消失了。

我的问题是,closure是什么意思?哪个部分是closuredefine_statement

在我移动可能为空的替代方案后,defines规则可以在'[Defines]' define_statement+('UEFI_SPECIFICATION_VERSION' EQ SpecVersion_VersionVal)?之间切换,这意味着defines可以仍然匹配空字符串。错误怎么可能消失?

ADD 1

为了使事情更清楚,我用简化的语法重新编写了这个错误:

grammar test;

rule : alternate+; // <<<<< HERE
alternate : '1'?;

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

  

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

如果我在?使用HERE,ANTLR会报告警告

  

'规则规则至少包含一个可选块   一个可以匹配空字符串'

的替代方法

我仍然不确定原因。

ADD 2

alternate中的每一个都是rule的子节点,因此如果alternate可以是空字符串,那么在逻辑上可能导致{{1}的无限子节点}}。所以我想这可以解释为什么ANTLR禁止我使用rulealternate+执行此操作。但如果它与alternate*匹配,则最多会有一个子节点。这只是一个性能问题。所以ANTLR只会产生警告。

2 个答案:

答案 0 :(得分:2)

让我们从警告开始。该应用程序仅提醒您空字符串可以匹配某些内容。这是一个警告,因为大多数情况下,您不希望令牌与空字符串匹配。

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)?
                  ;

由于('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
                  | 
                  ;

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

如果您删除替代方案,则错误消失,因为为了清晰起见,我们再次重写:

define_statement  : 'INF_VERSION' EQ SpecVersion_VersionVal 
                  | 'BASE_NAME' EQ BaseName
                  | 'FILE_GUID' EQ RegistryFormatGUID
                  | 'MODULE_TYPE' EQ Edk2ModuleType
                  ;

那里没有任何可选项。其中一个必须匹配。

你已经在评论中提到过,你明白为什么将规则移动到自己的规则 - 这可能与无数个空字符串相匹配 - 是一个坏主意,所以我不会在这里讨论

但是当你这样做时,为什么错误会消失呢?因为

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

保证匹配某些,即使它只是令牌[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

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

有意义吗?我希望我能帮到你!

编辑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;

enter image description here

答案 1 :(得分:1)

添加我的解决方案。归功于@JLH。

要获得的重要一点是对不同性质的行 2个单独的规则

  • linesGroup1_Defines
  • linesGroup2_Defines

这样,可以通过optional nature(选择)而不是|(可选)来访问行的?

grammar inf;

start : configSections;

configSections: configSection+
                EOF;

configSection: section_Defines
             | bSection
             ;

section_Defines : '[Defines]'
                 sectionLine_Defines*;

sectionLine_Defines  : linesGroup1_Defines | linesGroup2_Defines;

linesGroup1_Defines : 'INF_VERSION' EQ SpecVersion_VersionVal 
           | 'BASE_NAME' EQ BaseName
           | 'FILE_GUID' EQ RegistryFormatGUID
           | 'MODULE_TYPE' EQ Edk2ModuleType
           ;
linesGroup2_Defines : 'UEFI_SPECIFICATION_VERSION' EQ SpecVersion_VersionVal
              | 'PI_SPECIFICATION_VERSION' EQ SpecVersion_VersionVal
              ;


bSection : '[b]'
           SectionLine_b+;

(为简洁起见,省略了一些必要的标记定义)

enter image description here

ADD 1

再考虑一下,通过上述解决方案,我没有涵盖linesGroup1_Deines是强制性的linsGroup2_Defines[Defines] UEFI_SPECIFICATION_VERSION = 0x00010005 是可选的语义。 实际上两者都是可选的。它只接受输入,只有下面的可选行:

CLAVE = "some_string".encode('utf-8').bytes

我不确定语法中是否涵盖了这种语义可以/应该。也许我需要进一步完善它。