编译原理实验e4:从语言SUM到栈式计算机STACK的机器语言的翻译

实验目的:

熟悉语言SUM 到栈式计算机STACK的机器语言的翻译过程,理解编译的一般步骤。

实验内容:

sum.c是用c语言写的从sum语言到栈式计算机STACK的机器语言的编译器(省略了词法语法分析部分)。该程序的基本功能是先构造SUM语言的某句子的抽象语法树,然后将该语法树翻译成STACK的机器语言程序,并按顺序打印出该机器语言程序的指令。程序中有两段内容不完整(在程序中用TODO表示),请读懂并编译通过该程序,再将TODO的部分补充完整,并编译通过。

实验要求:

  1. 读懂程序sum.c并编译通过。(该程序可以使用gcc编译通过,其他编译环境请自行调试)
  2. 用你自己写的程序段替换程序中的TODO部分,使程序功能与实验内容的描述一致。
    3.(此要求为额外要求,供学有余力的同学自行选择。)将程序的输入改为句子1+(2+3)的抽象语法树,尝试程序能否输出正确的结果。
    重要算法或文法产生式:
    实验提供的sum.c文件中,总共有两处需要自己补全代码,一处是后续遍历语法树,另一处是输出STACK指令。整棵语法树的结构是一棵二叉树,根节点存放在exp中,语法树的构建由Exp_Sum_new函数完成。当语法树构建完成之后,由Exp_print输出这个语法树所表示的表达式,再由compile函数后续遍历语法树,以实现sum语言到STACK语言的转换,转换之后的结果存放在以all作为表头的链表中,最后调用List_reverse_print函数输出STACK指令。
    在main函数中调用compile函数时,需要将语法树的根节点作为参数传递进去。在compile函数中,如果当前节点的类型是INT型,那么说明遇到了叶子节点,需要将这个节点的数值部分入栈;如果当前节点的类型是SUM型(即语法树中的加号),那么需要递归的调用compile函数,先遍历这个节点的左子树,再遍历右子树,最后将栈顶的两个元素弹出相加再入栈。
    当完成由sum语言到STACK语言的转换之后,调用List_reverse_print函数输出STACK指令,因为最后产生的指令会放在指令链表的最后,因此需要逆序输出,可以通过判断当前节点是否为空,若为空则return,否则递归访问下一个节点,最后根据当前节点来输出STACK指令。

实验结果:

需要自己补全的代码部分:

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->i));
        break;
    }
    case EXP_SUM: {
        //TODO();
        struct Exp_Sum* p = (struct Exp_Sum*)exp;
        compile(p->left);
        compile(p->right);
        emit(Stack_Add_new());
        break;
    }
    default:
        break;
    }
}
void List_reverse_print(struct List_t* list)    //逆序输出
{
    //TODO();
    if (list == NULL)
        return;
    List_reverse_print(list->next);
    if (list->instr->kind == STACK_ADD)
        printf("ADD\n");
    else {
        struct Stack_Push* p = (struct Stack_Push*) list->instr;
        printf("PUSH %d\n", p->i);
    }
}

当输入的句子为1+2+3时,输出结果如下图:
编译原理实验e4:从语言SUM到栈式计算机STACK的机器语言的翻译_第1张图片

当输入句子改为1+(2+3)时,需要修改一下语法树的构造代码,修改之后的代码如下:

struct Exp_t* exp = Exp_Sum_new(Exp_Int_new(1),
							 Exp_Sum_new(Exp_Int_new(2),Exp_Int_new(3)));

输出结果如下图:
编译原理实验e4:从语言SUM到栈式计算机STACK的机器语言的翻译_第2张图片

完整的源代码

#include 
#include 

#define TODO()                                  \
  do{                                                           \
    printf ("\nAdd your code here: file \"%s\", line %d\n",     \
            __FILE__, __LINE__);                                \
  }while(0)

///////////////////////////////////////////////
// Data structures for the Sum language.
enum Exp_Kind_t { EXP_INT, EXP_SUM };	//表达式的类型
struct Exp_t	//作为指针,既可以指向数,也可以指向加法
{
    enum Exp_Kind_t kind;
};

struct Exp_Int	//表达式是一个数
{
    enum Exp_Kind_t kind;
    int i;
};

struct Exp_Sum	//表达式是加法
{
    enum Exp_Kind_t kind;
    struct Exp_t* left;
    struct Exp_t* right;
};

// "constructors"
struct Exp_t* Exp_Int_new(int i)	//构造一个数节点
{
    struct Exp_Int* p = (struct Exp_Int *)malloc(sizeof(*p));
    p->kind = EXP_INT;
    p->i = i;
    return (struct Exp_t*)p;
}

