在上一章的旅程中,我们已经实现了表达式类代码生成器分派函数,而在这一章的旅程中,我们将要实现if语句和while语句的代码生成器分派函数。if语句和while语句是两种典型的带有跳转指令的语句。观察CMM指令集的实现不难发现,跳转指令,实际上就是通过强行修改IP的值,使得虚拟机下一次看到的CS[IP]并不是当前CS[IP]的下一条语句。所以,跳转指令的摆放位置及其参数,是我们在实现if语句和while语句的分派函数的过程中,需要思考的问题。那么接下来,就让我们先从if语句开始吧。

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

IfStmt节点,即if语句节点,其可能含有两个或三个子节点:第一子节点代表了if语句的条件表达式部分;而第二子节点代表了如果满足条件,需要执行的部分;第三子节点则代表了else部分。不难想到:如果一个IfStmt节点只含有两个子节点,即不含有else部分,那么其代码模型如下所示:

if 第一子节点生成的代码(其结果最终存放于AX中)

    JZ END

    第二子节点生成的代码

END: ...

现在的问题是:我们的CMM指令集根本就没有"JZ END"这一说,JZ只能后接一个数字,这怎么办呢?很简单,我们将"END"转换为第二子节点生成的代码的条数即可。例如:第二子节点一共生成了5条指令,那么,我们就需要生成一条"JZ 6"指令,请注意,JZ后面的数字是代码条数5再加上1,如果不加1的话,就会跳转到第二子节点生成的最后一条代码上,这样的边界问题是一定要注意的。

而如果一个IfStmt节点含有三个子节点呢?此时的代码模型就变为了这样:

if 第一子节点生成的代码(其结果最终存放于AX中)

    JZ ELSE

    第二子节点生成的代码

    JMP END

else

    ELSE: 第三子节点生成的代码

END: ...

此时的情况就要稍复杂一些,"JMP END"需要被转换为第三子节点生成的代码的条数加1;而由于"JMP END"的存在,"JZ ELSE"就需要被转换为第二子节点生成的代码的条数加1,再加1,以同时越过第二子节点生成的所有代码以及一条多出来的"JMP END"指令。

有了上面代码模型的铺垫,我们就不难得到IfStmt节点的分派函数的实现了。请看:

vector<pair<__Instruction, string>> __CodeGenerator::__generateIfStmtCode(__AST *root, const string &curFuncName) const
{
/*
__TokenType::__IfStmt
|
|---- __Expr
|
|---- __StmtList
|
|---- [__StmtList]
*/ auto codeList = __generateExprCode(root->__subList[0], curFuncName);
auto ifCodeList = __generateStmtListCode(root->__subList[1], curFuncName); if (root->__subList.size() == 2)
{
/*
if ... JZ END ... END: ...
*/
codeList.emplace_back(__Instruction::__JZ, to_string(ifCodeList.size() + 1));
codeList.insert(codeList.end(), ifCodeList.begin(), ifCodeList.end());
}
else
{
/*
if ... JZ ELSE ... JMP END else ELSE: ... END: ...
*/
auto elseCodeList = __generateStmtListCode(root->__subList[2], curFuncName); ifCodeList.emplace_back(__Instruction::__JMP, to_string(elseCodeList.size() + 1)); codeList.emplace_back(__Instruction::__JZ, to_string(ifCodeList.size() + 1));
codeList.insert(codeList.end(), ifCodeList.begin(), ifCodeList.end());
codeList.insert(codeList.end(), elseCodeList.begin(), elseCodeList.end());
} return codeList;
}

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

WhileStmt节点,即while语句节点,其实现与上文中的if语句是类似的。稍有不同的是,while语句中需要用到一次JMP的参数为负数的向前跳转,但其原理是不变的;并且,while语句也没有子节点数量的差异,一定具有两个子节点。接下来,就让我们来看看while语句的代码模型吧:

START: while 第一子节点生成的代码(其结果最终存放于AX中)

    JZ END

    第二子节点生成的代码

    JMP START

END: ...

和我们讨论if语句时一样,我们现在需要思考的是:如何转换这两条"JZ END"和"JMP START"指令?首先看"JZ END"指令,和上文中那个"加1,再加1"的"JZ ELSE"指令类似,由于"JMP START"指令的存在,"JZ END"指令的"END"需要被转换为第二子节点生成的代码的条数加1,再加1;而"JMP START"指令现在对我们来说也不是一个难点了,其需要被转换为负的第二子节点生成的代码的条数再减1(请注意,这里并不是减1,再减1)。

有了上面代码模型的铺垫,我们就不难得到WhileStmt节点的分派函数的实现了。请看:

vector<pair<__Instruction, string>> __CodeGenerator::__generateWhileStmtCode(__AST *root, const string &curFuncName) const
{
/*
__TokenType::__WhileStmt
|
|---- __Expr
|
|---- __StmtList
*/ auto codeList = __generateExprCode(root->__subList[0], curFuncName);
auto stmtCodeList = __generateStmtListCode(root->__subList[1], curFuncName); /*
START: while ... JZ END ... JMP START END: ...
*/
codeList.emplace_back(__Instruction::__JZ, to_string(stmtCodeList.size() + 2));
codeList.insert(codeList.end(), stmtCodeList.begin(), stmtCodeList.end());
codeList.emplace_back(__Instruction::__JMP, "-" + to_string(codeList.size())); return codeList;
}

至此,if语句和while语句的代码生成器分派函数的实现就全部完成了。接下来,我们将要实现的是一类看似平淡无奇,实则暗藏玄机的操作。请看下一章:《变量存取的代码生成器分派函数的实现》。

