[2021 spring] CS61A Lab 11: Interpreters

Lab 11: https://inst.eecs.berkeley.edu/~cs61a/sp21/lab/lab11/
任务: 构建PyCombinator解释器,完成expr.py

Q1:序言

先试着理解已经写好的部分代码:

  • repl.py包含REPL循环的逻辑,重复读取(read)表达式作为用户输入,计算(evaluate)并打印(print out)它们的值。

  • reader.py包含解释器的读取器。函数read调用函数tokenizeread_expr,将表达式字符串转换为一个Expr对象。

  • expr.py包含解释器对表达式和值的表示。ExprValue的子类封装了PyCombinator语言中的所有表达式和值的类型。global_env字典包含了原始函数的信息。

REPL部分:

prologue_reader:

prologue_expr:

Q2: Evaluating Names

查询字典,字典中存在则返回对应的值,不存在则返回None。

# class Name
    def eval(self, env):
        """
        >>> env = {
        ...     'a': Number(1),
        ...     'b': LambdaFunction([], Literal(0), {})
        ... }
        >>> Name('a').eval(env)
        Number(1)
        >>> Name('b').eval(env)
        LambdaFunction([], Literal(0), {})
        >>> print(Name('c').eval(env))
        None
        """
        "*** YOUR CODE HERE ***"
        if self.var_name in env:
            return env[self.var_name]

Q3: Evaluating Call Expressions


阅读Value类,Pycombinator值的类型包括numbers,lambda functions, primitive functions。

  1. self.operator是Name类型,调用Name.eval方法。
  2. self.operands列表包含numbers类型和函数(原始函数和lambda函数)类型。创建一个新列表存放参数,如果operand是数字类型,直接添加到列表中;否则,调用eval方法将返回值添加到列表中。
  3. 调用apply方法,参数为2中列表。
# class CallExpr
    def eval(self, env):
        """
        >>> from reader import read
        >>> new_env = global_env.copy()
        >>> new_env.update({'a': Number(1), 'b': Number(2)})
        >>> add = CallExpr(Name('add'), [Literal(3), Name('a')])
        >>> add.eval(new_env)
        Number(4)
        >>> new_env['a'] = Number(5)
        >>> add.eval(new_env)
        Number(8)
        >>> read('max(b, a, 4, -1)').eval(new_env)
        Number(5)
        >>> read('add(mul(3, 4), b)').eval(new_env)
        Number(14)
        """
        "*** YOUR CODE HERE ***"
        eval_operands = []
        for operand in self.operands[:]:
            if isinstance(operand, Number):
                eval_operands.append(operand)
            else:
                eval_operands.append(operand.eval(env))
        return self.operator.eval(env).apply(eval_operands)

Q4: Applying Lambda Functions


LambdaFunction类的三个实例属性:parameters、body、parent。

  1. 创建parent environment的copy。
  2. 将parameters和arguments更新到copy中,形参和实参对应。
  3. evaluate body。
# class LambdaFunction
    def apply(self, arguments):
        """
        >>> from reader import read
        >>> add_lambda = read('lambda x, y: add(x, y)').eval(global_env)
        >>> add_lambda.apply([Number(1), Number(2)])
        Number(3)
        >>> add_lambda.apply([Number(3), Number(4)])
        Number(7)
        >>> sub_lambda = read('lambda add: sub(10, add)').eval(global_env)
        >>> sub_lambda.apply([Number(8)])
        Number(2)
        >>> add_lambda.apply([Number(8), Number(10)]) # Make sure you made a copy of env
        Number(18)
        >>> read('(lambda x: lambda y: add(x, y))(3)(4)').eval(global_env)
        Number(7)
        >>> read('(lambda x: x(x))(lambda y: 4)').eval(global_env)
        Number(4)
        """
        if len(self.parameters) != len(arguments):
            raise TypeError("Oof! Cannot apply number {} to arguments {}".format(
                comma_separated(self.parameters), comma_separated(arguments)))
        "*** YOUR CODE HERE ***"
        new_env = self.parent.copy()
        for parameter, argument in zip(self.parameters, arguments):
            new_env[parameter] = argument
        return self.body.eval(new_env)

Q5: Handling Exceptions

repl.py,使用try...except...处理异常。
针对算术异常:

        except (OverflowError, ZeroDivisionError) as e:
            print('Arithmetic error!')
            print(type(e).__name__ + ':', e)
posted @ 2021-07-23 23:49  ikventure  阅读(1161)  评论(0编辑  收藏  举报