1. 编译器概述
编译器:将用编程语言(源语言)编写的计算机代码翻译成另一种语言(目标语言)的计算机程序。
编译程序以高级程序源代码作为输入,以汇编语言或机器语言表示的目标程序作为输出。目标程序会在机器上运行,得到所需的结果。编译器可能执行以下操作:预处理、词法分析、语法分析、语义分析、中间代码生成、代码优化和目标代码生成。
编译器前端和中端理论知识与代码可视化的实现最为相关,后端部分和目标机器代码、特定机器架构相关一般很少用到可视化中。
2. 语义分析
语义分析器(semantic analyzer)使用语法树和符号表中的信息来检查源程序是否和语言定义的语义一致。它同时也收集类型信息,并把这些信息存放在语法树或符号表中,以便在随后的中间代码生成过程中使用。语义规则一般包括但不限于:
- 类型检查:确保操作数的类型与操作符兼容,例如不允许将整数赋值给字符串类型的变量;
- 变量绑定:确保变量和函数的声明与使用匹配,变量在使用前已被声明,函数调用时参数的数量和类型与声明相符;
- 控制流检查:确保程序中的控制流语句(如循环和条件语句)的使用是合法的;
- 唯一性检查:确保标识符的声明在同一作用域内是唯一的,例如不允许在同一作用域内声明两个相同名称的变量;
- 权限和可访问性检查:确保对变量和函数的访问符合其声明的权限,例如私有成员只能在其类内部被访问。
由于每种语言都有其独特的语义规则和特性,例如类型系统、作用域规则、合法的操作符重载等都是语言特定的。因此,语义分析必须针对每种语言的规范来设计。
3. 浅析javac语义分析
由于不同语言的语义分析实现差异较大,没有通用的语义分析器生成工具。因此我们直接来阅读一下Java编译器中的相关源码,了解一下实现逻辑。javac中语义分析的源码位于com.sun.tools.javac.code和com.sun.tools.javac.comp包中。
以下列举了一些用于语义分析的类,源码就不贴了,可以在langtools下载阅读:
-
Symbol:表示所有的语言符号,包括变量、方法、类等。这些符号在编译过程中被创建并填充到符号表中;
-
Scope:用于管理作用域,它保存了一个作用域内所有的符号。这对于解析变量名和方法名非常重要,以确保它们在当前上下文中是可见的和有效的;
-
Type:代表Java语言中的所有类型,包括基本类型、类和接口类型、数组类型等。javac在类型检查过程中使用这个类来确定类型兼容性和执行类型转换;
-
Attr:进行属性分析的核心类,它负责将类型信息和其他属性关联到语法树的节点上。它执行类型检查、方法解析和变量捕获等任务;
-
Check:用于执行各种语义检查,例如检查类型是否存在循环继承,或者一个类是否实现了其接口的所有方法;
-
Resolve:用于解析方法调用、类型名和变量名。它在符号表中查找正确的符号,并处理如方法重载解析等复杂情况;
-
Annotate:处理注解相关的语义分析,包括注解的解析和应用;
-
Types:提供了一组用于类型操作的实用方法,如确定一个类型是否可以赋值给另一个类型,或者查找最近公共祖先类型等;
-
Flow: 负责进行控制流分析,比如检查变量在使用前是否已经被赋值,或者方法是否总是有返回值;
-
LambdaToMethod:Java 8 引入了 Lambda 表达式,这个类负责将 Lambda 表达式转换为匿名类或静态方法;
-
TransTypes和Lower: 这些类负责某些类型转换,包括泛型的桥接方法和自动装箱/拆箱。
4. 扩展阅读