实验五-编译-用语法制导方式生成中间代码生成器(共13页).doc
精选优质文档-倾情为你奉上实验5 用语法制导方式生成中间代码生成器一、实验目的掌握语法制导定义和翻译的原理和技术,在语法分析器的基础上,加上语义分析,构造一个中间代码生成器。二、实验内容在实验四生成的语法分析器基础上加入语义动作,将源程序翻译为对应的中间代码序列。三、实验要求1. 个人完成,提交实验报告。实验报告必须包括设计的思路,以及测试报告(输入测试例子,输出结果)。2. 实验报告中给出采用测试源代码片断,及其对应的三地址码形式(内部表示形式可以自行考虑)。例如,程序片断 对应的中间代码为:四、实验过程本次实验运用flex和bison工具进行中间代码的生成。并自动生成中间代码。1. 首先创建一个example文件夹,该文件夹中包含有flex.exe2. 用文本编译器编辑相应的flex文件mylex.l,此次mylex.l可以在上次实验的l文件上做一些修改,再利用flex将l文件生成相应的lex.yy.c程序,mylex.l的代码如下所示: mylex.l%#include "myyacc.tab.h"%delim tnrwsdelim+letterA-Za-zdigit0-9idletter(letter|digit)*integerdigit+exponentE+-?integernumberintegerexponent?realinteger(.integer)?exponent?%option noyywrap%"<"|"<="|">"|">="|"!="|"=" filloperator(&yylval, yytext); return( REL); if return( IF ); else return( ELSE ); while return( WHILE ); do return( DO ); for return( FOR ); switch return( SWITCH ); case return( CASE ); default return( DEFAULT ); break return( BREAK ); true return( TRUE ); false return( FALSE ); int return( INT ); long return( LONG ); char return( CHAR ); bool return( BOOL ); float return( FLOAT ); double return( DOUBLE ); "&&" return( AND ); "|" return( OR ); "!" return( '!'); "+" return( INC ); "-" return( DEC ); "+" return( '+' ); "-" return( '-' ); "*" return( '*' ); "/" return( '/' ); "=" return( '=' ); "" return( '' ); "" return( '' ); "" return( '' ); "" return( '' ); "(" return( '(' ); ")" return( ')' ); "" return( '' ); ws id filllexeme(&yylval, yytext); return( ID ); number filllexeme(&yylval, yytext); return( NUMBER ); real filllexeme(&yylval, yytext); return( REAL ); %在代码中,先定义正则定义,即对letter,digit,专用符号, 空格进行声明;接着在转换规则中,定义一些识别规则的代码。完成词法分析后,就可以将获取的每一个词素用于语法分析器使用。 将mylex.l与myyacc.y相结合的方法是在每获得一个词素,则用return语句返回,即如果获得的是if,则return(if),并且在头文件中加入#include "myYacc.tab.h",则在myyacc中定义的类型在mylex中可利用,否则会出现返回的单元未定义的错误。3. 用文本编译器编辑相应的bison文件myyacc.y,myyacc.y文件中,在每个生成式后加上语法制导翻译,主要是依据truelist和falselist来实现回填功能。编写完后,在myyacc.y中以头文件的方式加入自己编写的myyacc.h文件,编译即可。Myyacc.y的代码如下所示:Myyacc.y%#include "myyacc.h"#define YYSTYPE node#include "myyacc.tab.h"int yyerror();int yyerror(char* msg);extern int yylex();codelist* list;%token BASIC NUMBER REAL ID TRUE FALSE%token INT LONG CHAR BOOL FLOAT DOUBLE%token REL%token IF ELSE WHILE DO BREAK FOR SWITCH CASE DEFAULT %token OR AND%left OR%left AND%right '!'%left '+' '-'%left '*' '/'%right UMINUS%right INC DEC%program : block ;block : '' decls statementlist '' ;decls: decls decl | ;decl: type ID '' ;type: type '' NUMBER '' | BASIC ;statementlist: statementlist M statement backpatch(list, $1.nextlist, $2.instr); $.nextlist = $3.nextlist; | statement $.nextlist = $1.nextlist; ;Statement : IF '(' boolean ')' M statement ELSE N M statement backpatch(list, $3.truelist, $5.instr); backpatch(list, $3.falselist, $9.instr); $6.nextlist = merge($6.nextlist, $8.nextlist); $.nextlist = merge($6.nextlist, $10.nextlist); | IF '(' boolean ')' M statement backpatch(list, $3.truelist, $5.instr); $.nextlist = merge($3.falselist, $6.nextlist); | WHILE M '(' boolean ')' M statement backpatch(list, $7.nextlist, $2.instr); backpatch(list, $4.truelist, $6.instr); $.nextlist = $4.falselist; gen_goto(list, $2.instr); | DO M statement M WHILE '(' boolean ')' M '' backpatch(list, $3.nextlist, $4.instr); backpatch(list, $7.truelist, $9.instr); $.nextlist = $7.falselist; gen_goto(list, $2.instr); | FOR '(' assignment '' M boolean '' M assignment ')' N M statement backpatch(list, $6.truelist, $12.instr);backpatch(list, $11.nextlist, $5.instr);backpatch(list, $13.nextlist, $8.instr);$.nextlist = $6.falselist;gen_goto(list, $8.instr); | BREAK '' | '' statementlist '' $.nextlist = $2.nextlist; | assignment '' $.nextlist = NULL; ;assignment: ID '=' boolean copyaddr(&$1, $1.lexeme); gen_assignment(list, $1, $3); ;loc : loc '' boolean '' | ID copyaddr(&$, $1.lexeme); ;boolean : boolean OR M boolean backpatch(list, $1.falselist, $3.instr); $.truelist = merge($1.truelist, $4.truelist); $.falselist = $4.falselist; | boolean AND M boolean backpatch(list, $1.truelist, $3.instr); $.truelist = $4.truelist; $.falselist = merge($1.falselist, $4.falselist); | '!' boolean $.truelist = $1.falselist; $.falselist = $1.truelist; | '(' boolean ')' $.truelist = $1.truelist; $.falselist = $1.falselist; | expression REL expression $.truelist = new_instrlist(nextinstr(list); $.falselist = new_instrlist(nextinstr(list)+1); gen_if(list, $1, $2.oper, $3); gen_goto_blank(list); | TRUE copyaddr(&$, "TRUE"); gen_goto_blank(list); | FALSE copyaddr(&$, "FALSE"); gen_goto_blank(list); | expression copyaddr_fromnode(&$, $1); ;M : $.instr = nextinstr(list); ;N : $.nextlist = new_instrlist(nextinstr(list); gen_goto_blank(list); ;expression : expression '+' expression new_temp(&$, get_temp_index(list); gen_3addr(list, $, $1, "+", $3); | expression '-' expression new_temp(&$, get_temp_index(list); gen_3addr(list, $, $1, "-", $3); | expression '*' expression new_temp(&$, get_temp_index(list); gen_3addr(list, $, $1, "*", $3); | expression '/' expression new_temp(&$, get_temp_index(list); gen_3addr(list, $, $1, "/", $3); | '-' expression %prec UMINUS new_temp(&$, get_temp_index(list); gen_2addr(list, $, "-", $2); | loc copyaddr_fromnode(&$, $1); | NUMBER copyaddr(&$, $1.lexeme); | REAL copyaddr(&$, $1.lexeme); ;%int yyerror(char* msg)printf("nERROR with message: %sn", msg);return 0;int main()list = newcodelist();freopen("text.in", "rt+", stdin);freopen("text.out", "wt+", stdout);yyparse();print(list);fclose(stdin);fclose(stdout);return 0;在代码中,先定义一些头文件,extern int yylex(); extern int yyerror();这两个语句是必须的,引用全局变量是为了能够使用之前词法分析器所获取的词素。并且调用一个yyerror函数用来当发生错误时能够报告错误。接着定义一些终结符号,比如说%token NUM。yacc规定每个终结符都有一个唯一的编号(token number)。当我们用上面的方式定义终结符时,终结符的编号由yacc内部决定,其编号规则是从257开始依次递增,每次加1。为了能够联合词法分析器和语法分析器,词法分析程序名字必须是yylex,调法分析程序向语法分析程序提供当前输入的单词符号。yylex提供给yyparse的不是终结符本身,而是终结符的编号,即token number,如果当前的终结符有语义值,yylex必须把它赋给yylval。 接着是翻译规则部分,这个翻译规则由一个文法产生式和一个相关联的语义动作组成,在这里在每个生成式后边加上语法制导翻译以实现回填的功能。4. 另外编写一个头文件myyacc.h将其加入到myyacc.y文件中去文件如下所示:Myyacc.h#ifndef CP_H#define CP_H#include <stdio.h>#include <string.h>#include <malloc.h>typedef struct listeleint instrno;struct listele *next;listele;listele* new_listele(int no)listele* p = (listele*)malloc(sizeof(listele);p->instrno = no;p->next = NULL;return p;typedef struct instrlistlistele *first,*last;instrlist;instrlist* new_instrlist(int instrno)instrlist* p = (instrlist*)malloc(sizeof(instrlist);p->first = p->last = new_listele(instrno);return p;instrlist* merge(instrlist *list1, instrlist *list2)instrlist *p;if (list1 = NULL) p = list2;elseif (list2!=NULL)if (list1->last = NULL)list1->first = list2->first;list1->last =list2->last;else list1->last->next = list2->first;list2->first = list2->last = NULL;free(list2);p = list1;return p;typedef struct node instrlist *truelist, *falselist, *nextlist;char addr256;char lexeme256;char oper3;int instr;node;int filloperator(node *dst, char *src)strcpy(dst->oper, src);return 0;int filllexeme(node *dst, char *yytext)strcpy(dst->lexeme, yytext);return 0;int copyaddr(node *dst, char *src)strcpy(dst->addr, src);return 0;int new_temp(node *dst, int index)sprintf(dst->addr, "t%d", index);return 0;int copyaddr_fromnode(node *dst, node src)strcpy(dst->addr, src.addr);return 0;typedef struct codelist int linecnt, capacity;int temp_index;char *code;codelist; codelist* newcodelist()codelist* p = (codelist*)malloc(sizeof(codelist);p->linecnt = 0;p->capacity = 1024;p->temp_index = 0;p->code = (char*)malloc(sizeof(char*)*1024);return p;int get_temp_index(codelist* dst)return dst->temp_index+;int nextinstr(codelist *dst) return dst->linecnt; int Gen(codelist *dst, char *str)if (dst->linecnt >= dst->capacity)dst->capacity += 1024;dst->code = (char*)realloc(dst->code, sizeof(char*)*dst->capacity);if (dst->code = NULL)printf("short of memeoryn");return 0;dst->codedst->linecnt = (char*)malloc(strlen(str)+20);strcpy(dst->codedst->linecnt, str);dst->linecnt+;return 0;char tmp1024;int gen_goto_blank(codelist *dst)sprintf(tmp, "goto");Gen(dst, tmp);return 0;int gen_goto(codelist *dst, int instrno)sprintf(tmp, "goto %d", instrno);Gen(dst, tmp);return 0;int gen_if(codelist *dst, node left, char* op, node right)sprintf(tmp, "if %s %s %s goto", left.addr, op, right.addr);Gen(dst, tmp);return 0;int gen_1addr(codelist *dst, node left, char* op)sprintf(tmp, "%s %s", left.addr, op);Gen(dst, tmp);return 0;int gen_2addr(codelist *dst, node left, char* op, node right)sprintf(tmp, "%s = %s %s", left.addr, op, right.addr);Gen(dst, tmp);return 0;int gen_3addr(codelist *dst, node left, node op1, char* op, node op2)sprintf(tmp, "%s = %s %s %s", left.addr, op1.addr, op, op2.addr);Gen(dst, tmp);return 0;int gen_assignment(codelist *dst, node left, node right)gen_2addr(dst, left, "", right);return 0;int backpatch(codelist *dst, instrlist *list, int instrno)if (list!=NULL)listele *p=list->first;char tmp20;sprintf(tmp, " %d", instrno);while (p!=NULL)if (p->instrno<dst->linecnt)strcat(dst->codep->instrno, tmp);p=p->next;return 0;int print(codelist* dst)int i;for (i=0; i < dst->linecnt; i+)printf("%5d: %sn", i, dst->codei);return 0;#endif五、实验测试和实验结果在mayyacc代码中用于打开test.in文件,即本次的测试文件,内容如下:当程序正在测试时,显示Compiling.!,当测试结束后,显示completed successfully!中间代码生成结果如下所示:六、实验心得 此次实验是编写一个用语法制导方式生成的中间代码,虽然上次实验是实现一个语法分析器,可以承接上次实验,但这次实验同上次差别还是比较大的。除了使用软件flex和bison实现生成两个c文件和一个h文件外,还需要自己编写一个h文件。此次实验还需要运用回填技术实现中间代码的生成,如何合理的使用这一技术更是成功输出结果的关键所在。由于刚学过该知识,没能很好地巩固,编写起来还是比较困难的。原因是不知道该用什么语句将l文件和y文件结合起来,该在什么地方加上回填。后来经过对网上资料和书本知识的认真学习下发现,其实大部分知识都是课堂上曾经讲过的,只是加上了一些固定的格式函数。而要想联系这几个文件,可以增加一些头文件即可实现连接的功能。 总的来说,此次实验难度比之前几次都要难,不仅覆盖了之前实验的内容,而且还需要额外加入新的知识和编程技术。由于对书本知识掌握的不是很精通,以及对知识软件的掌握程度还不够,所以对理论知识有一定的了解,但要将它真正地运用到程序编写中还是有一定难度的。这几次实验,从词法分析器到语法分析器再到中间代码的生成,内容步步深入,程度逐渐加深,对程序编写是一个比较大的锻炼。专心-专注-专业