编译器实现之旅——第十三章 if语句和while语句的代码生成器分派函数的实现的更多相关文章

  1. Gradle 1.12用户指南翻译——第二十三章. Java 插件

    其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Github上的地址: https://g ...

  2. C++ Primer Plus学习:第十三章

    第十三章 类继承 继承的基本概念 类继承是指从已有的类派生出新的类.例: 表 0-1 player.h class player { private: string firstname; string ...

  3. 20190928 On Java8 第二十三章 注解

    第二十三章 注解 定义在 java.lang 包中的5种标准注解: @Override:表示当前的方法定义将覆盖基类的方法.如果你不小心拼写错误,或者方法签名被错误拼写的时候,编译器就会发出错误提示. ...

  4. 20190825 On Java8 第十三章 函数式编程

    第十三章 函数式编程 函数式编程语言操纵代码片段就像操作数据一样容易. 虽然 Java 不是函数式语言,但 Java 8 Lambda 表达式和方法引用 (Method References) 允许你 ...

  5. 【C++】《C++ Primer 》第十三章

    第十三章 拷贝控制 定义一个类时,需要显式或隐式地指定在此类型地对象拷贝.移动.赋值和销毁时做什么. 一个类通过定义五种特殊的成员函数来控制这些操作.即拷贝构造函数(copy constructor) ...

  6. PRML读书会第十三章 Sequential Data(Hidden Markov Models,HMM)

    主讲人 张巍 (新浪微博: @张巍_ISCAS) 软件所-张巍<zh3f@qq.com> 19:01:27 我们开始吧,十三章是关于序列数据,现实中很多数据是有前后关系的,例如语音或者DN ...

  7. <构建之法>第十三章到十七章有感以及这个项目读后感

    <构建之法>第十三章到十七章有感 第13章:软件测试方法有哪些? 主要讲了软件测试方法:要说有什么问题就是哪种效率最高? 第14章:质量保障 软件的质量指标是什么?怎么样能够提升软件的质量 ...

  8. 《Linux命令行与shell脚本编程大全》 第二十三章 学习笔记

    第二十三章:使用数据库 MySQL数据库 MySQL客户端界面 mysql命令行参数 参数 描述 -A 禁用自动重新生成哈希表 -b 禁用 出错后的beep声 -B 不使用历史文件 -C 压缩客户端和 ...

  9. 《Android群英传》读书笔记 (5) 第十一章 搭建云端服务器 + 第十二章 Android 5.X新特性详解 + 第十三章 Android实例提高

    第十一章 搭建云端服务器 该章主要介绍了移动后端服务的概念以及Bmob的使用,比较简单,所以略过不总结. 第十三章 Android实例提高 该章主要介绍了拼图游戏和2048的小项目实例,主要是代码,所 ...

  10. Gradle 1.12 翻译——第十三章 编写构建脚本

    有关其它已翻译的章节请关注Github上的项目:https://github.com/msdx/gradledoc/tree/1.12,或訪问:http://gradledoc.qiniudn.com ...

随机推荐

  1. vscode 使用 python 进行 UG 二次开发 实现代码提示功能

    vscode 使用 python 进行 UG 二次开发的 实现代码提示功能 用 VSCODE 进行 UG 二次开发的时候, 想要用代码提示的时候,可以用 pydev 插件, 但是,pydev 只有一个 ...

  2. 如何合理开发Java接口(安全性,可重复调用,稳定性,追溯性)

    一.接口开发规范 签名:对外提供的接口要做签名认证,认证不通过的请求不允许访问接口.提供服务. 加密:敏感数据在网络传输过程中应该加密. IP白名单:限制请求的IP,增加IP白名单,一般在网关层处理. ...

  3. yb课堂实战之LoginInterceptor注册和放行路径 《十二》

    LoginInterceptor 拦截器注册和路径校验配置 继承WebMvcConfigurer 配置拦截路径和放行路径 InterceptorConfig.java package net.ybcl ...

  4. Spring(注解方式)简单入门

    环境准备 maven jdk Spring Eclipse 项目创建 pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0 ...

  5. [oeasy]python0078_变量部分总结_variable_summary

    删除变量 回忆上次内容 上次研究了变量的死 有生就有死 原本的死是在程序退出的时候自动执行的 也可以手动给变量执行死刑 del     del(a)之后 dir()就无法在当前作用域(scope)内观 ...

  6. [oeasy]python0135_变量名与下划线_dunder_声明与赋值

    变量定义 回忆上次内容 变量 就是 能变的量 上次研究了 变量标识符的 规则 第一个字符 应该是 字母或下划线 合法的标识符可以包括 大小写字母 数字 下划线     还研究了字符串(str)的函数 ...

  7. TIER 1: Responder

    TIER 1: Responder Active Directory Active Directory(AD)是由微软开发的目录服务,用于在网络环境中管理和组织用户.计算机.应用程序和其他资源.它是 ...

  8. 什么是spring.factories,引入未知模块报错如何解决

    对于maven中引入其他外部包加入容器的过程,需要用到spring.factories spring.factories的作用:将自动配置类与对应的配置类集中在一起,方便springboot自动装配, ...

  9. 11、Git之自建项目托管平台(GitLab极狐)

    11.1.简介 Github 是国外的项目托管平台,由于网络问题,在国内访问和使用不是很方便. Gitee 是国内的项目托管平台,虽然在访问和使用上挺方便的,但依然让人担忧项目代码可能会被泄露. 因此 ...

  10. 【Mybatis + Spring】 Mybatis - Spring 结合

    环境搭建 EvBuild 软件环境准备 - MySQL 5.0 + - IDEA 2018 + - JDK1.8 + 依赖包相关 - Junit单元测试 - JDBC驱动 - Mybatis 组件 - ...