码迷,mamicode.com
首页 > 其他好文 > 详细

编译原理第六单元习题

时间:2015-07-30 23:25:47      阅读:221      评论:0      收藏:0      [点我收藏+]

标签:bison   compiler   yacc   

获得更多资料欢迎进入我的网站或者 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+4*6

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;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

编译原理第六单元习题

标签:bison   compiler   yacc   

原文地址:http://blog.csdn.net/peace1213/article/details/47156949

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!