CS61A-Scheme解释器学习
本文是在阅读伯克利大学CS61A项目4源码后的小结,作者将记录以下重点:
- 文件功能介绍
- repl流程分析,即项目基本架构流程
- eval: scheme_eval和scheme_apply
文件介绍
-
scheme.py
主程序入口文件,repl流程实现,Frame(环境)定义,各种Procedure和special form。 -
scheme_reader.py
处理输入表达式有两步:- 词法分析(lexical),拆解为基本符号(token)。
- 语法分析(semantic),生成的抽象语法树是列表形式。如:
Pair('+', Pair(2, Pair(2, nil)))
语法分析的实现函数是scheme_read,词法分析的过程我们进行了逐层抽象,后面对repl的解析可以看出,我们读入表达式是通过调用next_line函数实现。这个函数形参只会被传入两种函数:buffer_input和buffer_lines。前者是命令行输入,后者是文件处理(实际这里还有-i参数的处理)。下面是函数实现:
点击查看代码
def buffer_input(prompt='scm> '): """Return a Buffer instance containing interactive input.""" return Buffer(tokenize_lines(InputReader(prompt))) def buffer_lines(lines, prompt='scm> ', show_prompt=False): """Return a Buffer instance iterating through LINES.""" if show_prompt: input_lines = lines else: input_lines = LineReader(lines, prompt) return Buffer(tokenize_lines(input_lines))
-
buffer.py
主要实现Buffer类和Reader类。
Buffer中current方法自动判断读取下一行。
Reader重写__iter__方法,每一个新建的Reader实例只会读入一个完整表达式。 -
scheme_tokens.py
- 读取完整合法的token: next_candidate_token();
- 读取一行的token: tokenize_line() -
scheme_primitives.py
使用函数修饰符,最终添加基本函数到全局Frame。
REPL
REPL的全称是read_eval_print_loop,也就是读入表达式,解析计算,打印结果然后重复上述过程直至遭遇异常退出。
EVAL
这个过程通过scheme_eval和scheme_apply相互调用完成。
点击查看代码
def scheme_eval(expr, env, _=None): # Optional third argument is ignored
"""Evaluate Scheme expression EXPR in environment ENV.
>>> expr = read_line('(+ 2 2)')
>>> expr
Pair('+', Pair(2, Pair(2, nil)))
>>> scheme_eval(expr, create_global_frame())
4
"""
# Evaluate atoms
if scheme_symbolp(expr):
return env.lookup(expr)
elif self_evaluating(expr):
return expr
# All non-atomic expressions are lists (combinations)
if not scheme_listp(expr):
raise SchemeError('malformed list: {0}'.format(repl_str(expr)))
first, rest = expr.first, expr.second
if scheme_symbolp(first) and first in SPECIAL_FORMS:
return SPECIAL_FORMS[first](rest, env)
else:
# BEGIN PROBLEM 5
operator = scheme_eval(first, env)
check_procedure(operator)
operands = rest.map(lambda item: scheme_eval(item, env))
return scheme_apply(operator, operands, env)
# END PROBLEM 5
def scheme_apply(procedure, args, env):
"""Apply Scheme PROCEDURE to argument values ARGS (a Scheme list) in
environment ENV."""
check_procedure(procedure)
if isinstance(procedure, PrimitiveProcedure):
return procedure.apply(args, env)
else:
new_env = procedure.make_call_frame(args, env)
return eval_all(procedure.body, new_env)