1. scheme_classes.py
import builtins
from pair import *
class SchemeError(Exception):
"""Exception indicating an error in a Scheme program."""
class Frame:
"""An environment frame binds Scheme symbols to Scheme values."""
def __init__(self, parent):
"""An empty frame with parent frame PARENT (which may be None)."""
self.bindings = {}
self.parent = parent
def __repr__(self):
if self.parent is None:
return ''
s = sorted(['{0}: {1}'.format(k, v) for k, v in self.bindings.items()])
return '<{{{0}}} -> {1}>'.format(', '.join(s), repr(self.parent))
def define(self, symbol, value):
"""Define Scheme SYMBOL to have VALUE."""
"*** YOUR CODE HERE ***"
self.bindings[symbol] = value
def lookup(self, symbol):
"""Return the value bound to SYMBOL. Errors if SYMBOL is not found."""
"*** YOUR CODE HERE ***"
if symbol in self.bindings.keys():
return self.bindings[symbol]
p = self.parent
while p != None:
if symbol in p.bindings.keys():
return p.bindings[symbol]
p = p.parent
raise SchemeError('unknown identifier: {0}'.format(symbol))
def make_child_frame(self, formals, vals):
"""Return a new local frame whose parent is SELF, in which the symbols
in a Scheme list of formal parameters FORMALS are bound to the Scheme
values in the Scheme list VALS. Both FORMALS and VALS are represented
as Pairs. Raise an error if too many or too few vals are given.
>>> env = create_global_frame()
>>> formals, expressions = read_line('(a b c)'), read_line('(1 2 3)')
>>> env.make_child_frame(formals, expressions)
<{a: 1, b: 2, c: 3} -> >
"""
if len(formals) != len(vals):
raise SchemeError('Incorrect number of arguments to function call')
"*** YOUR CODE HERE ***"
child_frame = Frame(self)
f = formals
v = vals
while f is not nil and v is not nil:
child_frame.define(f.first, v.first)
f = f.rest
v = v.rest
return child_frame
class Procedure:
"""The the base class for all Procedure classes."""
class BuiltinProcedure(Procedure):
"""A Scheme procedure defined as a Python function."""
def __init__(self, py_func, need_env=False, name='builtin'):
self.name = name
self.py_func = py_func
self.need_env = need_env
def __str__(self):
return '#[{0}]'.format(self.name)
class LambdaProcedure(Procedure):
"""A procedure defined by a lambda expression or a define form."""
def __init__(self, formals, body, env):
"""A procedure with formal parameter list FORMALS (a Scheme list),
whose body is the Scheme list BODY, and whose parent environment
starts with Frame ENV."""
assert isinstance(env, Frame), "env must be of type Frame"
from scheme_utils import validate_type, scheme_listp
validate_type(formals, scheme_listp, 0, 'LambdaProcedure')
validate_type(body, scheme_listp, 1, 'LambdaProcedure')
self.formals = formals
self.body = body
self.env = env
def __str__(self):
return str(Pair('lambda', Pair(self.formals, self.body)))
def __repr__(self):
return 'LambdaProcedure({0}, {1}, {2})'.format(
repr(self.formals), repr(self.body), repr(self.env))
class MuProcedure(Procedure):
"""A procedure defined by a mu expression, which has dynamic scope.
_________________
< Scheme is cool! >
-----------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
"""
def __init__(self, formals, body):
"""A procedure with formal parameter list FORMALS (a Scheme list) and
Scheme list BODY as its definition."""
self.formals = formals
self.body = body
def __str__(self):
return str(Pair('mu', Pair(self.formals, self.body)))
def __repr__(self):
return 'MuProcedure({0}, {1})'.format(
repr(self.formals), repr(self.body))
2. scheme_eval_apply.py
import sys
from pair import *
from scheme_utils import *
from ucb import main, trace
import scheme_forms
def scheme_eval(expr, env, _=None):
"""Evaluate Scheme expression EXPR in Frame ENV.
>>> expr = read_line('(+ 2 2)')
>>> expr
Pair('+', Pair(2, Pair(2, nil)))
>>> scheme_eval(expr, create_global_frame())
4
"""
if scheme_symbolp(expr):
return env.lookup(expr)
elif self_evaluating(expr):
return expr
if not scheme_listp(expr):
raise SchemeError('malformed list: {0}'.format(repl_str(expr)))
first, rest = expr.first, expr.rest
if scheme_symbolp(first) and first in scheme_forms.SPECIAL_FORMS:
return scheme_forms.SPECIAL_FORMS[first](rest, env)
else:
"*** YOUR CODE HERE ***"
operator = scheme_eval(first, env)
validate_procedure(operator)
operands = rest.map(lambda x : scheme_eval(x, env))
return scheme_apply(operator, operands, env)
def scheme_apply(procedure, args, env):
"""Apply Scheme PROCEDURE to argument values ARGS (a Scheme list) in
Frame ENV, the current environment."""
validate_procedure(procedure)
if not isinstance(env, Frame):
assert False, "Not a Frame: {}".format(env)
if isinstance(procedure, BuiltinProcedure):
"*** YOUR CODE HERE ***"
args_list = []
while args != nil:
args_list += [args.first]
args = args.rest
if procedure.need_env:
args_list += [env]
try:
"*** YOUR CODE HERE ***"
return procedure.py_func(*args_list)
except TypeError as err:
raise SchemeError('incorrect number of arguments: {0}'.format(procedure))
elif isinstance(procedure, LambdaProcedure):
"*** YOUR CODE HERE ***"
frame = Frame(procedure.env).make_child_frame(procedure.formals, args)
return eval_all(procedure.body, frame)
elif isinstance(procedure, MuProcedure):
"*** YOUR CODE HERE ***"
child_frame = env.make_child_frame(procedure.formals, args)
return eval_all(procedure.body, child_frame)
else:
assert False, "Unexpected procedure: {}".format(procedure)
def eval_all(expressions, env):
"""Evaluate each expression in the Scheme list EXPRESSIONS in
Frame ENV (the current environment) and return the value of the last.
>>> eval_all(read_line("(1)"), create_global_frame())
1
>>> eval_all(read_line("(1 2)"), create_global_frame())
2
>>> x = eval_all(read_line("((print 1) 2)"), create_global_frame())
1
>>> x
2
>>> eval_all(read_line("((define x 2) x)"), create_global_frame())
2
"""
if expressions is nil:
return None
e = expressions
res = nil
while e is not nil:
res = scheme_eval(e.first, env)
e = e.rest
return res
class Unevaluated:
"""An expression and an environment in which it is to be evaluated."""
def __init__(self, expr, env):
"""Expression EXPR to be evaluated in Frame ENV."""
self.expr = expr
self.env = env
def complete_apply(procedure, args, env):
"""Apply procedure to args in env; ensure the result is not an Unevaluated."""
validate_procedure(procedure)
val = scheme_apply(procedure, args, env)
if isinstance(val, Unevaluated):
return scheme_eval(val.expr, val.env)
else:
return val
def optimize_tail_calls(unoptimized_scheme_eval):
"""Return a properly tail recursive version of an eval function."""
def optimized_eval(expr, env, tail=False):
"""Evaluate Scheme expression EXPR in Frame ENV. If TAIL,
return an Unevaluated containing an expression for further evaluation.
"""
if tail and not scheme_symbolp(expr) and not self_evaluating(expr):
return Unevaluated(expr, env)
result = Unevaluated(expr, env)
"*** YOUR CODE HERE ***"
return optimized_eval
3. scheme_forms.py
from scheme_eval_apply import *
from scheme_utils import *
from scheme_classes import *
from scheme_builtins import *
def do_define_form(expressions, env):
"""Evaluate a define form.
>>> env = create_global_frame()
>>> do_define_form(read_line("(x 2)"), env) # evaluating (define x 2)
'x'
>>> scheme_eval("x", env)
2
>>> do_define_form(read_line("(x (+ 2 8))"), env) # evaluating (define x (+ 2 8))
'x'
>>> scheme_eval("x", env)
10
>>> # problem 10
>>> env = create_global_frame()
>>> do_define_form(read_line("((f x) (+ x 2))"), env) # evaluating (define (f x) (+ x 8))
'f'
>>> scheme_eval(read_line("(f 3)"), env)
5
"""
validate_form(expressions, 2)
signature = expressions.first
if scheme_symbolp(signature):
validate_form(expressions, 2, 2)
"*** YOUR CODE HERE ***"
env.define(signature, scheme_eval(expressions.rest.first, env))
return signature
elif isinstance(signature, Pair) and scheme_symbolp(signature.first):
"*** YOUR CODE HERE ***"
procedure = do_lambda_form(Pair(signature.rest, expressions.rest), env)
env.define(signature.first, procedure)
return signature.first
else:
bad_signature = signature.first if isinstance(signature, Pair) else signature
raise SchemeError('non-symbol: {0}'.format(bad_signature))
def do_quote_form(expressions, env):
"""Evaluate a quote form.
>>> env = create_global_frame()
>>> do_quote_form(read_line("((+ x 2))"), env) # evaluating (quote (+ x 2))
Pair('+', Pair('x', Pair(2, nil)))
"""
validate_form(expressions, 1, 1)
"*** YOUR CODE HERE ***"
return expressions.first
def do_begin_form(expressions, env):
"""Evaluate a begin form.
>>> env = create_global_frame()
>>> x = do_begin_form(read_line("((print 2) 3)"), env) # evaluating (begin (print 2) 3)
2
>>> x
3
"""
validate_form(expressions, 1)
return eval_all(expressions, env)
def do_lambda_form(expressions, env):
"""Evaluate a lambda form.
>>> env = create_global_frame()
>>> do_lambda_form(read_line("((x) (+ x 2))"), env) # evaluating (lambda (x) (+ x 2))
LambdaProcedure(Pair('x', nil), Pair(Pair('+', Pair('x', Pair(2, nil))), nil), )
"""
validate_form(expressions, 2)
formals = expressions.first
validate_formals(formals)
"*** YOUR CODE HERE ***"
return LambdaProcedure(formals, expressions.rest, env)
def do_if_form(expressions, env):
"""Evaluate an if form.
>>> env = create_global_frame()
>>> do_if_form(read_line("(#t (print 2) (print 3))"), env) # evaluating (if #t (print 2) (print 3))
2
>>> do_if_form(read_line("(#f (print 2) (print 3))"), env) # evaluating (if #f (print 2) (print 3))
3
"""
validate_form(expressions, 2, 3)
if is_scheme_true(scheme_eval(expressions.first, env)):
return scheme_eval(expressions.rest.first, env)
elif len(expressions) == 3:
return scheme_eval(expressions.rest.rest.first, env)
def do_and_form(expressions, env):
"""Evaluate a (short-circuited) and form.
>>> env = create_global_frame()
>>> do_and_form(read_line("(#f (print 1))"), env) # evaluating (and #f (print 1))
False
>>> # evaluating (and (print 1) (print 2) (print 4) 3 #f)
>>> do_and_form(read_line("((print 1) (print 2) (print 3) (print 4) 3 #f)"), env)
1
2
3
4
False
"""
"*** YOUR CODE HERE ***"
if expressions is nil:
return True
sub_expressions = expressions
while sub_expressions is not nil:
res = scheme_eval(sub_expressions.first, env)
if is_scheme_false(res):
return False
sub_expressions = sub_expressions.rest
return res
def do_or_form(expressions, env):
"""Evaluate a (short-circuited) or form.
>>> env = create_global_frame()
>>> do_or_form(read_line("(10 (print 1))"), env) # evaluating (or 10 (print 1))
10
>>> do_or_form(read_line("(#f 2 3 #t #f)"), env) # evaluating (or #f 2 3 #t #f)
2
>>> # evaluating (or (begin (print 1) #f) (begin (print 2) #f) 6 (begin (print 3) 7))
>>> do_or_form(read_line("((begin (print 1) #f) (begin (print 2) #f) 6 (begin (print 3) 7))"), env)
1
2
6
"""
"*** YOUR CODE HERE ***"
if expressions is nil:
return False
sub_expressions = expressions
while sub_expressions is not nil:
res = scheme_eval(sub_expressions.first, env)
if is_scheme_true(res):
return res
sub_expressions = sub_expressions.rest
return False
def do_cond_form(expressions, env):
"""Evaluate a cond form.
>>> do_cond_form(read_line("((#f (print 2)) (#t 3))"), create_global_frame())
3
"""
while expressions is not nil:
clause = expressions.first
validate_form(clause, 1)
if clause.first == 'else':
test = True
if expressions.rest != nil:
raise SchemeError('else must be last')
else:
test = scheme_eval(clause.first, env)
if is_scheme_true(test):
"*** YOUR CODE HERE ***"
if clause.rest is nil:
return test
else:
return eval_all(clause.rest, env)
expressions = expressions.rest
def do_let_form(expressions, env):
"""Evaluate a let form.
>>> env = create_global_frame()
>>> do_let_form(read_line("(((x 2) (y 3)) (+ x y))"), env)
5
"""
validate_form(expressions, 2)
let_env = make_let_frame(expressions.first, env)
return eval_all(expressions.rest, let_env)
def make_let_frame(bindings, env):
"""Create a child frame of Frame ENV that contains the definitions given in
BINDINGS. The Scheme list BINDINGS must have the form of a proper bindings
list in a let expression: each item must be a list containing a symbol
and a Scheme expression."""
if not scheme_listp(bindings):
raise SchemeError('bad bindings list in let form')
names = vals = nil
"*** YOUR CODE HERE ***"
while bindings is not nil:
binding = bindings.first
validate_form(binding, 2, 2)
names = Pair(binding.first, names)
vals = Pair(scheme_eval(binding.rest.first, env), vals)
bindings = bindings.rest
validate_formals(names)
return env.make_child_frame(names, vals)
def do_define_macro(expressions, env):
"""Evaluate a define-macro form.
>>> env = create_global_frame()
>>> do_define_macro(read_line("((f x) (car x))"), env)
'f'
>>> scheme_eval(read_line("(f (1 2))"), env)
1
"""
"*** YOUR CODE HERE ***"
def do_quasiquote_form(expressions, env):
"""Evaluate a quasiquote form with parameters EXPRESSIONS in
Frame ENV."""
def quasiquote_item(val, env, level):
"""Evaluate Scheme expression VAL that is nested at depth LEVEL in
a quasiquote form in Frame ENV."""
if not scheme_pairp(val):
return val
if val.first == 'unquote':
level -= 1
if level == 0:
expressions = val.rest
validate_form(expressions, 1, 1)
return scheme_eval(expressions.first, env)
elif val.first == 'quasiquote':
level += 1
return val.map(lambda elem: quasiquote_item(elem, env, level))
validate_form(expressions, 1, 1)
return quasiquote_item(expressions.first, env, 1)
def do_unquote(expressions, env):
raise SchemeError('unquote outside of quasiquote')
def do_mu_form(expressions, env):
"""Evaluate a mu form."""
validate_form(expressions, 2)
formals = expressions.first
validate_formals(formals)
"*** YOUR CODE HERE ***"
return MuProcedure(formals, expressions.rest)
SPECIAL_FORMS = {
'and': do_and_form,
'begin': do_begin_form,
'cond': do_cond_form,
'define': do_define_form,
'if': do_if_form,
'lambda': do_lambda_form,
'let': do_let_form,
'or': do_or_form,
'quote': do_quote_form,
'define-macro': do_define_macro,
'quasiquote': do_quasiquote_form,
'unquote': do_unquote,
'mu': do_mu_form,
}