import builtins
from pair import*classSchemeError(Exception):"""Exception indicating an error in a Scheme program."""################# Environments #################classFrame:"""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 isNone:return'<Global Frame>'
s =sorted(['{0}: {1}'.format(k, v)for k, v in self.bindings.items()])return'<{{{0}}} -> {1}>'.format(', '.join(s),repr(self.parent))defdefine(self, symbol, value):"""Define Scheme SYMBOL to have VALUE."""# BEGIN PROBLEM 1"*** YOUR CODE HERE ***"
self.bindings[symbol]= value
# END PROBLEM 1deflookup(self, symbol):"""Return the value bound to SYMBOL. Errors if SYMBOL is not found."""# BEGIN PROBLEM 1"*** 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
# END PROBLEM 1raise SchemeError('unknown identifier: {0}'.format(symbol))defmake_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} -> <Global Frame>>
"""iflen(formals)!=len(vals):raise SchemeError('Incorrect number of arguments to function call')# BEGIN PROBLEM 8"*** YOUR CODE HERE ***"
child_frame = Frame(self)
f = formals
v = vals
while f isnot nil and v isnot nil:
child_frame.define(f.first, v.first)
f = f.rest
v = v.rest
return child_frame
# END PROBLEM 8############### Procedures ###############classProcedure:"""The the base class for all Procedure classes."""classBuiltinProcedure(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)classLambdaProcedure(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."""assertisinstance(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):returnstr(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))classMuProcedure(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):returnstr(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
############### Eval/Apply ###############defscheme_eval(expr, env, _=None):# Optional third argument is ignored"""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
"""# Evaluate atomsif scheme_symbolp(expr):return env.lookup(expr)elif self_evaluating(expr):return expr
# All non-atomic expressions are lists (combinations)ifnot 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:# BEGIN PROBLEM 3"*** 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)# END PROBLEM 3defscheme_apply(procedure, args, env):"""Apply Scheme PROCEDURE to argument values ARGS (a Scheme list) in
Frame ENV, the current environment."""
validate_procedure(procedure)ifnotisinstance(env, Frame):assertFalse,"Not a Frame: {}".format(env)ifisinstance(procedure, BuiltinProcedure):# BEGIN PROBLEM 2"*** YOUR CODE HERE ***"
args_list =[]while args != nil:
args_list +=[args.first]
args = args.rest
if procedure.need_env:
args_list +=[env]# END PROBLEM 2try:# BEGIN PROBLEM 2"*** YOUR CODE HERE ***"return procedure.py_func(*args_list)# END PROBLEM 2except TypeError as err:raise SchemeError('incorrect number of arguments: {0}'.format(procedure))elifisinstance(procedure, LambdaProcedure):# BEGIN PROBLEM 9"*** YOUR CODE HERE ***"
frame = Frame(procedure.env).make_child_frame(procedure.formals, args)return eval_all(procedure.body, frame)# END PROBLEM 9elifisinstance(procedure, MuProcedure):# BEGIN PROBLEM 11"*** YOUR CODE HERE ***"
child_frame = env.make_child_frame(procedure.formals, args)return eval_all(procedure.body, child_frame)# END PROBLEM 11else:assertFalse,"Unexpected procedure: {}".format(procedure)defeval_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
"""# BEGIN PROBLEM 6if expressions is nil:returnNone
e = expressions
res = nil
while e isnot nil:
res = scheme_eval(e.first, env)
e = e.rest
return res
# END PROBLEM 6################### Tail Recursion ###################classUnevaluated:"""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
defcomplete_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)ifisinstance(val, Unevaluated):return scheme_eval(val.expr, val.env)else:return val
defoptimize_tail_calls(unoptimized_scheme_eval):"""Return a properly tail recursive version of an eval function."""defoptimized_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 andnot scheme_symbolp(expr)andnot self_evaluating(expr):return Unevaluated(expr, env)
result = Unevaluated(expr, env)# BEGIN PROBLEM EC"*** YOUR CODE HERE ***"# END PROBLEM ECreturn optimized_eval
################################################################# Uncomment the following line to apply tail call optimization ################################################################## scheme_eval = optimize_tail_calls(scheme_eval)
3. scheme_forms.py
from scheme_eval_apply import*from scheme_utils import*from scheme_classes import*from scheme_builtins import*################## Special Forms ################### Each of the following do_xxx_form functions takes the cdr of a special form as# its first argument---a Scheme list representing a special form without the# initial identifying symbol (if, lambda, quote, ...). Its second argument is# the environment in which the form is to be evaluated.defdo_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)# Checks that expressions is a list of length at least 2
signature = expressions.first
if scheme_symbolp(signature):# assigning a name to a value e.g. (define x (+ 1 2))
validate_form(expressions,2,2)# Checks that expressions is a list of length exactly 2# BEGIN PROBLEM 4"*** YOUR CODE HERE ***"
env.define(signature, scheme_eval(expressions.rest.first, env))return signature
# END PROBLEM 4elifisinstance(signature, Pair)and scheme_symbolp(signature.first):# defining a named procedure e.g. (define (f x y) (+ x y))# BEGIN PROBLEM 10"*** YOUR CODE HERE ***"
procedure = do_lambda_form(Pair(signature.rest, expressions.rest), env)
env.define(signature.first, procedure)return signature.first
# END PROBLEM 10else:
bad_signature = signature.first ifisinstance(signature, Pair)else signature
raise SchemeError('non-symbol: {0}'.format(bad_signature))defdo_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)# BEGIN PROBLEM 5"*** YOUR CODE HERE ***"return expressions.first
# END PROBLEM 5defdo_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)defdo_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), <Global Frame>)
"""
validate_form(expressions,2)
formals = expressions.first
validate_formals(formals)# BEGIN PROBLEM 7"*** YOUR CODE HERE ***"return LambdaProcedure(formals, expressions.rest, env)# END PROBLEM 7defdo_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)eliflen(expressions)==3:return scheme_eval(expressions.rest.rest.first, env)defdo_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
"""# BEGIN PROBLEM 12"*** YOUR CODE HERE ***"if expressions is nil:returnTrue
sub_expressions = expressions
while sub_expressions isnot nil:
res = scheme_eval(sub_expressions.first, env)if is_scheme_false(res):returnFalse
sub_expressions = sub_expressions.rest
return res
# END PROBLEM 12defdo_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
"""# BEGIN PROBLEM 12"*** YOUR CODE HERE ***"if expressions is nil:returnFalse
sub_expressions = expressions
while sub_expressions isnot nil:
res = scheme_eval(sub_expressions.first, env)if is_scheme_true(res):return res
sub_expressions = sub_expressions.rest
returnFalse# END PROBLEM 12defdo_cond_form(expressions, env):"""Evaluate a cond form.
>>> do_cond_form(read_line("((#f (print 2)) (#t 3))"), create_global_frame())
3
"""while expressions isnot nil:
clause = expressions.first
validate_form(clause,1)if clause.first =='else':
test =Trueif expressions.rest != nil:raise SchemeError('else must be last')else:
test = scheme_eval(clause.first, env)if is_scheme_true(test):# BEGIN PROBLEM 13"*** YOUR CODE HERE ***"if clause.rest is nil:return test
else:return eval_all(clause.rest, env)# END PROBLEM 13
expressions = expressions.rest
defdo_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)defmake_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."""ifnot scheme_listp(bindings):raise SchemeError('bad bindings list in let form')
names = vals = nil
# BEGIN PROBLEM 14"*** YOUR CODE HERE ***"while bindings isnot 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)# END PROBLEM 14return env.make_child_frame(names, vals)defdo_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
"""# BEGIN PROBLEM OPTIONAL_1"*** YOUR CODE HERE ***"# END PROBLEM OPTIONAL_1defdo_quasiquote_form(expressions, env):"""Evaluate a quasiquote form with parameters EXPRESSIONS in
Frame ENV."""defquasiquote_item(val, env, level):"""Evaluate Scheme expression VAL that is nested at depth LEVEL in
a quasiquote form in Frame ENV."""ifnot scheme_pairp(val):return val
if val.first =='unquote':
level -=1if level ==0:
expressions = val.rest
validate_form(expressions,1,1)return scheme_eval(expressions.first, env)elif val.first =='quasiquote':
level +=1return val.map(lambda elem: quasiquote_item(elem, env, level))
validate_form(expressions,1,1)return quasiquote_item(expressions.first, env,1)defdo_unquote(expressions, env):raise SchemeError('unquote outside of quasiquote')################## Dynamic Scope ##################defdo_mu_form(expressions, env):"""Evaluate a mu form."""
validate_form(expressions,2)
formals = expressions.first
validate_formals(formals)# BEGIN PROBLEM 11"*** YOUR CODE HERE ***"return MuProcedure(formals, expressions.rest)# END PROBLEM 11
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,}