我试图获取ANTLR4中上下文的开始和结束位置(行和列)。我正在使用this Python3语法。我写了一个监听器,打印开始和结束行:
class MyListener extends Python3BaseListener {
@Override
public void enterFuncdef(@NotNull Python3Parser.FuncdefContext ctx) {
Token start = ctx.getStart();
System.out.print("start: ");
System.out.print(start.getText());
System.out.print(": ");
System.out.println(start.getLine());
Token stop = ctx.getStop();
System.out.print("stop: ");
System.out.print(stop.getText());
System.out.print(": ");
System.out.println(stop.getLine());
}
}
我的测试输入:
def factorial(n):
if n == 1:
return 1
else:
return n * factorial(n-1)
def iterative_factorial(n):
result = 1
for i in range(2,n+1):
result *= i
return result
我的听众打印
start: def: 1
stop: DEDENT: 0
start: def: 7
stop: DEDENT: 0
但我希望
start: def: 1
stop: DEDENT: 5
start: def: 7
stop: DEDENT: 11
出了什么问题?
答案 0 :(得分:1)
funcdef
规则匹配的最后一个令牌(计算它调用的后代规则)是DEDENT
令牌。 DEDENT
标记不是由词法分析器规则生成的,而是由重写的nextToken()
方法中的操作生成的。创建DEDENT
令牌的代码不会分配任何位置信息,大概是因为它们实际上不是输入的一部分。有两种方法可以解决这个问题。
创建时,将位置信息分配给DEDENT
标记。此信息可能是跟随DEDENT
令牌之前的最后一个真实输入标记的最后一个字符的零宽度标记。
编写自己的getStop()
方法实现,忽略所有DEDENT
标记,以确保只返回包含正确位置信息的标记。
答案 1 :(得分:1)
这是完成280Z28第一个提案的方法:
grammar Python3;
...
@lexer::members {
...
// The most recently produced token.
private Token lastToken = null;
...
@Override
public Token nextToken() {
// Check if the end-of-file is ahead and there are still some DEDENTS expected.
if (_input.LA(1) == EOF && !this.indents.isEmpty()) {
// First emit an extra line break that serves as the end of the statement.
this.emit(new CommonToken(Python3Parser.NEWLINE, "\n"));
// Now emit as much DEDENT tokens as needed.
while (!indents.isEmpty()) {
this.emit(createDedent());
indents.pop();
}
}
Token next = super.nextToken();
if (next.getChannel() == Token.DEFAULT_CHANNEL) {
// Keep track of the last token on the default channel.
this.lastToken = next;
}
return tokens.isEmpty() ? next : tokens.poll();
}
private Token createDedent() {
CommonToken dedent = new CommonToken(Python3Parser.DEDENT, "DEDENT");
dedent.setLine(this.lastToken.getLine());
return dedent;
}
...
}
...
NEWLINE
: ( '\r'? '\n' | '\r' ) SPACES?
{
...
if (opened > 0 || next == '\r' || next == '\n' || next == '#') {
...
}
else {
...
if (indent == previous) {
...
}
else if (indent > previous) {
...
}
else {
// Possibly emit more than 1 DEDENT token.
while(!indents.isEmpty() && indents.peek() > indent) {
this.emit(createDedent());
indents.pop();
}
}
}
}
;
...
变化是:
lastToken: Token
,用于跟踪在nextToken()
方法中设置的最新发出的令牌; createDedent()
,其中使用与lastToken
相同的行号创建dedent令牌。查看此提交中的确切更改:https://github.com/bkiers/python3-parser/commit/1a1118f8f540843ebc2d6bb2a76c56f894869b93
完整的语法可以在这里找到:https://github.com/bkiers/python3-parser/blob/master/src/main/antlr4/nl/bigo/pythonparser/Python3.g4
打印:
start: def: 1 stop: DEDENT: 5 start: def: 7 stop: DEDENT: 11
输入。