语法分析器是将词法分析器分析的Token流组建成更加结构化的语法树,也就是将一个个单词组装成一句话,一个完整的语句。哪些词组合在一起是主语、哪些是谓语、宾语、定语…要做进一步区分。
语法树及各种语法节点对应的类关系图如下:
每个语法树上的节点都是com.sun.tools.javac.tree.JCTree的一个实例,关于语法树有如下规则:
1).每个语法节点都会实现一个接口xxxTree,这个接口又继承自com.sun.source.tree.Tree接口,如IfTree语法节点表示一个if类型的表达式,BinaryTree语法节点代表一个二元操作表达式等
2).每个语法节点都是JCTree的子类,并且会实现第一节点中的xxxTree接口类,这个类的名称类似于JCxxx,如实现IfTree接口的实现类为JCIf,实现BinaryTree接口的类为JCBinary等
3).所有的JCxxx类都作为一个静态内部类定义在JCTree中
JCTree中有3个重要属性需要说明说明一下:
TreeTag:每个语法节点都会用一个整形常数表示,并且每个节点类型的数值是在前一个的基础上加1。顶层节点TOPLEVEL是1,而IMPORT节点等于TOPLEVEL加1,等于2
pos:也是一个整数,它存储的是这个语法节点在源代码中的起始位置,一个文件的位置是0,而-1表示不存在
type:表示这个节点是什么java类型,如int、float还是String
回顾一下package的词法分析方法
public JCExpression qualident(boolean allowAnnos) {
JCExpression t = toP(F.at(token.pos).Ident(ident()));
while (token.kind == DOT) {
int pos = token.pos;
nextToken();
List<JCAnnotation> tyannos = null;
if (allowAnnos) {
tyannos = typeAnnotationsOpt();
}
t = toP(F.at(pos).Select(t, ident()));
if (tyannos != null && tyannos.nonEmpty()) {
t = toP(F.at(tyannos.head.pos).AnnotatedType(tyannos, t));
}
}
return t;
}
该函数中第一句就是调用TreeMaker类,根据Name对象构建了一个JCIdent语法节点。
JCExpression t = toP(F.at(token.pos).Ident(ident()));
根据上一篇文章所说,Package节点解析完成后会进入while循环,首先解析importDeclaration,解析规则与pakcage的类似:首先检查Token是不是IMPORT,如果是,用import的语法规则来解析import节点,最后构造一个import语法树。源码如下:
protected JCTree importDeclaration() {
int pos = token.pos;
nextToken();
boolean importStatic = false;
if (token.kind == STATIC) {
importStatic = true;
nextToken();
}
JCExpression pid = toP(F.at(token.pos).Ident(ident()));
do {
int pos1 = token.pos;
accept(DOT);
if (token.kind == STAR) {
pid = to(F.at(pos1).Select(pid, names.asterisk));
nextToken();
break;
} else {
pid = toP(F.at(pos1).Select(pid, ident()));
}
} while (token.kind == DOT);
accept(SEMI);
return toP(F.at(pos