自制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…模块的解析功能。

原文发布于微信公众号 - Coding迪斯尼(gh_c9f933e7765d)

原文发表时间:2018-03-30

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏xx_Cc的学习总结专栏

OC-基础总结(一)

38811
来自专栏程序员互动联盟

【答疑解惑】 || 和 & 在编程中小知识

语音版: 在学习编程语言时基本上都会遇到与(&&),或(||)逻辑运算符。 运算规则是: a&&b,a和b全为true时结果才是ture; a||b,a或者b有...

3439
来自专栏Micro_awake web

es6(五):class关键字(extends,super,static)

ES5中,生成对象通过构造函数: 1 function A(name,age){ 2 this.name=name; 3 this...

2207
来自专栏锦小年的博客

python学习笔记7.3-内建模块collections

Python的内建模块collections为我们提供了一系列的优化操作,本质上就是元组、字典、集合、列表的特殊功能版。 1. namedtuple name...

2205
来自专栏Golang语言社区

Golang语言社区--【基础知识】函数

函数是一组一起执行任务的语句。每Go程序具有至少一个函数,它一般是main(),以及所有的最琐碎程序可以定义附加函数。 你可以将代码放到独立的功能。如何划分代码...

3037
来自专栏编程

PYTHON数据类型

Python3 中有六个标准的数据类型: Number(数字) String(字符串) List(列表) Tuple(元组) Sets(集合) Dictiona...

2106
来自专栏有趣的Python

代码模板:python-基础-2(条件语句)

2 条件语句 在写代码的时候, 往往需要根据某些条件进行判断,并根据判断结果执行不同的分支代码。 2.1 if 语句 elif else c = 1 if c...

2866
来自专栏转载gongluck的CSDN博客

野指针分析

1. 野指针的概念   所谓的野指针,就是说指针指向的那块内存,你没有合法操作的权限,也就是指针指向非法的内存空间,这样的指针就叫做野指针。 2. 野指针产...

3857
来自专栏Laoqi's Linux运维专列

文件类型+变量+数值字符串

2006
来自专栏我的博客

Go的基础知识1

1.关键字 break    default      func    interface    select case     defer        ...

3649

扫码关注云+社区

领取腾讯云代金券