zoukankan      html  css  js  c++  java
  • 编译原理第六单元习题

    获得更多资料欢迎进入我的网站或者 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("
    ");
      while(list)
      { 
            switch(list->instr->kind)
            {        
                      case STACK_ADD:printf("ADD
    ");break;
                      case STACK_SUB:printf("SUB
    ");break;
                      case STACK_TIMES:printf("TIMES
    ");break;
                      case STACK_DIV:printf("DIV
    ");break;
                      case STACK_PUSH:p=(struct Stack_Push*)list->instr;printf("PUSH %d
    ",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
    ", 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("
    Compile finished
    ");
      return 0;
    }
    
  • 相关阅读:
    QTP 11.05下载并完成+皴
    ZOJ Monthly, June 2014 月赛BCDEFGH题题解
    Linux makefile 教程 很具体,且易懂
    oracle中imp命令具体解释
    html5实现摇一摇
    AfxMessageBox和MessageBox差别
    Android传感器概述(六)
    线性代数之矩阵与坐标系的转换
    測试新浪微博@小冰 为代码机器人的一些方法
    破解中国电信华为无线猫路由(HG522-C)自己主动拨号+不限电脑数+iTV
  • 原文地址:https://www.cnblogs.com/onepeace/p/4690733.html
Copyright © 2011-2022 走看看