python 写一个scheme解释器(一),,解释器的本质?我们换


解释器的本质

?

我们换一种语言来写解释器的时候,其实本质和scheme写scheme是一样的,即将输入的一串字符串作为源程序执行而语法和语义均由自己预先设计好并严格执行。

?

这里我们采用python 来实现我们的第二版的scheme解释器,首先python支持的列表推导式、lambda、模式匹配等语法糖十分适合去编写解释器,另一方面,python内置数据结构齐全,使用简单。

当然我们采用C++ 也是完全可行的,但由于C++对字符串的处理功能比较弱,并且不同类型数据的转换都需要手工去完成,实现的时候噪音太多,即关注在一些并非本质问题上的时间比较多。但其实完全从头用C++ 写一个的话更能提高对这一问题的认识。

在难实现和收获大这样的问题上的选择自然是见仁见智的了。

java 或者c#当然更是可行的了,但如果采用C语言来做的话,感觉稍显麻烦,得不偿失。

?

解释器实现的流程:

如果看过任何一本编译原理的书籍,绪论中就会介绍这个过程

词法分析

将字符串流转换为一个个有意义的token单元:

比如(cons 1 2)应该分解为 ‘(‘ ‘cons‘ ‘1‘ ‘2‘ ‘)‘ ,这里将左右括号也作为token列出,因为它是scheme中一个简单表达式的定界符。

?

语法分析

将tokens转换为语法分析树,一般编译原理的教材这里都会以表达式求值为例,比如 1+2 *3-2

.-

.2 . +

.1 *

.2 .3

?

对于前序的表达式:(/ (+ 1 (* 3 2 ) (- 2 3) )

./

.+ .-

.1 . * .2 .3

.3 .2

之前的博文《递归下降解决表达式求值》一文中详细的讨论过类似的问题,这里我们利用表达式自身的递归性质,即我们只要把最小的表达式单元都找到并且括起来然后求值就可以了,嵌套的复杂表达式的每一部分都会递归的调用解释器去求值。

举个简单的例子, (cons 1 (cons 2 3) )

词法分析之后: ‘(‘ ‘cons‘ ‘1‘ ‘(‘ ‘cons‘ ‘2‘ ‘3‘ ‘)‘ ‘)‘

语法分析之后: [‘cons‘ 1 [‘cons‘ 2 3] ]

观察发现主要就做了两件事情:

数字等自求值的单元转换为其值;将子表达式组成一个列表;求值

在将输入的字符串转换为语法分析后的结果后,我们应该做的就是根据语义去求值了,这里用python实现,所以直接将其翻译为python中的某些数据结构和函数上,到这一步之后解释器就结束了,不涉及中间代码或者汇编的翻译。

?

上两篇博文谈到了,求值器需要的参数是:程序 + 求值环境,而环境本质上就是一个变量和值的键对,所以用python中的dict 实现就可以了。

上面实现完成之后其实解释器已经完成了,剩下的是一些交互界面的问题。

?

语法的定义和筛选

?

上面的流程大致就是那样了,和用scheme来实现如出一辙,同样我们需要考虑的是我们的解释器提供哪些功能和能力,这里最好的方式是在提供足够的抽象屏障的基础上,由简单到复杂逐步去扩充,而不是先定义完备的语法,然后按照上面的流程去一一实现。

?

比如我们先实现一个算术求值器,它包含了上面流程中的所有步骤,然后再添加比如cons、car、cdr、list等一些内置的数据结构和常用的函数,之后再添加变量的支持、lambda函数的支持、过程调用的支持。

?

需要注意的是函数和环境的实现,在之前用scheme实现的时候已经提到了,比如函数的闭包性质需要保持、环境的接口性质必须遵守等等。

?

这一部分才是真正关键而有意义的部分,更具体的内容和实现见下次博文。

?

?

?

?

python 写一个scheme解释器(一)

评论关闭