在ANTLR版本3中,如何在高级树解析器规则的@init操作中获取行号?
例如,在下面的@init操作中,我想将行号与句子文本一起推送.
sentence
@init { myNodeVisitor.pushScriptContext( new MyScriptContext( $sentence.text )); }
: assignCommand
| actionCommand;
finally {
m_nodeVisitor.popScriptContext();
}
Run Code Online (Sandbox Code Playgroud)
我需要在执行与规则中的符号相关联的操作之前推送上下文.
有些事情,不工作:
$sentence.line- 它没有定义,即使$sentence.text是.getTreeNodeStream().getTreeAdaptor().getToken( $sentence.start ).getLine(). 编辑: 实际上,这确实有效,如果$ sentence.start要么是真实的令牌,要么带有参考的虚构 - 请参阅下面的Bart Kiers答案.似乎我可以很容易地在@init规则中获得匹配的文本和第一个匹配的标记,因此应该有一种简单的方法来获取行号.
您可以使用以下内容在树语法的令牌/树流中向前看1步:CommonTree ahead = (CommonTree)input.LT(1)您可以将其放在该@init部分中.
Every CommonTree(TreeANTLR中的默认实现)有一个getToken()返回Token与此树关联的方法.每个方法Token都有一个getLine()方法,毫不奇怪,它返回此标记的行号.
因此,如果您执行以下操作:
sentence
@init {
CommonTree ahead = (CommonTree)input.LT(1);
int line = ahead.getToken().getLine();
System.out.println("line=" + line);
}
: assignCommand
| actionCommand
;
Run Code Online (Sandbox Code Playgroud)
你应该能够看到一些被打印正确的行号.我说一些,因为在所有情况下这都不会按计划进行.让我演示使用一个简单的示例语法:
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();};
Run Code Online (Sandbox Code Playgroud)
其树语法如下:
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
;
Run Code Online (Sandbox Code Playgroud)
如果您运行以下测试类:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
你会看到以下内容被打印出来:
line=4
line=0
Run Code Online (Sandbox Code Playgroud)
如您所见,"ABC = 123"产生了预期的输出(第4行),但"start ABC"没有产生(第0行).这是因为action规则的根是一个ACTION标记,并且该标记永远不会在词法分析器中定义,只能在tokens{...}块中定义.并且因为输入中并不存在,所以默认情况下会将0行附加到输入中.如果要更改行号,则需要提供"引用"标记作为此所谓的虚构 ACTION标记的参数,该标记用于将属性复制到自身中.
因此,如果您actionCommand将组合语法中的规则更改为:
actionCommand
: ref=action ID -> ^(ACTION[$ref.start] action ID)
;
Run Code Online (Sandbox Code Playgroud)
行号将如预期的那样(第6行).
请注意,每个解析器规则都有一个start和end属性,它分别是对第一个和最后一个令牌的引用.如果action是词法分析器规则(比如说FOO),那么你可以省略.start它:
actionCommand
: ref=FOO ID -> ^(ACTION[$ref] action ID)
;
Run Code Online (Sandbox Code Playgroud)
现在,ACTION令牌已经复制了$ref指向的任何属性,除了令牌的类型,当然int ACTION.但这也意味着它复制了text属性,因此在我的例子中,创建的AST ref=action ID -> ^(ACTION[$ref.start] action ID)可能如下所示:
[text=START,type=ACTION]
/ \
/ \
/ \
[text=START,type=START] [text=ABC,type=ID]
Run Code Online (Sandbox Code Playgroud)
当然,这是一个正确的AST,因为类型的节点是唯一的,但它使调试混乱,因为ACTION和START共享相同的.text属性.
您可以将所有属性复制到虚构的令牌,除了.text和.type提供第二个字符串参数,如下所示:
actionCommand
: ref=action ID -> ^(ACTION[$ref.start, "Action"] action ID)
;
Run Code Online (Sandbox Code Playgroud)
如果您现在再次运行相同的测试类,您将看到以下内容:
line=4
line=6
Run Code Online (Sandbox Code Playgroud)
如果你检查生成的树,它将如下所示:
[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']
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1945 次 |
| 最近记录: |