gpt4 book ai didi

java - 如何在 ANTLR3 tree-parser @init 操作中获取行号

转载 作者:塔克拉玛干 更新时间:2023-11-01 22:32:34 25 4
gpt4 key购买 nike

在 ANTLR 版本 3 中,如何在高级树解析器规则的 @init 操作中获取行号?

例如,在下面的@init 操作中,我想将行号与句子文本一起推送。

sentence
@init { myNodeVisitor.pushScriptContext( new MyScriptContext( $sentence.text )); }
: assignCommand
| actionCommand;
finally {
m_nodeVisitor.popScriptContext();
}

我需要在执行与规则中符号关联的操作之前推送上下文。

一些不起作用的东西:

  • 使用 $sentence.line -- 它没有定义,尽管 $sentence.text 是。
  • 将解释推送移动到规则操作中。放置在规则之前,规则中没有 token 可用。放置在规则之后,操作发生在与规则符号关联的操作之后。
  • 在编译但返回值 0 的 @init 操作中使用此表达式:getTreeNodeStream().getTreeAdaptor().getToken( $sentence.start ).getLine()编辑: 实际上,如果 $sentence.start 是一个真实的标记或带有引用的虚构标记,这确实有效——请参阅下面的 Bart Kiers 回答。

看来如果我可以在@init 规则中轻松获取匹配的文本和第一个匹配的标记,那么应该也有一种简单的方法来获取行号。

最佳答案

您可以使用以下代码在树语法的标记/树流中向前看 1 步:CommonTree ahead = (CommonTree)input.LT(1),您可以将其放在@init 部分。

每个 CommonTree(ANTLR 中默认的 Tree 实现)都有一个返回 TokengetToken() 方法> 与这棵树相关联。并且每个 Token 都有一个 getLine() 方法,毫不奇怪,该方法返回此标记的行号。

因此,如果您执行以下操作:

sentence
@init {
CommonTree ahead = (CommonTree)input.LT(1);
int line = ahead.getToken().getLine();
System.out.println("line=" + line);
}
: assignCommand
| actionCommand
;

您应该能够看到打印出一些正确的行号。我说一些,因为这不会在所有 情况下按计划进行。让我用一个简单的示例语法来演示:

grammar ASTDemo;

options {
output=AST;
}

tokens {
ROOT;
ACTION;
}

parse
: sentence+ EOF -> ^(ROOT sentence+)
;

sentence
: assignCommand
| actionCommand
;

assignCommand
: ID ASSIGN NUMBER -> ^(ASSIGN ID NUMBER)
;

actionCommand
: action ID -> ^(ACTION action ID)
;

action
: START
| STOP
;

ASSIGN : '=';
START : 'start';
STOP : 'stop';
ID : ('a'..'z' | 'A'..'Z')+;
NUMBER : '0'..'9'+;
SPACE : (' ' | '\t' | '\r' | '\n')+ {skip();};

其树语法如下:

tree grammar ASTDemoWalker;

options {
output=AST;
tokenVocab=ASTDemo;
ASTLabelType=CommonTree;
}


walk
: ^(ROOT sentence+)
;

sentence
@init {
CommonTree ahead = (CommonTree)input.LT(1);
int line = ahead.getToken().getLine();
System.out.println("line=" + line);
}
: assignCommand
| actionCommand
;

assignCommand
: ^(ASSIGN ID NUMBER)
;

actionCommand
: ^(ACTION action ID)
;

action
: START
| STOP
;

如果你运行下面的测试类:

import org.antlr.runtime.*;
import org.antlr.runtime.tree.*;

public class Main {
public static void main(String[] args) throws Exception {
String src = "\n\n\nABC = 123\n\nstart ABC";
ASTDemoLexer lexer = new ASTDemoLexer(new ANTLRStringStream(src));
ASTDemoParser parser = new ASTDemoParser(new CommonTokenStream(lexer));
CommonTree root = (CommonTree)parser.parse().getTree();
ASTDemoWalker walker = new ASTDemoWalker(new CommonTreeNodeStream(root));
walker.walk();
}
}

您将看到以下内容正在打印:

line=4
line=0

如您所见,"ABC = 123" 产生了预期的输出(第 4 行),但 "start ABC" 没有(第 0 行)。这是因为 action 规则的根是一个 ACTION 标记,这个标记从未在词法分析器中定义,只在 tokens{...}/ block 。因为它并不真正存在于输入中,默认情况下第 0 行附加到它。如果你想改变行号,你需要提供一个“引用”标记作为这个所谓的imaginary ACTION 标记的参数,它用来将属性复制到自身.

因此,如果将组合语法中的 actionCommand 规则更改为:

actionCommand
: ref=action ID -> ^(ACTION[$ref.start] action ID)
;

行号如预期(第 6 行)。

请注意,每个解析器规则都有一个 startend 属性,它们分别是对第一个和最后一个标记的引用。如果 action 是一个词法分析器规则(例如 FOO),那么您可以从中省略 .start:

actionCommand
: ref=FOO ID -> ^(ACTION[$ref] action ID)
;

现在 ACTION token 已经复制了 $ref 指向的所有属性,除了 token 的类型,当然是 int ACTION。但这也意味着它复制了 text 属性,所以在我的示例中,AST 由 ref=action ID -> ^(ACTION[$ref.start] action ID) 可能看起来像:

            [text=START,type=ACTION]
/ \
/ \
/ \
[text=START,type=START] [text=ABC,type=ID]

当然,它是一个合适的 AST,因为节点的类型是唯一的,但它使调试变得困惑,因为 ACTIONSTART 共享相同的 .text 属性。

您可以通过提供第二个字符串参数将除 .text.type 之外的所有属性复制到虚构 标记,如下所示:

actionCommand
: ref=action ID -> ^(ACTION[$ref.start, "Action"] action ID)
;

如果您现在再次运行相同的测试类,您将看到以下打印内容:

line=4
line=6

如果您检查生成的树,它将如下所示:

[type=ROOT, text='ROOT']
[type=ASSIGN, text='=']
[type=ID, text='ABC']
[type=NUMBER, text='123']
[type=ACTION, text='Action']
[type=START, text='start']
[type=ID, text='ABC']

关于java - 如何在 ANTLR3 tree-parser @init 操作中获取行号,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8344264/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com