美文网首页让前端飞
自制Monkey语言编译器:解释执行哈希表对象

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

作者: 望月从良 | 来源:发表于2018-08-10 17:15 被阅读6次

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

    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符号对象,上面的代码完成后,在编辑框中输入如下代码:

    屏幕快照 2018-08-10 下午4.19.22.png

    点击底下的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
        }
    

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

    屏幕快照 2018-08-10 下午4.59.09.png

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

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

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

    更详细的讲解和代码调试演示过程,请点击链接

    更多技术信息,包括操作系统,编译器,面试算法,机器学习,人工智能,请关照我的公众号:


    这里写图片描述

    相关文章

      网友评论

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

        本文链接:https://www.haomeiwen.com/subject/vqdavftx.html