语义分析概述
程序设计语言的结构常常使用上下文无关文法来描述,基于此开发的语法分析程序可以检查程序中存在的语法错误。但是语法正确的程序不一定时完全正确的,程序的正确还和程序中的上下文有关系:
- 变量的作用于问题
- 同一作用域中的同名变量问题
- 表达式和赋值语句中的类型一致问题
注意:
设计使用上下文有关文法来描述语言中的上下文结构在理论上是可行的,但是实践上非常困难。
因此,使用语法制导的翻译技术实现语义的分析,设计专门的语义动作补充山下文无关文法的分析程序。
语义分析的任务
语义分析程序通过将变量的定义和变量的引用联系起来,对源程序的含义进行检查。
语义分析程序会在分析声明语句时,将所声明标识符的信息收集到符号表中,收集到信息包括:类型、存储位置、作用域等。主要编译时控制处在声明该标识符的程序块中,就可以从符号表中查到该标识符的记录。
类型检查可以分成两种:在目标程序运行时进行的检查称为动态检查。而读入源程序但不执行源程序的情况下进行的检查就是静态检查。类型检查的内容包括:
- 检验结构的类型是否和上下文所期望的一致、检查操作的合法性和数据类型的相容性
- 唯一性检查:一个标识符在同一作用域中只能被声明一次
- 控制流检查:控制语句是否转移到一个合法的位置
语义分析程序的位置
以语法树作为基础,根据语言的语义,检查每个语法程序在语义上是否满足上下文对于它的要求。
同时,语义分析的结果也有利于生成正确的目标代码,例如在存在重载运算符和类型强制转换的场景中。
错误处理
语义分析程序在发现错误时,需要显示出错信息,报告错误出现的位置和错误的性质。在完成报告之后还需要恢复分析器,继续对后面的结构进行检查。
符号表
符号表在翻译过程中有着两方面的作用:
- 检查语义的正确性
- 辅助正确的生成代码
符号表是一张动态表,在编译期间符号表的入口会不断的增加或者删除。编译程序需要频繁和符号表进行交互,符号表的效率会直接影响到编译程序的效率。
符号表的建立和访问时机
对于多遍的编译程序:
在词法分析的阶段建立符号表,标识符在符号表中的位置作为记号的属性。适用于非块结构语言的编译。
对于合并遍的编译程序:
符号表的内容
符号表的操作
符号表组织
非块结构语言
块结构语言
对于夸结构语言来说,模块中可嵌套字块,每个块中均可以定义局部变量。每个程序块中有一个字表,保存该块中声明的变量和属性。
符号表使用栈式符号表或者栈式哈希符号表组织。
例如,对于如下的PASCAL
程序:
根据上述程序可以
栈式符号表简介:
当遇到变量声明时,将包含变量属性的记录入栈;当到达块结尾时,将该块中声明的所有变量的记录出栈。在块索引表中记录每个块开始的栈位置。
在栈式符号表中的各种操作:
- 插入:需要检查字表中是否有重名的变量,如果没有就正常入栈,反之报告错误
- 检索:从栈顶到栈底线性检索。如果在当前字表中找到就是局部变量,在其他字表中找到就是非局部变量。通过遍历顺序实现了最近嵌套作用域的原则。
- 定位:将栈顶战阵的位置压入块索引表,块索引表中的元素就指向相应块的字表中第一个记录在栈中的位置。
- 重定位:用块索引表中顶端元素的值恢复栈顶指针,直接清除了刚刚编译完的块在栈中的记录。
栈式哈希表符号表的简介:
使用哈希函数将符号名字映射到符号表中的地址。
类型检查
对于类型检查,不同的语言有着不同的观点。
- 强调最大程序的限制,执行严格的类型检查。
- 强调数据类型应用的灵活性,建议采用隐式类型,在编译时不进行类型检查,在程序运行期间对类型进行扩展检查。
一个简单的类型检查程序
假设现在存在一个简单语言的文法如下: