java-在ANTLR中,如何使用生成的Visitor代码0.5.0

3 篇文章 0 订阅

java-在ANTLR中,如何使用生成的Visitor代码

背景

  1. 上一遍文章说了使用listener去遍历树,这篇使用visitor去遍历,上一篇文章链接是

https://blog.csdn.net/m0_60688978/article/details/141994891

  1. 目标是将类似字符串{42,43,{32,7+6,3+9,5,6+55},3,7+1,44},不是加的按照原来模式返回16进制,是加法的返回两个数之和。

g4文件

这个和listener有点不一样,为了更好的使用visitor,我们经常会把备选规则使用#标记起来,便于后面生成visitor代码的时候会针根据标记生成,如下

grammar Hello;
line:LEFT value(','value)* RIGHT;
value:INT        #intShow
      | line     #lineShow
      | INT'+'INT    #getSUM
      ;

INT:[0-9]+;
LEFT:'{';
RIGHT:'}';

生成对应的visitor文件

idea中生成参考链接

https://blog.csdn.net/m0_60688978/article/details/141893455

最终生成的文件中,我们关系visitor的,如下

D:\源码\kafka-2.1\antlr\gen\HelloBaseVisitor.java
D:\源码\kafka-2.1\antlr\gen\HelloVisitor.java

其中base的内容如下,和自己在g4文件中定义的标记一致,如果不定义标记,就会根据每条规则生成方法,下面就是根据备选规则标记而生成的内容,如下:

// Generated from D:/源码/kafka-2.1/antlr/Hello.g4 by ANTLR 4.13.1
import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor;

/**
 * This class provides an empty implementation of {@link HelloVisitor},
 * which can be extended to create a visitor which only needs to handle a subset
 * of the available methods.
 *
 * @param <T> The return type of the visit operation. Use {@link Void} for
 * operations with no return type.
 */
@SuppressWarnings("CheckReturnValue")
public class HelloBaseVisitor<T> extends AbstractParseTreeVisitor<T> implements HelloVisitor<T> {
	/**
	 * {@inheritDoc}
	 *
	 * <p>The default implementation returns the result of calling
	 * {@link #visitChildren} on {@code ctx}.</p>
	 */
	@Override public T visitLine(HelloParser.LineContext ctx) { return visitChildren(ctx); }
	/**
	 * {@inheritDoc}
	 *
	 * <p>The default implementation returns the result of calling
	 * {@link #visitChildren} on {@code ctx}.</p>
	 */
	@Override public T visitIntShow(HelloParser.IntShowContext ctx) { return visitChildren(ctx); }
	/**
	 * {@inheritDoc}
	 *
	 * <p>The default implementation returns the result of calling
	 * {@link #visitChildren} on {@code ctx}.</p>
	 */
	@Override public T visitLineShow(HelloParser.LineShowContext ctx) { return visitChildren(ctx); }
	/**
	 * {@inheritDoc}
	 *
	 * <p>The default implementation returns the result of calling
	 * {@link #visitChildren} on {@code ctx}.</p>
	 */
	@Override public T visitGetSUM(HelloParser.GetSUMContext ctx) { return visitChildren(ctx); }
}

main方法调用

  1. 需要使用自己的visitor类
  2. 使用自己的visitor类对语法树进行遍历
  3. override的方法,不管返回什么都可以,这里还不知道使用的场景在哪里?
  4. 测试字符串共计长度36个,从0开始,所以最后应该是35,这里不知道为什么34是可以的?
  5. 这里自己写了一个方法检测当前的节点的位置,不知道有没有其他方法,暂时没找到,自己写了一个
   public int getLastPosition(String ruleString){
      // String cc="[@25,28:29='44',<3>,1:28]";
       String lastPositionStr=ruleString.split("=")[0].split(":")[1];
       return Integer.parseInt(lastPositionStr);
   }

具体代码如下

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;

import java.util.List;

public class main {
    public static void main(String[] args) {
        //需要分析的语句
        String inputStr = "{42,43,{32,7+6,3+9,5,6+55},3,7+1,44}";
        System.out.println("input size is "+inputStr.length());
        //将字符串转换为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());

        }

