您的位置:首页 > 理论基础

编译器实践五 之 构造一个支持加减乘除的栈式计算机

2015-08-16 19:22 561 查看
我的第一篇博客:编译器实践一 之 加法栈式计算机

最近学习了一点简单的Bison再加上抽象语法树,于是做了一下MOOC的留下的作业,按照要求,改了和增加了几处代码,感觉学到了很多东西。不过为了减少难度,突出侧重点,我没有去管理内存,内存泄露有点严重,代码仅供参考。

功能是输入一行或者多行算术表达式,对表达式建立抽象语法树,然后输出(主要是为了查看抽象表达式建立的是否准确),编译,完成对栈式计算机的模拟,原来的代码不支持减法,除法,括号表达式,并且只能完成一行的编译,我把它推广到了任意行数。

下面是对抽象语法树的定义:

/*ast.h*/
#ifndef AST_H
#define AST_H

enum Exp_Kind_t{
  EXP_INT,
  EXP_ADD,
  EXP_TIMES,
  EXP_DIV,
  EXP_SUB};

/*
   E -> n
      | 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_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);

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

#endif


下面是ast.h的实现:

/*ast.c*/
#include <stdio.h>
#include <stdlib.h>
#include "ast.h"

// "constructors"
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_Times_new (Exp_t left, Exp_t right)
{
  Exp_Add 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_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;
}

// all operations on "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_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;
  }
  case EXP_SUB:{
    Exp_Sub p = (Exp_Sub)exp;
    printf ("(");
    Exp_print (p->left);
    printf (") - (");
    Exp_print (p->right);
    printf (")");
    return;
  }
  default:
    return;
  }
}


下面是栈式计算机的模拟,下面是栈的数据结构:

/*stack.h*/
#ifndef _STACK_H_
#define _STACK_H_
#include "ast.h"
enum Stack_Kind_t {STACK_ADD, STACK_SUB,STACK_TIMES,STACK_DIV,STACK_PUSH};
struct Stack_t
{
  enum Stack_Kind_t kind;
};

struct Stack_Add
{
  enum Stack_Kind_t kind;
};
struct Stack_Sub
{
  enum Stack_Kind_t kind;
};
struct Stack_Times
{
  enum Stack_Kind_t kind;
};
struct Stack_Div
{
  enum Stack_Kind_t kind;
};
struct Stack_Push
{
  enum Stack_Kind_t kind;
  int i;
};

struct Stack_t *Stack_Add_new ();
struct Stack_t *Stack_Times_new ();
struct Stack_t *Stack_Div_new ();
struct Stack_t *Stack_Sub_new ();
struct Stack_t *Stack_Push_new (int i);

#endif


下面是实现:

/*stack.c*/
#include "stack.h"
#include <stdlib.h>
// "constructors"
struct Stack_t *Stack_Add_new ()
{
  struct Stack_Add *p = (struct Stack_Add *)malloc (sizeof(struct Stack_Add));
  p->kind = STACK_ADD;
  return (struct Stack_t *)p;
}

struct Stack_t *Stack_Times_new ()
{
  struct Stack_Times *p = (struct Stack_Times *)malloc (sizeof(struct Stack_Times));
  p->kind = STACK_TIMES;
  return (struct Stack_t *)p;
}
struct Stack_t *Stack_Div_new ()
{
  struct Stack_Div *p = (struct Stack_Div *)malloc (sizeof(struct Stack_Div));
  p->kind = STACK_DIV;
  return (struct Stack_t *)p;
}

struct Stack_t *Stack_Sub_new ()
{
  struct Stack_Sub *p = (struct Stack_Sub *)malloc (sizeof(struct Stack_Sub));
  p->kind = STACK_SUB;
  return (struct Stack_t *)p;
}

struct Stack_t *Stack_Push_new (int i)
{
  struct Stack_Push *p = (struct Stack_Push *)malloc (sizeof(struct Stack_Push));
  p->kind = STACK_PUSH;
  p->i = i;
  return (struct Stack_t *)p;
}


下面是存储栈式计算机的每条命令,并且编译,输出的.h文件,:

