第七章语法制导翻译和中间代码生成
7.1语义处理概述
7.2属性文法和 语法制导翻译
7.3 中间代码生成 (一些语句的翻译 )
7.4符号表源语言程序中间代码汇编代码词法分析语义分析语法分析中间代码生成代码生成在编译中的逻辑阶段前端处理后端处理语义处理
7.1 语义处理( 语义分析和中间代码生成)
源语言程序汇编代码词法分析语义分析语法分析代码生成前端处理后端处理语义处理语义处理语义处理语义处理的任务:
静态语义检查
静态语义,语法规则的良形式条件
静态语义检查:审查静态语义
动态语义处理
动态语义:程序单元执行的操作
动态语义处理,生成 (中间 /目标 )代码语义处理语义处理的实现:
属性文法:描述语义规则。
语法制导翻译:在语法分析的同时,执行语义规则描述的动作:
检查静态语义
生成中间代码 /目标代码语义处理语义处理的环境:符号表
为语义分析提供类型、作用域等信息。
为代码生成提供类型、作用域、存储类别、
存储(相对)位置等信息。
语义处理
if sym = callsym then
begin
getsym;
if sym <> ident then error(14)
else begin
i,= position(id);
if i = 0 then error(11)
else
with table[i] do
if kind = procedur
then gen(cal,lev-level,adr)
else error(15);
getsym
end
end
PL/0编译程序的语义处理(一) call语句的处理语义处理
error 11:标识符未说明;
error 12:赋值语句中,赋值号左部的标识符属性应为变量;
error 15,call后标识符的属性应为过程;
error 21:表达式内标识符属性不能为过程;
error 32,read语句括号中的标识符不是变量。

