前几节,我们大费周章的详细解释如何对复杂的算术表达式进行语法解析,也就是让程序懂得理解复杂的算术表达式,本节我们探讨如何执行复杂表达式对应的运算并给出最终结果。我们先看这么个算术表达式:
(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)
得到的执行结果如下:
这里写图片描述更多技术信息,包括操作系统,编译器,面试算法,机器学习,人工智能,请关照我的公众号:
这里写图片描述
网友评论