lisp解释器实现 (2)
介绍一下Lisp中的变量与环境的概念。
变量与环境
在lisp语言中,定义一个环境变量所使用的语句如下:
1 | (let [<symbol> <value>] <exper>) |
在方括号里面可以定义多个符号和值。
然而为了简单起见,我们可以将这个函数写成只能定义一个变量的形式:
1 | (let <symbol> <value> <exper>) |
然后我们可以通过多个let语句来定义多个变量:
1 | (let <symbol1> <value1> (let <symbol2> <value2> <...>)) |
这个语句的实现我们可以通过引入环境这个概念来实现。
所谓环境,就是一个存储符号和对应值的键值对的存储结构。
而且我们可以在Clojure里面试一下这个语句:
1 | (let [x 1] |
它的输出应该为3,也就是说,环境这个存储结构应该是一个先进后出的栈,它会在栈中寻找一个最先匹配到的变量,得到他的值然后输出。
Clojure语法
Clojure 中定义数据结构的语法是这样的:
1 | (defstruct <struct name> :arg1 :arg2) |
我们需要定义一个包含符号-值
的键值对的数据结构:
1 | (defstruct Pair :sym :value) |
环境是一个存储键值对的空间。我们可以用lisp的列表来存储它。在Clojure里,这个叫做Sequences
。
定义一个空的Sequences像是下面这样:
1 | (def emptyEnv '()) |
我们对环境所需要进行的操作只有两个:
存储 和 查找并取得值
在存储函数中我们构造一个Pair结构体,然后把他们放到环境中返回环境。这两个分别对应的就是Clojure里面的(struct Pair *)
和(cons *)
1 | (defn binding |
取值函数则是顺序查找环境,然后返回第一个匹配的数据结构的:value
属性,这个对应的是Clojure里面的some函数。我们可以这样写:
1 | (defn getValue |
它会返回第一个为逻辑真值的值。
我们定义的symEq函数,应该是接受一个参数,返回一个函数。返回的函数接受一个Pair的参数,并判断它的:sym
与symEq参数是否相等,相等则返回这个参数本身。
其定义应该如下:
1 | (defn symEq |
其中fn是定义一个匿名函数,接受item参数,然后比较它的值并返回
这样我们就处理好了解释器和变量之间的关系了。