返回

日拱一卒,用Python写一个Lisp解释器(三)

闲谈

大家好,日拱一卒,我是梁唐。本文始发于公众号Coder梁。

我们继续来肝伯克利CS61A的scheme project,这是这个project的第三篇,如果漏掉了之前的建议先去补一下。

课程链接:https://www.youtube.com/watch?v=PngQkdxD2xc&list=PL-XXv-cvO-8kT69O7lfcH7G_Scy-qVDL1

项目原文链接:https://inst.eecs.berkeley.edu/~cs61a/fa19/projects/scheme/scheme.html

项目仓库:https://github.com/josephzeng/scheme

前两节的解析:

Lisp解释器 一: 基础类型和变量绑定
Lisp解释器 二: 宏定义

这节我们来实现几个函数的解释,因为大部分列表相关的函数都靠对 cons cell 的操作实现,而 cons cell 也是 scheme 内置的数据结构,所以这一节有几个函数实现起来和 C 语言写 Scheme 解释器基本一样。

cons

对应的python代码

def cons(env, a, b):
  pair = memory.make_object('pair', env)
  memory.set(pair, a)
  memory.set(pair + 1, b)
  return pair
car

对应的python代码

def car(env, a):
  if not isinstance(a, int) or a < memory.min_addr or a >= memory.max_addr or a % 2 != 0:
    raise SchemeError("Car requires a pair")
  return memory.get(a)
cdr

对应的python代码

def cdr(env, a):
  if not isinstance(a, int) or a < memory.min_addr or a >= memory.max_addr or a % 2 != 0:
    raise SchemeError("Car requires a pair")
  return memory.get(a + 1)
display

对应的python代码

def display(env, a):
  print(a)
eval_string

对应的python代码

def eval_string(env, s):
  s = s.strip()
  if len(s) == 0:
    raise SchemeError("Eval_string requires an input string")

  a = tokenize_string(env, s)
  b = eval_list(env, a)

  if b != None:
    display(env, b)
begin

对应的python代码

def begin(env, *args):
  ret = None

  for arg in args:
    ret = eval_sexpr(env, arg)

  return ret
lambda

对应的python代码

def lambda(env, *args):
  params = args[0]
  body = list(args[1:])

  exp = memory.make_object('lambda', env)

  memory.set(exp, params)
  memory.set(exp + 1, body)
  memory.set(exp + 2, env)

  return exp
if

对应的python代码

def if_stmt(env, *args):
  cond = args[0]
  true_clause = args[1]
  false_clause = None

  if len(args) > 2:
    false_clause = args[2]

  if eval_sexpr(env, cond) == TRUE:
    return eval_sexpr(env, true_clause)
  else:
    return eval_sexpr(env, false_clause)
define

对应的python代码

def define(env, *args):
  if len(args) < 2:
    raise SchemeError("Define requires at least 2 arguments")

  var = args[0]
  exp = eval_sexpr(env, args[1])

  if not isinstance(var, str):
    raise SchemeError("Define requires a variable name")

  env.define(var, exp)
quote

对应的python代码

def quote(env, *args):
  return args[0]
set!

对应的python代码

def set(env, *args):
  if len(args) != 2:
    raise SchemeError("Set! requires 2 arguments")

  var = args[0]
  exp = eval_sexpr(env, args[1])

  if not isinstance(var, str):
    raise SchemeError("Set! requires a variable name")

  env.set(var, exp)
or

对应的python代码

def or_expr(env, *args):
  ret = FALSE

  for arg in args:
    ret = eval_sexpr(env, arg)

    if ret == TRUE:
      return ret

  return ret
and

对应的python代码

def and_expr(env, *args):
  ret = TRUE

  for arg in args:
    ret = eval_sexpr(env, arg)

    if ret == FALSE:
      return ret

  return ret

这节的大部分代码和 C 语言写的 Scheme 解释器是一致的,只是在 Python 中,内建函数的定义风格不太一样。

完整项目在这里:https://github.com/josephzeng/scheme