编译原理——中间代码生成

本文探讨编译原理中中间代码生成的过程,介绍四元式作为中间代码的表示形式,并详细阐述如何为语法树节点分配place属性来表示ID、常数或临时变量。实验目的是理解中间代码生成的原理和技巧,通过对C源代码翻译成四元式,实现基本表达式的翻译。文章还讨论了在实现过程中遇到的内存问题和解决方案,以及临时变量编号的管理策略。
摘要由CSDN通过智能技术生成

预备知识

源语言->中间代码->目标语言
中间代码(Intermediate Representation或者IR):复杂性介于源程序语言和机器语言的一种表示形式。
编译程序锁使用的中间代码有多种形式。常见的有逆波兰记号,三元式,四元式,和树形表示。四元式是一种普遍采用的中间代码形式,很类似于三地址指令,有时把这类中间表示称为“三地址代码”,这种表示可以看作是一种虚拟三地址机的通用汇编码,每条”指令“包含操作符和三个地址,两个是为运算对象的,一个是为结果的。
基本表达式的翻译模式:
(1)
Exp:Exp ASSIGNOP Exp
|Exp AND Exp
|Exp OR Exp
|Exp PLUS Exp
|Exp MINUS Exp
|Exp STAR Exp
|Exp DIV Exp
|ID
|INTEGER
|FLOAT
;
分析:
(1)要给每一个语法树结点增加一个place属性,这个place属性的值根据以下规则计算。
1)如果EXP->ID,不需要为ID建立临时变量,place的值就等于变量ID的名字;
2)如果EXP->INTEGER|FLOAT,即推出常数,不需要为常数建立临时变量,place的值就等于常数值。
3)E->E1=E2,E1->ID

emit($1);printf("=");emit($3);

step1:输出ID;
step2:输出=
step3:输出E2.place

4)i+1;E->E1+E2,newtemp,建立一个临时变量,E.place=E1.place+E2.place,

$$>t=newtemp();//创建临时变量
emit($$);//输出创建E.place
printf("=");//输出=
emit($1);//输出E1.place
printf("+");//输出+
emit($3);//输出E2.place

也就是说,在语法树的叶子结点存在两种place,一种是ID,即变量名,一种是整数或者浮点数,这两种place值都和临时变量无关,不需要建立临时变量的操作。
place的值对应到四元式中:
1)常数
2)源程序中变量名ID
3)为生成四元式而建立的临时变量名。
方法一:变量名采用t加上数字的形式,为所有的临时变量名创建一个符号表,记其在符号表中的位置,这个位置是一个在整数v,从0开始,输出时,输出’t’+v,就可以输出t0,t1这种形式,而且有效地避免了变量名的重复。
方法二:直接将place设置成整数类型,再设置一个标记数组used[],用了一个整数i,就在标记数组中将used[i]=-1,表示ti已经被用过了。这样一来,创建一个临时变量,相当于设置place的值,然后更改used[]数组的值。

place应该是一个Union,有3种可能值。
1)int i(对应INTEGER)
2)char* (对应变量名ID)
3)float (对应FLOAT)
4)int t;对应临时变量编号
为了能识别place的值是4种中哪一种,再为语法树结点增添一个ptag,1为int,2为float,3为char,4为临时变量编号。

union
{
int i;
char* s;
float f;
}place;
int tag;//标志位

int main()
{
place.s="ID";
tag=1;
if(tag==1)
printf("%s",place);
}

除了EXP->ID,EXP->常数,每一条基本表达式规则要有一个输出四元式的操作,emit。

代码更改:
由于用union导致了内存的错乱(具体什么原因,过后还需要研究),因此将union去掉,4种类型都设置为语法树结点的成员,这样一来可能浪费了空间,但是能保证代码的正确性。

一、【实验目的】

通过在词法分析,语法分析和语义分析程序的基础上,将C—源代码翻译成中间代码,认识中间代码的表示形式和生成中间代码的原理和技巧,掌握对简单赋值语句的翻译过程,从而达到对编译器的编译原理有更深的理解,提高代码能力和代码修养。

二、【实验任务】

在词法分析,语法分析和语义分析程序的基础上,将C—源代码翻译成中间代码,中间代码的表示采用四元式,本实验中只对基本表达式进行翻译,即将以下规则翻译成四元式:表达式赋值语句,两个表达式的逻辑与,逻辑或,加,减,乘,除。

Exp:Exp ASSIGNOP Exp 
|Exp AND Exp 
|Exp OR Exp 
|Exp PLUS Exp 
|Exp MINUS Exp 
|Exp STAR Exp 
|Exp DIV Exp 
|ID 
|INTEGER 
|FLOAT 
;

