美文网首页Web 前端开发 让前端飞数据结构和算法分析
自制Monkey语言编译器:执行复杂算术表达式的运算

自制Monkey语言编译器:执行复杂算术表达式的运算

作者: 望月从良 | 来源:发表于2018-02-23 17:32 被阅读14次

    前几节,我们大费周章的详细解释如何对复杂的算术表达式进行语法解析,也就是让程序懂得理解复杂的算术表达式,本节我们探讨如何执行复杂表达式对应的运算并给出最终结果。我们先看这么个算术表达式:

    (5 + 10 * 2 + 15 / 3) * 2 + -10
    

    上面算术表达式涉及到多种运算符,再加上含有括号,因此程序在解读这个表达式时,还需考虑到运算的优先级。我们看看如何让程序懂得把这个运算式的结果计算出来。前面我们提到过中序表达式,它是具备这种性质的表达式,中间是一个运算符,运算符的左右两边也是表达式,回顾前几节代码,在MonkeyCompilerParser.js中,我们用以下代码来表示中序表达式:

    class InfixExpression extends Expression {
      constructor(props) {
        super(props)
        this.token = props.token
        this.left = props.leftExpression
        this.operator = props.operator
        this.right = props.rightExpression
        var s = "(" + this.left.getLiteral() + " " + this.operator 
                + this.right.getLiteral() + ")"
        this.tokenLiteral = s
        this.type = "InfixExpression"
      }
    }
    

    当语法解析读取表达式后,会执行上面代码构造一个InfixExpression对象。该对象含有三个关键部分,一个是left,对应的是运算符左边的表达式,一个是operator,也就是运算符,另一个是right,也就是运算符右边表达式。我们做代码执行时,需要先解释执行左边表达式得到表达式结果,然后执行右边表达式得到表达式运算结果,最后根据运算符将左右表达式的值进行相应运算,由此相应的执行代码实现如下,在MonkeyCompilerEvaluator.js中添加如下代码:

    class MonkeyEvaluator {
        eval (node) {
            var props = {}
            switch (node.type) {
            ...
            case "InfixExpression":
              var left = this.eval(node.left)
              var right = this.eval(node.right)
              return this.evalInfixExpression(node.operator, left, right)
            ...
            }
    

    在解析中序表达式时,代码先分别递归式的解析运算符左右两边表达式,得到左右表达式的运行结果并把结果封装在相应的符号对象里,然后执行evalInfixExpression,该函数根据中间运算符对左右表达式运算结果进行计算。我们看看该函数的实现:

    evalInfixExpression(operator, left, right) {
            if (left.type() === left.INTEGER_OBJ && 
                right.type() === right.INTEGER_OBJ) {
                return this.evalIntegerInfixExpression(
                    operator, left, right)
            }
    
            return null
        }
    
        evalIntegerInfixExpression(operator, left, right) {
            var leftVal = left.value
            var rightVal = right.value
            var props = {}
            var resultType = "integer"
            
            switch (operator) {
                case "+":
                  props.value = leftVal + rightVal
                  break;
                case "-":
                  props.value = leftVal - rightVal
                  break;
                case "*":
                  props.value = leftVal * rightVal
                  break;
                case "/":
                  props.value = leftVal / rightVal
                  break;
                default:
                  return null
            }
            console.log("eval infix expression result is:",
                props.value)
                
            var result = null
            if (resultType === "integer") {
                result = new Integer(props)
            }
            
            return result
        }
    

    它先确保左右表达式执行后得到的是整形,然后调用evalIntegerInfixExpression根据表达式中间运算符进行相应的计算。完成上面代码后,编译器就能计算出开头所给表达式的最终结果,代码运行结果如下:

    这里写图片描述

    经过多重步骤的运算后,编译器对表达式的计算所得结果为50.

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

    我们继续完善代码,使得编译器对算术表达式的运算能支持比较运算符,也就是我们要让编译器能懂得如下表达式的运算:

    1 < 2; 1 > 2; 1 == 1; 1 != 2;
    

    代码修改如下:

    evalIntegerInfixExpression(operator, left, right) {
        ....
        switch (operator) {
        ....
        case "==":
                  resultType = "boolean"
                  props.value = (leftVal === rightVal)
                  break;
                 case "!=":
                   resultType = "boolean"
                   props.value = (leftVal !== rightVal)
                   break
                 case ">":
                   resultType = "boolean"
                   props.value = (leftVal > rightVal)
                   break
                 case "<":
                   resultType = "boolean"
                   props.value = (leftVal < rightVal)
                   break
                 case ">=":
                   resultType = "boolean"
                   props.value = (leftVal >= rightVal)
                   break
                 case "<=":
                   resultType = "boolean"
                   props.value = (leftVal <= rightVal)
                   break
                 default:
                   return null
        }
        console.log("eval infix expression result is:",
                props.value)
        var result = null
        if (resultType === "integer") {
            result = new Integer(props)
        } else if (resultType === "boolean") {
            result = new Boolean(props)
        }
        
        return result
    }
    

    上面代码完成后,编译器可以解释执行比较性质的算术表达式,在编辑框中输入如下内容:

    (1+2) > (0 + 3)
    

    得到的执行结果如下:

    这里写图片描述

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

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


    这里写图片描述

    相关文章

      网友评论

        本文标题:自制Monkey语言编译器:执行复杂算术表达式的运算

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