/*list.h*/
#ifndef _LIST_H_
#define _LIST_H_
#include "stack.h"
#include "ast.h"
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);
void compile (struct Exp_t *exp) ;

#endif


下面是实现:

/*list.c*/
#include <stdio.h>
#include <stdlib.h>
#include "list.h"

/// instruction list

struct List_t *List_new (struct Stack_t *instr, struct List_t *next)
{
  struct List_t *p = (struct List_t *)malloc (sizeof (struct List_t));
  p->instr = instr;
  p->next = next;
  return p;
}

// "printer"
void List_reverse_print (struct List_t *list)
{
	if(list == NULL)
		return ;
	List_reverse_print(list->next) ;
	switch (list->instr->kind)
	{
		case STACK_PUSH:printf("PUSH %d\n",((struct Stack_Push *)(list->instr))->i) ;break ;
		case STACK_ADD:puts("ADD") ;break ;
		case STACK_SUB:puts("Sub") ; break ;
		case STACK_TIMES:puts("Mul") ;break ;
		case STACK_DIV:puts("div") ; break ;
		default : break ;
	}
}

//////////////////////////////////////////////////
// a compiler from Sum to Stack
struct List_t *all = 0;
void emit (struct Stack_t *instr)
{
  all = List_new (instr, all);
}

void compile (struct Exp_t *exp)
{
  switch (exp->kind){
  case EXP_INT:{
    struct Exp_Int *p = (struct Exp_Int *)exp;
    emit (Stack_Push_new (p->n));
    break;
  }
  case EXP_ADD:{

	struct Exp_Add * t = (struct Exp_Add *)exp ;
	compile(t->left) ;
	compile(t->right) ;
	emit(Stack_Add_new()) ;
    break;
  }
  case EXP_TIMES :{
  	struct Exp_Times * t = (struct Exp_Times *)exp ;
	compile(t->left) ;
	compile(t->right) ;
	emit(Stack_Times_new()) ;
	break;
  }
  case EXP_SUB :{
  	struct Exp_Sub * t = (struct Exp_Sub *)exp ;
	compile(t->left) ;
	compile(t->right) ;
	emit(Stack_Sub_new()) ;
	break;
  }
  case EXP_DIV :{
  	struct Exp_Div * t = (struct Exp_Div *)exp ;
	compile(t->left) ;
	compile(t->right) ;
	emit(Stack_Div_new()) ;
	break;
  }
  default:
    break;
  }
}


下面是利用bison生成抽象语法树exp.y,单独手工构造抽象语法树太复杂了:

%{
#include <stdio.h>
#include "list.h"
#include "stack.h"
#include "ast.h"

  int yylex(); // this function will be called in the parser
  void yyerror(char *);
  extern struct List_t *all;

  %}

%union{
  Exp_t exp;
 }

%type <exp> digit exp program

%left '+' '-'
%left '*' '/'

%start program

%%

program: program exp '\n' {Exp_t tree = $2;
  Exp_print (tree);
  puts("\nSTART COMPILE...") ;
  compile(tree) ;
  List_reverse_print (all);
  all = 0 ;
  puts("\nEND COMPILE...") ;}
| program exp {Exp_t tree = $2;
  Exp_print (tree);
  puts("\nSTART COMPILE...") ;
  compile(tree) ;
  List_reverse_print (all);
  all = 0 ;
  puts("\nEND COMPILE...") ;}
| /*empty*/
;

exp: digit     {$$ = $1;}
| exp '+' exp  {$$ = Exp_Add_new ($1, $3);}
| exp '*' exp  {$$ = Exp_Times_new ($1, $3);}
| exp '/' exp  {$$ = Exp_Div_new ($1, $3);}
| exp '-' exp  {$$ = Exp_Sub_new ($1, $3);}
| '(' exp ')'  {$$ = $2;}
;

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


把上述所有文件放到一个文件夹里,然后执行bison exp.y

会生成exp.tab.c的文件

然后执行 gcc main.c ast.c stack.c list.c exp.tab.c

最后执行 a.exe <test..txt

注:test.txt是测试文件,里面存放的是算术表达式。

与君共勉
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: