文章目录
- 一、语义分析
- 1、任务
- 二、符号表
- 1、概述
- 2、操作
- 3、基本结构
- 4、组织方式
- 三、非分程序的符号表
- 1、概念
- 2、标识符的作用域及基本处理办法
- 3、符号表的组织方式
- 四、分程序的符号表:处理作用域嵌套
- 1、概念
- 2、处理方法
- 五、栈式符号表
- 六、基于符号表的存储组织与管理
- 1、运行时的存储组织及管理
- 2、静态存储分配和动态存储分配
- 3、静态存储分配策略
一、语义分析
1、任务
对识别出的各种语法成分进行语义分析(理解意思),并产生相应的中间代码。
- 上下文有关分析:即标识符的作用域 (Scope)
子需求:标识符要先声明,再使用。
这是上下文有关分析 - 类型的一致性检查 (Type Checking)
- 语义处理
声明语句:其语义是声明变量的类型等,并不要求做其他的操作。
编译程序的工作是填符号表,登录名字的特征信息,分配存储。
执行语句:语义是要做某种操作。
语义处理的任务:按某种操作的目标结构生成中间代码
用上下文无关文法只能描述语言的语法结构,而不能描述其语义。因此语法分析不能解决上述问题
二、符号表
1、概述
目的:解决Scope问题
概念:在编译过程中,编译程序用来记录源程序中各种名字的特性信息, 所以也称为名字特性表。
重要性:建表和查表的必要性 (符号表在编译过程中的作用)
源程序中变量要先声明,再引用
- 用户通过声明语句,声明各种名字,并给出它们的类型、维数等信息
编译程序在处理这些声明语句时,将声 明中的名字及其信息登录到符号表中,同时给变量分配存储单元,并将储单元地址登录在符号表中 - 当编译程序编译到引用所声明的变量时(赋值或引用其值),要进行语法语义正确性检查(类型是否符合要求)和生成相应的目标程序,这就需要查符号表以取得相关信息
2、操作
- 填表
当分析到程序中的说明或定义语句时,将说明或定义的名字,以及与之有关的信息填入符号表中。 - 查表
填表前查表,检查在程序的同一作用域内名字是否重复定义。
检查名字的种类是否与说明一致。
对于强类型语言,要检查表达式中各变量的类型是否一致;
生成目标指令时,要取得所需要的地址。
3、基本结构
- “名字”域: 存放名字,一般为标识符的符号串,也可为指向标识符字符串的指针
- “特性”域: 可包括多个子域 , 分别表示标识符的有关信息,如
名字(标识符)的种类:简单变量、函数、过程、数组、标号、参数等
类型:如整型、浮点型、字符型、指针等
性质:变量形参、值形参等
值: 常量名所代表的数值
地址:变量所分配单元的首址或地址位移
大小:所占的字节数
作用域的嵌套层次
4、组织方式
- 统一符号表:不论什么名字都填入统一格式的符号表中
符号表表项应按信息量最大的名字设计,填表、查表比较方便, 结构简单, 但是浪费大量空间。 - 对于不同种类的名字分别建立各种符号表
节省空间, 但是填表和查表不方便。 - 折中办法:大部分共同信息组成统一格式的符号表,特殊信息另设附表,两者用指针连接。
三、非分程序的符号表
1、概念
每个可独立进行编译的程序单元是一个不包含有子模块的单一模块,如FORTRAN语言。
2、标识符的作用域及基本处理办法
- 作用域: 全局:子程序名,函数名和公共区名。 局部: 程序单元中定义的变量。
- 符号表组织
- 基本处理办法
子程序、函数名和公共区名填入全局符号表。
在子程序(函数)声明部分读到标识符,造局部符号表。
在语句部分读到标识符,查表。
程序单元结束: 释放该程序单元的局部符号表。
程序编译完成: 释放全部符号表。
3、符号表的组织方式
无序符号表: 按扫描顺序建表,查表要逐项查找。
有序符号表:符号表按变量名进行字典式排序。
散列符号表(Hash表):符号表地址 = Hash(标识符) 需要解决冲突
四、分程序的符号表:处理作用域嵌套
1、概念
分程序结构语言:模块内可嵌入子模块。
标识符的作用域和基本处理方法:
- 作用域:标识符局部于所定义的模块(最小模块)
模块中所定义的标识符作用域是定义该标识符的子程序
过程或函数说明中定义的标识符(包括形参)其作用域为本过程体
2、处理方法
- 在程序声明部分读到标识符时(声明性出现),建表
- 在语句中读到标识符(引用性出现),查表:
例子:
五、栈式符号表
当过程和函数体编译完成后,应将与之相应的参数名和局部变量名以及后者的特性信息从符号表中删去。
要求:给出一段程序,会画出其栈式符号表。
六、基于符号表的存储组织与管理
1、运行时的存储组织及管理
目标程序运行时所需存储空间的组织与管理以及源程序中变量存储空间的分配
2、静态存储分配和动态存储分配
- 静态存储分配:
在编译阶段由编译程序实现对存储空间的管理和为源程序中的变量分配存储的方法。
如果在编译时能够确定源程序中变量在运行时的数据空间大小,且运行时不改变,那么就可以采用静态存储分配方法。 - 动态存储分配
在目标程序运行阶段由目标程序实现对存储空间的组织与管理,和为源程序中的变量
分配存储的方法。
在目标程序运行时进行变量的存储分配。 编译时要生成进行动态分配的目标指令
3、静态存储分配策略
由于每个变量所需空间的大小在编译时已知,因此可以用简单的方法给变量分配目标地址。
开辟一数据区。(首地址在加载时定)
按编译顺序给每个模块分配存储空间。
在模块内部按顺序给模块的变量分配存储,一般用相对地址,所占数据区的大小由变量类型决定。
目标地址填入变量的符号表中。