PL/0编译程序的语义处理(二)语义错误列表
PL/0编译器的符号表
const a=10;
var b,c;
procedure p;
beginc:=b+a;
end;
begin
read(b);
while b#0 do
begin
call p;
write(2*c);
read(b);
end
end.
name kind val level address size
a constant 10
b variable 0 3
c variable 0 4
p procedure 0 3
编译程序分析到第 8行时符号表的内容
const a=10;
var b,c;
procedure p;
begin
c:=b+a;
end;
begin
read(b);
while b#0 do
begin
call p;
write(2*c);
read(b);
end
end.
( 0) jmp 0 8 转向 主程序入口
( 1) jmp 0 2 转向 过程 p入口
( 2) int 0 3 过程 p入口,为过程 p开辟空间
( 3) lod 1 3 取变量 b的值到栈顶
( 4) lit 0 10 取常数 10到栈顶
( 5) opr 0 2 次栈顶与栈顶相加
( 6) sto 1 4 栈顶值送变量 c中
( 7) opr 0 0 退栈并返回调用点 (16)
( 8) int 0 5 主程序入口开辟 5个栈空间
( 9) opr 0 16 从命令行读入值置于栈顶
(10) sto 0 3 将栈顶值存入变量 b中
(11) lod 0 3 将变量 b的值取至栈顶
(12) lit 0 0 将常数值 0进栈
(13) opr 0 9 次栈顶与栈顶是否不等
(14) jpc 0 24 等时转 (24)( 条件不满足转 )
(15) cal 0 2 调用过程 p
(16) lit 0 2 常数值 2进栈
(17) lod 0 4 将变量 c的值取至栈顶
(18) opr 0 4 次栈顶与栈顶相乘 (2*c)
(19) opr 0 14 栈顶值输出至屏幕
(20) opr 0 15 换行
(21) opr 0 16 从命令行读取值到栈顶
(22) sto 0 3 栈顶值送变量 b中
(23) jmp 0 11 无条件转到循环入口 (11)
(24) opr 0 0 结束退栈归纳:语义分析(静态语义处理)
( 1) 类型检查 。 验证程序中执行的每个操作是否遵守语言的类型系统的过程,,编译程序必须报告不符合类型系统的信息 。
( 2) 控制流检查 。 控制流语句必须使控制转移到合法的地方 。
例如,在 C语言中 break语句使控制跳离包括该语句的最小
while,for或 switch语句 。 如果不存在包括它的这样的语句,
则就报错 。
( 3) 一致性检查 。 在很多场合要求对象只能被定义一次 。 例如 Pascal语言规定同一标识符在一个分程序中只能被说明一次,同一 case语句的标号不能相同,枚举类型的元素不能重复出现等等 。
( 4) 相关名字检查 。 有时,同一名字必须出现两次或多次 。
例如,Ada 语言程序中,循环或程序块可以有一个名字,
出现在这些结构的开头和结尾,编译程序必须检查这两个地方用的名字是相同的 。
(5)名字的作用域分析类型检查程序的设计
1.辨认语言中可用的类型
2.辨认具有类型的语言结构
3.辨认语言的语义规则基础 类型的 基本概念类型的 基本概念
数据类型的三要素,
用于区别这种数据类型的数据对象的属性这种类型数据对象可以具有的值可以施用在这种类型的数据对象上的操作
数据类型分为:
基本 (初等 )数据类型:数值数据,逻辑数据,字符数据,指针类型 等。
复合数据类型,数组、结构、表、栈、树等。
抽象数据类型,Ada的包 (Package),C++的类
(Class)等。
类型的基本概念类型的等价关系和相容关系:
等价关系
如果在任何场合下,类型 A和类型 B的表达式都可以互相替代,则称类型 A与类型 B等价。
相容关系
如果在类型 A的表达式出现的任何场合下,
都可以用类型 B的表达式替换,则称类型 B
相容于类型 A。
类型的 基本概念声明和定义,使用:
声明:
程序通过声明语句把标识符的名称、类型和作用域等信息传递给编译器。
声明语句本身传递名字和类型信息,声明语句的位置传递作用域信息。
定义:
变量、类的声明就是定义。
函数可以先声明一个原型,在定义中再给出实现的代码。
类型的 基本概念强类型语言和弱类型语言:
强类型语言
标识符必须先声明后才能使用。
标识符的类型信息在编译时是已知的。
例如,PASCAL,C
弱类型语言
标识符不声明就可以直接引用。
标识符的类型信息在编译时不能确定,在运行时推测得到。
例如,Lisp,ML
运算符(函数)的重载 多态函数重载运算符 ( overloading operator) 是指该符号有多个含义,
但根据上下文可以确定唯一的运算 。 如+是重载符号,在 A
+ B中,当 A和 B为整数,实数,复数或者矩阵时,运算符执行不同类型的运算,当出现重载运算符时,要确定它所表示的唯一的意义,即进行运算符识别并检查运算符的操作数 。
多态函数 (过程 )----函数 (过程 )允许参数的类型变化,多态函数 (过程 )的特点是,每次被调用时,传递过来的参数可以具有不同类型 。
作用域分析
分程序结构的作用域规则
作用域分析的实现作用域作用域类型,
Global scope
在任何函数和类定义之外的区域。
所声明的标识符具有全局作用域。
Class scope
特指类定义的作用域。
所声明的类类型具有全局作用域。
所声明的类成员具有类作用域。
Function scope
特指函数形参表中参数的作用域。
所声明的函数具有全局作用域,或类作用域,或其它局部作用域 。
所声明的形参具有函数作用域。
Local scope
由定界符 { }/begin end分隔,定义在过程 (函数 )体或其它局部作用域 (分程序 )内。
所声明的标识符具有局部作用域。
分程序结构语言的作用域规则
当前开放的作用域中的标识符才能够被访问;
在同一作用域中,不允许声明同名的标识符;
在不同的作用域中,内层声明的标识符将遮蔽外层声明的同名标识符,使其变为不可见的。
{ int a …
{ char a; …
{ float a; …

…a… ∥ 引用 char a;


函数形参表中的变量在函数体中是可见的;
int a;
float func( float a,float b)
{
… a… ∥ 引用 float a;

分程序结构语言作用域举例
(1) main(){
(2) int a;
(3) …
(4) {
(5) bool a;
(6) …
(7) {
(8) double a;
(9) …
(10) }
(11) {
(12) …a…
(13) }
(14) }
(15) …
(16) }
scope 3
scope 2
scope 1
scope 1,(1) - (16)
scope 2,(4) - (14) 嵌套于 scope 1中
scope 3,(7) - (10) 嵌套于 scope 2中
scope 4,(11)- (12)嵌套于 scope 2中
a(int) 在 scope 1中可见;
a(bool) 在 scope 2,4中可见;
a(double)在 scope 3中可见程序运行到第 12行时:
scope 4是当前作用域;
scope 1,2,4是开放的作用域;
scope 3是关闭的作用域。
scope 4