坏的解释器: 没有那个文件或目录_使用python实现一个简单的scheme解释器

很久之前看到王垠的怎样写一个解释器,感觉还是蛮简单的。

昨天看了SICP的视频,元循环求值器,就是在讲如果用scheme实现scheme解释器。那个老师戴上了巫师帽,披上魔法袍,在黑板上完成了这些代码,非常的有趣。

但是用语言A实现语言A,总是有种怪怪的感觉,因此决定用python重新实现一个基本的scheme解释器。

parser(expt)

选择scheme来实现解释器的唯一理由是lisp代码本身就是一颗抽象语法树,免去了parser的麻烦。用python解析scheme代码只需要简单的正则替换为一个嵌套的list就可以了,这里顺便将float和int的字符串做了转化。

eval和apply

正如SICP封面上的这个类似地球仪的球显示的一样(可能是个水晶球),解释器的核心就是eval和apply的相互递归调用。对于一个计算函数,eval对函数名和参数递归的调用自身进行求值。再调用apply将 参数-参数名 绑定到函数内部的环境中,然后对函数的body依次求值。

6282a5ba4596f7e71ff1c8e8520e748f.png

对于特殊的语法规则,如定义(define)、匿名函数(lambda)、条件语句(cond)等,无法用一个简单的函数规则去描述他们,因此在eval中进行了专门的处理。

对于数字(字符串也一样,这里未实现)的求值,它本身是自求值的,因此eval直接返回了它本身。

对于函数,分为了两类:基本函数和用户自定义函数,理论上我们只需要+、-、*、/等有限的基本函数就可以了。由于函数本身是不进行任何运算的(除非显式的调用它),对于函数,eval只返回了一个标记。

由于现代的scheme都是静态作用域,在函数定义的时候需要保存函数定义时环境的拷贝(函数的子环境),也就是上下文,并且将父环境也绑定到这个子环境上。此外也保存了用户自定义函数的参数表args及函数体body。

在apply的时候,对于基本函数调用python本身的功能进行求值。对于用户自定义函数,首先将 形参-实参 这样的key-value绑定到函数的子环境中,再对body进行求值。

对于变量,首先会在当前的环境中查找对应的值,如果没有的话会递归调用eval在当前的父环境上。为了避免死循环需要判断父环境是否为空,并且初始的全局环境是没有父环境的。

全部代码:

import re


def parse(expt):
    expt = re.sub("(", " ( ", expt)
    expt = re.sub(")", " ) ", expt)
    expt = re.sub("s+", " ", expt)
    expt = expt.strip().split(" ")
    res = []
    for i in expt:
        if i == "(":
            res.append(i)
        elif i == ")":
            temp = []
            while res[-1] != "(":
                temp.append(res.pop())
            res.pop()
            res.append(list(reversed(temp)))
        else:
            if re.match("^[0-9]+$", i):
                res.append(int(i))
            elif re.match("^[0-9][0-9.]*$", i):
                res.append(float(i))
            else:
                res.append(i)
    return res


def eval(expt, envs):
    # 特殊语法及自定义函数(lambda 和cond的行为与普通的函数不一样,所以特殊处理)
    if type(expt) == list:
        if expt[0] == "define":
            envs[expt[1]] = eval(expt[2], envs)
        elif expt[0] == "lambda":
            new_envs = envs.copy()
            new_envs["PAR"] = envs
            return ("lambda", expt[1], expt[2:], new_envs)
        elif expt[0] == "cond":
            for p, t in expt[1:]:
                if p == "else" or eval(p, envs):
                    return eval(t, envs)
        else:
            expt = [eval(i, envs) for i in expt]
            return apply(expt[0], expt[1:])
    # 数字
    elif type(expt) == float or type(expt) == int:
        return expt
    # 基本过程
    elif expt == "+":
        return ("proc", "+")
    elif expt == "-":
        return ("proc", "-")
    elif expt == "*":
        return ("proc", "*")
    elif expt == "/":
        return ("proc", "/")
    elif expt == "=":
        return ("proc", "=")
    # 变量
    elif expt in envs:
        return envs[expt]
    elif len(envs["PAR"]) > 0:
        return eval(expt, envs["PAR"])


def apply(proc, args):
    if proc[0] == "proc":
        if proc[1] == "+":
            return sum(args)
        elif proc[1] == "-":
            return args[0] - args[1]
        elif proc[1] == "*":
            return args[0] * args[1]
        elif proc[1] == "/":
            return args[0] / args[1]
        elif proc[1] == "=":
            return args[0] == args[1]
    elif proc[0] == "lambda":
        args_t, body, envs = proc[1:]
        for k, v in zip(args_t, args):
            envs[k] = v
        for i in body:
            res = eval(i, envs)
        return res


def run(code):
    print("run start!!!")
    ast = parse(code)
    env_golbal = {}
    for i in ast:
        res = eval(i, env_golbal)
    print(res)


if __name__ == '__main__':
    # fib(20) = 10946
    code = """
(define fib
    (lambda (n)
        (define iter
            (lambda (a b i)
                (cond ((= i n) a)
                    (else (iter b (+ a b) (+ i 1))))))
        (iter 1 1 0)))
(fib 20)
    """
    code2 = """
(((lambda (F)
  ((lambda (f)
     (lambda (n)
       ((F (f f)) n)))
   (lambda (f)
     (lambda (n)
       ((F (f f)) n)))))
 (lambda (fib)
  (lambda (n)
    (cond ((= n 0) 1)
          ((= n 1) 1)
          (else (+ (fib (- n 1))
                   (fib (- n 2))))))))
 20)
    """
    run(code)
    run(code2)

这里的两个case分别是迭代版和递归版的斐波那契数列,其中递归版使用了Y算子。个人感觉Y算子是非常有意思的,可以在完全不使用函数定义的情况下实现递归调用(纯函数式不需要定义)。Y算子的前提是函数f本身是收敛的(有结束的,也就是函数不动点存在),然后利用F(f) = f,无限递归找到函数的不动点。

这里是python版本的Y算子

(
    # Y算子   Y(F) = F(f(f))
    lambda F: (
        (
            lambda f: (
                lambda n:
                F(f(f))(n)
            )
        )(
            lambda f: (
                lambda n:
                F(f(f))(n)
            )
        )
    )
)(
    # F(fib) = fib = lambda n
    lambda fib: (
        lambda n: 1 if n <= 1 else fib(n - 1) + fib(n - 2)
    )
)(20)

迭代版的是用尾递归实现的,(然而并没有尾递归优化)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值