编译原理第七章
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
2014年6月15日12时14分
对于多维数组也可作类似处理。一个二维数组,可以按 行或按列存放。FORTRAN采用按列存放,PASCAL采用 按行存放。
A[1,1] 第一行 A[1,2] A[1,3] A[2,1] 第二行 A[2,2] A[2,3] A[1,1] A[2,1] A[1,2] A[2,2] A[1,3] A[2,3] 第三列 第二列 第一列
2014年6月15日12时14分
7.4 布尔表达式的翻译 在程序设计中,布尔表达式有两个基本作用:一个是用作 计算逻辑值;另一个用作控制流语句之中的条件表达式。 计算布尔表达式的值通常有两种办法。一种办法是,如同 计算算术表达式一样,一步不差地从表达式各部分的值计 算出整个表达式的值。例如,按通常的习惯,用数值1代 表true,用0代表false,那么,布尔式1 or (not 0 and 0) or 0的计算过程是: 1 or (not 0 and 0) or 0 =1 or (1 and 0) or 0 =1 or 0 or 0 =1 or 0 =1
子表达式 (base-((low1*n2)+low2)*w的值是可以在编译时 确定的。
2014年6月15日12时14分
例 1 设A为一个10*20的数组,即n1=10,n2=20。并设w=4。 赋值语句x:=A[y, z] 被翻译成如下三地址语句序列: T1:=y*20 T1:=T1+z T2:=A-84 T3:=4*T1 T4:=T2[T3] x:=T4
2014年6月15日12时14分
产生式
P→D
D→D;D D→id:T T→integer T→real
语义规则
{ offset:=0}
{enter(id.name, T.type, offset); offset:=offset+T.width} { T.type:=integer;
T.width:=4}
{T.type:=real; T.width:=8} T→ array[num] of T1 {T.type:=array(num.val, T1.type); T.width:=num.val*T1.width}
2014年6月15日12时14分
7.3 赋值语言句的翻译 7.3.1 简单算术表达式及赋值语言句 属性id.name表示id所代表的名字本身; 过程lookup(id.name)检查符号表中相应此名字的入口; 过程emit产生三地址代码。
2014年6月15日12时14分
7.3.2 数组元素的引用 假设数组A每个元素宽度为w,则A[i]这个元素的起始地址 为 base+(i-low)*w 其中low为数组下标的下界并且base是分配给数组的相对 地址。对上式进行整理,得到 i*w+(base-low*w)
则其中子表达式C=base-low*w可以在处理数组说明时计算 出来,A[i]相对地址可由i*w+C计算出来。
2014年6月15日12时14分
上述这两种计算法对于不包含布尔函数调用的式子是没有 什么差别的。但是,假若一个布尔式中含有布尔函数调用, 并且这种函数调用引用起副作用 (指对全局量的赋值) 时, 那么,上述两种计算法未必是等价的。
7.4.1 数值表示法
用1表示真,0表示假来实现布尔表达式的翻译。
E→E1 or E2 E→E1and E2 {E.place:=newtemp; emit(E.place’:=’E1.place’or’E2.place)} {E.place:=newtemp; emit(E.place’:=’E1.place’and’E2.place)}
{E.place:=id.place);
E→id
2014年6月15日12时14分
例 7.2 将布尔表达式a<b or c<d and e<f 翻译成三地址代码。
100 if a<b goto 103 101 T1:=0 102 goto 104 103 T1:=1 104 if c<d goto 107
2014年6月15日12时14分
另一种计算法是采取某种优化。假定要计算A or B,如果 计算出A 的值为1,那么,B的值就无须计算了。因为不管 B的结果是什么,A or B的值都为1。同时,在计算A and B 时,若发现A为0,则B的值也就无需再计算了。这种计算 法可以用if-then-else来解释or,and和not。也就是 把 A or B 解释成 把 A and B 解释成 把 not A 解释成 if A then true else B if A then B else false if A then false else true
编译原理
主讲教师:雷向东
Baidu Nhomakorabea
2014年6月15日12时14分
第七章 语义分析和中间代码产生
紧接词法分析和语法分析之后,编译程序要做的工作就是 进行静态语义检查和翻译。静态语义检查通常包括: (1) 类型检查;如果操作符作用于不相容的操作数,编译 程序必须报告出错信息。 (2) 控制流检查;控制流语句必须使控制转移到合法的地 方。如果不存在包括它的这样的语句,则应报错。 (3) 一致性检查;在很多场合要求对象只能被定义一次。 (4) 相关名字检查。有时,同一名字必须出现两次或多次。 其他如名字的作用域分析等也都是静态语义分析的工作。
2014年6月15日12时14分
例如,赋值语句a:=b*-c+b*-c的四元式为
Op (0) (1) (2) (3) (4) uminus * uminus * + Arg1 c b c b T3 T3 T4 T1 Arg2 Result T1 T2 T3 T4 T5
(5)
:=
T5
a
2014年6月15日12时14分
按行存放
2014年6月15日12时14分
按列存放
若二维数组A按行存放,则可用如下公式计算A[i1, i2]的相 对地址: base+((i1-low1)*n2+i1-low2)*w
其中,low1,low2分别为i1、i2的下界;n2是i2可取值的 个数。即若high2为i2的上界,则n2=high2-low2+1。假定i1、 i2是编译时唯一尚未知道的值,重写上述表达式为 ((i1*n2)+i2)*w+(base-((low1*n2)+low2)*w)
(4) 三元式 为了避免把临时变量填入到符号表,我们可以通过计算这 个临时变量值的语句的位置来引用这个临时变量。这样表 示三地址代码的记录只需要三个域:op、arg1和arg2。因 为用了三个域,所以称之为三元式。例如,赋值语句 a:=b*-c+b*-c的三元式为
Op (0) uminus Arg1 c Arg2
2014年6月15日12时14分
虽然源程序可以直接翻译为目标语言代码,但是许多编译 程序却采用了独立于机器的、复杂性介于源语言和机器语 言之间的中间语言。这样的好处是: (1) 于进行与机器无关的代码优化工作;
(2) 便编译程序改变目标机更容易;
(3) 便编译程序的结构在逻辑上更为简单明确。以中间语 言为界面,编译前端和后端的接口更清晰。
(1)
(2) (3) (4) (5)
2014年6月15日12时14分
*
uminus * + :=
b
c b (1) a
(0)
(2) (3) (4)
(5)间接三元式
为了便于代码优化处理,另设一张指示器(称为间接码表) 它将按运算符的顺序列出有关三元式在三元表中的位置。 换句话说就是,我们用一张间接码表辅以三元式表的办法 来表示中间代码。这种表示法称为间接三元式。 当在代码优化过程中需要调整运算顺序时,只需重新安排 间接码表,无需改动三元式表。事实上,改动三元式表是 很困难的,因为,许多三元式通过指示器紧密相联系。
2014年6月15日12时14分
非终结符号T有两个综合属性 T.type 和T.width,分别表示 名字的类型和名字的域宽 (即该类型名字所占用的存储单 元个数)。 假定整数类型域宽为4;实数域宽为8;一个数组的域宽可 以通过把数组元素数目与一个元素的域宽相乘获得;每个 指针类型的域宽假定为4。 计算说明语句中名字的类型和相对地址的翻译模式如下:
S→id:=E {p:=lookup(id.name); if p≠nil then emit(p’:=’E.place) else error} {E.place:=newtemp; emit(E.place’:=’E1.place’+’E2.place)}
E→E1+E2
2014年6月15日12时14分
2014年6月15日12时14分
(2) 三地址代码
三地址代码是由下面一般形式的语句构成的序列:
x:=y op z
其中,x,y,z为名字、常数或编译时产生的临时变量; op代表运算符号加定点运算符、浮点运算符、逻辑运算符 等等。每个语句的右边只能有一个运算符。例如,源语言 表达式 x+y*z 可以被翻译为如下语句序列:
2014年6月15日12时14分
E→not E1 E→(E1)
{E.place:=newtemp; emit(E.place’:=’’not’E1.place)} {E.place:=E1.place}
E→id1 relop id2
{E.place:=newtemp; emit(‘if’id1.place relop.op id2.place’goto’nestquad+3); emit(E.place’:=’’0’); emit(‘goto’nestquad+2); emuit(E.place’:=’’1’)}
T1:=y*z T2:=x+T1 其中,T1,T2为编译时产生的临时变量。三地址语句通 常有三种表示方法:四元式、三元式、间接三元式。
2014年6月15日12时14分
(3) 四元式
一个四元式是一个带有四个域的记录结构,这四个域分别 称为op、arg1、arg2及result。域op包含一个代表运算符的 内部码。三地址语句 x;=y op z 可表示为:将y置于arg1域, z置于arg2域,x置于result域,:=为算符。通常,四元式中 的arg1,arg2和result的内容都是一个指针,此指针指向有 关名字的符号表入口。这样,临时变量也要填入符号表中。
(1)
7.2 说明语句
在C、PASCAL、FORTRAN D 等语言的语法中,允许在 一个过程中的所有 说明语句作为一个组来处理, 把它们 安排在一个数据区中。 全程变量offset来跟踪一个可用的相对地址的位置; 过程enter (name, type, offset) 用来把名字name填入到符号 表中,并给出此名字的类型为type及在过程数据区中的相 对地址为offset。
E→E1*E2 E→-E1 E→(E1) E→id
{E.place:=newtemp; emit(E.place’:=’E1.place’*’E2.place)} {E.place:=newtemp; emit(E.place’:=’’uminus’E1.place)} {E.place:=E1.place} {p:=lookup(id.name); if p≠nil then E.place:=p else error}
2014年6月15日12时14分
静态语义检查和中间代码产生的编译程序中的地位
中间代码 语法分析器 静态检查器 优化器
中间代码产生 器
2014年6月15日12时14分
7.1中间语言 常见中间语言形式:后缀式、四元式、三元式和间接三元 式。 (1) 后缀式,又称逆波兰表示式,这种表示法是,把运算量 (操作数) 写在前面,把算符写在后面 (后缀)。这种表示法 用不着使用括号。 例如,表达式 a* (b+ c)的后缀式为abc* 表达式(a+b)*(c+d)的后缀式为ab+cd+* 只要知道每个算符的目数,对于后缀式,不论文从哪一端 进行扫描,都能对它正确进行唯一分解。 后缀表示形式可以从表达式推广到其它语言成分。
2014年6月15日12时14分
例如,语句
X:=(A+B)*C
Y:=D ↑(A+B)
间接三元式为
间接代码
(1) (2) (3)
三元式表
Op (1) (2) (3) + * := arg1 A (1) X arg2 B C (2)
(4)
(5)
2014年6月15日12时14分
(4)
(5)
↑
:=
D
Y
(1)
对于多维数组也可作类似处理。一个二维数组,可以按 行或按列存放。FORTRAN采用按列存放,PASCAL采用 按行存放。
A[1,1] 第一行 A[1,2] A[1,3] A[2,1] 第二行 A[2,2] A[2,3] A[1,1] A[2,1] A[1,2] A[2,2] A[1,3] A[2,3] 第三列 第二列 第一列
2014年6月15日12时14分
7.4 布尔表达式的翻译 在程序设计中,布尔表达式有两个基本作用:一个是用作 计算逻辑值;另一个用作控制流语句之中的条件表达式。 计算布尔表达式的值通常有两种办法。一种办法是,如同 计算算术表达式一样,一步不差地从表达式各部分的值计 算出整个表达式的值。例如,按通常的习惯,用数值1代 表true,用0代表false,那么,布尔式1 or (not 0 and 0) or 0的计算过程是: 1 or (not 0 and 0) or 0 =1 or (1 and 0) or 0 =1 or 0 or 0 =1 or 0 =1
子表达式 (base-((low1*n2)+low2)*w的值是可以在编译时 确定的。
2014年6月15日12时14分
例 1 设A为一个10*20的数组,即n1=10,n2=20。并设w=4。 赋值语句x:=A[y, z] 被翻译成如下三地址语句序列: T1:=y*20 T1:=T1+z T2:=A-84 T3:=4*T1 T4:=T2[T3] x:=T4
2014年6月15日12时14分
产生式
P→D
D→D;D D→id:T T→integer T→real
语义规则
{ offset:=0}
{enter(id.name, T.type, offset); offset:=offset+T.width} { T.type:=integer;
T.width:=4}
{T.type:=real; T.width:=8} T→ array[num] of T1 {T.type:=array(num.val, T1.type); T.width:=num.val*T1.width}
2014年6月15日12时14分
7.3 赋值语言句的翻译 7.3.1 简单算术表达式及赋值语言句 属性id.name表示id所代表的名字本身; 过程lookup(id.name)检查符号表中相应此名字的入口; 过程emit产生三地址代码。
2014年6月15日12时14分
7.3.2 数组元素的引用 假设数组A每个元素宽度为w,则A[i]这个元素的起始地址 为 base+(i-low)*w 其中low为数组下标的下界并且base是分配给数组的相对 地址。对上式进行整理,得到 i*w+(base-low*w)
则其中子表达式C=base-low*w可以在处理数组说明时计算 出来,A[i]相对地址可由i*w+C计算出来。
2014年6月15日12时14分
上述这两种计算法对于不包含布尔函数调用的式子是没有 什么差别的。但是,假若一个布尔式中含有布尔函数调用, 并且这种函数调用引用起副作用 (指对全局量的赋值) 时, 那么,上述两种计算法未必是等价的。
7.4.1 数值表示法
用1表示真,0表示假来实现布尔表达式的翻译。
E→E1 or E2 E→E1and E2 {E.place:=newtemp; emit(E.place’:=’E1.place’or’E2.place)} {E.place:=newtemp; emit(E.place’:=’E1.place’and’E2.place)}
{E.place:=id.place);
E→id
2014年6月15日12时14分
例 7.2 将布尔表达式a<b or c<d and e<f 翻译成三地址代码。
100 if a<b goto 103 101 T1:=0 102 goto 104 103 T1:=1 104 if c<d goto 107
2014年6月15日12时14分
另一种计算法是采取某种优化。假定要计算A or B,如果 计算出A 的值为1,那么,B的值就无须计算了。因为不管 B的结果是什么,A or B的值都为1。同时,在计算A and B 时,若发现A为0,则B的值也就无需再计算了。这种计算 法可以用if-then-else来解释or,and和not。也就是 把 A or B 解释成 把 A and B 解释成 把 not A 解释成 if A then true else B if A then B else false if A then false else true
编译原理
主讲教师:雷向东
Baidu Nhomakorabea
2014年6月15日12时14分
第七章 语义分析和中间代码产生
紧接词法分析和语法分析之后,编译程序要做的工作就是 进行静态语义检查和翻译。静态语义检查通常包括: (1) 类型检查;如果操作符作用于不相容的操作数,编译 程序必须报告出错信息。 (2) 控制流检查;控制流语句必须使控制转移到合法的地 方。如果不存在包括它的这样的语句,则应报错。 (3) 一致性检查;在很多场合要求对象只能被定义一次。 (4) 相关名字检查。有时,同一名字必须出现两次或多次。 其他如名字的作用域分析等也都是静态语义分析的工作。
2014年6月15日12时14分
例如,赋值语句a:=b*-c+b*-c的四元式为
Op (0) (1) (2) (3) (4) uminus * uminus * + Arg1 c b c b T3 T3 T4 T1 Arg2 Result T1 T2 T3 T4 T5
(5)
:=
T5
a
2014年6月15日12时14分
按行存放
2014年6月15日12时14分
按列存放
若二维数组A按行存放,则可用如下公式计算A[i1, i2]的相 对地址: base+((i1-low1)*n2+i1-low2)*w
其中,low1,low2分别为i1、i2的下界;n2是i2可取值的 个数。即若high2为i2的上界,则n2=high2-low2+1。假定i1、 i2是编译时唯一尚未知道的值,重写上述表达式为 ((i1*n2)+i2)*w+(base-((low1*n2)+low2)*w)
(4) 三元式 为了避免把临时变量填入到符号表,我们可以通过计算这 个临时变量值的语句的位置来引用这个临时变量。这样表 示三地址代码的记录只需要三个域:op、arg1和arg2。因 为用了三个域,所以称之为三元式。例如,赋值语句 a:=b*-c+b*-c的三元式为
Op (0) uminus Arg1 c Arg2
2014年6月15日12时14分
虽然源程序可以直接翻译为目标语言代码,但是许多编译 程序却采用了独立于机器的、复杂性介于源语言和机器语 言之间的中间语言。这样的好处是: (1) 于进行与机器无关的代码优化工作;
(2) 便编译程序改变目标机更容易;
(3) 便编译程序的结构在逻辑上更为简单明确。以中间语 言为界面,编译前端和后端的接口更清晰。
(1)
(2) (3) (4) (5)
2014年6月15日12时14分
*
uminus * + :=
b
c b (1) a
(0)
(2) (3) (4)
(5)间接三元式
为了便于代码优化处理,另设一张指示器(称为间接码表) 它将按运算符的顺序列出有关三元式在三元表中的位置。 换句话说就是,我们用一张间接码表辅以三元式表的办法 来表示中间代码。这种表示法称为间接三元式。 当在代码优化过程中需要调整运算顺序时,只需重新安排 间接码表,无需改动三元式表。事实上,改动三元式表是 很困难的,因为,许多三元式通过指示器紧密相联系。
2014年6月15日12时14分
非终结符号T有两个综合属性 T.type 和T.width,分别表示 名字的类型和名字的域宽 (即该类型名字所占用的存储单 元个数)。 假定整数类型域宽为4;实数域宽为8;一个数组的域宽可 以通过把数组元素数目与一个元素的域宽相乘获得;每个 指针类型的域宽假定为4。 计算说明语句中名字的类型和相对地址的翻译模式如下:
S→id:=E {p:=lookup(id.name); if p≠nil then emit(p’:=’E.place) else error} {E.place:=newtemp; emit(E.place’:=’E1.place’+’E2.place)}
E→E1+E2
2014年6月15日12时14分
2014年6月15日12时14分
(2) 三地址代码
三地址代码是由下面一般形式的语句构成的序列:
x:=y op z
其中,x,y,z为名字、常数或编译时产生的临时变量; op代表运算符号加定点运算符、浮点运算符、逻辑运算符 等等。每个语句的右边只能有一个运算符。例如,源语言 表达式 x+y*z 可以被翻译为如下语句序列:
2014年6月15日12时14分
E→not E1 E→(E1)
{E.place:=newtemp; emit(E.place’:=’’not’E1.place)} {E.place:=E1.place}
E→id1 relop id2
{E.place:=newtemp; emit(‘if’id1.place relop.op id2.place’goto’nestquad+3); emit(E.place’:=’’0’); emit(‘goto’nestquad+2); emuit(E.place’:=’’1’)}
T1:=y*z T2:=x+T1 其中,T1,T2为编译时产生的临时变量。三地址语句通 常有三种表示方法:四元式、三元式、间接三元式。
2014年6月15日12时14分
(3) 四元式
一个四元式是一个带有四个域的记录结构,这四个域分别 称为op、arg1、arg2及result。域op包含一个代表运算符的 内部码。三地址语句 x;=y op z 可表示为:将y置于arg1域, z置于arg2域,x置于result域,:=为算符。通常,四元式中 的arg1,arg2和result的内容都是一个指针,此指针指向有 关名字的符号表入口。这样,临时变量也要填入符号表中。
(1)
7.2 说明语句
在C、PASCAL、FORTRAN D 等语言的语法中,允许在 一个过程中的所有 说明语句作为一个组来处理, 把它们 安排在一个数据区中。 全程变量offset来跟踪一个可用的相对地址的位置; 过程enter (name, type, offset) 用来把名字name填入到符号 表中,并给出此名字的类型为type及在过程数据区中的相 对地址为offset。
E→E1*E2 E→-E1 E→(E1) E→id
{E.place:=newtemp; emit(E.place’:=’E1.place’*’E2.place)} {E.place:=newtemp; emit(E.place’:=’’uminus’E1.place)} {E.place:=E1.place} {p:=lookup(id.name); if p≠nil then E.place:=p else error}
2014年6月15日12时14分
静态语义检查和中间代码产生的编译程序中的地位
中间代码 语法分析器 静态检查器 优化器
中间代码产生 器
2014年6月15日12时14分
7.1中间语言 常见中间语言形式:后缀式、四元式、三元式和间接三元 式。 (1) 后缀式,又称逆波兰表示式,这种表示法是,把运算量 (操作数) 写在前面,把算符写在后面 (后缀)。这种表示法 用不着使用括号。 例如,表达式 a* (b+ c)的后缀式为abc* 表达式(a+b)*(c+d)的后缀式为ab+cd+* 只要知道每个算符的目数,对于后缀式,不论文从哪一端 进行扫描,都能对它正确进行唯一分解。 后缀表示形式可以从表达式推广到其它语言成分。
2014年6月15日12时14分
例如,语句
X:=(A+B)*C
Y:=D ↑(A+B)
间接三元式为
间接代码
(1) (2) (3)
三元式表
Op (1) (2) (3) + * := arg1 A (1) X arg2 B C (2)
(4)
(5)
2014年6月15日12时14分
(4)
(5)
↑
:=
D
Y
(1)