编译原理生成中间代码(flex和bison版)
簡單說明
flex和bison并不難可以看看如下視頻,大概花一個小時熟悉一下就可以了
Flex和bison視頻講解1
Flex和bison視頻講解2
題目要求
題目來源:陳火旺《編譯原理》P218習(xí)題7
實驗環(huán)境:Ubuntu 20.04
前置環(huán)境:sudo apt-get install flex bison安裝flex和bison
翻譯模式
第一步當(dāng)然是設(shè)計翻譯模式,按照書上給定的翻譯模式進(jìn)行組合,得到如下翻譯模式,當(dāng)然如下翻譯模式并不完美,存在移進(jìn)/規(guī)約沖突,后續(xù)可以改進(jìn).
對于不同文法需要設(shè)計不同的翻譯模式,但是對于書上課后題通常只需要進(jìn)行簡單修改即可
順便把語法樹畫了一下
源代碼
一共要有三個核心文件
lex.l
這里只需要掌握說明部分視頻里的知識即可
簡要說明 :
其他的應(yīng)該沒問題
再第二部分使用了filloperator 函數(shù),這個函數(shù)的定義再xu.h,對于RELOP關(guān)系運算符通過filloperator 函數(shù)來進(jìn)行傳遞.
%{#include "yacc.tab.h" %} delim [ \t\n\r] ws [delim]+ letter [A-za-z] digit [0-9] id {letter}({letter}|{digit})* number {digit}+%% if { return( IF ); } else { return( ELSE ); } then { return( THEN ); } while { return( WHILE ); } do { return( DO ); } and {return( AND ); } "+" { return( '+' ); } "-" { return( '-' ); } "*" { return( '*' ); } "/" { return( '/' ); } ":=" { filloperator(&yylval, yytext); return( ASSIGN ); } "<"|"<="|">"|">="|"!="|"=" { filloperator(&yylval, yytext); return( RELOP ); } ";" { return( ';' ); } {ws} { } {id} { filllexeme(&yylval, yytext); return( ID ); } {number} { filllexeme(&yylval, yytext); return( NUMBER ); } %%int yywrap() { return (1); }yacc.y
只是實現(xiàn)了翻譯模式中的語法規(guī)則,對于語法規(guī)則的實現(xiàn)在說明部分的視頻中也有提到,我這里說幾個重要一點的函數(shù)和變量
其余函數(shù)在xu.h中實現(xiàn)
%{#include "xu.h"#define YYSTYPE node#include "yacc.tab.h"int yyerror();int yyerror(char* msg);extern int yylex(); codelist* list; %}%token IF ELSE THEN %token WHILE DO %token RELOP %token NUMBER ID %token AND %left AND %left '+' '-' %left '*' '/' %right '=' %right ASSIGN%% S : IF E THEN M S { backpatch(list, $3.truelist, $$.instr); $$.nextlist = merge($2.falselist, $5.nextlist); }|IF E THEN M S ELSE N M S { backpatch(list, $2.truelist, $4.instr); backpatch(list, $2.falselist, $8.instr);$5.nextlist = merge($5.nextlist, $7.nextlist); $$.nextlist = merge($5.nextlist, $9.nextlist); } |WHILE M E DO M S{ backpatch(list, $6.nextlist, $2.instr); backpatch(list, $3.truelist, $5.instr);$$.nextlist = $3.falselist; gen_goto(list, $2.instr); }|OP ASSIGN E{copyaddr(&$1, $1.lexeme); gen_assignment(list, $1, $3);}|L{$$.nextlist = $1.nextlist;}| {};L : L ';' M S{backpatch(list, $1.nextlist, $3.instr);$$.nextlist = $4.nextlist;}|S{$$.nextlist = $1.nextlist;};E : E AND M E { backpatch(list, $1.truelist, $3.instr);$$.truelist = $4.truelist; $$.falselist = merge($1.falselist, $4.falselist); }|OP RELOP OP{ $$.truelist = new_instrlist(nextinstr(list));$$.falselist = new_instrlist(nextinstr(list)+1);gen_if(list, $1, $2.oper, $3);gen_goto_blank(list); }|OP{copyaddr_fromnode(&$$, $1);};OP : OP '+' OP { new_temp(&$$, get_temp_index(list)); gen_3addr(list, $$, $1, "+", $3); }|NUMBER {copyaddr(&$$, $1.lexeme);}|ID {copyaddr(&$$, $1.lexeme);};M : { $$.instr = nextinstr(list); };N : {$$.nextlist = new_instrlist(nextinstr(list));gen_goto_blank(list);};%%int yyerror(char* msg) { printf("\nERROR with message: %s\n", msg); return 0; }xu.h
這里只有拉鏈-回填會比較難理解
為了實現(xiàn)拉鏈-回填的過程需要定義如下結(jié)構(gòu)體和函數(shù),listele代表鏈表,其中的instrno代表四元式的標(biāo)號,對于構(gòu)造的鏈表結(jié)構(gòu)體instrlist有一個頭指針指向鏈?zhǔn)?#xff0c;一個尾指針指向鏈尾,對于指向相同出口的鏈表進(jìn)行合并,在回填過程中就可以實現(xiàn)對于同一出口的跳轉(zhuǎn)語句進(jìn)行一次回填即可。
其實對于中間代碼生成這里基本不用修改,只需要看懂都可以拿去用,只需要修改Gen等函數(shù)中的printf函數(shù)就可以變成生成三元式的程序了
#ifndef CP_H #define CP_H#include <stdio.h> #include <string.h> #include <malloc.h>typedef struct listele {int 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 instrlist {listele *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;else{if (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 addr[256];char lexeme[256];char oper[3];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-100);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 = 100;p->capacity = 1024;p->temp_index = 100;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 memeory\n");return 0;}}dst->code[dst->linecnt] = (char*)malloc(strlen(str)+20);strcpy(dst->code[dst->linecnt], str);dst->linecnt++;return 0;}char tmp[1024];int gen_goto_blank(codelist *dst){Gen(dst, tmp);return 0;}int gen_goto(codelist *dst, int instrno){sprintf(tmp, "(j, -, -, %d)", instrno);Gen(dst, tmp);return 0;}int gen_if(codelist *dst, node left, char* op, node right){sprintf(tmp, "(j%s, %s, %s,", op, left.addr, 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)", op, right.addr, left.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) ", op, op1.addr, op2.addr, left.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 tmp[20];sprintf(tmp, " %d)", instrno);while (p!=NULL){if (p->instrno < dst->linecnt)strcat(dst->code[p->instrno], tmp);p=p->next;}}return 0;}int print(codelist* dst){int i;for (i=100; i <= dst->linecnt; i++)printf("%5d: %s\n", i, dst->code[i]);return 0;}#endif編譯過程
命令行窗口輸入如下三行命令就可以完成編譯了,建議把這三行寫入txt文件中,在重新編譯時只需要bash xxx.txt就可以了,當(dāng)然寫makefile也是可以的
bison -d yacc.y flex lex.l gcc yacc.tab.c lex.yy.c -olm總結(jié)
以上是生活随笔為你收集整理的编译原理生成中间代码(flex和bison版)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 巧用JS位运算
- 下一篇: VMware 未能初始化监视器