[指令生成]语法制导的指令生成[2]

非常抱歉我选择的字体有些问题,导致字符绘制的图表中右侧竖线没有对齐,追求视觉效果的同学可以将它们拷贝下来,粘贴之后改用“宋体”或其它等宽,并且1个中文字符宽度等于2个ASCII字符宽度的字体显示。

 

    Jerry语言比较简单,流程控制仅限于分支和循环(而且只有while循环)。这里并不会对它们的指令生成做优化,只是给出能够运作的最基本解决方案。

    首先是分支语句。完整的分支语句指令结构是这样的

 

    +----------------------+
    |    condition 指令    |
    +----------------------+
    |      跳转指令 a   -------+
    +----------------------+   |
    |      valid 指令      |   |
    +----------------------+   |
+-------   跳转指令 b      |   |
|   +----------------------+ <-+
|   |     invalid 指令     |
+-> +----------------------+

其中跳转指令 a的指令码为JNT(JMP_NOT_TOP),即当栈顶值为0时跳转,而跳转指令 b的指令码为JMP,是无条件跳转。各路跳转指令的箭头目标都指向指令间的中缝处有点不太好,所以这里在引入一些NOP指令,让整个结构看起来是这样的

 

    +----------------------+
    |    condition 指令    |
    +----------------------+
    |      跳转指令 a   -------+
    +----------------------+   |
    |      valid 指令      |   |
    +----------------------+   |
+-------   跳转指令 b      |   |
|   +----------------------+   |
|   |       NOP 指令       | <-+
|   +----------------------+
|   |     invalid 指令     |
|   +----------------------+
+-> |       NOP 指令       |
    +----------------------+

对于else分支或者甚至两个分支都为空的分支语句,并不进行特别的优化处理,仅仅是让上图中对应的区块的指令数目为0而已。

    现在上代码。

struct List* insIfElseNode(void* node)
{
    struct IfElseNode* self = (struct IfElseNode*)node;
    AcceptType conditionType = self->condition->typeOf(self->condition);

    if (REAL == conditionType) {
        // 报错: 实型不可以作为条件
        return (struct List*)newArrayList();
    }
    struct List* conditionIns = self->condition->
                                            createInstruction(self->condition);
    struct JumpInstruction* toElse = (struct JumpInstruction*)
                                    allocate(sizeof(struct JumpInstruction));
    // 这个指令跳转到 invalid 分支之前的那条 NOP 指令

    struct JumpInstruction* getOut = (struct JumpInstruction*)
                                    allocate(sizeof(struct JumpInstruction));
    // 这条语句在 valid 分支之后, 跳转到整个分支语句之后的那条 NOP 指令

    struct NoParamInstruction* beforeInv = (struct NoParamInstruction*)
                                    allocate(sizeof(struct NoParamInstruction));
    // invalid 分支之前的 NOP 指令

    struct NoParamInstruction* outlet = (struct NoParamInstruction*)
                                    allocate(sizeof(struct NoParamInstruction));
    // 整个语句之后的 NOP 指令

    beforeInv->code = outlet->code = NOP;

    toElse->code = JMP_NOT_TOP;
    toElse->targetIns = (struct AbstractInstruction*)beforeInv;
    getOut->code = JMP;
    getOut->targetIns = (struct AbstractInstruction*)outlet;

    appendIns(conditionIns, toElse);

    if(NULL != self->valid) { // 若是 NULL, 则不管它
        appendInsList(conditionIns,
                      self->valid->createInstruction(self->valid));
    }
    appendIns(appendIns(conditionIns, getOut), beforeInv);

    if(NULL != self->invalid) { // 若是 NULL, 则不管它
        appendInsList(conditionIns,
                      self->invalid->createInstruction(self->invalid));
    }

    return appendIns(conditionIns, outlet);
}

 

    循环语句的结构看起来比分支要简单些,不过,一个循环语句仍然会产生两条跳转语句(同样的,对于循环体为空的循环,现在并不做优化)。它们看起来是这样的

 

    +----------------------+
