昨天实现了一个解释器,感觉这个东西需要记录一下,不然大概会忘得一干二净(也是咸鱼了好久没有写博客了)

关于Clojure

函数式语言,我用过的也不实在不多;在暑假期间学习的Clojure就是这样的一种lisp语言。它是运行在JVM上的Lisp编程,可以和Java互相调用。

安装Clojure大多通过 leiningen 来使用,参见 [此处] (https://leiningen.org/)

它跟一般的Lisp语法上有一些不同。还有一些lisp的函数,我也不是非常熟悉。

通过defn定义函数:

1
(defn <函数名> [<参数>] <函数体>)

let 语句定义一些变量:

1
2
(let [<参数> <参数的值>] <其他语句>)
; 如 (let [x 1] x)

cond 语句相当于switch case函数。他会依次判断这个函数的奇数次参数,如果为真,就执行他后面的参数。最后的参数是所有都不匹配的情况返回的结果。

1
2
3
4
(cond
(= 1 2) 5
(= 1 1) 10
:else (0))

可以通过symbol?number? 判断是否是符号还是函数。

计算器

因为lisp语法中自带AST,所以不需要分析符号的优先级问题。计算器在lisp下的实现非常简单,此处就单独列一个部分即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
(defn pair? [x] (and (list? x) (= (count x) 3)))
(defn arg1 [s] (second s))
(defn arg2 [s] (second (rest s)))

(defn symbol_map [symbol]
(cond
(= symbol '+) +
(= symbol '-) -
(= symbol '*) *
(= symbol '/) /

:else (prn "error!")))

(defn calculas
[exp]
(cond
(number? exp) exp
(and (pair? exp) (symbol? (first exp)))
((symbol_map (first exp))
(calculas (arg1 exp))
(calculas (arg2 exp)))

:else (prn "error!")))

解释器函数calculas对exp参数分为了两种情况:

  • 一种是数字的exp,不进行处理,直接返回
  • 一种是解析式,用exp解析,作为符号的+-*/映射成为+-*/函数,分别用calculas递归处理其参数(相当于计算其左右两侧参数的值),然后传给映射得的函数。

这样就是一个简单的计算器了。这将是接下来做的解析器的基础。

测试

可以通过下面这些语句来测试计算器的效果:

1
2
(calculas '(+ 1 3))
(calcluas '(* 2 5))

在Clojure中,在语句前加'表示暂缓执行,即将它们作为一个列表而不是一个语句传入。

完整代码在这里