把Java代码解析为抽象语法树:Python库----javalang⽤法分析
0x00 前⾔
最近在研究抽象语法树的编码,需要使⽤Python的Javalang库解析Java源代码为抽象语法树,记录⼀波该库的⼀些⽤法。
这⾥javalang的版本选择最新的0.13.0,⽼版本的0.11.0显⽰信息不全,不便于学习。
0x01 编译单元CompilationUnit
以最简单的代码为例,学习javalang的结构:
package;
import;
import;
public class Main {
private String s;
}
代码仅包含⼀个类,且类中仅包含⼀个变量声明。使⽤Python的javalang库来解析它:
import javalang
fd =open("C:/Users/root/Desktop/java.java","r", encoding="utf-8")#读取Java源代码
tree = javalang.parse.ad())# 根据源代码解析出⼀颗抽象语法树
print(tree)
效果:
# 打印整颗语法树:CompilationUnit(imports=[Import(path=com.sta.aaa, static=False, wildcard=False), Import(path=com.sta.bbb, static=False, wildcard=Fal se)], package=PackageDeclaration(annotations=None, documentation=None, modifiers=None, name=fuck), types=[ClassDeclaration(annotations=[], body= [FieldDeclaration(annotations=[], declarators=[VariableDeclarator(dimensions=[], initializer=None, name=s)], documentation=None, modifiers={'private'}, ty pe=ReferenceType(arguments=None, dimensions=[], name=String, sub_type
=None))], documentation=None, extends=None, implements=None, modifiers ={'public'}, name=Main, type_parameters=None)])
这⾥返回了⼀个CompilationUnit类型,表⽰整颗抽象语法树(以后简称AST)python转java代码
在print(tree)后⾯添加以下代码,打印该编译单元的各个⼦节点:
for i in range(0,len(tree.children)):
print(tree.children[i])
效果:
children[0]:PackageDeclaration(annotations=None, documentation=None, modifiers=None, name=fuck)
chileren[1]:[Import(path=com.sta.aaa, static=False, wildcard=False), Import(path=com.sta.bbb, static=False, wildcard=False)]
children[2]:[ClassDeclaration(annotations=[], body=[FieldDeclaration(annotations=[], declarators=[Var
iableDeclarator(dimensions=[], initializer=None, name =s)], documentation=None, modifiers={'private'},type=ReferenceType(arguments=None, dimensions=[], name=String, sub_type=None))], documentation= None, extends=None, implements=None, modifiers={'public'}, name=Main, type_parameters=None)]
可以看到:CompilationUnit(编译单元)的children是⼀个数组,由三个元素构成:[包声明,Import声明数组,类声明]
值得注意的是,如果源代码中不存在包声明、Import声明、类声明,相应的children[0]、children[1]、children[2]只会被置为“[]”,依然要占位置,也就是说CompilationUnit。children的结构是固定的。
包声明和Import声明数组没什么好说的,接下来要研究ClassDeclaration。
0x02 类声明ClassDeclaration
为了简化问题,再次把Java源代码精简⼀下:
package;
import;
import;
public class Main {
}
只包含⼀个类,且该类是空的。
得到的类声明如下:
ClassDeclaration(annotations=[], body=[], documentation=None, extends=None, implements=None, modifiers={'public'}, name=Main, type_parameters=No ne)
这⾥annotations表⽰注释,body表⽰类括号⾥⾯的内容,我们主要看body⾥⾯的内容。
这⾥空类不⾏了,要在⾥⾯加点内容:
package;
import;
import;
public class Main {
private String s;
public static void main(String[] args){
System.out.println();
}
}
然后打印body⾥⾯的内容:print(tree.children[2][0].body)
[FieldDeclaration(annotations=[], declarators=[VariableDeclarator(dimensions=[], initializer=None, name=s)], documentation=None, modifiers={'private'},ty pe=ReferenceType(arguments=None, dimensions=[], name=String, sub_type=None)), MethodDeclaration(annotations=[], body=[StatementExpression(exp ression=MethodInvocation(arguments=[], member=println, postfix_operators=[], prefix_operators=[], qualifier=System.out, selectors=[], type_arguments=N one), label=None)], documentation=None, modifiers={'static','public'}, name=main, parameters=[FormalPar
ameter(annotations=[], modifiers=set(), name= args,type=ReferenceType(arguments=None, dimensions=[None], name=String, sub_type=None), varargs=False)], return_type=None, throws=None, type _parameters=None)]
这⾥打印body的type可以知道body也是⼀个数组,包含不同的声明,可以有FieldDeclaration、MethodDeclaration、LocalVariableDeclaration等,这⾥声明了⼀个变量,⼀个函数,所以body的长度就是2,如果只有⼀个变量被声明,那么body的长度就是1,此外,注释不算在body⾥⾯。
变量声明FieldDeclaration放到后⾯再说,按照编译单元-----类声明------函数声明的顺序,从⼤到⼩来解析。下⾯到函数声MethodDeclaration
0x03 函数声明MethodDeclaration
Java⾥⾯函数也叫⽅法,是⾯向对象的称呼。
这⾥源码的函数声明也很简单:
打印函数声明看看:
MethodDeclaration(annotations=[], body=[StatementExpression(expression=MethodInvocation(argu
ments=[], member=println, postfix_operators=[], prefix_ operators=[], qualifier=System.out, selectors=[], type_arguments=None), label=None)], documentation=None, modifiers={'static','public'}, name=main, para meters=[FormalParameter(annotations=[], modifiers=set(), name=args,type=ReferenceType(arguments=None, dimensions=[None], name=String, sub_typ e=None), varargs=False)], return_type=None, throws=None, type_parameters=None)
函数声明的结构和类声明类似,也是看body,body也是⼀个数组,根据代码顺序确定⾥⾯的元素。
body⾥⾯的元素就是表达式语句StatementExpression。
0x04 表达式语句StatementExpression

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。