Scope(作用域)
在这一节中,我们会介绍一个全新但是很重要(非常非常非常重要:重要的事情说三遍)的概念。请读者搬好板凳,仔细听课。
在上一节中,我们介绍了let语句,其形式举例如下:
let x = 5 * 5
如果我们仅添加对该let语句解释(Evaluating)的支持是不够的。我们必须确保执行了上面的语句后,变量x的值为10。而且之后的语句用到这个变量x的时候,能够正确的取到这个变量x中存储的值。
那么我们怎么样保存这个变量x的值呢?这就是Scope发挥作用的时候了。简单来说Scope就是用来存储变量及其值的地方。更直接点说,Scope实际上就类似一个哈希表,哈希表的key存储的是变量的名称,哈希表的'value'存储的是变量的值。
有的文章称之为【Environment(环境)】,也有的称之为【Context(上下文)】。实际上和我们这里讲的
Scope大致是同一个概念。
下面我们来看看Scope的代码表示:
//scope.go
//Scope结构
type Scope struct {
store map[string]Object //存储变量名和变量值的哈希
parentScope *Scope //父Scope
//这个Write变量的主要目的:
// 当我们的解释程序被用在命令行的时候,我们程序的输出是往标准输出(stdout)上输出的。
// 但是当我们的解释程序是用来和浏览器交互的时候,我们程序的输出就不能往标准输出(stdout)上输出,
// 而应该往一个buffer中输出,然后将这个buffer的内容传递给wasm程序(与网页交互的程序)。
Writer io.Writer
}
//获取参数`name`中存储的对象(Object)值
func (s *Scope) Get(name string) (Object, bool) {
obj, ok := s.store[name] //在自身的map中寻找
if !ok && s.parentScope != nil { //没有找到,则在父scope中查找(如果有父scope的话)
obj, ok = s.parentScope.Get(name)
}
return obj, ok
}
//将对象值(val)存储到参数为'name'的哈希中
func (s *Scope) Set(name string, val Object) Object {
s.store[name] = val
return val
}
//从Scope中删除指定的name所对应的条目
func (s *Scope) Del(name string) {
delete(s.store, name)
}
//创建一个新的Scope
//当有函数调用的时候,第一个参数'parent'通常不为nil
func NewScope(parent *Scope, w io.Writer) *Scope {
s := make(map[string]Object) //创建一个哈希对象
ret := &Scope{store: s, parentScope: parent}
if parent == nil {
ret.Writer = w
} else {
ret.Writer = parent.Writer
}
return ret
}
Scope结构中的parentScope有必要说明一下。假设我们有下面的代码:
{
a = 10
{
println(a)
}
}
当解释器(Evaluator)解释内层的块语句(3-5行)中的println(a)函数的时候,这个变量a在内层块中并不存在,解释器就会去外层块(1-6行)中去找。如果找到了,就会使用外层块中存储的变量a的值。
有了这一节介绍的知识,下一节我们就可以实现对前一节let语句和第二节中介绍的标识符的解释(Evaluating)了。小伙伴们是不是很期待呢?我得承认我也很期待。:smile: