ANTLR4.7:规则 XXX 包含一个闭包,该闭包至少有一个可以匹配空字符串的替代项'
ANTLR4.7: rule XXX contains a closure with at least one alternative that can match an empty string'
我正在尝试创建一个语法来匹配如下内容:
(要重现此问题的简单语法,请参阅 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 defines contains a closure with at least one
alternative that can match an empty string'
但是如果我像这样更改语法:
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
是什么意思? closure
是哪一部分? define_statement
?
在我移动可能为空的备选方案后,defines
规则可以在 '[Defines]' define_statement+
和 ('UEFI_SPECIFICATION_VERSION' EQ SpecVersion_VersionVal)?
之间交替,这意味着 defines
仍然可以 匹配空字符串。错误怎么会消失?
添加 1
为了让事情更清楚,我用简化的语法重现这个错误:
grammar test;
rule : alternate+; // <<<<< HERE
alternate : '1'?;
如果我在HERE
处使用+
或*
,ANTLR将报告一个错误:
'rule rule contains a closure with at least one alternative that can
match an empty string'
如果我在 HERE
处使用 ?
,ANTLR 将报告一个 警告:
'rule rule contains an optional block with at least
one alternative that can match an empty string'
我仍然不确定为什么。
添加 2
alternate
中的每一个都将是rule
的子节点,所以如果alternate
可以是空字符串,那么逻辑上可能会导致[=无限的子节点=28=]。所以我想这可以解释为什么 ANTLR 禁止我使用 alternate+
或 alternate*
来做这件事。但是如果是alternate?
,最多也就一个个子节点。这只是一个性能问题。所以 ANTLR 只是生成一个警告。
让我们从警告开始。该应用程序只是提醒您空字符串可以匹配某些内容。这是一个警告,因为大多数时候,您不希望标记与空字符串匹配。
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
;
保证匹配 something,即使它只是标记 [Defines]
,这是一个隐式词法分析器标记。所以即使 UEFI 是空字符串,仍然有 something 需要解析。在我们检查的第一个版本中情况并非如此;事实上,整个 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;
只需添加我的解决方案。感谢@JLH。
重要的是要为具有不同性质.
的行设置2条单独的规则
linesGroup1_Defines
linesGroup2_Defines
.
这样,一行的optional nature
可以通过|
(选择)而不是?
(可选)到达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+;
(为了简洁省略了一些必要的令牌定义)
加 1
再想一想,通过上述解决方案,我没有涵盖 linesGroup1_Deines
是强制性的而 linsGroup2_Defines
是可选的语义。 实际上现在两者都是可选的。它可以接受只有可选行的输入,如下所示:
[Defines]
UEFI_SPECIFICATION_VERSION = 0x00010005
我不确定这个语义 can/should 是否包含在语法中。也许我需要进一步完善它。
我正在尝试创建一个语法来匹配如下内容:
(要重现此问题的简单语法,请参阅 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 defines contains a closure with at least one alternative that can match an empty string'
但是如果我像这样更改语法:
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
是什么意思? closure
是哪一部分? define_statement
?
在我移动可能为空的备选方案后,defines
规则可以在 '[Defines]' define_statement+
和 ('UEFI_SPECIFICATION_VERSION' EQ SpecVersion_VersionVal)?
之间交替,这意味着 defines
仍然可以 匹配空字符串。错误怎么会消失?
添加 1
为了让事情更清楚,我用简化的语法重现这个错误:
grammar test;
rule : alternate+; // <<<<< HERE
alternate : '1'?;
如果我在HERE
处使用+
或*
,ANTLR将报告一个错误:
'rule rule contains a closure with at least one alternative that can match an empty string'
如果我在 HERE
处使用 ?
,ANTLR 将报告一个 警告:
'rule rule contains an optional block with at least one alternative that can match an empty string'
我仍然不确定为什么。
添加 2
alternate
中的每一个都将是rule
的子节点,所以如果alternate
可以是空字符串,那么逻辑上可能会导致[=无限的子节点=28=]。所以我想这可以解释为什么 ANTLR 禁止我使用 alternate+
或 alternate*
来做这件事。但是如果是alternate?
,最多也就一个个子节点。这只是一个性能问题。所以 ANTLR 只是生成一个警告。
让我们从警告开始。该应用程序只是提醒您空字符串可以匹配某些内容。这是一个警告,因为大多数时候,您不希望标记与空字符串匹配。
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
;
保证匹配 something,即使它只是标记 [Defines]
,这是一个隐式词法分析器标记。所以即使 UEFI 是空字符串,仍然有 something 需要解析。在我们检查的第一个版本中情况并非如此;事实上,整个 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;
只需添加我的解决方案。感谢@JLH。
重要的是要为具有不同性质.
的行设置2条单独的规则linesGroup1_Defines
linesGroup2_Defines
.
这样,一行的optional nature
可以通过|
(选择)而不是?
(可选)到达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+;
(为了简洁省略了一些必要的令牌定义)
加 1
再想一想,通过上述解决方案,我没有涵盖 linesGroup1_Deines
是强制性的而 linsGroup2_Defines
是可选的语义。 实际上现在两者都是可选的。它可以接受只有可选行的输入,如下所示:
[Defines]
UEFI_SPECIFICATION_VERSION = 0x00010005
我不确定这个语义 can/should 是否包含在语法中。也许我需要进一步完善它。