Java 如何在ANTLR3树解析器@init action中获取行号
在ANTLR版本3中,如何在高级树解析器规则的@init操作中获得行号 例如,在下面的@init操作中,我想将行号与句子文本一起推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
sentence
@init { myNodeVisitor.pushScriptContext( new MyScriptContext( $sentence.text )); }
: assignCommand
| actionCommand;
finally {
m_nodeVisitor.popScriptContext();
}
我需要在执行与规则中的符号相关联的操作之前推送上下文
一些不起作用的事情:
- 使用
——它没有定义,即使$句子.line
没有定义$句子.text
- 将释义推进规则操作。放置在规则之前,规则中没有可用的令牌。放置在规则之后,操作发生在与规则符号关联的操作之后李>
- 在@init操作中使用此表达式,该操作可编译但返回值0:
EDIT:事实上,如果$SENTURE.start是一个带引用的真实标记或虚构标记,那么这确实有效——请参见下面的巴特·基尔斯答案getTreeNodeStream().gettreeadapter().getToken($句子.start).getLine()
在@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
如果你检查一下