编译器实现之旅——第十二章 表达式类代码生成器分派函数的实现

在上一章的旅程中,我们已经实现了一类最为简单的,只含有委托调用的分派函数,而在这一章的旅程中,我们将要实现表达式类节点的分派函数。表达式类节点,包括数字常量节点、算术运算类节点和比较运算类节点。在我们展开讨论之前,不知道你有没有从CMM指令集中注意到这样一件事:CMM指令集中的所有指令,如果只需要一个参数,那么这个参数就一定存放在AX中;而如果需要两个参数,那么第一参数就一定存放在栈顶,而第二参数存放在AX中;此外,如果指令产生了一个结果,那么这个结果也一定会存放在AX中。这段话暗示着代码生成器的一个十分重要的原则:在任何时候,如果我们需要为某种运算做准备,那么进行运算的操作数就应存入AX,或栈顶和AX中;且在任何时候,如果我们需要某个表达式的结果,我们都可以确定其存放在AX中。我们将在本章的旅程中不断见到这一原则的应用。接下来,就让我们开始吧。

1. Number节点的分派函数的实现

Number节点,非常简单:一个数字字面量。当我们看到一个Number节点时,我们并不知道这个数字字面量是被用来做什么的,其可能是某个表达式的某个操作数,也可能就是一个单独的数字。但不管怎么样,我们只需要将这个数字存入AX中,以供其更上层的分派函数使用即可。请看:

vector<pair<__Instruction, string>> __CodeGenerator::__generateNumberCode(__AST *root, const string &) const
{
    /*
        __TokenType::__Number
    */

    return {{__Instruction::__LDC, root->__tokenStr}};
}

2. SimpleExpr节点的分派函数的实现

SimpleExpr节点用于执行一次可能存在的比较运算,其含有一个或三个子节点。如果只含有一个子节点,则不存在比较运算,此时,我们就将这个节点的分派函数的实现委托给AddExpr节点的分派函数即可;而如果含有三个子节点,则存在一次比较运算,此时,我们应先求出第一子节点的值,这个值将被存放在AX中,所以,我们将其压栈,作为比较运算的左操作数;然后,我们求出第三子节点的值,作为比较运算的右操作数;最后,我们使用第二子节点进行比较运算,并将第一子节点的值退栈。请看:

vector<pair<__Instruction, string>> __CodeGenerator::__generateSimpleExprCode(__AST *root, const string &curFuncName) const
{
    /*
        __TokenType::__SimpleExpr
            |
            |---- __AddExpr
            |
            |---- [__RelOp]
            |
            |---- [__AddExpr]
    */

    if (root->__subList.size() == 1)
    {
        return __generateAddExprCode(root->__subList[0], curFuncName);
    }
    else
    {
        auto codeList      = __generateAddExprCode(root->__subList[0], curFuncName);
        auto midCodeList   = __generateRelOpCode(root->__subList[1], curFuncName);
        auto rightCodeList = __generateAddExprCode(root->__subList[2], curFuncName);

        codeList.emplace_back(__Instruction::__PUSH, "");
        codeList.insert(codeList.end(), rightCodeList.begin(), rightCodeList.end());
        codeList.insert(codeList.end(), midCodeList.begin(), midCodeList.end());
        codeList.emplace_back(__Instruction::__POP, "");

        return codeList;
    }
}

3. AddExpr和Term节点的分派函数的实现

AddExpr和Term节点分别用于执行加减法和乘除法运算,其含有奇数个子节点,即1、3、5等等个子节点。但我们只需要区分子节点的数量是否大于1即可,如果子节点只有一个,则说明并不存在任何的加减法或乘除法运算,此时,我们直接将这个节点的分派函数的实现委托给下级节点的分派函数即可;反之,则说明确实存在至少一次的加减法或乘除法运算,此时,我们就需要循环执行"压栈,计算第二操作数,执行运算,退栈"这一系列动作了。请看:

