编译器实现之旅——第十一章 一些较为顶层的代码生成器分派函数的实现
从这一章的旅程开始,我们就要开始逐步的实现各个代码生成器分派函数了。首先,什么叫"较为顶层的代码生成器分派函数"?原来呀,这类分派函数都有一个共同特点:其自身不产生任何代码,而只是通过调用一些其他的分派函数,并合并这些函数所产生的代码,以实现自身功能。接下来,就让我们来看看这样的函数都有哪些,分别都是怎么实现的吧。
1. StmtList节点的分派函数的实现
StmtList节点具有若干Stmt子节点,故我们只需要不断的调用Stmt节点的分派函数,并将其结果合并即可,请看:
vector<pair<__Instruction, string>> __CodeGenerator::__generateStmtListCode(__AST *root, const string &curFuncName) const
{
/*
__TokenType::__StmtList
|
|---- [__Stmt]
.
.
.
*/
vector<pair<__Instruction, string>> codeList;
for (auto stmtPtr: root->__subList)
{
auto stmtCodeList = __generateStmtCode(stmtPtr, curFuncName);
codeList.insert(codeList.end(), stmtCodeList.begin(), stmtCodeList.end());
}
return codeList;
}
2. Stmt节点的分派函数的实现
Stmt节点是一个多重选择类节点,在这个节点上实际存在的可能是ExprStmt、CompoundStmt、IfStmt、WhileStmt或ReturnStmt节点;需要注意的是,实际上并没有真正的ExprStmt节点存在,查看ExprStmt节点的抽象语法树结构可知:ExprStmt节点实际上要么是一个空指针,要么是一个Expr节点。所以,我们的实现只需要判断并调用这些不同类型的节点所对应的分派函数即可,而如果当前节点是空指针,那么显然:我们什么代码都不需要生成。请看:
vector<pair<__Instruction, string>> __CodeGenerator::__generateStmtCode(__AST *root, const string &curFuncName) const
{
/*
__ExprStmt | __IfStmt | __WhileStmt | __ReturnStmt
(__ExprStmt __AST: __Expr | nullptr)
*/
if (!root)
{
return {};
}
switch (root->__tokenType)
{
case __TokenType::__Expr:
return __generateExprCode(root, curFuncName);
case __TokenType::__IfStmt:
return __generateIfStmtCode(root, curFuncName);
case __TokenType::__WhileStmt:
return __generateWhileStmtCode(root, curFuncName);
case __TokenType::__ReturnStmt:
return __generateReturnStmtCode(root, curFuncName);
default:
throw runtime_error("Invalid __TokenType value");
}
}
3. ReturnStmt节点的分派函数的实现
ReturnStmt节点用于函数的返回值,这个节点含有一个或零个子节点,分别代表了return的后面有或没有一个表达式。所以,当ReturnStmt节点含有一个子节点时,我们就将这个节点的分派函数的实现委托给Expr节点的分派函数即可;否则,我们什么代码都不需要生成。请看:
vector<pair<__Instruction, string>> __CodeGenerator::__generateReturnStmtCode(__AST *root, const string &curFuncName) const
{
/*
__TokenType::__ReturnStmt
|
|---- [__Expr]
*/
if (root->__subList.empty())
{
return {};
}
else
{
return __generateExprCode(root->__subList[0], curFuncName);
}
}
4. Expr节点的分派函数的实现
虽然在语法分析器的实现中,Expr节点给我们带来了不小的麻烦。但在代码生成器中,Expr节点却是平淡无奇的。这个节点含有一个或两个子节点,子节点数量的不同代表着这个节点所对应的动作不同:如果只有一个子节点,那么就说明这个Expr节点实际上是一个简单表达式,我们将这个节点的分派函数的实现委托给SimpleExpr节点的分派函数即可;而如果有两个子节点,那么就说明这个Expr节点实际上是一个赋值动作,此时,我们应当先求出赋值号右侧表达式的值,再执行赋值动作,所以,此时这个节点的分派函数的实现,应当先是其第二子节点生成的代码,再加上其第一子节点生成的代码。请看:
vector<pair<__Instruction, string>> __CodeGenerator::__generateExprCode(__AST *root, const string &curFuncName) const
{
/*
__TokenType::__Expr
|
|---- __Var
|
|---- __Expr
----------------------
__TokenType::__Expr
|
|---- __SimpleExpr
*/
if (root->__subList.size() == 1)
{
return __generateSimpleExprCode(root->__subList[0], curFuncName);
}
else
{
auto codeList = __generateExprCode(root->__subList[1], curFuncName);
auto assignCodeList = __generateAssignCode(root->__subList[0], curFuncName);
codeList.insert(codeList.end(), assignCodeList.begin(), assignCodeList.end());
return codeList;
}
}
5. Factor节点的分派函数的实现
和上面的Stmt节点类似,Factor节点也是一个多重选择类节点。所以,我们只需要判断并调用这些不同类型的节点所对应的分派函数即可。请看:
vector<pair<__Instruction, string>> __CodeGenerator::__generateFactorCode(__AST *root, const string &curFuncName) const
{
/*
__Expr | __TokenType::__Number | __Call | __Var
*/
switch (root->__tokenType)
{
case __TokenType::__Expr:
return __generateExprCode(root, curFuncName);
case __TokenType::__Number:
return __generateNumberCode(root, curFuncName);
case __TokenType::__Call:
return __generateCallCode(root, curFuncName);
case __TokenType::__Var:
return __generateVarCode(root, curFuncName);
default:
throw runtime_error("Invalid __TokenType value");
}
}
至此,一些较为顶层的代码生成器分派函数的实现就全部完成了。让我们继续,接下来我们将要实现的是表达式类代码生成器分派函数。请看下一章:《表达式类代码生成器分派函数的实现》。