        System.out.println(" ");
//        catToDog cc=new catToDog();
//        ParseTreeWalker walker=new ParseTreeWalker();
//        walker.walk(cc,parseTree);
        myVisitor mv = new myVisitor();
        mv.visit(parseTree);

    }

    public static class myVisitor extends HelloBaseVisitor<String> {
        /**
         * {@inheritDoc}
         *
         * <p>The default implementation returns the result of calling
         * {@link #visitChildren} on {@code ctx}.</p>
         */
        @Override
        public String visitLine(HelloParser.LineContext ctx) {
//            System.out.println("visitLine"+ctx.value().toString()+" text:"+ctx.getText());
            System.out.printf("{");
            return visitChildren(ctx);
        }
        /**
         * {@inheritDoc}
         *
         * <p>The default implementation returns the result of calling
         * {@link #visitChildren} on {@code ctx}.</p>
         */
        @Override
        public String visitIntShow(HelloParser.IntShowContext ctx) {
            int stopInt=ctx.getParent().getStop().getCharPositionInLine()-1;
//            System.out.println("dd1:"+getLastPosition(ctx.getStop().toString()) +" "+stopInt);
            if(getLastPosition(ctx.getStop().toString())==stopInt){

                System.out.printf(Integer.toHexString(Integer.parseInt(ctx.getText()))+"},");

                if(getLastPosition(ctx.getStop().toString())==34){
                    System.out.printf(Integer.toHexString(Integer.parseInt(ctx.getText()))+"}");
                }else{
                    System.out.printf(Integer.toHexString(Integer.parseInt(ctx.getText()))+"},");
                }
            }else{
                System.out.printf(Integer.toHexString(Integer.parseInt(ctx.getText()))+",");
            }


            return visitChildren(ctx);
        }
        /**
         * {@inheritDoc}
         *
         * <p>The default implementation returns the result of calling
         * {@link #visitChildren} on {@code ctx}.</p>
         */
        @Override
        public String visitLineShow(HelloParser.LineShowContext ctx) {
            return visitChildren(ctx);
        }


        /**
         * {@inheritDoc}
         *
         * <p>The default implementation returns the result of calling
         * {@link #visitChildren} on {@code ctx}.</p>
         */
        @Override public String visitGetSUM(HelloParser.GetSUMContext ctx) {
            int stopInt=ctx.getParent().getStop().getCharPositionInLine()-1;
//            System.out.println("dd:"+getLastPosition(ctx.INT(1).getSymbol().toString()) +" "+stopInt);
            if(getLastPosition(ctx.INT(1).getSymbol().toString())==stopInt){

                if(getLastPosition(ctx.INT(1).getSymbol().toString())==34){
                    System.out.printf((Integer.parseInt(ctx.INT(0).toString())+Integer.parseInt(ctx.INT(1).toString()))+"}");
                }else{
                    System.out.printf((Integer.parseInt(ctx.INT(0).toString())+Integer.parseInt(ctx.INT(1).toString()))+"},");
                }
            }else{
                System.out.printf((Integer.parseInt(ctx.INT(0).toString())+Integer.parseInt(ctx.INT(1).toString()))+",");
            }
            return "4444";
        }


        public int getLastPosition(String ruleString){
           // String cc="[@25,28:29='44',<3>,1:28]";
            String lastPositionStr=ruleString.split("=")[0].split(":")[1];
            return Integer.parseInt(lastPositionStr);
        }

    }
}

测试结果如下,可以看到成功的将{42,43,{32,7+6,3+9,5,6+55},3,7+1,44}转换成了{2a,2b,{20,13,12,5,61},3,8,2c},2c}

input size is 36
([] { ([5] 42) , ([7] 43) , ([7] ([16 7] { ([5 16 7] 32) , ([7 16 7] 7 + 6) , ([7 16 7] 3 + 9) , ([7 16 7] 5) , ([7 16 7] 6 + 55) })) , ([7] 3) , ([7] 7 + 1) , ([7] 44) })
child 0:{
child 1:([5] 42)
child 2:,
child 3:([7] 43)
child 4:,
child 5:([7] ([16 7] { ([5 16 7] 32) , ([7 16 7] 7 + 6) , ([7 16 7] 3 + 9) , ([7 16 7] 5) , ([7 16 7] 6 + 55) }))
child 6:,
child 7:([7] 3)
child 8:,
child 9:([7] 7 + 1)
child 10:,
child 11:([7] 44)
child 12:}
 
{2a,2b,{20,13,12,5,61},3,8,2c},2c}

总结

  1. 如何获取最后一个字符串的位置
int stopInt=ctx.getParent().getStop().getCharPositionInLine()-1

也可以使用getStart().getCharPositionInLine()获取开始的字符串位置

  1. 根据标记生产的方法什么时候会执行

就是在遍历语法树的时候,会执行对应的方法,如给规则INT’+'INT 定义了一个#getSUM标记,在定义visitor的时候,就会有一个生成的getSUM方法。在字符串中如果有多个加法的情况下,会在每个加法的时候调用getSUM

  1. listener的遍历和visiotr的遍历方法不同

listener使用的walk,而visitor使用的是myVisitor.visit(parseTree)

  1. ctx.getStop()返回的格式是[@25,28:29=‘44’,<3>,1:28]
  2. 如果要计算一个表达式的值,如数值加减乘除,可以是使用visit(ctx.expr(xxxx))
  • 15
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值