专栏首页Coding迪斯尼自制Monkey语言编译器:解释执行哈希表对象

自制Monkey语言编译器:解释执行哈希表对象

我们在上节完成了对哈希表对象的解析,这一节我们给编译器添加执行哈希表对象的功能,完成本节代码后,编译器能执行以下代码:

let  hash = {'hello':'world'}
let y = hash['hello']

当编译器执行上面代码后,变量y的值就是字符串’world’,接下来我们看相关代码的实现。我们需要在解释器中创建哈希表的符号对象,因此先添加如下代码:

class BaseObject {
    constructor (props) {
         ....
         // change 1
        this.HASH_OBJ = "Hash"
    }
    ....
}

// change 2
class Hash extends BaseObject {
    constructor(props) {
        super(props)
        this.keys = props.keys
        this.values = props.values
    }

    type () {
        return this.HASH_OBJ
    }

    inspect () {
        var s = "{"
        for (var i = 0; i < this.keys.length; i++) {
            var pair = "" + this.keys[i].inspect()
            pair += ":"
            pair += this.values[i].inspect()
            pair += ","
            s += pair
        }

        s += "}"
        return s
    }
}

紧接着我们添加解析执行哈希表的代表:

eval (node) {
    var props = {}
    switch (node.type) {
        ....
        case "HashLiteral":
        return this.evalHashLiteral(node)
        ....
        }
      ....
}

// change 4
    evalHashLiteral(node) {
        /*
        先递归的解析哈希表的key,然后解析它的value,对于如下类型的哈希表代码
        let add = fn (x, y) { return x+y};
        let byOne = fn (z) { return z+1;}
        {add(1,2) : byOne(3)}
        编译器先执行add(1,2)得到3,然后执行byOne(3)得到4
        */
        var props = {}
        props.keys = []
        props.values = []

        for (var i = 0; i < node.keys.length; i++) {
            var key = this.eval(node.keys[i])
            if (this.isError(key)) {
                return key
            }

            if (this.hashable(key) != true) {
                return new this.Error("unhashable type:" +
                    key.type())
            }

            var value = this.eval(node.values[i])
            if (this.isError(value)) {
                return value 
            }

            props.keys.push(key)
            props.values.push(value)
        }

        var hashObj = new Hash(props)
        console.log("eval hash object: " + hashObj.inspect())
        return hashObj
    }

    hashable(node) {
        if (node.type() == node.INTEGER_OBJ || 
            node.type() == node.STRING_OBJ || 
            node.type() == node.BOOLEAN_OBJ) {
            return true
        }

        return false
    }

当parser解析哈希表后会生成一个类型为HashLiteral的语法节点,该节点会传入到解析器的eval函数,我们在里面探测到节点类型为HashLiteral时,调用evalHashLiteral函数来进行解析,后者会从数组keys中取出每个元素,调用eval去解析哈希表对应的key,这个key可以是字符串,数字,变量,以及函数调用,但解析后得到的结果必须是整形,字符串和布尔型,这点检测会在hashable函数中进行,然后再从数组values中取出每个元素进行解析,得到对应的符号对象,把解析结果分别存入数组keys和values,最后用来构建一个Hash符号对象,上面的代码完成后,在编辑框中输入如下代码:

点击底下的Parsing按钮,于是我们前面添加的代码就会执行,打开控制台就可以看到如下解析结果:

eval hash object: {integer with value:3:integer with value:4,}

我们只实现了哈希表定义的解析,接下来我们需要实现哈希表的取值操作,也就是编译器能执行如下代码:

let bob = {"name" : "Bob", "age" : 90}
let name = bob["name"]

上面代码执行后,变量name的值是”Bob”。我们看看实现该功能的代码如何编写:

evalIndexExpression(left, index) {
        if (left.type() === left.ARRAY_OBJ && 
            index.type() === index.INTEGER_OBJ) {
            return this.evalArrayIndexExpression(left, index)
        }
        // change 4
        if (left.type() == left.HASH_OBJ) {
            return this.evalHashIndexExpression(left, index)
        }
    }

    //change 5
    evalHashIndexExpression(hash, index) {
        if (!this.hashable(index)) {
            return new this.Error("unhashable type: " + index.type())
        }

        for (var i = 0; i < hash.keys.length; i++) {
            if (hash.keys[i].value == index.value) {
                console.log("return hash value :" + 
                    hash.values[i])
                return hash.values[i]
            }
        }

        return null
    }

完成上面代码后,在编辑框中输入如下内容:

然后点击底下”Parsing”按钮,在控制台中可以看到如下输出:

the index value is :name with content : content of string is: Bob

至此我们整个编译器的开发就结束了。我们的编译器所至此的Monkey语言其实与Javascript没有太大区别,它支持多种数据类型,例如整形,布尔值,数字,字符串,它还支持复杂数据结构,例如数组和哈希表,它具有高级语言特点,例如支持函数传参,函数闭包调用等,唯一遗憾的是它暂不支持面向对象编程的类定义,但只要你吃透了课程所介绍的编译原理,添加相应功能并没有太大难度,事实上当前Monkey语言编译器已经是一个完整可用的编程语言了,据说当今世界能做编译器的人最多坐满一个20平方的会议室,如果你走到这的话,会议室里为你留了一把椅子!

本文分享自微信公众号 - Coding迪斯尼(gh_c9f933e7765d)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-08-10

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

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 自制Monkey编程语言编译器:增加数组操作API和Mapsh数据类型

    望月从良
  • 自制monkey语言编译器:符号系统与代码执行

    望月从良
  • 自制Monkey语言编译器:实现函数闭包功能和为语言增加复杂数据结构

    望月从良
  • Python模拟登陆 —— 征服验证码 2 B站

    B站的登录密码用了rsa加密(两个大质数的乘积很难进行逆向分解,所以可以用这个乘积来做公钥)。 所以运行py文件之前,使用镜像,先用pip安装rsa库: pi...

    SeanCheney
  • mongo数据库命令简单学习

    db.getCollection('product').update({status:"offline"},{$set:{status:"online"}},f...

    蓓蕾心晴
  • 如何在Tomcat中做TLS客户端认证

    常见的https网站做的是服务端认证(server authentication),浏览器通过证书判断你所访问的https://baidu.com是否真的是百度...

    颇忒脱
  • iOS 全景播放器最简单的解决方案

    酷走天涯
  • 如何配置Spring Boot Tomcat

    Spring Boot Web应用程序默认包含预配置的嵌入式Web服务器。但在某些情况下,我们要修改默认配置以满足自定义要求。

    乱敲代码
  • 表格及布局——0606上午

    今天上午学习了表格的应用以及如何用表格进行页面布局。以下面代码为例: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 ...

    二十三年蝉
  • ARTS第四周

    例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。27 写做 XXVII, 即为 XX + V + II 。

    zx钟

扫码关注云+社区

领取腾讯云代金券