Created
November 3, 2019 23:11
-
-
Save chelseatroy/d31e7bdb2d5bfa40a4c2b61b0ebe7b8b to your computer and use it in GitHub Desktop.
Interpreter 2: Scope
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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