编译原理第六单元习题
获得更多资料欢迎进入我的网站或者 csdn或者博客园
这几篇关于编译原理的文章是,我学习中国科学技术大学《编译原理》时,所做的习题总结。之后会将这门课的所有习题补上,用于给大家参考;
题目要求
在这个题目中,你将完整的实现抽象语法树(包括数据结构的定义、语法树的生成等)。首先,请下载我们提供的代码包:
http://staff.ustc.edu.cn/~bjhua/mooc/ast.zip
代码的运行方式是:
首先生成语法分析器:
$ bison exp.y
然后生成编译器:
$ gcc main.c exp.tab.c ast.c
最后使用编译器编译某个源文件:
$ a.out <test.txt
在提供的代码里,我们已经提供了抽象语法树的定义、若干操作、及由bison生成语法树的代码框架。你的任务是:
进一步完善该代码框架,使其能够分析减法、除法和括号表达式;(你需要修改语法树的定义,修改bison源文件及其它代码)
重新研究第一次作业中的从Sum编译到Stack的小型编译器代码,把他移植到目前的代码框架中,这样你的编译器能够从文本文件中读入程序,然后输出编译的结果。(注意,你必须扩展你的编译器,让他能够支持减法和除法。)j
使用的工具
其实还是与第五单元的一样使用bison中的yacc工具实现简单计算器的语法制导翻译。
作业结果如下
test.txt内容:(1+2)3+46
peace@peace:~/workspace/compiler/编译器题目/ast$ bison exp.y
peace@peace:~/workspace/compiler/编译器题目/ast$ gcc main.c exp.tab.c ast.c
peace@peace:~/workspace/compiler/编译器题目/ast$ ./a.out <test.txt
(((1) + (2)) * (3)) + ((4) * (6))
PUSH 1
PUSH 2
ADD
PUSH 3
TIMES
PUSH 4
PUSH 6
TIMES
ADD
Compile finished
代码修改步骤
先修改ast.h和ast.c
ast.h修改后的代码以及注释如下
#ifndef AST_H
#define AST_H
//终结符号定义:int是数值的意思;其他分别是+ - × /
enum Exp_Kind_t{
EXP_INT,
EXP_ADD,
EXP_SUB,
EXP_TIMES,
EXP_DIV};
/*
E -> n
| E + E
| E * E
| E - E
| E / E
| (E)
*/
typedef struct Exp_t *Exp_t;
struct Exp_t{
enum Exp_Kind_t kind;
};
// all operations on "Exp"打印语法分析
void Exp_print (Exp_t exp);
//没用到
int Exp_numNodes (Exp_t exp);
typedef struct Exp_Int *Exp_Int;
struct Exp_Int{
enum Exp_Kind_t kind;
int n;
};
Exp_t Exp_Int_new (int n);
/*
* 加法数据结构定义,以及加法构造方法
*/
typedef struct Exp_Add *Exp_Add;
struct Exp_Add{
enum Exp_Kind_t kind;
Exp_t left;
Exp_t right;
};
Exp_t Exp_Add_new (Exp_t left, Exp_t right);
/*
* 减法数据结构定义,以及减法构造方法
*/
typedef struct Exp_sub *Exp_sub;
struct Exp_sub{
enum Exp_Kind_t kind;
Exp_t left;
Exp_t right;
};
Exp_t Exp_sub_new (Exp_t left, Exp_t right);
/*
* 乘法数据结构定义,以及乘法构造方法
*/
typedef struct Exp_Times *Exp_Times;
struct Exp_Times{
enum Exp_Kind_t kind;
Exp_t left;
Exp_t right;
};
Exp_t Exp_Times_new (Exp_t left, Exp_t right);
/*
* 除法数据结构定义,以及除法构造方法
*/
typedef struct Exp_Div *Exp_Div;
struct Exp_Div{
enum Exp_Kind_t kind;
Exp_t left;
Exp_t right;
};
Exp_t Exp_Div_new (Exp_t left, Exp_t right);
/*
* 建立以栈为依托的抽象语法树;其中ADD标示:+号SUB:- TIMES:× DIV:/
*/
enum Stack_Kind_t {STACK_ADD,STACK_SUB,STACK_TIMES,STACK_DIV, STACK_PUSH};
typedef struct Stack_t *Stack_t;
struct Stack_t
{
enum Stack_Kind_t kind;
};
// ADD
typedef struct Stack_Add *Stack_Add;
struct Stack_Add
{
enum Stack_Kind_t kind;
};
Stack_t Stack_Add_new ();
// sub
typedef struct Stack_Sub *Stack_Sub;
struct Stack_Sub
{
enum Stack_Kind_t kind;
};
Stack_t Stack_Sub_new ();
// Times
typedef struct Stack_Times *Stack_Times;
struct Stack_Times
{
enum Stack_Kind_t kind;
};
Stack_t Stack_Times_new ();
// DIv
typedef struct Stack_Div *Stack_Div;
struct Stack_Div
{
enum Stack_Kind_t kind;
};
Stack_t Stack_Div_new ();
//push number
typedef struct Stack_Push *Stack_Push;
struct Stack_Push
{
enum Stack_Kind_t kind;
int i;
};
Stack_t Stack_Push_new (int i);
//建立栈用的数据结构
struct List_t
{
struct Stack_t *instr;
struct List_t *next;
};
//
struct List_t *List_new (struct Stack_t *instr, struct List_t *next);
//打印得到抽象语法树
void List_reverse_print (struct List_t *list);
//建立栈
void emit (struct Stack_t *instr);
#endif
ast.c修改后的代码以及注释如下
#include <stdio.h>
#include <stdlib.h>
#include "ast.h"
struct List_t *all = 0;
// 整数的类构造函数
Exp_t Exp_Int_new (int n)
{
Exp_Int p = malloc (sizeof (*p));
p->kind = EXP_INT;
p->n = n;
return (Exp_t)p;
}
//+号的类构造函数
Exp_t Exp_Add_new (Exp_t left, Exp_t right)
{
Exp_Add p = malloc (sizeof (*p));
p->kind = EXP_ADD;
p->left = left;
p->right = right;
return (Exp_t)p;
}
//减号的类构造函数
Exp_t Exp_sub_new (Exp_t left, Exp_t right)
{
Exp_sub p = malloc (sizeof (*p));
p->kind = EXP_SUB;
p->left = left;
p->right = right;
return (Exp_t)p;
}
//乘号的构造函数
Exp_t Exp_Times_new (Exp_t left, Exp_t right)
{
Exp_Times p = malloc (sizeof (*p));
p->kind = EXP_TIMES;
p->left = left;
p->right = right;
return (Exp_t)p;
}
//除号的构造函数
Exp_t Exp_Div_new (Exp_t left, Exp_t right)
{
Exp_Div p = malloc (sizeof (*p));
p->kind = EXP_DIV;
p->left = left;
p->right = right;
return (Exp_t)p;
}
// 将输入的树:用exp打印出来。+-×/都用到了递归,分别打印左边与右边。
void Exp_print (Exp_t exp)
{
switch (exp->kind){
case EXP_INT:{
Exp_Int p = (Exp_Int)exp;
printf ("%d", p->n);
return;
}
case EXP_ADD:{
Exp_Add p = (Exp_Add)exp;
printf ("(");
Exp_print (p->left);
printf (") + (");
Exp_print (p->right);
printf (")");
return;
}
case EXP_SUB:{
Exp_sub p = (Exp_sub)exp;
printf ("(");
Exp_print (p->left);
printf (") - (");
Exp_print (p->right);
printf (")");
return;
}
case EXP_TIMES:{
Exp_Times p = (Exp_Times)exp;
printf ("(");
Exp_print (p->left);
printf (") * (");
Exp_print (p->right);
printf (")");
return;
}
case EXP_DIV:{
Exp_Div p = (Exp_Div)exp;
printf ("(");
Exp_print (p->left);
printf (") / (");
Exp_print (p->right);
printf (")");
return;
}
default:
return;
}
}
//一下为建立抽象语法树用到的;
//加号节点的建立
Stack_t Stack_Add_new ()
{
Stack_Add p = malloc (sizeof(*p));
p->kind = STACK_ADD;
return (Stack_t)p;
}
//减号节点的建立
Stack_t Stack_Sub_new ()
{
Stack_Sub p = malloc (sizeof(*p));
p->kind = STACK_SUB;
return (Stack_t)p;
}
//乘号节点的建立
Stack_t Stack_Times_new ()
{
Stack_Times p = malloc (sizeof(*p));
p->kind = STACK_TIMES;
return (Stack_t)p;
}
//除号节点的建立
Stack_t Stack_Div_new ()
{
Stack_Div p = malloc (sizeof(*p));
p->kind = STACK_DIV;
return (Stack_t)p;
}
//数值压入
Stack_t Stack_Push_new (int i)
{
Stack_Push p = malloc (sizeof(*p));
p->kind = STACK_PUSH;
p->i = i;
return (Stack_t)p;
}
//输出抽象语法树。注意是用栈进行模拟的;
void List_reverse_print (struct List_t *list)
{
struct Stack_Push *p;
printf("\n");
while(list)
{
switch(list->instr->kind)
{
case STACK_ADD:printf("ADD\n");break;
case STACK_SUB:printf("SUB\n");break;
case STACK_TIMES:printf("TIMES\n");break;
case STACK_DIV:printf("DIV\n");break;
case STACK_PUSH:p=(struct Stack_Push*)list->instr;printf("PUSH %d\n",p->i);break;
}
list=list->next;
}
}
struct List_t *List_new (struct Stack_t *instr, struct List_t *next)
{
struct List_t *p = malloc (sizeof (*p));
p->instr = instr;
p->next = next;
return p;
}
//树的建立
void emit (struct Stack_t *instr)
{
all = List_new (instr, all);
}
修改exp.y
可以参考我的五单元习题
带注释的代码如下:
%{
//需要用到的头文件
#include <stdio.h>
#include "ast.h"
//声明函数,必不可少
int yylex(); // this function will be called in the parser
void yyerror(char *);
//expt的建立
Exp_t tree;
%}
%union{
Exp_t exp;
}
%type <exp> digit exp program
//左结合,× /优先级更高
%left '+' '-'
%left '*' '/'
%start program
%%
//将最后的结果赋值给tree
program: exp {tree = $1;}
;
//上下文无关文法
exp: digit {$$ = $1;}
| exp '+' exp {$$ = Exp_Add_new ($1, $3);}
| exp '-' exp {$$ = Exp_sub_new ($1, $3);}
| exp '*' exp {$$ = Exp_Times_new ($1, $3);}
| exp '/' exp {$$ = Exp_Div_new ($1, $3);}
| '(' exp ')' {$$ =$2;}
;
//数值:0-9
digit: '0' {$$ = Exp_Int_new (0);}
| '1' {$$ = Exp_Int_new (1);}
| '2' {$$ = Exp_Int_new (2);}
| '3' {$$ = Exp_Int_new (3);}
| '4' {$$ = Exp_Int_new (4);}
| '5' {$$ = Exp_Int_new (5);}
| '6' {$$ = Exp_Int_new (6);}
| '7' {$$ = Exp_Int_new (7);}
| '8' {$$ = Exp_Int_new (8);}
| '9' {$$ = Exp_Int_new (9);}
;
%%
//获得输入字符
int yylex ()
{
int c = getchar();
return c;
}
// bison needs this function to report
// error message
void yyerror(char *err)
{
fprintf (stderr, "%s\n", err);
return;
}
修改main.c
#include <stdio.h>
#include "ast.h"
//引入结果
extern Exp_t tree;
//引入树
extern struct List_t *all;
//声明yyparse ();
void yyparse ();
//将tree转化为抽象语法树
void compile (Exp_t exp)
{
switch (exp->kind){
case EXP_INT:{
Exp_Int p = (Exp_Int)exp;
emit (Stack_Push_new (p->n));
break;
}
case EXP_ADD:{
//相当于后续遍历;
Exp_Add p = (Exp_Add)exp;
/*对左右分别编译
将+号存入栈中*/
emit (Stack_Add_new ());
compile(p->right);
compile(p->left);
break;
}
case EXP_SUB:{
//相当于后续遍历;
Exp_sub p = (Exp_sub)exp;
/*对左右分别编译
将-号存入栈中*/
emit (Stack_Sub_new ());
compile(p->right);
compile(p->left);
break;
}
case EXP_TIMES:{
//相当于后续遍历;
Exp_Times p = (Exp_Times)exp;
/*对左右分别编译
将×号存入栈中*/
emit (Stack_Times_new ());
compile(p->right);
compile(p->left);
break;
}
case EXP_DIV:{
//相当于后续遍历;
Exp_Div p = (Exp_Div)exp;
/*对左右分别编译
将/号存入栈中*/
emit (Stack_Div_new ());
compile(p->right);
compile(p->left);
break;
}
default:
break;
}
}
int main (int argc, char **argv)
{
yyparse();
//print out this tree:
Exp_print (tree);
// compile this tree to Stack machine instructions
compile (tree);
// print out the generated Stack instructons:
List_reverse_print (all);
printf("\nCompile finished\n");
return 0;
}