Java编译原理学习:二、词法分析

package compile;
/**
* javac
*/
public class Cifa{
    int a;
    int c=a+1;
}

我们通过以上简单的类来看看词法分析的过程。以下是词法分析器设计的类图

Javac的主要词法分析器的接口类是com.sun.tools.javac.parser.Lexer,它的默认实现类是com.sum.tools.javac.parser.Scanner,Scanner会逐个读取Java源文件的单个字符,然后解析出符合Java语言规范的token序列。

可以看出,由两个Factory生成了两个接口的实现类Scanner和JavacParser。这两个类负责整个词法分析的过程控制,JavaParser规定了哪些词是符合Java语言规范的的词,而具体读取和归类不同词法的操作交给Scanner完成。token规定了所有Java语言的合法关键词,Names用来存储和表示解析后的词法。

词法分析过程是在JavaParser的parseCompilationUtil方法中完成的,这个方法的代码如下:

从这段代码可以看出Javac分析词法的原貌,从源文件中一个字符开始,按照Java语法规范依次找出package、import、类定义,以及属性和方法定义,最后构建一个抽象语法树。

语法分析器的分析结构就是将这个类中所有关键词匹配到Token类的所有项中的任何一项。

Cifa类的token如下:

Token.IDENTIFIER用于表示用户定义的名称,如类名、包名、变量名、方法名等。

有两个关键点:Javac是如何分辨出这一个一个的Token的呢?例如,它怎么知道package就是一个Token.package而不是用户自定义额Token.IDENITIFIER的名称呢?另外一个问题是:Java如何分辨出一个token的,如compile这个词就是一个token,为什么不是com或者comp等,也就是说Javac如何知道哪些字符组合在一起就是一个token,如何从一个字符流中划分出Token来?这些为都是词法分析器不得不解决的问题?

第一个问题的答案是这样的:Javac在进行词法分析的时候会由javacParser根据Java语言规范来控制什么顺序、什么地方应该出现什么token,下面以Java源码的package关键词为例来说明词法分析器是如何解析出上例前三个词法的。

回到javacparser的源码,在执行javacparser的构造函数的时候,Scanner会读取第一个token。整个词法分析的过程都是在JavacParser的parseCompilationUnit方法中完成的,但读取的第一个token,先判断是不是Token.PACKAGE,如果是的话,就会读取下一个token,而这个token就是第二个Token.IDENTIFIER,然后调用qualident方法,这个方法的源码如下

这段代码的第一行是根据Token.IDENTIFIER的token构建一个JCIdent的语法节点,然后再去取下一个Token,判断这个token是否是Token.DOT,如果是的话,就进入while循环去读取全路径。这个package的最后一行代码是accept(SEMI)。判断下一个token是不是一个Token.SEMI,这样整个“package compile;”代码就解析完成了。

可以看出,读取每个Token是由javacParser类来规定的,token流的顺序是要符合Java语言规范的。

接下来回答第二个问题,为什么每次读取就能获取一个token?

我们在写代码的时候都会有语法规则,这些规则除了一些Java语法规定的关键字之外就是用户自定义的变量名称,关键字和自定义名称之间用用空格隔开,每个语法表达式用分号结束,而如何判断哪些字符组合是一个Token的规则是在Scanner的nextToken方法中定义的,每调用一次这个方法就会构造一个Token,而这些Token必然是com.sun.tools.javac.parser.Token中任何元素之一。

 

最后,在读取每一个token时,都需要一个转换过程,如package中的“compile”报名要如何转换成Token.IDENTIFIER类型,Java源码中所有字符集合都要找到com.sun.tools.javac.parser.Token中定义的对应关系,这个任务是在com.sun.tools.javac.parser.Keywords类中完成的,keywords负责将所有的字符集合对应到Token集合中。

字符集合到Token转化相关的类关系图如下:

每个Token.*都会是一个Name对象,都存储在Name.Table这个内部类中,而keyworks会将token.name先转为Name对象,然后建立Name对象和Token的对应关系,这个关系保存在KeyWorks类的Key数组中

其中maxkey的值是这个token中最后一个token对应的Name在Name.Table表中的起始位置,如果name.getIndex>maxkey,那么肯定不是关键字的token,所以默认为IDENTIFIER.

字符集合转为Name对象,Name对象对应到Token的转换关系如下:

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值