[英]How can I define an ANTLR4 indentation block based grammar?
我正在尝试使用 ANTLR4 定义一种语言来生成其解析器。 虽然该语言实际上有点复杂,但这是我希望解析器读取的文件的一个很小的有效示例,它触发了我试图解决的问题:
features \\ Keyword which initializes the "features" block
Server
mandatory \\ Relation word
FileSystem
OperatingSystem
optional \\ Relation word
Logging
特征词只是开始块,而必需和可选是关系词。 剩下的词只是简单的词(在这种情况下称为特征)。 我想要的是使功能块的 Server 子项,然后是 Server 的子项和最后, FileSystem 和 OperatingSystem 的子项以及可选的 Logging 子项。
以下语法是我实现这种结构的尝试:
grammar MyGrammar;
tokens {
INDENT,
DEDENT
}
@lexer::header {
from antlr_denter.DenterHelper import DenterHelper
from UVLParser import UVLParser
}
@lexer::members {
class UVLDenter(DenterHelper):
def __init__(self, lexer, nl_token, indent_token, dedent_token, ignore_eof):
super().__init__(nl_token, indent_token, dedent_token, ignore_eof)
self.lexer: UVLLexer = lexer
def pull_token(self):
return super(UVLLexer, self.lexer).nextToken()
denter = None
def nextToken(self):
if not self.denter:
self.denter = self.UVLDenter(self, self.NL, UVLParser.INDENT, UVLParser.DEDENT, True)
return self.denter.next_token()
}
// parser rules
feature_model: features?;
features: 'features' INDENT child;
child: feature_spec INDENT relation* DEDENT;
relation: relation_spec INDENT child* DEDENT;
feature_spec: WORD ('.' WORD)*;
relation_spec: RELATION_WORD;
//lexer rules
RELATION_WORD: ('alternative' | 'or' | 'optional' | 'mandatory');
WORD: [a-zA-Z][a-zA-Z0-9_]*;
WS: [ \n\r]+ -> skip;
NL: ('\r'? '\n' '\t');
我正在使用antlr-denter来管理缩进和缩进。
然后,我在词法分析器中分别定义 RELATION_WORD 和 WORD。
最后,解析器规则尝试构建我之前描述的结构。 我希望功能词后跟一个孩子。 然后,任何子项都将成为一个特征规范,然后是 INDENT 和 DEDENT 之间的任意数量的关系。 关系是一个关系规范,后跟一组类似的孩子,这个循环无限重复,也会发生同样的情况。
但是,我无法让解析器正确读取此结构。 使用前面的示例作为输入,我必须成为 Server 的孩子,但不是可选的。 将示例更改为以下示例:
features
Server
mandatory
optional
Logging
强制和可选都被解释为强制的孩子。 它必须与 INDENT 和 DEDENT 解释有关才能正确找到块,但到目前为止我一直无法找到解决方案。
任何解决此问题的想法都将受到欢迎。 提前致谢!
尝试按如下方式更改您的child
和feature
规则:
child: feature_spec (INDENT relation* DEDENT)?;
relation: relation_spec (INDENT child* DEDENT)?;
正如@Kaby76 所提到的,打印出令牌流以了解您的解析器流如何查看令牌流非常有帮助。
我以前没有使用过 antlr-denter,但从它的插入方式来看,您似乎不会仅仅通过使用grun
工具来获得令牌流。
作为替代,我尝试只组成 INDENT 和 OUTDENT 令牌(我分别使用了->
和<-
)
修改语法:
grammar MyGrammar;
// parser rules
feature_model: features?;
features: 'features' INDENT child;
child: feature_spec INDENT relation* DEDENT;
relation: relation_spec INDENT child* DEDENT;
feature_spec: WORD ('.' WORD)*;
relation_spec: RELATION_WORD;
//lexer rules
RELATION_WORD: ('alternative' | 'or' | 'optional' | 'mandatory');
WORD: [a-zA-Z][a-zA-Z0-9_]*;
WS: [ \n\r]+ -> skip;
// Temporary
//NL: ('\r'? '\n' '\t');
NL: ('\r'? '\n' '\t') -> skip;
INDENT: '->';
DEDENT: '<-';
并修改为输入文件以使用显式标记:
features
->Server
->mandatory
optional
->Logging
只需进行此更改,您就会注意到示例中没有<-
标记。
但是,现在我可以转储令牌流:
➜ grun MyGrammar tokens -tokens < MGIn.txt
[@0,0:7='features',<'features'>,1:0]
[@1,12:13='->',<'->'>,2:3]
[@2,14:19='Server',<WORD>,2:5]
[@3,28:29='->',<'->'>,3:7]
[@4,30:38='mandatory',<RELATION_WORD>,3:9]
[@5,47:48='->',<'->'>,4:7]
[@6,49:56='optional',<RELATION_WORD>,4:9]
[@7,69:70='->',<'->'>,5:11]
[@8,71:77='Logging',<WORD>,5:13]
[@9,78:77='<EOF>',<EOF>,5:20]
现在让我们尝试解析:
➜ grun MyGrammar feature_model -tree < MGIn.txt
line 4:9 mismatched input 'optional' expecting {WORD, '<-'}
line 5:20 mismatched input '<EOF>' expecting {'.', '->'}
(feature_model (features features -> (child (feature_spec Server) -> (relation (relation_spec mandatory) ->) (relation (relation_spec optional) -> (child (feature_spec Logging))) <missing '<-'>)))
因此,您的语法要求'mandatory'
(作为RELATION_WORD
)后跟INDENT
和DEDENT
(不存在)。 这是有道理的,因为他们没有任何孩子,所以,似乎INDENT
/ DEDENT
需要连接到是否有任何孩子:
让我们改变一下:
child: feature_spec (INDENT relation* DEDENT)?;
relation: relation_spec (INDENT child* DEDENT)?;
再试一次:
➜ grun MyGrammar feature_model -tree < MGIn.txt
➜ grun MyGrammar feature_model -tree < MGIn.txt
line 5:20 extraneous input '<EOF>' expecting {WORD, '<-'}
(feature_model (features features -> (child (feature_spec Server) -> (relation (relation_spec mandatory)) (relation (relation_spec optional) -> (child (feature_spec Logging))) <missing '<-'>)))
现在我们在 EOF 处缺少一个<-
( OUTDENT
)。 对此的解决方案取决于 antlr-denter 是否关闭了<EOF>
处的所有INDENT
假设是这样,我的假输入应该是这样的:
features
->Server
->mandatory
optional
->Logging
<-
<-
<-
并且,我们再试一次:
grun MyGrammar feature_model -gui < MGIn.txt
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.