struct Exp_t* Exp_Sum_new(struct Exp_t* left, struct Exp_t* right)	//构造一个加法节点
{
    struct Exp_Sum* p = (struct Exp_Sum *)malloc(sizeof(*p));
    p->kind = EXP_SUM;
    p->left = left;
    p->right = right;
    return (struct Exp_t*)p;
}

// "printer"
void Exp_print(struct Exp_t* exp)	//根据构造出的语法树,将表达式输出
{
    switch (exp->kind) {
    case EXP_INT: {
        struct Exp_Int* p = (struct Exp_Int*)exp;
        printf("%d", p->i);
        break;
    }
    case EXP_SUM: {
        struct Exp_Sum* p = (struct Exp_Sum*)exp;
        Exp_print(p->left);
        printf("+");
        Exp_print(p->right);
        break;
    }
    default:
        break;
    }
}

//////////////////////////////////////////////
// Data structures for the Stack language.
enum Stack_Kind_t { STACK_ADD, STACK_PUSH };
struct Stack_t
{
    enum Stack_Kind_t kind;
};

struct Stack_Add
{
    enum Stack_Kind_t kind;
};

struct Stack_Push
{
    enum Stack_Kind_t kind;
    int i;
};

// "constructors"
struct Stack_t* Stack_Add_new()
{
    struct Stack_Add* p = (struct Stack_Add *)malloc(sizeof(*p));
    p->kind = STACK_ADD;
    return (struct Stack_t*)p;
}

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

/// instruction list
struct List_t
{
    struct Stack_t* instr;	//指令
    struct List_t* next;		//下一条指令
};

struct List_t* List_new(struct Stack_t* instr, struct List_t* next)	//生成一个指令节点
{
    struct List_t* p = (struct List_t *)malloc(sizeof(*p));
    p->instr = instr;
    p->next = next;
    return p;
}

// "printer"
void List_reverse_print(struct List_t* list)    //逆序输出
{
    //TODO();
    if (list == NULL)
        return;
    List_reverse_print(list->next);
    if (list->instr->kind == STACK_ADD)
        printf("ADD\n");
    else {
        struct Stack_Push* p = (struct Stack_Push*) list->instr;
        printf("PUSH %d\n", p->i);
    }
}

//////////////////////////////////////////////////
// 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->i));
        break;
    }
    case EXP_SUM: {
        //TODO();
        struct Exp_Sum* p = (struct Exp_Sum*)exp;
        compile(p->left);
        compile(p->right);
        emit(Stack_Add_new());
        break;
    }
    default:
        break;
    }
}

//////////////////////////////////////////////////
// program entry
int main()
{
    printf("Compile starting\n");
    // build an expression tree:
    //            +
    //           / \
    //          +   4
    //         / \
    //        2   3
    struct Exp_t* exp = Exp_Sum_new(Exp_Int_new(1),
									Exp_Sum_new(Exp_Int_new(2),Exp_Int_new(3)));
	//struct Exp_t* exp = Exp_Sum_new(Exp_Sum_new(Exp_Int_new(1),Exp_Int_new(2)),
	//								Exp_Int_new(3));
    // print out this tree:
    printf("the expression is:\n");
    Exp_print(exp);
    // compile this tree to Stack machine instructions
    compile(exp);
	printf("\n\nThe Stack Instrction\n");
    // print out the generated Stack instructons:
    List_reverse_print(all);

    printf("\nCompile finished\n");
    return 0;
}

遇到的问题、实验难点、解决方法:

理解sum.c的源代码花了很多的时间,因为以前没有接触过SUM语言和STACK语言,所以又通过百度搜索学习了一些相关的内容。sum.c中涉及很多的结构体,这使得我们初次看代码时感觉非常晦涩难懂,后来把所有的结构体都手抄了下来,发现这些结构体是可以分类的。总体上可以分为两类,一类是与SUM语言有关的结构体,另一类是与STACK语言有关的结构体,按照这个思路,基本上就可以读懂sum.c的源代码了。
本题涉及一定的数据结构的知识,对于树的后续遍历,我们又重新找到了数据结构的相关章节进行了复习,发现后续遍历语法树还是很简单的。输出STACK指令时遇到了一些问题,因为越是后面产生的STACK指令,越是放在链表的前面,最后一条STACK指令放在链表的表头节点,这就要求我们必须逆序输出链表,我们想了很多的方法来解决逆序问题,比如将链表改造成双向链表,翻转整个链表等,但是感觉这样执行效率低,且对源代码的修改较大,后来通过百度发现可以用递归的方式,实现对链表的逆序访问。

你可能感兴趣的:(编译原理实验)