简单说明

  1. 有些地方懒得再输入一遍,所以从课设报告上截了不少图
  2. 最好有一定的flex和bison基础

flex和bison并不难可以看看如下视频,大概花一个小时熟悉一下就可以了

Flex和bison视频讲解1

Flex和bison视频讲解2

题目要求


题目来源:陈火旺《编译原理》P218习题7
实验环境:Ubuntu 20.04
前置环境:sudo apt-get install flex bison安装flex和bison

翻译模式

第一步当然是设计翻译模式,按照书上给定的翻译模式进行组合,得到如下翻译模式,当然如下翻译模式并不完美,存在移进/规约冲突,后续可以改进.

对于不同文法需要设计不同的翻译模式,但是对于书上课后题通常只需要进行简单修改即可

顺便把语法树画了一下

源代码

一共要有三个核心文件

  1. lex.l //词法分析器
  2. yacc.y //语法分析器
  3. xu.h //头文件

lex.l

这里只需要掌握说明部分视频里的知识即可
简要说明 :

  1. delim 空格回车换行
  2. ws 多个空格回车换行

其他的应该没问题

再第二部分使用了filloperator 函数,这个函数的定义再xu.h,对于RELOP关系运算符通过filloperator 函数来进行传递.

%{#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

只是实现了翻译模式中的语法规则,对于语法规则的实现在说明部分的视频中也有提到,我这里说几个重要一点的函数和变量

  1. 变量nextquadnextquadnextquad,它指向下一条将要生成但尚未生成的四元式地址。nextquadnextquadnextquad的初值为1,每当执行一次GenGenGen之后,nextquadnextquadnextquad自动加一
  2. 函数makelist(i)makelist(i)makelist(i),它创建一个仅包含i的新链表,其中i是四元式数组的一个下标,函数返回指向这个链的指
  3. 函数merge(p1,p2)merge(p_1,p_2)merge(p1​,p2​),把以p1p_1p1​和P2P_2P2​为链首的两条链合并为一,作为函数值,回送合并后的链首。
  4. 函数backpatch(p,t)backpatch(p,t)backpatch(p,t)其功能是完成“回填”,把p所链接的每个四元式的第四区都填为t。

其余函数在xu.h中实现

%{#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

这里只有拉链-回填会比较难理解

为了实现拉链-回填的过程需要定义如下结构体和函数,listele代表链表,其中的instrno代表四元式的标号,对于构造的链表结构体instrlist有一个头指针指向链首,一个尾指针指向链尾,对于指向相同出口的链表进行合并,在回填过程中就可以实现对于同一出口的跳转语句进行一次回填即可。

其实对于中间代码生成这里基本不用修改,只需要看懂都可以拿去用,只需要修改Gen等函数中的printf函数就可以变成生成三元式的程序了

#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就可以了,当然写makefile也是可以的

bison -d yacc.y
flex lex.l
gcc yacc.tab.c lex.yy.c -olm

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

  1. 编译原理-如何使用flex和yacc工具构造一个高级计算器

    Flex工具的使用方法 Lex 是一种生成扫描器的工具. Lex是Unix环境下非常著名的工具,主要功能是生成一个扫描器(Scanner)的C源码. 扫描器是一种识别文本中的词汇模式的程序. 这些词汇 ...

  2. 编译原理及其实践教程(第二版)总结

    编译原理总结 第一章概述 1.1 程序设计语言与编译程序 1.2 编译过程和编译程序的结构 第二章 高级语言设计基础 2.1 高级语言设计基础 2.2.3 文法的二义性 第三章 词法分析 3.1 词法 ...

  3. 编译原理课后习题答案(清华大学第三版)

    编译原理期末速成详细笔记在本人发布的资源里,可自行下载. 第二章答案 第三章答案 第四章答案 第五章答案 第六章答案

  4. 基础爬虫——以豆丁网《编译原理》(清华大学出版社第二版)课后习题答案为例

    目录 寻找目标地址规律 写代码 寻找目标地址规律 目标地址:目标地址 在此之前,讲一个小技巧,要在IE浏览器中打开这个网址,在谷歌浏览器中打开这个网址是找不到答案图片网址的,Firefox没试过.IE ...

  5. 编译原理陈火旺第三版第六章课后题答案

    下面的答案仅供参考! 1.按照表6.1所示的属性文法,构造表达式(4*7+1) *2的附注语法树. 答: 首先考虑最底最左边的内部结点,它对应于产生式F→digit,相应的语义规则为F. val: = ...

  6. 【编译原理】中间代码(一)

    在编译器的分析-综合模型中,前端对源程序进行分析并产生中间表示,后端在此基础上生成目标代码.理想情况下,和源语言相关的细节在前端分析中处理,而关于目标机器的细节则在后端处理.和中间代码相关的内容包括中 ...

  7. 编译原理陈火旺第三版第七章课后题答案

    下面的答案仅供参考! 1. 给出下面表达式的逆波兰表示(后缀式): a*(-b+c)                               not A or not (C or not D) a ...

  8. flex和bison实例分析

    最近在学习编译原理,利用flex和bison编写一个基于文本识别的简单计算器程序,参考<flex于bison>中内容,对程序进行一些简单的修改,加入Makefile.该计算器程序主要实现识 ...

  9. 学了编译原理能否用 Java 写一个编译器或解释器?

    16 个回答 默认排序​ RednaxelaFX JavaScript.编译原理.编程 等 7 个话题的优秀回答者 282 人赞同了该回答 能.我一开始学编译原理的时候就是用Java写了好多小编译器和 ...

  10. 编译原理——lex 与yacc实例剖析

    这段时间一直在反思教育问题,把自己以前的书翻出来好好读,发现了许多不明白,未曾真懂得东西. 刚刚看完了词法分析和语法分析,越看越简单,不知道以前怎么会觉得它这么难.总之以前还是缺少实践. 下面来谈谈我 ...

最新文章

  1. 为什么会出现网络流量管理?
  2. oracle数据库的详细安装,Oracle 11g数据库详细安装图文教程
  3. mysql连接查询拒绝服务漏洞_MySQL-连接查询
  4. SQL注入之布尔盲注——sql-lab第八关
  5. 【论文串讲】从GPT和BERT到XLNet
  6. boost::shared_from_raw相关的测试程序
  7. 解题报告——习题2-5 分数化小数(decimal) 输入正整数a,b,c,输出a/b的小数形式,精确到小数点后c位。
  8. 湖南工业大学c语言在线作业答案,湖南工业大学C语言期末考试复习题(机房题库)...
  9. 纳尼?我的Gradle build编译只要1s
  10. 第二次作业+105032014037
  11. 关于更新内容次序问题
  12. windows phone 7 学习笔记 五 TileSample
  13. mysql经纬度 微信_微信获取用户的经纬度
  14. 思维方式 | 深入浅出解释“第一性原理”
  15. android沉浸式模式简书,Android-沉浸式模式
  16. 在命令行cmd 下,输入dxdiag 查看关于电脑许多硬件的详细信息
  17. 英语听力采用计算机化考试,高考英语复习资料及听力机考特点与应对建议
  18. 除了秃顶,你和程序员大佬还有啥区别?
  19. 智力开发(赛马问题)
  20. PASCAL VOC2012 数据集讲解与制作自己的数据集

热门文章

  1. matlab多项式除法降阶,二进制多项式除法研究
  2. 史上最全计算机毕业设计题目4(10万套,大部分全国唯一)
  3. Java代码操作数据库(上)——JDBC(JDBC、JUnit、PreparedStatement) 练习
  4. 乐优商城(14)–订单服务
  5. 网站可行性报告范文_邢台写可行性报告写立项报告范文-环建
  6. WPF+prism框架实战源码和展示
  7. SVN分支管理以及跨分支打包
  8. Dxdesigner SCH to Mentor PCB
  9. smart3d加载到谷歌_到底什么是Google Smart Lock?
  10. C语言程序设计飞机售票口,C语言课程设计报告飞机订票系统