如何定义基于语法的 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 词只是块的开始,而 mandatoryoptional 是关系词。剩下的词只是简单的词(在这种情况下称为特征)。我想要的是使服务器 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解释有关才能正确找到块,但我至今没找到解决办法。

非常欢迎任何解决此问题的想法。提前致谢!

尝试按如下方式更改 childfeature 规则:

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