java-在ANTLR中,如何使用生成的Listener代码
测试基本环境
- windows 10
- java 19
- antlr 4.13.1
使用场景
实现ParseTreeListener 的接口实现自己的业务逻辑,在通过Listener遍历,从而构建自己的程序。
Listener概要
- 生成java代码后,会有两个listener的类,HelloListener是接口,HelloBaseListener是实现。
D:\源码\kafka-2.1\antlr\gen\HelloBaseListener.java
D:\源码\kafka-2.1\antlr\gen\HelloListener.java
- HelloBaseListener和HelloListener是根据定义的语法生成的,其中HelloListener继承ParseTreeListener。
public class HelloBaseListener implements HelloListener {
public interface HelloListener extends ParseTreeListener {
- 要触发Listener的事件,需要使用ParseTreeWalker对树进行遍历。
十进制转十六进制样例
这个样例主要是将字符串中的数字转换为16进制,然后按格式原样输出。如何生成ANTLR的java代码过程忽略了,可以参考之前的文章
Hello.g4文件
这是对字符串5{42,43,{32,3,5},44}ddd中的花括号里面的进行转换,
grammar Hello;
line:'{' value(','value)* '}';
value:INT
| line
;
INT:[0-9]+;
其实写成这样也没有问题?
grammar Hello;
line:'{' value(','value)* '}';
value:INT| line;
INT:[0-9]+;
main文件
这里需要继承HelloBaseListener,继承后你可以只需要重写enterLine、exitLine、enterValue就可以了,其他的方法根据情况来。代码如下:
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.ErrorNode;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import org.antlr.v4.runtime.tree.TerminalNode;
public class main {
public static void main(String[] args) {
//需要分析的语句
String inputStr="5{42,43,{32,3,5},44}ddd";
//将字符串转换为ANTLR的CharStream
CharStream input = CharStreams.fromString(inputStr);
//使用词法分析器分析转换后的输入
Lexer lexer = new HelloLexer(input);
//新建一个词法符号的缓冲区,存储生成的词法符号
CommonTokenStream commonTokenStream = new CommonTokenStream(lexer);
//使用语法分析器处理缓冲区的内容
HelloParser helloParser = new HelloParser(commonTokenStream);
//对第一个line规则进行语法分析
ParseTree parseTree = helloParser.line();
//获取树的子数目
int childCount = parseTree.getChildCount();
//打印LISP风格的树
System.out.println(parseTree.toStringTree());
//循环打印出子节点
for (int i = 0; i < childCount; i++) {
System.out.println("child "+i+":"+parseTree.getChild(i).toStringTree());
}
catToDog cc=new catToDog();
ParseTreeWalker walker=new ParseTreeWalker();
walker.walk(cc,parseTree);
}
public static class catToDog extends HelloBaseListener {
StringBuilder s=new StringBuilder();
@Override
public void enterLine(HelloParser.LineContext ctx) {
s.append("{");
}
@Override
public void exitLine(HelloParser.LineContext ctx) {
s.append("},");
System.out.println(s.toString().replaceAll(",}","}").substring(0,s.toString().replaceAll(",}","}").length()-1));
}
@Override
public void enterValue(HelloParser.ValueContext ctx) {
if(null!=ctx.INT()){
System.out.println("enterValue:"+ctx.INT().getText());
s.append(Integer.toHexString(Integer.parseInt(ctx.INT().getText()))+",");
}
}
}
}
打印结果
line 1:0 extraneous input '5' expecting '{'
line 1:20 token recognition error at: 'd'
line 1:21 token recognition error at: 'd'
line 1:22 token recognition error at: 'd'
([] 5 { ([5] 42) , ([7] 43) , ([7] ([16 7] { ([5 16 7] 32) , ([7 16 7] 3) , ([7 16 7] 5) })) , ([7] 44) })
child 0:5
child 1:{
child 2:([5] 42)
child 3:,
child 4:([7] 43)
child 5:,
child 6:([7] ([16 7] { ([5 16 7] 32) , ([7 16 7] 3) , ([7 16 7] 5) }))
child 7:,
child 8:([7] 44)
child 9:}
enterValue:42
enterValue:43
enterValue:32
enterValue:3
enterValue:5
{2a,2b,{20,3,5}
enterValue:44
{2a,2b,{20,3,5},2c}
可以看到,没有在花括号里面的d字母有提示没有识别到,最后成功的将字符串5{42,43,{32,3,5},44}ddd中花括号的十进制原格式转换为了十六进制{2a,2b,{20,3,5},2c}。