Javac词法分析
参考:《深入分析Java Web》技术内幕 许令波
词法分析过程涉及到的主要类及相关的继承关系如下:
词法分析的接口为Lexer,默认实现类为Scanner,Scanner会逐个读取Java源文件中的单个字符,然后解析出符合Java语言规范的Token序列。
举个例子,如下:
package compile; /** * Created by mazhi on 2018/6/5. */ public class Cifa { int a; int c = a + 1; }
其最终的语法树如下:
首先的一个语法节点为JCCompilationUnit,关于这个节点的介绍如下:
Compilation Units参考:https://docs.oracle.com/javase/specs/jls/se7/html/jls-7.html#jls-7.3
定义如下:
CompilationUnit: PackageDeclarationopt ImportDeclarationsopt TypeDeclarationsopt ImportDeclarations: ImportDeclaration ImportDeclarations ImportDeclaration TypeDeclarations: TypeDeclaration TypeDeclarations TypeDeclaration
可以看到一个CompilationUnit可以有多个TypeDeclaration声明。当前只有一个JCClassDecl节点,代码这个Cifa类。
其中涉及到了PackageDeclaration、ImportDeclaration与TypeDeclaration声明,其各个定义如下:
(1)PackageDeclaration节点定义如下:
PackageDeclaration: Annotationsopt package PackageName ;
参考:https://docs.oracle.com/javase/specs/jls/se7/html/jls-7.html#jls-7.4
(2)ImportDeclaration节点定义如下:
ImportDeclaration: SingleTypeImportDeclaration TypeImportOnDemandDeclaration SingleStaticImportDeclaration StaticImportOnDemandDeclaration
参考:https://docs.oracle.com/javase/specs/jls/se7/html/jls-7.html#jls-7.5
(3)TypeDeclaration节点定义如下:
ClassDeclaration: NormalClassDeclaration EnumDeclaration NormalClassDeclaration: ClassModifiersopt class Identifier TypeParametersopt Superopt Interfacesopt ClassBody
参考:https://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.1
查看JavaCompiler.java类中的方法parse(),代码如下:
/** * Parse contents of input stream. * * @param filename The name of the file from which input stream comes. * @param input The input stream to be parsed. */ protected JCCompilationUnit parse(JavaFileObject filename, CharSequence content) { long msec = now(); JCCompilationUnit tree = treeMaker.TopLevel(List.<JCAnnotation>nil(), null, List.<JCTree>nil()); if (content != null) { if (verbose) { log.printVerbose("parsing.started", filename); } Parser parser = parserFactory.newParser(content, keepComments(), genEndPos, lineDebugInfo); tree = parser.parseCompilationUnit(); if (verbose) { log.printVerbose("parsing.done", Long.toString(elapsed(msec))); } } tree.sourcefile = filename; return tree; }
这个方法有三点需要解析:
(1)参数content为源文件的字符输入流。是一个HeapCharBuffer对象,其中用char数组来保存输入的字符流内容,具体内容如下:
nextToken(0,7)=|package| Token.PACKAGE 0 = 'p' 112 1 = 'a' 97 2 = 'c' 99 3 = 'k' 107 4 = 'a' 97 5 = 'g' 103 6 = 'e' 101 processWhitespace(7,8)=| | 7 = ' ' 32 nextToken(8,15)=|compile| Token.IDENTIFIER 8 = 'c' 99 9 = 'o' 111 10 = 'm' 109 11 = 'p' 112 12 = 'i' 105 13 = 'l' 108 14 = 'e' 101 nextToken(15,16)=|;| Token.SEMI 15 = ';' 59 processTerminator(16,18)=|| 16 = '\r' 13 17 = '\n' 10 processTerminator(18,20)=|| 18 = '\r' 13 19 = '\n' 10 processComment(20,62,JAVADOC)=|/** * Created by mazhi on 2018/6/5. */| 20 = '/' 47 21 = '*' 42 22 = '*' 42 23 = '\r' 13 24 = '\n' 10 25 = ' ' 32 26 = '*' 42 27 = ' ' 32 28 = 'C' 67 29 = 'r' 114 30 = 'e' 101 31 = 'a' 97 32 = 't' 116 33 = 'e' 101 34 = 'd' 100 35 = ' ' 32 36 = 'b' 98 37 = 'y' 121 38 = ' ' 32 39 = 'm' 109 40 = 'a' 97 41 = 'z' 122 42 = 'h' 104 43 = 'i' 105 44 = ' ' 32 45 = 'o' 111 46 = 'n' 110 47 = ' ' 32 48 = '2' 50 49 = '0' 48 50 = '1' 49 51 = '8' 56 52 = '/' 47 53 = '6' 54 54 = '/' 47 55 = '5' 53 56 = '.' 46 57 = '\r' 13 58 = '\n' 10 59 = ' ' 32 60 = '*' 42 61 = '/' 47 processTerminator(62,64)=|| 62 = '\r' 13 63 = '\n' 10 nextToken(64,70)=|public| Token.PUBLIC 64 = 'p' 112 65 = 'u' 117 66 = 'b' 98 67 = 'l' 108 68 = 'i' 105 69 = 'c' 99 processWhitespace(70,71)=| | 70 = ' ' 32 nextToken(71,76)=|class| Token.CLASS 71 = 'c' 99 72 = 'l' 108 73 = 'a' 97 74 = 's' 115 75 = 's' 115 processWhitespace(76,77)=| | 76 = ' ' 32 nextToken(77,81)=|Cifa| Token.IDENTIFIER 77 = 'C' 67 78 = 'i' 105 79 = 'f' 102 80 = 'a' 97 processWhitespace(81,82)=| | 81 = ' ' 32 nextToken(82,83)=|{| Token.LBRACE 82 = '{' 123 processTerminator(83,85)=|| 83 = '\r' 13 84 = '\n' 10 processWhitespace(85,89)=| | 85 = ' ' 32 86 = ' ' 32 87 = ' ' 32 88 = ' ' 32 nextToken(89,92)=|int| Token.INT 89 = 'i' 105 90 = 'n' 110 91 = 't' 116 processWhitespace(92,93)=| | 92 = ' ' 32 nextToken(93,94)=|a| Token.IDENTIFIER 93 = 'a' 97 nextToken(94,95)=|;| Token.SEMI 94 = ';' 59 processTerminator(95,97)=|| 95 = '\r' 13 96 = '\n' 10 processWhitespace(97,101)=| | 97 = ' ' 32 98 = ' ' 32 99 = ' ' 32 100 = ' ' 32 nextToken(101,104)=|int| Token.INT 101 = 'i' 105 102 = 'n' 110 103 = 't' 116 processWhitespace(104,105)=| | 104 = ' ' 32 nextToken(105,106)=|c| Token.IDENTIFIER 105 = 'c' 99 processWhitespace(106,107)=| | 106 = ' ' 32 nextToken(107,108)=|=| Token.EQ 107 = '=' 61 processWhitespace(108,109)=| | 108 = ' ' 32 nextToken(109,110)=|a| Token.IDENTIFIER 109 = 'a' 97 processWhitespace(110,111)=| | 110 = ' ' 32 nextToken(111,112)=|+| Token.PLUS 111 = '+' 43 processWhitespace(112,113)=| | 112 = ' ' 32 nextToken(113,114)=|1| Token.INTLITERAL 113 = '1' 49 nextToken(114,115)=|;| Token.SEMI 114 = ';' 59 processTerminator(115,117)=|| 115 = '\r' 13 116 = '\n' 10 nextToken(117,118)=|}| Token.RBRACE 117 = '}' 125 processTerminator(118,120)=|| 118 = '\r' 13 119 = '\n' 10 nextToken(120,120)=|| 120 = '\u001A' 26 121 = '\u0000' 0 122 = '\u0000' 0 123 = '\u0000' 0 124 = '\u0000' 0 125 = '\u0000' 0 126 = '\u0000' 0 127 = '\u0000' 0 128 = '\u0000' 0 129 = '\u0000' 0
(2)通过调用treeMaker的TopLevel()方法来创建JCCompilationUnit节点,其TopLevel()方法的具体实现如下:
/** * Create given tree node at current position. * @param defs a list of ClassDef, Import, and Skip */ public JCCompilationUnit TopLevel(List<JCAnnotation> packageAnnotations, JCExpression pid, List<JCTree> defs) { Assert.checkNonNull(packageAnnotations); for (JCTree node : defs) { // cond/msg Assert.check(node instanceof JCClassDecl || node instanceof JCImport || node instanceof JCSkip || node instanceof JCErroneous || (node instanceof JCExpressionStatement && ((JCExpressionStatement) node).expr instanceof JCErroneous), node.getClass().getSimpleName()); } JCCompilationUnit tree = new JCCompilationUnit(packageAnnotations, pid, defs, null, null, null, null); tree.pos = pos; return tree; }
(3)调用parserFactory类的newParser()工厂方法生成一个Parser类单实例对象,然后调用parseCompilationUnit()方法,其源代码如下:
/** CompilationUnit = * [ { "@" Annotation } PACKAGE Qualident ";"] * {ImportDeclaration} * {TypeDeclaration} */ public JCCompilationUnit parseCompilationUnit() { int pos = S.pos(); JCExpression pid = null; String dc = S.docComment(); JCModifiers mods = null; List<JCAnnotation> packageAnnotations = List.nil(); if (S.token() == MONKEYS_AT) // @ 符号 mods = modifiersOpt(); // 解析修饰符 if (S.token() == PACKAGE) { // 解析package声明 if (mods != null) { checkNoMods(mods.flags); packageAnnotations = mods.annotations; mods = null; } S.nextToken(); pid = qualident(); accept(SEMI); // 分号 } ListBuffer<JCTree> defs = new ListBuffer<JCTree>(); boolean checkForImports = true; while (S.token() != EOF) { if (S.pos() <= errorEndPos) { // error recovery // 跳过错误字符 skip(checkForImports, false, false, false); if (S.token() == EOF) break; } if (checkForImports && mods == null && S.token() == IMPORT) { defs.append(importDeclaration()); // 解析import声明 } else { // 解析class类主体 JCTree def = typeDeclaration(mods); if (keepDocComments && dc != null && docComments.get(def) == dc) { // If the first type declaration has consumed the first doc // comment, then don't use it for the top level comment as well. // 如果在前面的类型声明中已经解析过了,那么在top level中将不再重复解析 dc = null; } if (def instanceof JCExpressionStatement) def = ((JCExpressionStatement)def).expr; defs.append(def); if (def instanceof JCClassDecl) checkForImports = false; mods = null; } } JCCompilationUnit toplevel = F.at(pos).TopLevel(packageAnnotations, pid, defs.toList()); attach(toplevel, dc); if (defs.elems.isEmpty()) storeEnd(toplevel, S.prevEndPos()); if (keepDocComments) toplevel.docComments = docComments; if (keepLineMap) toplevel.lineMap = S.getLineMap(); return toplevel; }
Javac词法分析的更多相关文章
- javac的词法分析
1.词法分析将Java源文件的字符流转变为对应的Token流. JavacParser类规定了哪些词是符合Java语言规范规定的词,而具体读取和归类不同词法的操作由Scanner类来完成. ...
- javac 编译与 JIT 编译
编译过程 不论是物理机还是虚拟机,大部分的程序代码从开始编译到最终转化成物理机的目标代码或虚拟机能执行的指令集之前,都会按照如下图所示的各个步骤进行: 其中绿色的模块可以选择性实现.很容易看出,上图中 ...
- Javac早期(编译期)
从Sun Javac的代码来看,编译过程大致可以分为3个过程: 解析与填充符号表过程. 插入式注解处理器的注解处理过程. 分析与字节码生成过程. Javac编译动作的入口是com.sun.tools. ...
- javac编译过程
编译器把一种语言规范转化为另一种语言规范的这个过程需要哪些步骤:
- Javac编译和JIT编译
编译过程 不论是物理机还是虚拟机,大部分的程序代码从开始编译到最终转化成物理机的目标代码或虚拟机能执行的指令集之前,都会按照如下图所示的各个步骤进行: 其中绿色的模块可以选择性实现.很容易看出,上图中 ...
- javac编译原理(一)
我们都知道,计算机只能识别二进制语言,是不能直接识别java c c++等高级语言的.将高级语言转化成计算机可以是别的二进制语言,这个过程就叫编译. 有次面试,面试官问了一道“java的编译原理是什么 ...
- Javac编译与JIT编译
本文转载自:http://blog.csdn.net/ns_code/article/details/18009455 编译过程 不论是物理机还是虚拟机,大部分的程序代码从开始编译到最终转化成物理机的 ...
- 【深入Java虚拟机】之七:Javac编译与JIT编译
转载请注明出处:http://blog.csdn.net/ns_code/article/details/18009455 编译过程 不论是物理机还是虚拟机,大部分的程序代码从开始编译到最终转化成物理 ...
- Javac 编译原理
写在前面 JDK & JRE JRE(Java Runtime Enviroment)是Java的运行环境.面向Java程序的使用者,而不是开发者.如果你仅下载并安装了JRE,那么你的系统只 ...
随机推荐
- 团体程序设计天梯赛L2-023 图着色问题 2017-04-17 09:28 269人阅读 评论(0) 收藏
L2-023. 图着色问题 时间限制 300 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 陈越 图着色问题是一个著名的NP完全问题.给定无向图 G ...
- Flink本地环境安装部署
本次主要介绍flink1.5.1版本的本地环境安装部署,该版本要求jdk版本1.8以上. 下载flink安装包:http://archive.apache.org/dist/flink/flink-1 ...
- CentOS将普通用户加入管理员组
将用户username加入wheel组: usermod -aG wheel username 将普通用户username加入root组: usermod -aG sudo username 永久生效 ...
- c#设计模式之装饰器模式(Decorator Pattern)
引子 在面向对象语言中,我们常常会听到这样一句话:组合优于继承.那么该如何去理解这句话呢? 下面我将以游戏装备为模型用简单的代码去展示它 先创建一个装备的抽象类,然后创建刀枪2个具体的业务子类 pub ...
- cesium编程入门(五)绘制形状
通过Entity添加形状 先来看一个添加立方体的例子 var viewer = new Cesium.Viewer('cesiumContainer'); var redBox = **viewer. ...
- 数据库路由中间件MyCat - 源代码篇(4)
此文已由作者易国强授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 2. 前端连接建立与认证 Title:MySql连接建立以及认证过程client->MySql:1.T ...
- Android : M 与 N 的权限管理
从 M 开始,permission 分为 Normal permission Runtime permission Normal permission 直接在 manifest 里声明就可以用了. a ...
- 微信小店调用api代码示例
刚开始调用微信小店api的时候,可能大家会遇到问题.系统总是提示system error,归根结底还是发送的参数不正确. 下面给出几个调用例子: 例子写得不全. <?php function c ...
- 474. Ones and Zeroes
In the computer world, use restricted resource you have to generate maximum benefit is what we alway ...
- 使用java中for循环,循环打印出五角星--
//5.0 输出五角星 int touHigh = 6; int jianHigh = 25 ; int kuang =50; for (int ...