antlr4例子步骤

antlr4 getting-started

1. 下载jar

#基于java8
wget  https://www.antlr.org/download/antlr-4.5-complete.jar

2. 下载词法定义、语法定义

git clone https://github.com/antlr/grammars-v4.git
#grammars-v4/java/java8/Java8Parser.g4  
#grammars-v4/java/java8/Java8Lexer.g4  

3. 生成解析器


java -cp antlr-4.5-complete.jar org.antlr.v4.Tool Java8Parser.g4
java -cp antlr-4.5-complete.jar org.antlr.v4.Tool Java8Lexer.g4
#生成了: Java8Lexer.java Java8Lexer.tokens Java8Parser.java Java8Parser.tokens  Java8ParserListener.java Java8ParserBaseListener.java

#或者
alias antlr4='java -Xmx500M -cp "./antlr-4.10.1-complete.jar:$CLASSPATH" org.antlr.v4.Tool'
alias grun='java -Xmx500M -cp "./antlr-4.10.1-complete.jar:$CLASSPATH" org.antlr.v4.gui.TestRig'

antlr4  Java8Parser.g4
antlr4 Java8Lexer.g4

4. 使用演示

原材料

import java.util.List;
import java.util.Map;
public class Demo {
   public static final long serialVersionUID = 1L;
   private static final void buszFunc1(int x, String y) {
      int[] arr={1,-8,90,-23};
      int swap;
      for(int i =0; i <arr.length; i++){
         return;
         for(int k =i; k < arr.length; k++){
            arr[i] = swap;
            swap=arr[k];
            arr[k]=swap;
         }
      }
      return;
   }
   protected int[ ] utilFunc() {
      int userCount=0;
      while(false){
         userCount++;
         userCount--;
      }
      return null;
   }
   List<Map<String, Integer>>[] debugFunc() {
      return null;
   }
}

antlr4操纵原材料


import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import org.antlr.v4.runtime.tree.TerminalNodeImpl;
import org.antlr.v4.runtime.tree.xpath.XPath;

import java.io.File;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

/**
 * from : https://www.freesion.com/article/6326863956/
 * */
public class InsertSerialIDTest {


    public static void main(String[] args) throws Exception {

        //1.读入java源码文件
        File file = new File("src/main/resources/Demo.java");
        System.out.println(file.exists());
        CharStream input=CharStreams.fromPath(file.toPath());

        //2.词法分析
        Java8Lexer lexer = new Java8Lexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);

        //3.语法分析
        Java8Parser parser = new Java8Parser(tokens);
        ParseTree tree = parser.compilationUnit(); // parse

        //4.将语法树喂给自定义监听器
        ParseTreeWalker walker = new ParseTreeWalker(); // create standard walker
        InsertSerialIDListener extractor = new InsertSerialIDListener(tokens);
        walker.walk(extractor, tree); // initiate walk of tree with listener

        //5.打印修改后的java源码
        String javaSourceModified=extractor.rewriter.getText();
        System.out.println(javaSourceModified);

        //各种尝试:单纯的在token列表中找到第一个void, 这貌似没什么用, 因为拿不到该void的parent
        Token voidFirst = tokens.getTokens().stream().filter(ti -> Java8Lexer.VOID == ti.getType()).findFirst().orElseGet(null);

        //各种尝试:xpath拿语法树中的所有方法声明
        //https://www.antlr.org/api/Java/org/antlr/v4/runtime/tree/xpath/XPath.html
        //https://github.com/antlr/antlr4/blob/master/doc/tree-matching.md
        XPath xpath = new XPath(parser, "//methodDeclaration");
        Collection<ParseTree> methodDeclarationList_ = xpath.evaluate(tree);
        /**methodDeclarationList_[0]:{@link Java8Parser.MethodDeclarationContext}*/
        Collection<ParseTree> methodDeclarationList = new XPath(parser, "//methodDeclaration").evaluate(tree);
        /**methodDeclarationList[0]:{@link Java8Parser.MethodDeclarationContext}*/
        Collection<ParseTree> anyNode = new XPath(parser, "/*").evaluate(tree);
        /**anyNode[0]:{@link Java8Parser.CompilationUnitContext}*/

        Java8Parser.ClassBodyContext classBodyContext= (Java8Parser.ClassBodyContext) find_element_by_xpath(parser,tree,"/compilationUnit/typeDeclaration/classDeclaration/normalClassDeclaration/classBody");
        find_elements_by_xpath(parser,classBodyContext,"//classBodyDeclaration");
        Collection<ParseTree> methodDeclarationContext = find_elements_by_xpath(parser, classBodyContext, "//classBodyDeclaration/classMemberDeclaration/methodDeclaration");

