第6章 语法制导翻译和中间代码生成Tsu版电.docx
《第6章 语法制导翻译和中间代码生成Tsu版电.docx》由会员分享,可在线阅读,更多相关《第6章 语法制导翻译和中间代码生成Tsu版电.docx(16页珍藏版)》请在冰豆网上搜索。
第6章语法制导翻译和中间代码生成Tsu版电
第6章语法制导翻译和中间代码生成
㈠语法分析和语义分析的区别
㈡语义分析主要工作
①建立符号表和常数表。
②诊察和报告源程序中的语义错误。
③根据语言的语义产生中间代码(或机器指令),或直接解释执行。
6.1语法制导翻译概述
㈠语法制导翻译方法简介
为每一个产生式配一个语义子程序。
在语法分析过程中,当一个产生式获得匹配或用于归约时,此产生式相应的语义子程序进入工作,完成既定的翻译任务。
㈡实现方法(以SLR分析器为例)
①分析表不变
②改造工作栈
●状态栈
●符号栈
●单词值栈:
用于保存单词的值(字符串形式)。
●语义栈:
用于记录分析过程中需保留的语义值。
例,值(val)、地址(addr)、种属(cat)、类型(type)等。
③修改总控程序
●在移进时,除移进状态和单词的种别外,还需移进单词的值。
●在用某个产生式进行归约时,除需执行归约动作外,还需调用相应语义子程序。
㈢解释执行例
①文法及语义子程序
②SLR分析表
③手工计算
7+9*5
6.2符号表和常数表
㈠符号表
①引入符号表的意义
②符号表的结构(略有修改)
struct{
void*addr;//标号或变量的地址
charid[5];//标识符名
unsignedcat:
4;//种属(4个二进制位)
unsignedtype:
4;//类型(4个二进制位)
}sym_table[NS];//NS表示符号表长度
③符号表的使用
④符号表地址使用说明
㈡常数表
①常数表结构
unsignedshortconst_int_table[NI];//NI表示整常数表长度
floatconst_real_table[NR];//NR表示实常数表长度
②常数表使用
③常数表地址使用说明
6.3中间代码
6.3.1三元式
㈠格式
OPARG1ARG2
运算符
第一运算量
第二运算量
㈡优点
代码生成无需引进临时变量。
㈢缺点
调整困难。
6.3.2四元式
㈠格式
OPARG1ARG2RESULT
运算符
第一运算量
第二运算量
运算结果
㈡优点
调整方便。
㈢缺点
在生成中间代码时引进大量临时变量。
㈣临时变量的处理
①将Ti作为标识符存入符号表
②设置临时变量表
6.4说明语句(简单变量)的翻译
㈠文法及修改
<语句>→integer<标识符表>S→aV
<语句>→real<标识符表>S→cV
<标识符表>→<标识符表>,标识符V→V,i
<标识符表>→标识符V→i
为了避免使用队列,文法修改如下:
<语句>→<说明>S→V
<说明>→<说明>,标识符V→V,i
<说明>→integer标识符V→ai
<说明>→real标识符V→ci
用这个文法来制导翻译,每当读进一个标识符,就可把它的变量名及其性质填入符号表,没有必要集中起来成批处理。
㈡语义子程序
V→ai{
fill_sym_table(wval,0,0);//填写符号表(标识符名,简单变量,整型)
V.cat=0;//保存语义值(简单变量)
V.type=0;//保存语义值(整型)
}
V→ci{
fill_sym_table(wval,0,1);//填写符号表(标识符名,简单变量,整型)
V.cat=0;//保存语义值(简单变量)
V.type=1;//保存语义值(实型)
}
V→V
(1),i{
fill_sym_table(wval,V
(1).cat,V
(1).type);//继承V
(1)的语义信息
V.cat=V
(1).cat;
V.type=V
(1).type;
}
S→V{;}//空
①语义变量.cat和.type
②fill_sym_table函数
㈢手工计算
intergera,b
6.5整型算术表达式及赋值语句的翻译
㈠文法
<语句>→标识符=<整型算术表达式>S→i=X
<整型算术表达式>→<整型算术表达式>+<项>X→X+Y
<整型算术表达式>→<项>X→Y
<项>→<项>*<因子>Y→Y*Z
<项>→<因子>Y→Z
<因子>→(<整型算术表达式>)Z→(X)
<因子>→-<因子>Z→-Z
<因子>→标识符Z→i
<因子>→无符号整常数Z→x
㈡语义子程序
S→i=X{
gen_code(=,X.addr,0,sym_entry(wval));//产生四元式。
}
X→X
(1)+Y{
X.addr=get_tmpvar(0);//申请临时变量(整型)
gen_code(+,X
(1).addr,Y.addr,X.addr);//产生四元式
}
X→Y{
X.addr=Y.addr;//传递语义值
}
Y→Y
(1)*Z{
Y.addr=get_tmpvar(0);//申请临时变量(整型)
gen_code(*,Y
(1).addr,Z.addr,Y.addr);//产生四元式
}
Y→Z{
Y.addr=Z.addr;//传递语义值
}
Z→(X){
Z.addr=X.addr;//传递语义值
}
Z→-Z
(1){
Z.addr=get_tmpvar(0);//申请临时变量(整型)
gen_code(-,Z
(1).addr,0,Z.addr)//产生四元式
}
Z→i{
Z.addr=sym_entry(wval);//wval表示单词的值
}
Z→x{
Z.addr=const_int_entry(atoi(wval));//wval表示单词的值,atoi为C语言系统函数。
}
①get_tmpvar函数
②sym_entry函数
③gen_code函数
④const_int_entry(wval)函数
㈢手工计算
a=-b*(c+2)
6.6混合型算术表达式及赋值语句的翻译
㈠概述
①拒绝接受混合运算,报错(如标准Fortran语言)。
②允许混合运算,在运算前将它们转换成同一类型(例Pascal语言、C语言)。
㈡引入定点、浮点和类型转换运算符
●运算符itr作用:
将整型量转换为实型量。
●运算符+r、+i、…,分别表示实数加、整数加、…。
设源程序为
inta,b;
realc,d;
c=d+a*b
符号表略,相应的四元式代码为
(*i,&a,&b,&T1)
(itr,&T1,0,&T2)
(+r,&d,&T2,&T3)
(=r,&T3,0,&c)
㈢文法
<语句>→标识符=<算术表达式>S→i=X
<算术表达式>→<算术表达式>+<项>X→X+Y
<算术表达式>→<项>X→Y
<项>→<项>*<因子>Y→Y*Z
<项>→<因子>Y→Z
<因子>→(<算术表达式>)Z→(X)
<因子>→-<因子>Z→-Z
<因子>→标识符Z→i
<因子>→无符号整常数Z→x
<因子>→无符号实常数Z→y
㈣语义子程序(X→X+Y)
void*t;
X→X
(1)+Y{
if(X
(1).type==Y.type)//类型相同
if(X
(1).type==0){//intopint
X.addr=get_tmpvar(0);
gen_code(+i,X
(1).addr,Y.addr,X.addr);//产生四元式
X.type=0;
}
else{//real=real
X.addr=get_tmpvar
(1);
gen_code(+r,X
(1).addr,Y.addr,X.addr);//产生四元式
X.type=1;
}
else{//类型不相同
X.type=1;//结果类型均为实型
t=get_tmpvar
(1);//申请临时变量(实型),用于类型转换。
X.addr=get_tmpvar
(1);
if(X
(1).type==0){//intopreal
gen_code(itr,X
(1).addr,0,t);
gen_code(+r,t,Y.addr,X.addr);
}
else{//realopint
gen_code(itr,Y.addr,0,t);
gen_code(+r,X
(1).addr,t,X.addr);
}
}
}
6.7布尔表达式的翻译
㈠布尔表达式作用
①控制语句的条件ifx+y<10gotoL
②计算逻辑值d=x>y
㈡程序设计语言的优先级和结合性
①标准Fortran语言(按表达式类别分级)
②Pascal语言(共分4级,同级运算优先性相同)
③C语言(共分17级,同级运算优先性相同)
㈢描述布尔表达式文法
以标准FORTRAN语言为基础,适当化简。
E→E∨E|E∧E|(E)|~E|XrX|X
X→X+X|X*X|(X)|-X|i|x
㈣布尔表达式计算方法
①根据优先性和结合性按步计算
②优化计算法
㈤布尔表达式的第一种翻译法
同算术表达式
㈥布尔表达式的第二种翻译法
①概述
②实例引入
③问题的提出
④解决办法
1)修改文法
2)引进语义变量.tc和.fc保存未填转移目标的四元式地址
3)变量和函数
●nxq指示器
●链合并函数merg(p1,p2)
●回填函数backpatch(p,t)
⑤语义子程序
E→X{
E.tc=nxq;
gen_code(jnz,X.addr,0,0);
E.fc=nxq;
gen_code(jmp,0,0,0);
}
Er→XrX
(1){
E.tc=nxq;
E.tc:
=nxq+1;
gen_code(jr,X.addr,X
(1).addr,0);
gen_code(jmp,0,0,0)
}
E→~E
(1){//真假出口链链首互换
E.tc=E
(1).fc;E.fc=E
(1).tc;
}
E→(E
(1)){//传递真假出口链链首
E.tc=E
(1).tc;E.fc=E
(1).fc;
}
EA→E∧{
backpatch(E.tc,nxq);//可填真出口(下一个四元式地址)
EA.fc=E.fc;//传递假出口链链首
}
E→EAE
(2){
E.tc=E
(2).tc;//传递真出口链链首
E.fc=merge(EA.fc,E
(2).fc);//合并假出口链
}
EO→E
(1)∨{
EO.tc=E
(1).tc;//传递真出口链链首
backpatch(E
(1).fc,nxq);//可填假出口(下一个四元式地址)
}
E→EOE
(2){
E.tc=merge(EO.tc,E
(2).tc);//合并真出口链
E.fc=E
(2).fc;//传递假出口链链首
}
X→i{
X.addr=sym_entry(wval);//wval表示单词的值。
}
X→x{
X.addr=const_int_entry(atoi(wval));//wval表示单词的值。
}
⑥手工计算
a∨b∨c
6.8标号和无条件转移语句的翻译
㈠标号和goto语句
①向后转移(程序首部方向)
②向前转移(程序尾部方向)
㈡文法及修改
<语句>→标识符:
<语句>S→i:
S
<语句>→goto标识符S→gi
标号用于标领一个语句,为了能及时填写标号的地址,将文法修改如下:
<语句>→<标号><语句>S→FS
<标号>→标识符:
F→i:
<语句>→goto标识符S→gi
㈢问题的提出和解决办法
①gotoL99是一个向后转移语句
②gotoL99是一个向前转移语句
●L99第一次出现
●L99非第一次出现
㈣语义子程序(不考虑出错情况)
F→i:
{
if(sym_entry(wval)==0){//标号未进入符号表,属先定位后使用。
fill_sym_table(wval,1,1);//将标号名填入符号表且标记已定位
(*sym_entry(wval)).addr=nxq;//nxq为标号i标领的语句第一个四元式地址
}
else{//标号已进入符号表,属先使用后定位,此时应回填。
backpatch((*sym_entry(wval)).addr,nxq);//回填
(*sym_entry(wval)).addr=nxq;
(*sym_entry(wval)).type=1;//标号已定位
}
}
S→gi{
if(sym_entry(wval)==0){/*标号未进入符号表,属先使用后定位且是第一个向前转移语句,此时产生新链,链中仅有一个四元式。
*/
fill_sym_table(wval,1,0);//将标号名填入符号表且标记为未定位
(*sym_entry(wval)).addr=nxq;//下一个无条件转移四元式地址(编号)
gen_code(jmp,0,0,0);//产生不完全四元式
}
else{//标号已进入符号表
if((*sym_entry(wval)).type==1)//标号已进入符号表且定位,直接产生四元式。
gen_code(jmp,0,0,(*sym_entry(wval)).addr);
else{/*标号已进入符号表,但未定位,即有以该标号为转移目标的单向链存在,将新产生的四元式插入单向链。
*/
void*t;
t=(*sym_entry(wval)).addr;
(*sym_entry(wval)).addr=nxq;
gen_code(jmp,0,0,t);
}
}
}
S→FS{;}//暂时可认为是空
6.9控制语句的翻译
㈠概述
㈡语义变量E.fc的传递
㈢引进语义变量.chain
6.9.1if-then语句的翻译
㈠文法及修改
<语句>→if<布尔表达式>then<语句>endifS→fEtS
(1)j
<语句>→标识符=<算术表达式>S→i=X
为了能及时回填真出口,文法修改如下:
C→if<布尔表达式>thenC→fEt
<语句>→C<语句>endifS→CS
(1)j
<语句>→标识符=<算术表达式>S→i=X
㈡语义子程序
C→fEt{
backpatch(E.tc,nxq);//回填真出口
C.chain=E.FC;//假出口是离开if-then语句
}
S→CS
(1)j{
S.chain=merge(C.chain,S
(1).chain);//S
(1)中可能含有离开if_then的四元式
}
S→i=X{//赋值语句按顺序执行,它的四元式代码中不存在需回填转移目标的四元式。
S.chain=0;
gen_code(=,X.addr,0,sym_entry(wval));
}
㈢手工计算
ifathenb=dendif
6.9.2if-then-else语句的翻译
㈠文法及修改
<语句>→if<布尔表达式>then<语句>else<语句>S→fEtS
(1)eS
(2)
当扫描到then可填真出口,当扫描到else可填假出口,当S
(1)执行完毕,应离开if-then-else语句。
为了能及时回填四元式,修改如下:
<语句>→TP<语句>S→TPS
(2)
TP→C<语句>elseTP→CS
(1)e
C→if<布尔表达式>thenC→fEt
㈡语义子程序
TP→CS
(1)e{//TP可理解为then-processed
void*t;
t=nxq;//t为下一条四元式地址,即(jmp,0,0,0)的地址(编号)。
gen_code(jmp,0,0,0);//执行完S
(1)后,离开if-then-else语句。
backpatch(C.chain,nxq);//回填假出口,这里C.chain相当于E.FC,此时nxq=t+1。
TP.chain=merge(S
(1).chain,t);//S
(1)中可能含有离开if_then-else的四元式
}
S→TPS
(2){
S.chain=merge(TP.chain,S
(2).chain);//S
(2)中可能含有离开if_then-else的四元式
}
C→fEt{…}//见6.9.1
㈢手工计算
ifathenb=celseb=d
6.9.3while-do语句的翻译
㈠文法及修改
<语句>→while<布尔表达式>do<语句>S→wEdS
(1)
为了便于语义分析,修改如下:
W→whileW→w
Wd→W<布尔表达式>doWd→WEd
<语句>→Wd<语句>S→WdS
(1)
㈡语义子程序
W→w{
W.quad=nxq;//记录E的第一个四元式编号
}
Wd→WEd{//Wd可理解为while-do
backpatch(E.TC,nxq);//回填真出口
Wd.chain=E.fc;//传递假出口、即while-do的出口。
Wd.quad=W.quad;//传递E的第一个四元式地址
}
S→WdS
(1){
backpatch(S
(1).chain,Wd.quad);/*回填S
(1).chain链,因S
(1)可能是控制语句,离开S
(1)的四元式的转移目标是E的第一个四元式。
*/
gen_code(jmp,0,0,Wd.quad);//生成转向E首址的无条件转移指令
S.chain=Wd.chain;//传递假出口、即while-do的出口。
}
㈢手工计算
whileadoifbthenc=dendif
6.9.4复合语句的翻译
㈠文法及修改
<语句>→begin<语句串>endS→{L}
<语句串>→<语句串>;<语句>L→L
(1);S
<语句串>→<语句>L→S
分号意味着一个语句的结束,当扫描到分号,就可回填转移目标。
为了能及时回填chain链,文法修改如下:
<语句>→begin<语句串>endS→{L}
<语句串>→LS<语句>L→LSS
LS→<语句串>;LS→L;
<语句串>→<语句>L→S
㈡语义子程序
L→S{
L.chain=S.chain;//传递
}
Ls→L;{
backpatch(L.chain,nxq);//回填
}
L→LsS{//LS表示L-S
L.chain=S.chain//因前一语句L已回填,而当前语句S尚未回填,故需传递。
}
S→{L}{
S.chain=L.chain;//传递
}
P→{L}{
backpatch(L.chain,nxq);//填写最后一个语句的chain链
gen_code(halt,0,0,0);//产生停机指令
}
㈢例子
设源程序为
begin
ifathenb=10endif;
whilec>ddo
begin
c=c-1;d=d+1
end
end
根据上述语义子程序,其相应四元式序列为:
⑴(jnz,&a,0,03)//if
⑵(jmp,0,0,04)
⑶(=,&10,0,&b)
⑷(j>,&c,&d,06)//while
⑸(jmp,0,0,011)
⑹(-,&c,&1,&T1)
⑺(=,&T1,0,&c)
⑻(+,&d,&1,&T2)
⑼(=,&T2,0,&d)
⑽(jmp,0,0,4)
⑾(halt,0,0,0)
6.10小结