前言
上篇文章我为大家介绍了语法解析
的一些基本概念,以及如何通过自定义的DSL语言实现Simple语言解释器的语法树解析。在本篇也是这个系列最后一篇文章中我将为大家介绍Simple解释器是如何执行生成的语法树的。
evaluate函数和作用域
前面在介绍语法解析相关知识的时候有出现过evaluate
函数,其实基本每一个AST节点都会有一个对应的evaluate函数
,这个函数的作用就是告诉Simple解释器如何执行当前AST节点。因此Simple解释器执行代码的过程就是:从根节点开始执行当前节点的evaluate函数然后递归地执行子节点evalute函数的过程
。
我们知道JavaScript代码执行的时候有一个概念叫做作用域
,当我们访问一个变量的时候,会先看看当前作用域有没有定义这个变量,如果没有就会沿着作用域链向上一直寻找到全局作用域,如果作用域链上都没有该变量的定义的话就会抛出一个Uncaught ReferenceError: xx is not defined
的错误。在实现Simple语言解释器的时候,我参照了JavaScript作用域的概念实现了一个叫做Environment
的类,我们来看看Evironment类的实现:
// lib/runtime/Environment.ts
// Environment类就是Simple语言的作用域
class Environment {
// parent指向当前作用域的父级作用域
private parent: Environment = null
// values对象会以key-value的形式存储当前作用域变量的引用和值
// 例如values = {a: 10},代表当前作用域有一个变量a,它的值是10
protected values: Object = {
}
// 当前作用域有新的变量定义的时候会调用create函数进行值的设置
// 例如执行 let a = 10 时,会调用env.create('a', 10)
create(key: string, value: any) {
if(this.values.hasOwnProperty(key)) {
throw new Error(`${
key} has been initialized`)
}
this.values[key] = value
}
// 如果某个变量被重新赋值,Simple会沿着当前作用域链进行寻找,找到最近的符合条件的作用域,然后在该作用域上进行重新赋值
update(key: string, value: any) {
const matchedEnvironment = this.getEnvironmentWithKey(key)
if (!matchedEnvironment) {
throw new Error(`Uncau