我的 ANTLR4 语法存在问题,无法正确解析字符串。我对学习如何解决我的问题比解决我的具体问题更感兴趣。如何生成任何类型的调试信息?我想知道解析器在解析字符串时正在“思考”什么。
语法可以在这里找到:https://github.com/Metrink/metrink-fe/blob/master/metrink.g4
我正在使用简单的测试字符串:
-1d metric('blah', 'blah', 'blah')
我收到以下错误:
1:2 missing TIME_INDICATOR at 'd'
语法将
TIME_INDICATOR
定义为 [shmd]
,所以我不确定当字符 TIME_INDICATOR
是可能的标记之一时,它是如何缺少 d
的。我在这里缺少什么?
我正在使用从 ANTLR4 生成的 Python3。
我通常做的是首先转储标记以查看是否创建了解析器期望的实际标记。
您可以使用像这样的小型测试类来做到这一点(可以轻松移植到Python):
public class Main {
static void test(String input) {
metrinkLexer lexer = new metrinkLexer(new ANTLRInputStream(input));
CommonTokenStream tokenStream = new CommonTokenStream(lexer);
tokenStream.fill();
System.out.printf("input: `%s`\n", input);
for (Token token : tokenStream.getTokens()) {
if (token.getType() != TLexer.EOF) {
System.out.printf(" %-20s %s\n", metrinkLexer.VOCABULARY.getSymbolicName(token.getType()), token.getText());
}
}
System.out.println();
}
public static void main(String[] args) throws Exception {
test("-1d metric('blah', 'blah', 'blah')");
}
}
如果运行上面的代码,以下内容将打印到您的控制台:
input: `-1d metric('blah', 'blah', 'blah')`
MINUS -
INTEGER_LITERAL 1
IDENTIFIER d
METRIC metric
LPAREN (
STRING_LITERAL 'blah'
COMMA ,
STRING_LITERAL 'blah'
COMMA ,
STRING_LITERAL 'blah'
RPAREN )
如您所见,
d
被标记为 IDENTIFIER
而不是 TIME_INDICATOR
。这是因为 IDENTIFIER
规则是在 TIME_INDICATOR
规则之前定义的。词法分析器不会“监听”解析器可能需要的内容,它只是匹配尽可能多的字符,如果两个或多个规则匹配相同数量的字符,则第一个定义的规则“获胜”。
因此,
d
可以标记为TIME_INDICATOR
或IDENTIFIER
。如果这取决于上下文,我建议您将其标记为 IDENTIFIER
(并删除 TIME_INDICATOR
)并创建如下解析器规则:
relative_time_literal:
MINUS? INTEGER_LITERAL time_indicator;
time_indicator:
{_input.LT(1)getText().matches("[shmd]")}? IDENTIFIER;
{ ... }?
称为谓词:ANTLR4中的语义谓词?
此外,
FALSE
和 TRUE
需要放置在 IDENTIFIER
规则之前。
Petr Pivonka 写道:
注意! “轻松移植到Python”这个注释需要解释一下! [...]
在 Python 中可能如下所示:
import antlr4
from metrinkLexer import metrinkLexer
def test(source):
lexer = metrinkLexer(antlr4.InputStream(source))
token_stream = antlr4.CommonTokenStream(lexer)
token_stream.fill()
print(f"input: {source}")
for token in token_stream.tokens:
rule = '<EOF>' if token.type == -1 else lexer.symbolicNames[token.type]
print(f" {rule.ljust(20)}{token.text}")
if __name__ == '__main__':
test("-1d metric('blah', 'blah', 'blah')")
将打印:
input: -1d metric('blah', 'blah', 'blah')
MINUS -
INTEGER_LITERAL 1
IDENTIFIER d
METRIC metric
LPAREN (
STRING_LITERAL 'blah'
COMMA ,
STRING_LITERAL 'blah'
COMMA ,
STRING_LITERAL 'blah'
RPAREN )
<EOF> <EOF>
换句话说:
metrinkLexer.VOCABULARY.getSymbolicName(type)
变成 metrinkLexer.symbolicNames[type]
token.getType()
变成 token.type
token.getText()
变成 token.text