把Java代码解析为抽象语法树:Python库----javalang用法分析

0x00 前言

最近在研究抽象语法树的编码,需要使用Python的Javalang库解析Java源代码为抽象语法树,记录一波该库的一些用法。
这里javalang的版本选择最新的0.13.0,老版本的0.11.0显示信息不全,不便于学习。

0x01 编译单元CompilationUnit

以最简单的代码为例,学习javalang的结构:

package fuck;
import com.sta.aaa;
import com.sta.bbb;
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.parse(fd.read())               # 根据源代码解析出一颗抽象语法树
print(tree)            

效果:

# 打印整颗语法树:CompilationUnit(imports=[Import(path=com.sta.aaa, static=False, wildcard=False), Import(path=com.sta.bbb, static=False, wildcard=False)], 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'}, type=ReferenceType(arguments=None, dimensions=[], name=String, sub_type=None))], documentation=None, extends=None, implements=None, modifiers={'public'}, name=Main, type_parameters=None)])

这里返回了一个javalang.tree.CompilationUnit类型,表示整颗抽象语法树(以后简称AST)
在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=[VariableDeclarator(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 fuck;
import com.sta.aaa;
import com.sta.bbb;
public class Main {
}

只包含一个类,且该类是空的。
得到的类声明如下:

ClassDeclaration(annotations=[], body=[], documentation=None, extends=None, implements=None, modifiers={'public'}, name=Main, type_parameters=None)

这里annotations表示注释,body表示类括号里面的内容,我们主要看body里面的内容。
这里空类不行了,要在里面加点内容:

package fuck;
import com.sta.aaa;
import com.sta.bbb;
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'}, type=ReferenceType(arguments=None, dimensions=[], name=String, sub_type=None)), MethodDeclaration(annotations=[], body=[StatementExpression(expression=MethodInvocation(arguments=[], member=println, postfix_operators=[], prefix_operators=[], qualifier=System.out, selectors=[], type_arguments=None), label=None)], documentation=None, modifiers={'static', 'public'}, name=main, parameters=[FormalParameter(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(arguments=[], member=println, postfix_operators=[], prefix_operators=[], qualifier=System.out, selectors=[], type_arguments=None), label=None)], documentation=None, modifiers={'static', 'public'}, name=main, parameters=[FormalParameter(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,body也是一个数组,根据代码顺序确定里面的元素。
body里面的元素就是表达式语句StatementExpression。

0x04 表达式语句StatementExpression

你可能感兴趣的:(Java,杂项,编译原理,抽象语法树)