日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

编译原理生成中间代码(flex和bison版)

發(fā)布時間:2023/12/14 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 编译原理生成中间代码(flex和bison版) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

簡單說明

  • 有些地方懶得再輸入一遍,所以從課設(shè)報告上截了不少圖
  • 最好有一定的flex和bison基礎(chǔ)
  • 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 //詞法分析器
  • yacc.y //語法分析器
  • xu.h //頭文件
  • lex.l

    這里只需要掌握說明部分視頻里的知識即可
    簡要說明 :

  • delim 空格回車換行
  • ws 多個空格回車換行
  • 其他的應(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ù)和變量

  • 變量nextquadnextquadnextquad,它指向下一條將要生成但尚未生成的四元式地址。nextquadnextquadnextquad的初值為1,每當(dāng)執(zhí)行一次GenGenGen之后,nextquadnextquadnextquad自動加一
  • 函數(shù)makelist(i)makelist(i)makelist(i),它創(chuàng)建一個僅包含i的新鏈表,其中i是四元式數(shù)組的一個下標(biāo),函數(shù)返回指向這個鏈的指
  • 函數(shù)merge(p1,p2)merge(p_1,p_2)merge(p1?,p2?),把以p1p_1p1?P2P_2P2?為鏈?zhǔn)椎膬蓷l鏈合并為一,作為函數(shù)值,回送合并后的鏈?zhǔn)住?/li>
  • 函數(shù)backpatch(p,t)backpatch(p,t)backpatch(p,t)其功能是完成“回填”,把p所鏈接的每個四元式的第四區(qū)都填為t。
  • 其余函數(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)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。