CS61A-Scheme解释器学习

本文是在阅读伯克利大学CS61A项目4源码后的小结,作者将记录以下重点:

  • 文件功能介绍
  • repl流程分析,即项目基本架构流程
  • eval: scheme_eval和scheme_apply

文件介绍

  1. scheme.py
    主程序入口文件,repl流程实现,Frame(环境)定义,各种Procedure和special form。

  2. scheme_reader.py
    处理输入表达式有两步:

    1. 词法分析(lexical),拆解为基本符号(token)。
    2. 语法分析(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))
    
    不难看出,读入是Reader封装,然后是符号处理并实现缓存。
  3. buffer.py
    主要实现Buffer类和Reader类。
    Buffer中current方法自动判断读取下一行。
    Reader重写__iter__方法,每一个新建的Reader实例只会读入一个完整表达式

  4. scheme_tokens.py
    - 读取完整合法的token: next_candidate_token();
    - 读取一行的token: tokenize_line()

  5. 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)
*
posted @ 2021-10-16 20:47  忘语川  阅读(429)  评论(0编辑  收藏  举报