上一章我们得到了Token序列,而语法分析就是根据Token序列构造抽象语法树的过程,抽象语法树是一种用来描述程序代码语法 结构的树形表示方式,这种结构化的表示方式将为后面语义分析、代码生成阶段提供极大的便利。语法树的每一个节点都代表了程序代码中的一个语法结构,如包、类型、修饰符、运算符、接口、返回值甚至代码注释等都可以是一个语法结构。Javac中,每个语法树节点都对应着一个具体的实现类,这一章将重点介绍抽象语法树上的各个语法树节点,只有认清了抽象语法树上的构造节点才能根据根据一定规则将token序列转换为抽象语法树。
由于语法树中涉及到的节点众多,我们可以大概将这些节点归纳为如下3类:
(1)定义及声明 例如编译单元的定义、方法的定义、import导入包的声明等
(2)语句 例如一些流程控制的if语句、while语句、do-while语句及for语句等
(3)表达式 如两个数值相加x+y等
1、定义及声明
Javac中有几个定义及声明,下面给出了涉及到的主要的几个语法树节点的继承关系图。
可以看到Javac语法树上的每个节点实现类都继承了一个抽象实现类JCTree,同时也实现了与自己想对应的接口,如JCxxx实现类实现xxxTree接口,而xxxTree接口与JCTree又同时实现了
Tree接口,可以说这个接口是一个顶层接口,代表了任意一个语法树节点。类似的,语句及表达式的实现类也遵循了这样的继承原则,只是语句是通过JCStatement间接继承了JCTree,而表达式是通过JCExpression抽象类间接继承了JCTree。
(1)JCCompilationUnit编译单元
JCCompilationUnit表示一个编译单元,一般是一个源文件内容对应一个编译单元,同时这也是顶层的树节点。所以如果一个文件中定义了多个类,则这些类也属于同一个编译单元。
认识这些树节点的实现类时,关系要认清里面的一些重要的属性,下面看一下JCCompilationUnit节点所包含的其它语法节点,如下:
public List<JCAnnotation> packageAnnotations;
public JCExpression pkgid;
public List<JCTree> defs;
多个包注解将会存储到packageAnnotations集合中,包名存储到pkgid属性,而关于多个类定义则存储到defs集合中。举个例子,首先定义一个包的注解,如下:
package com.test14;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
/**
* 定义标注在package上的注解
*/
@Target(ElementType.PACKAGE)
public @interface PkgAnnotation {
}
再创建一个package-info类,内容如下:
@PkgAnnotation
package com.test14;
import java.util.HashMap;
class A{} class B{
最终package-info类的语法树节点如下:
(2)ImportTree导入声明
用来表示一个具体的import声明,其实现类中定义了两个属性:
public boolean staticImport;
public JCTree qualid;
staticImport表示是否为静态导入,当静态导入时值为true。qualid表示具体导入的内容。例如声明一个HashMap的导入:
import java.util.HashMap;
语法节点qualid为如下JCFieldAccess类型,表示java.util.HashMap,由于是非静态导入,则属性staticImport为false。
(3)JCModifiers
用来表示修饰符,如public、abstract、native等,这个语法树节点同时也能表示注解到变量与方法上的注解,JCModifiers.java类中定义的重要属性如下:
public long flags;
public List<JCAnnotation> annotations;
其中flags是一个长整型,在Java中long类型由8个字节表示,最多有64个位,所以可以通过各个位来表示某些有修饰符的树结构,例如类、方法和变量等。具体哪个位代表哪个修饰符是由Flags类来定义的。Flags类中定义了非常多的静态变量,这里只了解一些常量的修饰符就可以了,如下:
public static final int PUBLIC = 1<<0; // 0x0001
public static final int PRIVATE = 1<<1; // 0x0002
public static final int PROTECTED = 1<<2; // 0x0004
public static final int STATIC = 1<<3; // 0x0008
public static final int FINAL = 1<<4; // 0x0010
public static final int SYNCHRONIZED = 1<<5; // 0x0020
public static final int VOLATILE = 1<<6; // 0x0040
public static final int TRANSIENT = 1<<7; // 0x0080
public static final int NATIVE = 1<<8; // 0x0100
public static final int INTERFACE = 1<<9; // 0x0200
public static final int ABSTRACT = 1<<10; // 0x0400
public static final int STRICTFP = 1<<11; // 0x0800
也就是long类型64位中的低12位可以表示这些,这样就可以通过一个数来表示所有可能的修饰符组合了,Flags类中也定义了最常量的组合:
/** Modifier mas