您好,欢迎访问三七文档
当前位置:首页 > 商业/管理/HR > 企业文档 > Eclipse AST(抽象语法树)使用指南
2.4EclipseASTEclipseAST是EclipseJDT的一个重要组成部分,定义在包org.eclipse.jdt.core.dom中,用来表示Java语言中的所有语法结构。EclipseAST采用工厂方法模式和访问者模式(见2.7节)来设计和实现,这样可以减轻用户深入了解其内部结构的压力,并且方便用户利用它们构建并处理AST。你可以打开Eclipse帮助页面,通过鼠标依次点击窗口左边的目录“JDTPlug-inDeveloperGuide”Æ“Reference”Æ“APIReference”Æ“org.eclipse.jdt.core.dom”,即可打开这个包的详细说明。本节将对这个包中重要的类作简要说明,为简便起见,这里省去这些类的包名,即org.eclipse.jdt.core.dom。在EclipseAST中,与本书的课程设计相关的类主要有以下三部分:zASTNode类及其派生类:用于描述各种AST节点的类,每个AST节点表示一个Java源程序中的一个语法结构,例如,一个名字、类型、表达式、语句或声明等。zAST类:创建AST节点的工厂类,类中包含许多创建各类AST节点的工厂方法,用户可以利用这些方法来构建AST。zASTVisitor类:AST的访问者抽象类,类中声明了一组访问各类AST节点的visit()方法、endVisit()方法和preVisit()方法。2.4.1AST节点类在EclipseAST中,Java源程序中的每个语法结构对应为一个AST节点,所有的AST节点按其在语法上的关系连接形成一棵AST树。类ASTNode是AST树中各类节点的抽象基类,其余的AST节点类都由它派生。在ASTNode类中声明有各个具体的AST节点类所对应的类型标识,如ASTNode.COMPILATION_UNIT代表Compilation_Unit节点类,这类节点用来表示一个Java源程序文件。为便于自顶向下(从父节点到子节点)或者自底向上(从子节点到父节点)访问AST树,AST节点含有指向其父节点的parent域以及若干关联的子节点域。在AST节点类中,以属性(property)来统一处理子节点以及用户自定义的节点属性,属性的访问方法有:voidsetProperty(StringpropertyName,Objectdata)//设置指定属性的值ObjectgetProperty(StringpropertyName)//取得指定属性的值Mapproperties()//返回节点的所有属性表,这个表是不可修改的在每个具体的AST节点类中,以类常量形式声明该类节点所拥有的基本属性(即基本的子节点)类别,并定义了存放属性值的域以及设置和访问属性的方法。例如,在一个Java源程序文件中,有可选的package声明、0个或多个import声明以及至少1个类型声明(可以是类声明或接口声明),从而在表示Java源程序文件(称为编译单元)的AST节点类CompilationUnit中就声明有final类变量PACKAGE_PROPERTY、IMPORTS_PROPERTY和TYPES_PROPERTY,分别表示package属性、imports属性和types属性,同时还定义有如下的访问方法:Listimports()//该节点的所有import声明,按在程序中的出现次序排列Listtypes()//该节点的所有顶层类型声明,按在程序中的出现次序排列voidsetPackage(PackageDeclarationpkgDecl)//设置该节点的package声明PackageDeclarationgetPackage()//取得该节点的package声明其中,类型相同的子节点组成的序列以java.util.List接口类来表示,这个接口类包含add、get、set、remove等方法用于访问和修改序列。在实际构造和访问AST树时,需要注意统一所使用的List接口类的实现类,例如,可以统一使用类java.util.LinkedList或者统一使用类java.util.ArrayList来表示序列。在AST节点类中,只提供获取父节点的方法,即ASTNodegetParent()而没有提供设置父节点的方法,这是因为对节点的parent域的设置是伴随着将该节点设置为其他节点的子节点而自动进行的。一个新创建的AST节点是没有设置其父节点的。当节点A通过形如setCHILD方法,如A.setPackage(B)方法,或者通过序列的add或set方法,如A.types().add(B)方法,将节点B设为自己的孩子时,B节点的parent域将自动设置为对A节点的引用;对于那些因上述操作导致不再是A节点的子节点来说,其parent域将被自动设置为null。每个AST节点及其子节点只能归属于一棵AST树。如果将一棵AST树中的某个AST节点添加到另一棵AST树中,则必须复制这个节点及其所有的子孙节点,以保证这些节点只属于一棵AST树。此外,AST树中不能含有环,如果某些操作会导致AST有环,则这些操作将失败。为支持对源程序的分析和类型检查等,每个AST节点还含有一组位标志(用一个int型数表示)用来传播与该节点有关的附加信息,这些位标志可以通过节点的以下方法来存取:voidsetFlags(intflags)intgetFlags()此外,EclipseAST还支持访问者模式,每个AST节点都含有方法:voidaccept(ASTVisitorvisitor)用于统一表示对当前节点访问时所要执行的任务,这个任务由参数visitor来给定。你可以进一步了解ASTVisitor类以及访问者模式(见2.7.2节)来了解对AST树的访问。2.4.2AST类org.eclipse.jdt.core.dom.AST是AST节点的工厂类,即它提供一系列形如TYPEnewTYPE()的工厂方法,用来创建名为TYPE的EclipseAST节点类的实例,新创建的节点并没有设置父节点。例如,方法CompilationUnitnewCompilationUnit()用来创建由这个AST所拥有的一个编译单元节点。要使用这些方法,首先需要创建AST类的实例:ASTast=AST.newAST(AST.JLS3);其中,参数AST.JLS3指示所生成的ast包含处理JLS3(Java语言规范第3版)的ASTAPI。JLS3是Java语言所有早期版本的超集,JLS3API可以用来处理直到JavaSE6(即JDK1.6)的Java程序。2.4.3ASTVisitor类org.eclipse.jdt.core.dom.ASTVisitor是AST树的访问者类,它提供一套方法来实现对给定节点的访问。这套方法中有两组是与具体的AST节点类T相关的,即visit方法和endVisit方法,有两个是与具体的AST节点类无关的,即preVisit方法和postVisit方法。这些方法都通过参数接收一个AST节点node,然后对这个节点进行访问以执行一些操作。zpublicbooleanvisit(Tnode)如果返回true,则接着访问node的子节点;如果返回false,则不再访问node的子节点。ASTVisitor类提供的各个visit方法的缺省实现是:什么也不做,直接返回true。子类可以根据需要重新实现这些方法中的部分或全部。zpublicvoidendVisit(Tnode)这类方法在节点node的子节点已经被访问或者是在visit(node)返回为false之后被调用。ASTVisitor类提供的各个endVisit方法的缺省实现是什么也不做。子类可以根据需要重新实现这些方法中的部分或全部。zpublicvoidpreVisit(ASTNodenode)这个方法在visit(node)之前被调用。zpublicvoidpostVisit(ASTNodenode)这个方法在endVisit(node)之后被调用。ASTVisitor类提供的preVisit方法和postVisit方法的缺省实现是什么也不做。子类可以根据需要来重新实现它们。在EclipseAST中,结合AST节点的accept()方法和ASTVisitor实例,假设待访问的AST树的根节点为root,则调用root.accept()就可以启动对这棵AST树的遍历。遍历是以深度优先搜索为基础的,你可以进一步查看EclipseJDT的源代码来确认这一点,你也可以从上查看相关的源代码。为帮助大家理解对AST树的遍历过程,这里简要给出accept方法的实现。所有的AST节点都执行在ASTNode类中定义的accept方法:publicfinalvoidaccept(ASTVisitorvisitor){if(visitor==null){thrownewIllegalArgumentException();}visitor.preVisit(this);//执行与节点类型无关的preVisit方法accept0(visitor);//调用accept0,执行与节点类型相关的visit/endVisit方法visitor.postVisit(this);//执行与节点类型无关的postVisit方法}ASTNode类中的accept0方法是一个抽象的方法:abstractvoidaccept0(ASTVisitorvisitor);每个具体的AST节点类中都必须实现accept0方法,实现该方法的通用模板如下:booleanvisitChildren=visitor.visit(this);//调用visit()访问本节点if(visitChildren){//如果visit()返回true,则访问子节点acceptChild(visitor,getProperty1());//访问非序列型属性acceptChildren(visitor,rawListProperty);//访问序列型属性acceptChild(visitor,getProperty2());}visitor.endVisit(this);//调用endVisit()执行一些节点访问后的操作从上面的模板可以看出,如果节点包含多个属性,如CompilationUnit节点中有imports属性和types属性等,则按这些属性在源程序中的先后次序来依次访问;如果一个属性为序列型,如CompilationUnit节点中的types属性,则调用acceptChildren方法来依次访问序列中的各个子节点;如果一个属性是非序列的,则调用acceptChild方法来访问。acceptChild和acceptChildren这两个方法的实现都与AST节点的具体类型无关,故放在ASTNode类中:finalvoidacceptChild(ASTVisitorvisitor,ASTNodechild){if(child==null){return;}child.accept(visitor);}finalvoidacceptChildren(ASTVisitorvisitor,ASTNode.NodeListchildren){NodeList.Cursorcursor=children.newCursor();try{while(cursor.hasNext()){ASTNodechild=(ASTNode)cursor.next();child.accept(visitor);}}finally{children.releaseCursor(cursor);}}基于上述的实现机制,当你需要对AST树实现特定的访问功能时,你只需要结合实际需求设计和实现ASTVisitor类的子类就可以了。例如,在本章的课程设计中,
本文标题:Eclipse AST(抽象语法树)使用指南
链接地址:https://www.777doc.com/doc-5904998 .html