前端编译过程是指将源代码转换为目标代码的过程。在Java中,这个过程主要由Java编译器(javac)完成。
package jvm.clssis;
public class ClassPractice {
public static void main(String[] args) {
System.out.println("hello world");
}
}
1. 词法分析
- 输入:Java源代码(.java文件)
- 输出:Token流
- 任务:源代码分解成token(标记)的过程,其中token代表源代码中的最小语法单元。如关键字、标识符、字面量、操作符和分隔符等。
示例代码ClassPractice生成的tokens :package、jvm.clssis、; 、public、class、ClassPractice、{、public、static、void、main、(、String、[、]、args、)、{、System、. 、out、. 、println、(、"hello world" 、) 、; 、}、}
示例代码ClassPractice分解成token过程:
1,package jvm.clssis;
-
- package(关键字)
- jvm.clssis(标识符)
- ;(分隔符)
2,public class ClassPractice {
-
- public(关键字)
- class(关键字)
- ClassPractice(标识符)
- {(分隔符)
3,public static void main(String[] args) {
-
- public(关键字)
- static(关键字)
- void(关键字)
- main(标识符)
- ((分隔符)
- String(标识符)
- [(分隔符)
- ](分隔符)
- args(标识符)
- )(分隔符)
- {(分隔符)
4,System.out.println("hello world");
-
- System(标识符)
- .(分隔符)
- out(标识符)
- .(分隔符)
- println(标识符)
- ((分隔符)
- "hello world"(字符串字面量)
- )(分隔符)
- ;(分隔符)
5, }
-
- }(分隔符)
6,}
-
- }(分隔符)
详细说明
- 关键字:如package、public、class、static、void等。这些都是Java语言的保留字,具有特定的含义。
- 标识符:如jvm.clssis、ClassPractice、main、String、args、System、out、println等。这些是用户定义的名字,用于类、方法、变量等的命名。
- 分隔符:如{、}、(、)、[、]、;、.等。这些符号用于分隔代码中的各个部分。
- 字面量:如"hello world"。这是一个字符串字面量,表示具体的字符串值。
2. 语法分析
- 输入:Token流
- 输出:AST
- 任务:将词法分析生成的一系列tokens按照语言的语法规则组织成一个抽象语法树(AST)。
-
- 任务1-验证语法:检查源代码是否符合语言的语法规则例如,检查类和方法的声明是否正确,括号是否成对匹配,语句是否以分号结束等。并报告任何语法错误。
- 任务2-构建AST: 将源代码转化为抽象语法树,提供一种结构化的表示,使得后续的编译步骤能够更容易地进行。AST去除了源代码中的某些冗余信息(如分号、括号等),并以更抽象的形式表示代码的语法结构
示例代码ClassPractice生成的AST过程
1. 输入tokens序列
从词法分析器接收tokens序列: package、jvm.clssis、; 、public、class、ClassPractice、{、public、static、void、main、(、String、[、]、args、)、{、System、. 、out、. 、println、(、"hello world" 、) 、; 、}、}
2. 创建根节点
- 创建CompilationUnit节点。解析过程从CompilationUnit开始,它是整个源代码文件的根节点。
3. 解析包声明
- 识别package关键字,接着是包名jvm.clssis,最后是分号;
- 验证包声明的语法规则,创建PackageDeclaration节点,内容为jvm.clssis。
- 将PackageDeclaration节点附加到CompilationUnit节点。
4. 解析类声明
- 识别并处理类的声明部分:public class ClassPractice { ... }
- 验证类声明的语法规则,创建TypeDeclaration节点,内容为ClassPractice,修饰符为public。
- 将TypeDeclaration节点附加到CompilationUnit节点。
5. 解析类体
- 识别类体的起始和结束符号 { ... }
- 创建ClassBody节点。
- 将ClassBody节点附加到TypeDeclaration节点。
6. 解析方法声明
- 识别并处理方法的声明部分:public static void main ( String [ ] args ) { ... }
- 验证方法声明的语法规则,创建MethodDeclaration节点,内容为main,修饰符为public static,返回类型为void。
- 将MethodDeclaration节点附加到ClassBody节点。
- 创建Parameters节点,包含一个Parameter节点,内容为String[] args。
- 将Parameters节点附加到MethodDeclaration节点。
7. 解析方法体
- 识别方法体的起始和结束符号 { ... }
- 创建Block节点。
- 将Block节点附加到MethodDeclaration节点。
- 创建ExpressionStatement节点。
- 将ExpressionStatement节点附加到Block节点。
- 识别并处理方法体中的表达式语句 System.out.println("hello world");
- 验证表达式语句的语法规则,创建MethodInvocation节点,内容为System.out.println。
- 将MethodInvocation节点附加到ExpressionStatement节点。
- 创建Expression节点,内容为System.out。
- 将Expression节点附加到MethodInvocation节点。
- 创建Arguments节点,包含一个Argument节点,内容为"hello world"。
- 将Arguments节点附加到MethodInvocation节点。
9. 创建根节点
- 识别并处理右大括号,标记方法体和类体的结束。
示例代码ClassPractice生成的AST:
CompilationUnit
├── PackageDeclaration: jvm.clssis
└── TypeDeclaration: ClassPractice
├── Modifiers: public
├── ClassBody
└── MethodDeclaration: main
├── Modifiers: public static
├── ReturnType: void
├── MethodName: main
├── Parameters
│ └── Parameter: String[] args
└── Block
└── ExpressionStatement
└── MethodInvocation: System.out.println
├── Expression: System.out
└── Arguments
└── Argument: "hello world"
3. 语义分析
- 输入:抽象语法树(AST)
- 输出:可能修改后的AST和符号表
- 任务: 确保代码的语义正确,即不仅仅是结构上正确,还要在逻辑上、类型上正确。
-
- 任务1:类型检查
-
-
- 确保每个表达式的类型是合法的。例如,检查变量的类型是否兼容,方法调用的参数类型是否正确,赋值语句中的左右两边类型是否匹配等。例如,检查System.out.println("hello world");中,println方法是否存在并且接受一个字符串参数。
-
-
- 任务2:作用域检查
-
-
- 确保每个变量和方法在使用前已经声明,并且它们在正确的作用域中使用。例如,检查args在main方法的参数列表中是否已经声明。
-
-
- 任务3: 符号表管理
-
-
- 构建和维护符号表,用于记录变量、方法、类等符号的信息(如名称、类型、作用域等)。
-
-
- 任务4:标识符解析
-
-
- 解析标识符的引用,确保每个标识符引用的是正确的定义。例如,解析System.out.println,确保System、out、println分别指向标准库中的正确定义。
-
-
- 任务5:检查语义规则
-
-
- 检查更多语义规则,如访问控制(private、protected、public)、常量表达式、数组边界、可能的空指针引用等。
-
示例代码ClassPractice语义分析具体过程:
1. 构建符号表
创建并初始化符号表:
- 将包名jvm.clssis加入符号表。
- 将类ClassPractice加入符号表,记录其修饰符public。
- 将方法main加入符号表,记录其修饰符public static、返回类型void、参数列表String[] args。
2. 作用域检查
检查标识符的作用域和声明:
- 确认args在main方法的参数列表中已经声明。
- 确认System、out、println在标准库中是合法的。
3. 类型检查
检查表达式和语句的类型:
- 确认System.out的类型是PrintStream。
- 确认PrintStream类中存在println方法,并且它接受一个String类型的参数。
4. 标识符解析
解析标识符的引用,确保引用的是正确的定义:
- 将System解析为java.lang.System类。
- 将out解析为System类的out字段。
- 将println解析为PrintStream类的println方法。
示例代码ClassPractice逐步语义分析过程
- package jvm.clssis;
- 将包名jvm.clssis加入符号表。
- public class ClassPractice {
- 将类名ClassPractice和修饰符public加入符号表。
- public static void main(String[] args) {
- 将方法main及其修饰符public static、返回类型void、参数String[] args加入符号表。
- System.out.println("hello world");
- 确认System类存在。
- 确认System类的out字段存在且类型为PrintStream。
- 确认PrintStream类的println方法存在且接受一个String类型参数。
4. 中间代码生成
- 输入:优化后的抽象语法树(AST)和符号表
- 输出:中间代码
- 任务:将AST转换为中间代码。
5. 中间代码优化
- 输入:中间代码
- 输出:优化后的中间代码
- 任务:对中间代码进行优化,以提高目标代码的执行效率。优化可能包括消除无用的代码块、简化计算、重新排列代码等。