语义分析和中间代码的产生

赋值语句翻译

算数表达式

属性文法略
翻译模式:
S→id:=E { p:=lookup(id.name);
if p!=nil then emit(p ‘:=’ E.place)
else error }
E→E1+E2 { E.place:=newtemp;
emit(E.place ‘:=’ E1.place ‘+’ E2.place)}
E→E1E2 { E.place:=newtemp;
emit(E.place ‘:=’ E 1.place ‘
’ E 2.place)}
E→-E1 { E.place:=newtemp;
emit(E.place‘:=’ ‘uminus’ E 1.place) }
E→(E1) { E.place:=E1.place }
E→id { p:=lookup(id.name);
if p!=nil then E.place:=p
else error }
E用来表示产生终结符。
相关属性说明:
place:综合属性,表示存放E值单元的名字或地址
code:综合属性,三地址代码。
相关函数说明:
newtemp:产生一个中间临时变量,如T1,T2…
emit:产生三地址代码(可能使用四元式形式表达,也可能是原始的三地址代码形式)
lookup:在符号表中查找相应入口

观察此翻译模式得到如下结论:
1、简单算数表达式的文法共6个产生式,注意其中“-”为单目运算符,即表示求相反数,取负号,不是减法。
2、此翻译模式与自下而上语法分析(规范规约)相结合。即从输入串(算数表达式)开始,进行自下而上的规约。每次规约时都进行相应的语义动作。
3、应该重点关注进行emit的语义动作。其中每次归约“-,+,*”及:=时进行emit(此步骤与练习题答案密切相关)
4、添加括号

例子:写出A:=B*(-C+D)产生的三地址代码
画出语法树(正常应该由规范归约产生,在规范归约过程中,一边产生语法树,一边进行翻译,但归约较麻烦,因此可以直接利用最左推导产生语法树,在利用语法树,判断归约过程):

共进行了4次emit,根据归约顺序:
T1:=@C
T2:=T1+ T1
T3:=BT2
A:=T3
观察语法树得到如下特点:
1、赋值号:=左边的终结符A未被归约成E,即A不是句柄,因为不存在E:=E这样的句型。
2、赋值号右边的终结符都要先归约成E,在进行含“-,+,
”的产生式归约时,每次归约都要emit。

含数组引用的翻译

数组元素地址的计算
符号说明:low为数组下界,base为数组A的第一个元素的地址即A[low]的地址(可以用数组名来表示),w为每个数组元素宽度。
1、一维数组:A[i]的地址为 base+(i-low)w=iw+(base-loww)
观察发现i
w随i变化,为可变部分,而base-low*w只与数组本身有关,为不变部分。
2、多维数组**(按行存放)**:A[i1,i2,…,ik]地址公式
((…i1 n2+i2)n3+i3)…)nk+ik)×w +base-((…((low1 n2+low2)n3+low3)…)nk+lowk)×w
同理可分为不变部分与可变部分。注意公式的递推性质:以不变部分为例,k(k>1)维不变部分为k-1维乘k维元素的维度加上k维,再乘w。不变部分即把i换成low,再用base减。

翻译模式
添加产生式:
L → id [ Elist ] | id
Elist → Elist,E | E
为了便于处理,文法改写为
L → Elist ] | id (即读到“]”就说明产生了一个数组元素)。
Elist → Elist, E | id [ E (读到[说明出现了数组元素,E表示最左下标,EList表示所有下标)
翻译模式:
1) S→L:=E
{ if L.offset=null then /L是简单变量/
emit(L.place ‘:=’ E.place)
else emit( L.place ‘ [’ L.offset ‘]’ ‘:=’ E.place)}

(2) E→E1 +E2
{ E.place:=newtemp;
emit(E.place ‘:=’ E 1.place ‘+’ E 2.place)}
(3) E→(E1) {E.place:=E1.place}

(4) E→L
{ if L.offset=null then /L是简单变量/
E.place:=L.place
else begin
E.place:=newtemp;
emit(E.place ‘:=’ L.place ‘[’ L.offset ‘]’ )
end
}
(5) L→Elist ]
{ L.place:=newtemp;
emit(L.place ‘:=’ Elist.array ‘-’ C); (数组名即为base)
L.offset:=newtemp;
emit(L.offset ‘:=’ w ‘*’ Elist.place) }

(6) L→id { L.place:=id.place; L.offset:=null }
(7) Elist→ Elist1, E
{ t:=newtemp;
m:=Elist1.ndim+1;
emit(t ‘:=’ Elist1.place ‘*’ limit(Elist1.array,m) );
emit(t ‘:=’ t ‘+’ E.place);**(此两条语义动作与之前描述的递归性相关)
Elist.place:=t;
Elist.ndim:=m
Elist.array:= Elist1.array;
}
(8) Elist→id [ E
{ Elist.place:=E.place;
Elist.ndim:=1;
Elist.array:=id.place }
函数与属性说明:
Elist有三个综合属性
Elist.ndim: 下标个数计数器
Elist.place: 保存临时变量的名字,存放已扫描的Elist中的下标计算出来的值(为了得到可变部分地址)
Elist.array: 保存数组名,数组名用来表示首元素地址。
limit(array,j) :函数过程,它给出数组array的第j维的长度
L有两个综合属性
L.place
若L为简单变量i, 指变量i的符号表入口
若L为下标变量,指存放不变部分的临时变量的名字
L.offset
若L为简单变量,null
若L为下标变量,指存放可变部分的临时变量的名字

将此文法与简单算数表达式对比:
1、共8条产生式,后4条的意义是,L用来表示产生终结符id或者数组元素id[Elist]。如果将一个数组元素看成终结符的话,此处的L与之前的简单算数表达式文法中的id对应。
2、8,7,5三条表达式是为了得到数组元素的可变地址与不可变地址,从8开始,利用7进行更新,5结束。
3、表达式4是为了得到终结符的地址(将数组元素看成一个终结符),用不变[可变]表示数组元素地址。

类型转换

增加综合属性E.type非终结符E的类型属性,只有integer和real两种取值。
1、相同类型可以进行运算,不同类型运算前需转换。转换只能从integer变到real
2、在运算符前添加real或integer。
3、运算结果为integer但且仅当参与运算的元素都为integer。

总结赋值语句翻译

1、翻译与自下而上的语法分析结合,每次用产生式归约时,进行相应的语义动作。
2、给定待翻译赋值语句中含数组元素,普通终结符,运算符号,()。在普通算数表达式中E产生id即终结符,再含数组元素时,L代替id的位置,L可能产生终结符或数组元素。注意赋值号左边的终结符在归约时的处理
3、翻译时重点关注emit动作。
4、数组元素主要针对二维数组。
3、提到类型时要进行类型转换。

基于陈火旺主编国防科技大学编写的《程序设计语言编译原理》

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值