Java 如何在ANTLR3树解析器@init action中获取行号

Java 如何在ANTLR3树解析器@init action中获取行号,java,antlr,antlr3,Java,Antlr,Antlr3,在ANTLR版本3中,如何在高级树解析器规则的@init操作中获得行号 例如,在下面的@init操作中,我想将行号与句子文本一起推 sentence @init { myNodeVisitor.pushScriptContext( new MyScriptContext( $sentence.text )); } : assignCommand | actionCommand; finally { m_nodeVisitor.popScriptC

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

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

sentence
    @init { myNodeVisitor.pushScriptContext( new MyScriptContext( $sentence.text )); }
    : assignCommand 
    | actionCommand;
    finally {
        m_nodeVisitor.popScriptContext();
    }
我需要在执行与规则中的符号相关联的操作之前推送上下文

一些不起作用的事情:

  • 使用
    $句子.line
    ——它没有定义,即使
    $句子.text
    没有定义
  • 将释义推进规则操作。放置在规则之前,规则中没有可用的令牌。放置在规则之后,操作发生在与规则符号关联的操作之后
  • 在@init操作中使用此表达式,该操作可编译但返回值0:
    getTreeNodeStream().gettreeadapter().getToken($句子.start).getLine()
    EDIT:事实上,如果$SENTURE.start是一个带引用的真实标记或虚构标记,那么这确实有效——请参见下面的巴特·基尔斯答案

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

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

每个
CommonTree
(ANTLR中的默认
Tree
实现)都有一个
getToken()
方法,该方法返回与此树关联的
标记。并且每个
令牌
都有一个
getLine()
方法,该方法毫不奇怪地返回该令牌的行号

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

句子
@初始化{
CommonTree ahead=(CommonTree)input.LT(1);
int line=ahead.getToken().getLine();
System.out.println(“line=“+line”);
}
:assignCommand
|动作命令
;
您应该能够看到正在打印的一些正确行号。我说了一些,因为这不会在所有情况下都按计划进行。让我用一个简单的语法示例演示:

语法演示;
选项{
输出=AST;
}
代币{
根;
行动;
}
作语法分析
:句子+EOF->^(词根句子+)
;
句子
:assignCommand
|动作命令
;
分配命令
:ID分配编号->^(分配ID编号)
;
动作命令
:操作ID->^(操作ID)
;
行动
:开始
|停止
;
分配:'=';
开始:“开始”;
停止:'停止';
ID:('a'..'z'|'a'..'z')+;
编号:'0'..'9'+;
空格:(“”|’\t’|’\r’|’\n’+{skip();};
其树语法如下所示:

树语法;
选择权{
输出=AST;
tokenVocab=ASTDemo;
ASTLabelType=CommonTree;
}
步行
:^(词根+句子)
;
句子
@初始化{
CommonTree ahead=(CommonTree)input.LT(1);
int line=ahead.getToken().getLine();
System.out.println(“line=“+line”);
}
:assignCommand
|动作命令
;
分配命令
:^(分配ID号)
;
动作命令
:^(操作ID)
;
行动
:开始
|停止
;
如果运行以下测试类:

import org.antlr.runtime.*;
导入org.antlr.runtime.tree.*;
公共班机{
公共静态void main(字符串[]args)引发异常{
字符串src=“\n\n\nABC=123\n\n开始ABC”;
ASTDemoLexer lexer=新的ASTDemoLexer(新的AntlStringStream(src));
ASTDemoParser=newastdemoparser(newcommontokenstream(lexer));
CommonTree根=(CommonTree)parser.parse().getTree();
ASTDemoWalker=新的ASTDemoWalker(新的CommonTreeNodeStream(根));
walker.walk();
}
}
您将看到正在打印以下内容:

line=4
直线=0
如您所见,
“ABC=123”
产生了预期的输出(第4行),但
“启动ABC”
没有(第0行)。这是因为
action
规则的根是
action
标记,并且该标记从未在lexer中定义,仅在
tokens{…}
块中定义。由于它实际上不存在于输入中,因此默认情况下,第0行附加到它。如果要更改行号,则需要提供一个“参考”标记作为此所谓的虚拟
操作
标记的参数,该标记用于将属性复制到自身中

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

actionCommand
:ref=操作ID->^(操作[$ref.start]操作ID)
;
行号与预期一致(第6行)

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

actionCommand
:ref=FOO ID->^(动作[$ref]动作ID)
;
现在,
ACTION
标记已经从
$ref
指向的任何对象复制了所有属性,除了标记的类型,当然是
int ACTION
。但这也意味着它复制了
text
属性,因此在我的示例中,由
ref=action ID->^(action[$ref.start]action ID)
创建的AST可能如下所示:

            [text=START,type=ACTION]
                  /         \
                 /           \
                /             \
   [text=START,type=START]  [text=ABC,type=ID]
当然,这是一个合适的AST,因为节点的类型是唯一的,但它会使调试变得混乱,因为
ACTION
START
共享相同的
.text
属性

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

actionCommand
:ref=操作ID->^(操作[$ref.start,“操作”]操作ID)
;
如果您现在再次运行相同的测试类,您将看到以下内容:

line=4
直线=6
如果你检查一下