        //各种尝试:试着拿到Demo.java的方法buszFunc1
        //https://stackoverflow.com/questions/23092081/antlr4-visitor-pattern-on-simple-arithmetic-example
        //https://wizardforcel.gitbooks.io/antlr4-short-course/content/calculator-visitor.html
        VisitInt visitor = new VisitInt();
        visitor.visit(tree);

    }
    public static final ParseTree find_element_by_xpath(Java8Parser parser, ParseTree tree, String xpath){
        Collection<ParseTree> elements = find_elements_by_xpath(parser, tree, xpath);
        if(elements==null||elements.size()==0){
            return null;
        }
        if(elements.size()>1){
            String msg=String.format("warning:find_element_by_xpath:elements.size:%s >1",elements.size());
            System.out.println(msg);
        }
        return (ParseTree) elements.toArray()[0];
    }
    public static final Collection<ParseTree> find_elements_by_xpath(Java8Parser parser, ParseTree tree, String xpath){
        Collection<ParseTree> xpathEvalResult = new XPath(parser, xpath).evaluate(tree);
        return xpathEvalResult;
    }
    public static class VisitInt extends Java8ParserBaseVisitor<Integer>{
        private static  final  <ParserRuleContextSubType extends ParserRuleContext> ParserRuleContextSubType findFirst(List<ParseTree> children, final Class<ParserRuleContextSubType>  clz){
            /**
             * 原写法:
             Java8Parser.MethodBodyContext methodBody = (Java8Parser.MethodBodyContext) ctx.children.stream().filter(ci -> ci instanceof Java8Parser.MethodBodyContext).findFirst().orElseGet(null);
             此写法可读性较高, 但 由于 java instanceof不能搭配泛型 只能把instanceof改成比较Class对象是否为同一个了, 这样改可读性查 但由于java的泛型擦除 导致对java只能如此改了.
             */

            return (ParserRuleContextSubType)Optional.ofNullable(children)
                    .orElse(Collections.emptyList())
                    .stream()
                    .filter(ci -> ci.getClass()==clz)
                    .findFirst()
                    .orElse(null);
        }

        @Override
        public Integer visitMethodDeclaration(Java8Parser.MethodDeclarationContext ctx) {
            /** 1. 参照文法Java8Parser.g4 从 方法声明 中 拿到 方法头、方法体
             * 参照  Java8Parser.g4 : 第425行 产生式:
             """
             methodDeclaration
             :	methodModifier* methodHeader methodBody
             ;
             """
             文法的非终结符methodDeclaration 由 入参 ctx 持有,
             文法的非终结符methodHeader 由 局部变量 methodBodyContext 持有,
             文法的非终结符methodBody 由 局部变量 methodHeaderContext 持有
             */
            Java8Parser.MethodHeaderContext methodHeaderContext = findFirst(ctx.children, Java8Parser.MethodHeaderContext.class);
            Java8Parser.MethodBodyContext methodBodyContext = findFirst(ctx.children, Java8Parser.MethodBodyContext.class);
            //methodBody就是Demo.java中的方法buszFunc1

            /** 2. 参照文法Java8Parser.g4 从 方法头 中 拿到 返回类型
             * 继续参照Java8Parser.g4中的产生式(第442行):
             """
             methodHeader
             :	result methodDeclarator throws_?
             |	typeParameters annotation* result methodDeclarator throws_?
             ;
             """
             文法的非终结符methodHeader 由 局部变量 methodBodyContext 持有,
             文法的非终结符result 由 局部变量 resultContext 持有,
             */
            Java8Parser.ResultContext resultContext = findFirst(methodHeaderContext.children, Java8Parser.ResultContext.class);
            TerminalNodeImpl resultTerminalNode = (TerminalNodeImpl) resultContext.children.get(0);
            boolean methodResultTypeIsVoid=resultTerminalNode.symbol.getType()==Java8Lexer.VOID;
            //methodResultTypeIsVoid:方法返回类型是否为void
            return 0;
        }

        //@Override public T visitMethodHeader(Java8Parser.MethodHeaderContext ctx) { return visitChildren(ctx); }
        @Override
        public Integer visitMethodHeader(Java8Parser.MethodHeaderContext ctx){
//            return visitChildren(ctx);
            return 0;
        }
    }
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hfcaoguilin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值