如何定义基于语法的 ANTLR4 缩进块?
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
features 词只是块的开始,而 mandatory 和 optional 是关系词。剩下的词只是简单的词(在这种情况下称为特征)。我想要的是使服务器 child 的功能块,然后是服务器的强制性和可选性 children,最后是强制性的文件系统和操作系统 children,以及日志记录 child 的可选。
下面的语法是我尝试实现这个结构的:
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。
最后,解析器规则尝试构建我之前描述的结构。我希望特征词后跟一个 child。然后,任何 child 都将成为一个特征规范,后跟任何数量的 INDENT 和 DEDENT 之间的关系。关系是一个关系规范,后面跟着一组类似的 children,这个循环无限期地重复。
但是,我无法使解析器正确读取此结构。使用前面的示例作为输入,我将强制作为服务器的 child,但不是可选的。将示例更改为这个示例:
features
Server
mandatory
optional
Logging
强制和可选都解释为child任强制。肯定和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
(不存在).这是有道理的,因为他们没有任何 children,所以,似乎 INDENT
/DEDENT
需要连接到是否有任何 children:
让我们改变一下:
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
我正在尝试使用 ANTLR4 定义一种语言来生成其解析器。虽然该语言实际上有点复杂,但这是我希望解析器读取的文件的一个小有效示例,它触发了我试图修复的问题:
features \ Keyword which initializes the "features" block
Server
mandatory \ Relation word
FileSystem
OperatingSystem
optional \ Relation word
Logging
features 词只是块的开始,而 mandatory 和 optional 是关系词。剩下的词只是简单的词(在这种情况下称为特征)。我想要的是使服务器 child 的功能块,然后是服务器的强制性和可选性 children,最后是强制性的文件系统和操作系统 children,以及日志记录 child 的可选。
下面的语法是我尝试实现这个结构的:
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。
最后,解析器规则尝试构建我之前描述的结构。我希望特征词后跟一个 child。然后,任何 child 都将成为一个特征规范,后跟任何数量的 INDENT 和 DEDENT 之间的关系。关系是一个关系规范,后面跟着一组类似的 children,这个循环无限期地重复。
但是,我无法使解析器正确读取此结构。使用前面的示例作为输入,我将强制作为服务器的 child,但不是可选的。将示例更改为这个示例:
features
Server
mandatory
optional
Logging
强制和可选都解释为child任强制。肯定和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
(不存在).这是有道理的,因为他们没有任何 children,所以,似乎 INDENT
/DEDENT
需要连接到是否有任何 children:
让我们改变一下:
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