三、【实验程序】

1,程序的编译说明

(1)程序总共包含4个文件gramtree_v1.h gramtree_v1.c gramtree.l gramtree_v1.y
gramtree_v1.h gramtree_v1.c定义和实现了:
1)语法树结构体,语法树创建函数,语法树遍历函数
2)变量符号表结构体,变量符号表的建立函数,查找变量是否已定义的函数,查找变量类型的函数
3)函数符号表结构体,函数符号表的建立函数,查找函数是否已定义,查找函数类型,查找函数形参个数。
4)数组符号表,数组符号表的建立函数,查找数组是否已定义的函数,查找数组类型的函数
5)结构体符号表,结构体符号表的建立函数,查找结构体是否已定义的函数
6)关于生成中间表达式的2个函数:newtemp()和emit()。newtemp生成了一个临时变量,emit将一个语法结点的place属性输出到屏幕。
(2)gramtree.l是flex词法分析模块
(3)gramtree_v1.y是bison语法分析模块。
在bison文件中,在对应的变量定义,函数定义,数组定义 ,结构体定义,变量调用,函数调用,数组调用,结构体调用对应的语法规则中,建立或者查询对应的符号表,实现语义分析和特定错误的检测 然后输出错误类型和错误行号。
(4)编译时使用Makefile文件:
1)编写Makefile文件内容,执行:vim Makefile

2,实验分析

(1)要给每一个语法树结点增加一个place属性,这个place属性的值根据以下规则计算。
1)如果EXP->ID,不需要为ID建立临时变量,place的值就等于变量ID的名字;
2)如果EXP->INTEGER|FLOAT,即推出常数,不需要为常数建立临时变量,place的值就等于常数值。
3)对于规则E->E1=E2,(E1->ID), E->E1&&E2,E->E1||E2,E->E1+E2,E->E1-E2,E->E1*E2,E->E1/E2:
step1:生成一个临时变量.
临时变量的名字符合t加上一个数字的表示形式,E.place的值就等于生成的临时变量;
step2:输出E.place->输出E1.place->输出操作符->输出E2.place。
这样就完成了一条四元式的生成和输出。

(2)属性place的设置
在语法树的叶子结点存在两种place,一种是ID,即变量名;另一种是整数或者浮点数,这两种place值都和临时变量无关,不需要建立临时变量的操作。
place的值对应到四元式中,有以下3种情况:
1)常数
2)源程序中变量名ID
3)为生成四元式而建立的临时变量名。

生成临时变量的方法
直接将place设置成整数类型,再设置一个标记数组used[],用了一个整数i,就在标记数组中将used[i]=-1,表示ti已经被用过了。这样一来,创建一个临时变量,相当于设置place的值,然后更改used[]数组的值。
于是place的数据类型有以下3种可能值。
1)int i(对应INTEGER)
2)char* (对应变量名ID)
3)float (对应FLOAT)
4)int t;对应临时变量编号
为了能识别place的值是4种中哪一种,再为语法树结点增添一个ptag,1为int,2为float,3为char,4为临时变量编号。

3,实验代码

(1)在语法树结点中增加以下5个成员:place的4种数据类型,和一个用以标志类型的整型变量ptag。

struct ast
{
/*用于生成中间代码的变量,place的4种类型,*/
    int i;//Integer
    float f;//FLOAT
    char id[30];//变量名ID
    int t;//临时变量t编号

    int ptag;//用以标志place的类型1,2,3,4
}

(2)生成临时变量的函数newtemp()和输出E.place的函数emit()

/*关于中间代码的实现函数:创建临时变量&输出四元式*/
int newtemp()//创建临时变量
{
    int i=0;
    for(i=0; i<100; ++i)//在used[]中找到一个没有被用过的编号
    {
        if(used[i]==0)
        {
            used[i]=i+1;
            return i;//返回的编号就是t的编号
        }
    }
}

void emit(struct ast* tp)//输出四元式
{
    if(tp->ptag==1)//place的值是INTEGER
        printf("%d",tp->i);
    else if(tp->ptag==2)
        printf("%2f",tp->f);//place的值是FLOAT
    else if(tp->ptag==3)
        printf("%s",tp->id);//place的值是ID变量名字
    else//place的值是临时变量编号
        printf("t%d",tp->t);
}

(3)在基本表达式的语义规则中增加:

/*Expressions*/
Exp:Exp ASSIGNOP Exp        /*E1->ID,不需要建立临时变量*/
{
emit($1);//输出E1.place
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值