• Eclipse JDT--AST入门


    最近做program analysis,需要解析Java的源代码,于是就去看了看Abstract Syntax Tree(AST,中文为抽象语法树)。有点无奈的是,网上关于这方面的资料比我想象中的少,可能是涉及的东西太底层了吧。AST一般属于编译原理方面的内容,也经常用于程序分析等等。简单来说,你写了一坨代码,编译器会把代码转化成一棵抽象语法树用于“理解”。每个树节点代表一个代码元素,也有自己的属性什么的。关于AST的细节一两句话肯定说不清楚, 不过好消息是,Eclipse JDT中有现成的ASTParser可以将源代码解析成AST,并且有很全面的节点类型和方法供用户操作。

    今天就先把网上一些关于Eclipse JDT中AST相关的资料汇总再这里,等我自己摸索的差不多了会及时更新这方面的内容。

     

    那啥,转载请注明出处哦~~~ http://blog.csdn.net/flying881114/archive/2011/02/16/6187061.aspx

     

    ---入门文档

    1. Eclipse Corner Article: Abstract Syntax Tree

    http://www.eclipse.org/articles/article.php?file=Article-JavaCodeManipulation_AST/index.html

    (英文,AST的概述,细节代码不多)

     

    2. 探索Eclipse的ASTParser

    http://www.ibm.com/developerworks/cn/opensource/os-ast/index.html#resources

    (中文,相对详细的讲解和代码)

     

    ---AST使用

    1. Eclipse JDT--AST and JavaModel Tutorial

    http://www.vogella.de/articles/EclipseJDT/article.html

    (英文,代码很多,但需要有更多的背景知识)

     

    2. Eclipse AST 使用指南

    http://wenku.baidu.com/view/a0b8e07931b765ce050814ac.html

    (pdf文档,更加详细地说明了AST 相关类与方法,可以与eclipse documentation结合使用)

     

    3. Eclipse documentation

    http://help.eclipse.org/helios/index.jsp

    其实,最靠谱的还是直接看eclipse文档。在这里你可以了解你想知道的一切。

     

    4. Yet another AST tutorial

    http://sahits.ch/blog/?p=228

    英文,有大量代码,从易到难讲解(这篇我还没看完~)

     

    ---使用AST相关

    1. 使用AST出现问题?不知道使用什么jar包?看看这里吧:

    http://lym6520.javaeye.com/blog/747840

     

    2. 想亲眼看看你源文件的AST是什么样的?为你的Eclipse装一个ASTview插件吧!

    http://www.eclipse.org/jdt/ui/astview/index.php

    (小心!就连Hello World的小程序也有很复杂的AST哦!)


    要解析Java源码,首先要建立ASTParser的实例。此过程最重要的一点是,你要告诉parser需要解析的内容类型。ASTParser支持对以下四种内容的解析:

    1. K_COMPILATION_UNIT: 一个编译单元,一般就是完整的Java文件

    2. K_STATEMENTS: Java statements,比如赋值语句,或是if语句块,while语句块等。此类型不需要文件是完整的编译单元,但需要是完整的statements。比如if语句块要作为一个完整的statement输入,否则会报错。

    3. K_EXPRESSION: Java expressions

    4. K_CLASS_BODY_DECLARATIONS: Java class里的元素

     

    以下的例子以K_COMPILATION_UNIT为例创建ASTParser,也就是解析一个完整的Java文件。

    [java] view plaincopy
    1. // Initialize ASTParser  
    2.         ASTParser parser = ASTParser.newParser(AST.JLS3); //initialize    
    3.         parser.setKind(ASTParser.K_COMPILATION_UNIT);     //to parse compilation unit  
    4.         parser.setSource(content.toCharArray());          //content is a string which stores the java source  
    5.         parser.setResolveBindings(true);  
    6.         CompilationUnit result = (CompilationUnit) parser.createAST(null);  

     

    其中,变量content是一个字符串,储存了读入的java源文件内容。最后一行,parser.createAST方法返回了一个ASTNode类。ASTNode是语法树各节点的抽象基类,它的子类分别代表各种类型的节点比如CompilationUnit, Statement, Expression, Comment, PackageDeclaration等等。具体参见

    http://help.eclipse.org/helios/index.jsp?topic=/org.eclipse.jdt.doc.isv/reference/api/org/eclipse/jdt/core/dom/WhileStatement.html。

    这里,我们将其转换成CompilationUnit类。

     

    其实以上这四种类型是存在层次关系的:一个完整的编译单元包括imported packages, class。Class里又包括field和methods,而method又可以被分成一系列statements。也就是说,一个完整的java文件被分成几个基本部分,每个部分又可以继续往下分。

    比如说,Statement的子类包括IfStatement, WhileStatement, ExpressionStatement等等。IfStatment类提供getElseStatement, getThenStatment,还有提取if条件等各种方法。像是ExpressionStatement类,支持通过getExpression方法得到源文件对应的Expression,而Expression类则包括如Assignment, MethodInvocation,ArrayCreation种种类型。

    关于Statement的类层次结构和各类的方法可以参见:

    http://help.eclipse.org/helios/index.jsp?topic=/org.eclipse.jdt.doc.isv/reference/api/org/eclipse/jdt/core/dom/WhileStatement.html

     

    至此,利用createAST,java的源文件已经被解析成AST并且储存于Compilation的实例result中了。我会在下一篇文章介绍一些使用AST的具体操作。

    创建ASTParser并将Java源文件解析成AST:

    [java] view plaincopy
    1. // Initialize ASTParser  
    2.         ASTParser parser = ASTParser.newParser(AST.JLS3); //initialize    
    3.         parser.setKind(ASTParser.K_COMPILATION_UNIT);     //to parse compilation unit  
    4.         parser.setSource(content.toCharArray());          //content is a string which stores the java source  
    5.         parser.setResolveBindings(true);  
    6.         CompilationUnit result = (CompilationUnit) parser.createAST(null);  

     

    调用imports()方法得到源文件的一系列import声明:

    [java] view plaincopy
    1. //show import declarations in order  
    2.         List importList = result.imports();    
    3.         System.out.println("import:");  
    4.         for(Object obj : importList) {  
    5.             ImportDeclaration importDec = (ImportDeclaration)obj;  
    6.             System.out.println(importDec.getName());  
    7.         }  

     

     

    调用types()方法得到类名称:

    [java] view plaincopy
    1. //show class name  
    2.         List types = result.types();    
    3.         TypeDeclaration typeDec = (TypeDeclaration) types.get(0);   
    4.         System.out.println("className:"+typeDec.getName());  

     

    调用TypeDeclaration的getField方法得到类里定义的field:

    [java] view plaincopy
    1. //show fields  
    2.         FieldDeclaration fieldDec[]=typeDec.getFields();  
    3.         System.out.println("Fields:");  
    4.         for(FieldDeclaration field: fieldDec)  
    5.         {  
    6.             System.out.println("Field fragment:"+field.fragments());  
    7.             System.out.println("Field type:"+field.getType());  
    8.         }  

     

     

    调用TypeDeclaration的getMethods方法得到类里定义的list of methods;同理,可以调用MethodDeclaration里的各种方法得到method的方法名,参数,返回类型等等:

    [java] view plaincopy
    1. //show methods  
    2.         MethodDeclaration methodDec[] = typeDec.getMethods();    
    3.         System.out.println("Method:");    
    4.         for (MethodDeclaration method : methodDec)    
    5.         {    
    6.             //get method name  
    7.             SimpleName methodName=method.getName();  
    8.             System.out.println("method name:"+methodName);  
    9.               
    10.             //get method parameters  
    11.             List param=method.parameters();  
    12.             System.out.println("method parameters:"+param);  
    13.               
    14.             //get method return type  
    15.             Type returnType=method.getReturnType2();  
    16.             System.out.println("method return type:"+returnType);  

     

     

    一个方法的内容对应一个block,可以用getBody()得到;一个block又可以被分解成一系列statements,可以用statements()方法得到:

    [java] view plaincopy
    1. //get method body  
    2.             Block body=method.getBody();  
    3.             List statements=body.statements();   //get the statements of the method body  
    4.             Iterator iter=statements.iterator();  
    5.             while(iter.hasNext())  
    6.             {  
    7.                 //get each statement  
    8.                 Statement stmt=(Statement)iter.next();  

     

    接下来需要根据每个statement的类型来对源码进行相应的解析。Statement有很多子类,这里只给出其中的几类介绍(具体可以参考我上一篇文章:ASTParser介绍;最好直接去Eclipse documentation官网查看相关文档)。基本流程就是先判断statement是哪种类型的实例,做出相应的ClassCast,再调用相应的方法。

     

    1. ExpressionStatement (又包括Assignement, MethodInvocation等子类)。

    [java] view plaincopy
    1. if(stmt instanceof ExpressionStatement)  
    2.                 {  
    3.                     ExpressionStatement expressStmt=(ExpressionStatement) stmt;  
    4.                     Expression express=expressStmt.getExpression();  
    5.                       
    6.                     if(express instanceof Assignment)  
    7.                     {  
    8.                         Assignment assign=(Assignment)express;  
    9.                         System.out.println("LHS:"+assign.getLeftHandSide()+"; ");  
    10.                         System.out.println("Op:"+assign.getOperator()+"; ");  
    11.                         System.out.println("RHS:"+assign.getRightHandSide());  
    12.                           
    13.                     }  
    14.                     else if(express instanceof MethodInvocation)  
    15.                     {  
    16.                         MethodInvocation mi=(MethodInvocation) express;  
    17.                         System.out.println("invocation name:"+mi.getName());  
    18.                         System.out.println("invocation exp:"+mi.getExpression());  
    19.                         System.out.println("invocation arg:"+mi.arguments());  
    20.                           
    21.                     }  
    22.                     System.out.println();  
    23.                       
    24.                 }  

     

     

     

    2. IfStatement

    [java] view plaincopy
    1. else if(stmt instanceof IfStatement)  
    2.                 {  
    3.                     IfStatement ifstmt=(IfStatement) stmt;  
    4.                     InfixExpression wex=(InfixExpression) ifstmt.getExpression();  
    5.                     System.out.println("if-LHS:"+wex.getLeftOperand()+"; ");  
    6.                     System.out.println("if-op:"+wex.getOperator()+"; ");  
    7.                     System.out.println("if-RHS:"+wex.getRightOperand());  
    8.                     System.out.println();  
    9.                 }  

     

    3. VariableDeclarationStatement

    [java] view plaincopy
    1. else if(stmt instanceof VariableDeclarationStatement)  
    2.                 {  
    3.                     VariableDeclarationStatement var=(VariableDeclarationStatement) stmt;  
    4.                     System.out.println("Type of variable:"+var.getType());  
    5.                     System.out.println("Name of variable:"+var.fragments());  
    6.                     System.out.println();  
    7.                       
    8.                 }  

     

    4. ReturnStatement

    [java] view plaincopy
    1. else if(stmt instanceof ReturnStatement)  
    2.                 {  
    3.                     ReturnStatement rtstmt=(ReturnStatement) stmt;  
    4.                     System.out.println("return:"+rtstmt.getExpression());  
    5.                     System.out.println();  
    6.                 }  

     

     

    ASTParser 作为programming language编译器的一部分,功能机制可以说是相当强大。以上的例子只是冰山一角。使用ASTParser可以对java的源文件进行各种解析,用户只需要指明源文件,调用一下ASTParser的createAST方法就能得到完整的AST了,剩下的就是用户根据实际需要对AST进行各种manipulate了。

    Eclipse documentation详细记录了AST相关的各类描述,继承关系等等。另外,如果对AST机制或者在Eclipse里使用ASTParser有什么疑问的话,可以参考我的另一篇文章:Eclipse JDT--AST入门,里面整理了很多相关资料。

     

    估计接下来一段时间就是继续研究AST了,还请各位高手多多指教~~~


     

  • 相关阅读:
    shell 去除utf8文件中bom头的方法
    bad interpreter:No such file or directory
    桥接配置虚拟机网络
    coreseek因为重启遇到的问题
    监听微信内置浏览器 返回点击事件
    phpredis 扩展装完后,重启php不生效的原因之一
    linux上ThinkPHP中原本正常的css,js文件找不到的解决方式
    vps
    java基础-《JAVA语言程序设计与数据结构》笔记
    面经问题总结——django相关
  • 原文地址:https://www.cnblogs.com/daichangya/p/12959203.html
Copyright © 2020-2023  润新知