Skip to content

Latest commit

 

History

History
43 lines (32 loc) · 2.65 KB

File metadata and controls

43 lines (32 loc) · 2.65 KB

3.3一个Tree-Walking解释器

我们将要构建的是一个Tree-Walking解释器,我们将使用解析器为我们构建的 AST 并“即时”解释它,而无需任何预处理或编译步骤。

我们的解释器将很像经典的 Lisp 解释器。 我们将使用的设计很大程度上受到“计算机程序的结构和解释(SICP)”中介绍的解释器的启发,尤其是它对环境的使用。这并不意味着我们正在复制一个特定的解释器,并不是这样,我们宁愿使用一个你可以在很多其他地方看到的蓝图。如果你眯得足够自诩。这种特殊设计的流行的确有很好的理由:它是最简单的入门方式,易于理解和稍后的扩展。

我们实际上仅仅需要做两件事:一个tree-walking评估器和一种在我们的宿主语言Go中表示Monkey值的方法。评估器看起来很强大,但它将仅仅是一个名为“eval”的函数。它的工作是评估AST。这里是一个伪代码版本,它说明了“即使评估”和“tree-walking”在解释上下文中的含义。

function eval(astNode) {
    if (astNode is integerliteral) {
        return astNode.integerValue

    } else if (astNode is booleanLiteral) {
        return astNode.booleanValue

    } else if (astNode is infixExpression) {
        leftEvaluated = eval(astNode.Left)
        rightEvaluated = eval(astNode.Right)

        if astNode.Operator == "+" {
            return leftEvaluated + rightEvaluated
        } else if ast.Operator == "-" {
            return leftEvaluated - rightEvaluated
    }
    }
}

正如你看到的,eval是递归的。当astNodeinfixExpression是true,eval调用它自己再次计算中缀表达式的左操作数和右操作数两次。这反过来可能会导致对另一个中缀表达式或整数文字或布尔值或者操作符的评估......我们已经在构建和测试AST时看到了递归。相同的概念在这里适用,只是在我们正在评估树而不是构建它。

查看这段伪代码,您可能会想象扩展此功能是多么容易。 这符合我们的优势。 我们将一点一点地构建我们自己的 Eval 函数,并在我们继续扩展我们的解释器时添加新的分支和功能。

但这段代码中最有趣的几行是 return 语句。 他们返回什么? 这里有两行将调用 eval 的返回值绑定到名称:

leftEvaluated = eval(astNode.Left)
rightEvaluated = eval(astNode.Right)

eval 在这里返回什么? 返回值属于哪种类型? 这些问题的答案与“我们的解释器将拥有什么样的内部对象系统?”的答案相同。

< 3.2评估策略 > 3.4表示对象