vector<pair<__Instruction, string>> __CodeGenerator::__generateAddExprCode(__AST *root, const string &curFuncName) const
{
    /*
        __TokenType::__AddExpr
            |
            |---- __Term
            |
            |---- [__AddOp]
            |
            |---- [__Term]
            .
            .
            .
    */

    auto codeList = __generateTermCode(root->__subList[0], curFuncName);

    for (int idx = 1; idx < (int)root->__subList.size(); idx += 2)
    {
        auto midCodeList   = __generateAddOpCode(root->__subList[idx], curFuncName);
        auto rightCodeList = __generateTermCode(root->__subList[idx + 1], curFuncName);

        codeList.emplace_back(__Instruction::__PUSH, "");
        codeList.insert(codeList.end(), rightCodeList.begin(), rightCodeList.end());
        codeList.insert(codeList.end(), midCodeList.begin(), midCodeList.end());
        codeList.emplace_back(__Instruction::__POP, "");
    }

    return codeList;
}


vector<pair<__Instruction, string>> __CodeGenerator::__generateTermCode(__AST *root, const string &curFuncName) const
{
    /*
        __TokenType::__Term
            |
            |---- __Factor
            |
            |---- [__MulOp]
            |
            |---- [__Factor]
            .
            .
            .
    */

    auto codeList = __generateFactorCode(root->__subList[0], curFuncName);

    for (int idx = 1; idx < (int)root->__subList.size(); idx += 2)
    {
        auto midCodeList   = __generateMulOpCode(root->__subList[idx], curFuncName);
        auto rightCodeList = __generateFactorCode(root->__subList[idx + 1], curFuncName);

        codeList.emplace_back(__Instruction::__PUSH, "");
        codeList.insert(codeList.end(), rightCodeList.begin(), rightCodeList.end());
        codeList.insert(codeList.end(), midCodeList.begin(), midCodeList.end());
        codeList.emplace_back(__Instruction::__POP, "");
    }

    return codeList;
}

4. RelOp、AddOp和MulOp节点的分派函数的实现

RelOp、AddOp和MulOp节点的分派函数的实现都十分简单,这里就不讨论了。请看:

vector<pair<__Instruction, string>> __CodeGenerator::__generateRelOpCode(__AST *root, const string &) const
{
    /*
        __TokenType::__Less         |
        __TokenType::__LessEqual    |
        __TokenType::__Greater      |
        __TokenType::__GreaterEqual |
        __TokenType::__Equal        |
        __TokenType::__NotEqual
    */

    switch (root->__tokenType)
    {
        case __TokenType::__Less:
            return {{__Instruction::__LT, ""}};

        case __TokenType::__LessEqual:
            return {{__Instruction::__LE, ""}};

        case __TokenType::__Greater:
            return {{__Instruction::__GT, ""}};

        case __TokenType::__GreaterEqual:
            return {{__Instruction::__GE, ""}};

        case __TokenType::__Equal:
            return {{__Instruction::__EQ, ""}};

        case __TokenType::__NotEqual:
            return {{__Instruction::__NE, ""}};

        default:
            throw runtime_error("Invalid __TokenType value");
    }
}


vector<pair<__Instruction, string>> __CodeGenerator::__generateAddOpCode(__AST *root, const string &) const
{
    /*
        __TokenType::__Plus | __TokenType::__Minus
    */

    if (root->__tokenType == __TokenType::__Plus)
    {
        return {{__Instruction::__ADD, ""}};
    }
    else
    {
        return {{__Instruction::__SUB, ""}};
    }
}


vector<pair<__Instruction, string>> __CodeGenerator::__generateMulOpCode(__AST *root, const string &) const
{
    /*
        __TokenType::__Multiply | __TokenType::__Divide
    */

    if (root->__tokenType == __TokenType::__Multiply)
    {
        return {{__Instruction::__MUL, ""}};
    }
    else
    {
        return {{__Instruction::__DIV, ""}};
    }
}

至此,表达式类代码生成器分派函数的实现就全部完成了。接下来,我们将要实现的是if语句和while语句的代码生成器分派函数。请看下一章:《if语句和while语句的代码生成器分派函数的实现》。

posted @ 2021-02-19 16:45  樱雨楼  阅读(195)  评论(0编辑  收藏  举报