Python 3.x ANTLR4:获取上下文的结束位置
我试图在ANTLR4中获取上下文的开始和结束位置(行和列)。我正在使用Python3语法。我写了一个监听器,它打印开始和结束行:Python 3.x ANTLR4:获取上下文的结束位置,python-3.x,antlr,antlr4,Python 3.x,Antlr,Antlr4,我试图在ANTLR4中获取上下文的开始和结束位置(行和列)。我正在使用Python3语法。我写了一个监听器,它打印开始和结束行: class MyListener extends Python3BaseListener { @Override public void enterFuncdef(@NotNull Python3Parser.FuncdefContext ctx) { Token start = ctx.getStart(); Syst
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
怎么了?与
funcdef
规则匹配的最后一个标记(计算它调用的后代规则)是DEDENT
标记。DEDENT
标记不是由lexer规则生成的,而是由重写的nextToken()
方法中的操作生成的。创建DEDENT
标记的代码不分配任何位置信息,可能是因为它们实际上不是输入的一部分。有两种方法可以解决这个问题
DEDENT
标记前面的最后一个真输入标记的最后一个字符之后getStop()
方法实现,该方法忽略所有DEDENT
标记,以确保只返回具有正确位置信息的标记以下是实现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();
}
}
}
}
;
...
这些变化是:
- lexer中有一个
,它跟踪在lastToken:Token
方法中设置的最近发出的令牌李>nextToken()
- 创建dedent令牌新实例的两个地方现在使用
创建dedent令牌,其中使用与createDedent()
相同的行号创建dedent令牌lastToken
谢谢您的意见。谢谢。我想实现1,但我在lexer的什么位置或如何获得该职位?非常感谢。我没想到,事情会那么棘手。我在createDedent中添加了列信息:
dedent.setCharPositionInLine(this.lastToken.getCharPositionInLine()+this.lastToken.getText().length()+1)代码>你认为这在任何情况下都有效吗?我在添加列信息的方法中发现了两个错误(在上面的评论中)。首先,省略+1
,因为getCharPositionInLine是基于零的。其次,发出的标记包含源代码中不存在的文本。因此,我根据他的开始和停止索引计算最后一个令牌的长度。为了能够将常规和自定义发出的令牌分开,我将发出令牌的开始和停止索引设置为-1
(在令牌接口中)。