Skip to content

Instantly share code, notes, and snippets.

@chelseatroy
Created November 3, 2019 23:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chelseatroy/d31e7bdb2d5bfa40a4c2b61b0ebe7b8b to your computer and use it in GitHub Desktop.
Save chelseatroy/d31e7bdb2d5bfa40a4c2b61b0ebe7b8b to your computer and use it in GitHub Desktop.
Interpreter 2: Scope
# scheme_environment.py
#
# Challenge: Can you add mutable state to scheme?:
env = {
'+': lambda x, y: x + y,
'-': lambda x, y: x - y,
'*': lambda x, y: x * y,
'/': lambda x, y: x / y,
'!=': lambda x, y: x != y,
'=': lambda x, y: x == y,
'>': lambda x, y: x > y,
'<': lambda x, y: x < y,
'<=': lambda x, y: x <= y,
'>=': lambda x, y: x >= y,
}
class Procedure:
def __init__(self, parameters, expressions, env):
self.parameters = parameters
self.expressions = expressions
self.env = env # PROCEDURE MUST REMEMBER THE ENVIRONMENT AT THE TIME THAT IT WAS DEFINED
def __call__(self, *args):
# Args are the argument values
# Make a new scope for the local variables
local_env = {}
# Bind the parameter names to values
for name, value in zip(self.parameters, args):
local_env[name] = value
for expression in self.expressions:
result = seval(expression, {**self.env, **local_env})
return result
# Evaluating expressions
def seval(sexp, env):
if isinstance(sexp, (int, float, Procedure)):
return sexp
elif isinstance(sexp, str): # A symbol of some kind
return env[sexp] # Environment lookup
elif isinstance(sexp, tuple):
# Special forms
if sexp[0] == 'define': #Creating a variable for the first time
name = sexp[1]
value = seval(sexp[2], env)
env[name] = value
return
elif sexp[0] == 'set!': #Modifying an existing variable
name=sexp[1]
value=seval(sexp[2], env)
while env:
if name in env:
env[name] = value
return
else:
raise NameError("That variable does not exist in this scope.")
elif sexp[0] == 'if':
condition = sexp[1]
thenClause = sexp[2]
elseClause = sexp[3]
if seval(condition, env):
return seval(thenClause, env)
else:
return seval(elseClause, env)
elif sexp[0] == 'lambda':
parameters = sexp[1]
expressions = sexp[2:]
return Procedure(parameters=parameters, expressions=expressions, env=env)
return sapply(sexp[0], sexp[1:], env)
else:
raise RuntimeError("bad expression")
# Applies a procedure (calling a procedure)
def sapply(proc, args, env=env):
actual_proc = seval(proc, env) # Get the "procedure" itself
evaluated_args = [seval(arg, env) for arg in args] # Evaluate the arguments
return actual_proc(*evaluated_args) # Call Procedure
assert seval(23, {}) == 23
assert seval("x", {"x": 23}) == 23
assert seval(("+", 1, 2), env) == 3
assert seval(("+", 1, ("*", 2, 3)), env) == 7
seval(("define", "x", 13), env) == 7
assert seval(("x"), env) == 13
assert seval(("if", ("<", 2, 3), 4, 5), env) == 4
assert seval(("if", (">", 2, 3), 4, 5), env) == 5
# A function definition expressed as a S-expression (in tuples)
fact = ('define', 'fact',
('lambda', ('n',), ('if', ('=', 'n', 1), 1, ('*', 'n', ('fact', ('-', 'n', 1))))))
seval(fact, env)
seval(('define', 'n', 5), env)
result = seval(('fact', 'n'), env)
assert result == 120
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment