我的第一篇博客:编译器实践一 之 加法栈式计算机
最近学习了一点简单的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; }
会生成exp.tab.c的文件
然后执行 gcc main.c ast.c stack.c list.c exp.tab.c
最后执行 a.exe <test..txt
注:test.txt是测试文件,里面存放的是算术表达式。
与君共勉