自制Monkey语言编译器:解释执行if..else判断语句
任何编程语言都少不了条件判断语句,Monkey语言也一样,有自己的If…else条件判断指令,本节我们看看如何解释执行该条件判断语句。根据我们原有的解释执行机制,我们只要在原框架的基础上添加若干代码就可以实现本节功能。当本节代码完成后,执行结果如下:
如图中的if else 语句被编译器解释执行后,在控制台的输出如下:
根据输出可知,编译器在执行了if里面的条件判断后,执行了if模块里面的语句,也就是执行了”10+10;”,然后输出计算结果是20。我们看看该功能是如何实现的。在MonkeyEvaluator.js中添加如下代码:
class MonkeyEvaluator {
eval (node) {
var props = {}
switch (node.type) {
....
// change 2
case "IfExpression":
return this.evalIfExpression(node)
...
}
...
}
eval函数会根据传入的语法树节点类型进行相应分析,当他检测到节点类型是IfExpression时,表明当前节点对应着if…else语句模块,因此调用evalIfExpression进行解析执行。我们看看后者的实现:
// change 3
evalIfExpression(ifNode) {
console.log("begin to eval if statment")
var condition = this.eval(ifNode.condition)
if (this.isTruthy(condition)) {
console.log("condition in if holds, exec statements in if block")
return this.eval(ifNode.consequence)
} else if (ifNode.alternative != null) {
console.log("condition in if no holds, exec statements in else block")
return this.eval(ifNode.alternative)
} else {
console.log("condition in if no holds, exec nothing!")
return null
}
}
在解析上面代码前,我们先看看if语句模块对应的语法树节点结构:
class IfExpression extends Expression {
constructor(props) {
super(props)
this.token = props.token
this.condition = props.condition
this.consequence = props.consequence
this.alternative = props.alternative
var s = "if expression width condtion: " +
this.condition.getLiteral()
s += "\n statements in if block are: "
s += this.consequence.getLiteral()
if (this.alternative) {
s += "\n statements in else block are: "
s += this.alternative.getLiteral()
}
this.tokenLiteral = s
//change here
this.type = "IfExpression"
}
}
ifExpression节点由三个部分组成,一个是condition,它对应的是if后面的条件判断语句,一个是consequence,它对应的是如果if条件判断为真时,要执行的语句组合,也就是if对应的大括号里面的语句,另一个是alternatvie,其对应的是else大括号里面的语句。回到函数evalIfExpression,一开始它先解析if括号里面的判断表达式,看看它返回的值是否为真,ifNode.condition对应的就是if后面括号里的表达式,调用eval解析它后会返回一个符号对象,接着代码调用isTruthy来判断返回的符号对象是否为真,我们看看该函数的实现:
isTruthy(condition) {
if (condition.type() == condition.INTEGER_OBJ) {
if (condition.value != 0) {
return true
}
return false
}
if (condition.type() == condition.BOOLEAN_OBJ) {
return condition.value
}
if (condition.type() == condition.NULL_OBJ) {
return false
}
return true
}
如果对if括号中的语句解释执行后返回来的符号对象类型是整形,那么则判断整形对象的值是否为0,如果是非零值,那就返回true。如果是布尔型,那么直接将符号对应的布尔值返回,如果返回的符号对象是NULL,则返回false。 继续回到evalIfExpression函数,它根据对if后面语句的解释执行返回来的值判断接下来是解释执行if语句块里面的语句还是else语句块里面的语句。无论是执行那部分语句,都会继续调用eval函数来进行。
回到eval函数中,无论是执行if语句块里面的语句还是else部分的语句,它们在语法解析里面都对应于节点类型”blockStatement”,因此我们要添加相应函数对这种节点进行解析。对应代码如下:
eval (node) {
var props = {}
switch (node.type) {
...
case "blockStatement":
return this.evalStatements(node)
default:
return new Null({})
}
return nuill
}
在evalIfExpression中对if或是else里面的语句模块进行调用eval函数进行解析时会进入到代码上面的blockStatement分支里,于是代码继续调用evalStatements来进行解析,我们再回顾一下语法解析模块构造的blockStatement对象:
class BlockStatement extends Statement {
constructor(props) {
super(props)
this.token = props.token
this.statements = props.statements
var s = ""
for (var i = 0; i < this.statements.length; i++) {
s += this.statements[i].getLiteral()
s += "\n"
}
this.tokenLiteral = s
// change here
this.type = "blockStatement"
}
}
该结构里面最重要的成分是statements,它是一个数组,存储了多个语法树节点,对该节点的解释执行转换为对statments数组里每个语法树节点的解释执行。我们看看evalStatements函数的实现:
evalStatements(node) {
var result = null
for (var i = 0; i < node.statements.length; i++) {
result = this.eval(node.statements[i])
if (result.type() == result.RETURN_VALUE_OBJECT
|| result.type() == result.ERROR_OBJ) {
return result
}
}
return result
}
在上面代码的实现中,我们把blockStatement节点里面statements数组存储的每个语法树节点再次调用eval函数来进行解释执行,然后判断每条语句解释执行后返回的符号对象,如果符号对象对应着return语句或是表示出错的Error符号对象类型,那么停止继续执行下面的语法树对象,直接返回。
上面代码完成后,编译器就具备了文章开头所展示的能够解释执行if…else…模块的解析功能。更详细的讲解和代码调试演示过程,请点击链接
更多技术信息,包括操作系统,编译器,面试算法,机器学习,人工智能,请关照我的公众号:
上一篇: python判断语句和循环语句