編譯器實踐五 之 構造一個支援加減乘除的棧式計算機
阿新 • • 發佈:2019-02-18
最近學習了一點簡單的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是測試檔案,裡面存放的是算術表示式。
與君共勉