+-> |       NOP 指令       |
|   +----------------------+
|   |    condition 指令    |
|   +----------------------+
|   |      跳转指令 a   -------+
|   +----------------------+   |
|   |      循环体指令      |   |
|   +----------------------+   |
+-------   跳转指令 b      |   |
    +----------------------+   |
    |       NOP 指令       | <-+
    +----------------------+

跳转指令 a是一条JNT指令,而跳转指令 b是JMP指令。除此之外,还有一点点不同,那就是——while语句需要管理一个跳出栈,这个跳出栈让循环内部的break语句知道该往哪里跳。所以,在实现循环的指令生成前,先需要做这样一些事情

struct Stack loopStack; // 循环栈

// 入栈
// 参数是循环的出口点
// 如刚才图示中 while 循环之后的那条 NOP 指令
void enterLoop(void* outlet)
{
    loopStack.push(&loopStack, outlet);
}

// 出栈
void leaveLoop(void)
{
    loopStack.pop(&loopStack);
}

// 返回栈顶出口指令
// 也就是 break 语句跳转的目标指令
// 当栈空时返回 NULL
struct AbstractInstruction* loopOutlet(void)
{
    return (struct AbstractInstruction*)(loopStack.peek(&loopStack));
}

有了这些,事情就好办了,while会这样生成指令

struct List* insWhileNode(void* node)
{
    struct WhileNode* self = (struct WhileNode*)node;
    if (REAL == self->condition->typeOf(self->condition)) {
        // 报错: 实型不可以作为条件
        return (struct List*)newArrayList();
    }

    struct List* insList = (struct List*)newArrayList(); // 新建空表
    struct NoParamInstruction* beforeAll = (struct NoParamInstruction*)
                                    allocate(sizeof(struct NoParamInstruction));
    // 循环之前的 NOP 指令

    struct NoParamInstruction* outlet = (struct NoParamInstruction*)
                                    allocate(sizeof(struct NoParamInstruction));
    // 循环出口

    struct JumpInstruction* terminate = (struct JumpInstruction*)
                                    allocate(sizeof(struct JumpInstruction));
    // 跳转指令, 条件失败时跳出循环

    struct JumpInstruction* loop = (struct JumpInstruction*)
                                    allocate(sizeof(struct JumpInstruction));
    // 跳转指令, 绕回循环开始

    terminate->code = JMP_NOT_TOP;
    terminate->targetIns = (struct AbstractInstruction*)outlet;
    loop->code = JMP;
    loop->targetIns = (struct AbstractInstruction*)beforeAll;

    beforeAll->code = outlet->code = NOP;
    enterLoop(outlet); // 将出口压入循环栈

    appendIns(insList, beforeAll);
    appendInsList(insList, self->condition->createInstruction(self->condition));
    appendIns(insList, terminate);

    if (NULL != self->loop) { // 若为 NULL, 则忽略它
        appendInsList(insList, self->loop->createInstruction(self->loop));
    }
    appendIns(insList, loop);
    appendIns(insList, outlet);

    leaveLoop(); // 离开循环
    return insList;
} 

 

    聊完了循环自然要谈谈 break,不过仰仗刚才的那些铺垫,这东西非常简单

struct List* insBreakNode(void* node)
{
    struct List* insList = (struct List*)newArrayList();
    struct AbstractInstruction* outlet = loopOutlet();
    if (NULL == outlet) {
        // 报错: break 不在循环内部
    } else {
        struct JumpInstruction* brk = (struct JumpInstruction*)
                                    allocate(sizeof(struct JumpInstruction));
        brk->code = JMP;
        brk->targetIns = outlet;
        appendIns(insList, brk);
    }
    return insList;
}

 

下集预告:变量取址。

你可能感兴趣的:(生成)