我正在尝试创建一个语法来匹配以下内容:
(有关重复此问题的简单语法,请参阅 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
是什么意思?哪个部分是closure
? define_statement
?
在我移动可能为空的替代方案后,defines
规则可以在'[Defines]' define_statement+
和('UEFI_SPECIFICATION_VERSION' EQ SpecVersion_VersionVal)?
之间切换,这意味着defines
可以仍然匹配空字符串。错误怎么可能消失?
为了使事情更清楚,我用简化的语法重新编写了这个错误:
grammar test;
rule : alternate+; // <<<<< HERE
alternate : '1'?;
如果我在+
使用*
或HERE
,ANTLR会报告错误:
'规则规则包含一个至少有一个可以替代的闭包 匹配一个空字符串'
如果我在?
使用HERE
,ANTLR会报告警告:
'规则规则至少包含一个可选块 一个可以匹配空字符串'
的替代方法
我仍然不确定原因。
alternate
中的每一个都是rule
的子节点,因此如果alternate
可以是空字符串,那么在逻辑上可能导致{{1}的无限子节点}}。所以我想这可以解释为什么ANTLR禁止我使用rule
或alternate+
执行此操作。但如果它与alternate*
匹配,则最多会有一个子节点。这只是一个性能问题。所以ANTLR只会产生警告。
答案 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
这使它完全是可选的。同样,只有你可以决定这对你的语法和预期输入是否有效。
有意义吗?我希望我能帮到你!
要解决错误,请尝试使用此语法(我为测试值制作了明确的令牌以使其运行):
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;
答案 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+;
(为简洁起见,省略了一些必要的标记定义)
再考虑一下,通过上述解决方案,我没有涵盖linesGroup1_Deines
是强制性的linsGroup2_Defines
和[Defines]
UEFI_SPECIFICATION_VERSION = 0x00010005
是可选的语义。 实际上两者都是可选的。它只接受输入,只有下面的可选行:
CLAVE = "some_string".encode('utf-8').bytes
我不确定语法中是否涵盖了这种语义可以/应该。也许我需要进一步完善它。