1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
|
"""A evaluator based on CPS form and trampolining.
FIXME: add a lot more documentation here about how this all works.
The core forms that the evaluator recognizes is the following:
self evaluating expressions
variable references
QUOTE
SET!
DEFINE
IF
LAMBDA
BEGIN
function application
Other special forms are handled by derivation: the expander module translates
the derived forms into these core forms.
FIXME: quasiquotation hasn't been pushed off into the expander yet.
"""
__license__ = "MIT License"
import pogo
import expressions
import environment
import exceptions
import traceback
import types
import pair
from parser import parse
from symbol import false, Symbol
from error import SchemeError
def identity(val):
"""The identity function."""
return val
"""We need to save Python's apply() function, since we use it later on
to evaluate primitive procedure calls. For symmetry, I'm also saving
Python's eval() function within evalInUnderlyingPython()."""
evalInUnderlyingPython = eval
applyInUnderlyingPython = apply
######################################################################
## The heart of the interpreter is eval/apply.
##
## Some notes here: I'm using trampolined style to get around Python's
## lack of tail recursion.
def eval(exp, env):
return pogo.pogo(teval(exp, env, pogo.land))
def teval(exp, env, cont):
"""Evaluates an expression 'exp' in an environment 'env'.
Exercise 4.3 asks us to rewrite this in a more natural
data-directed manner. Pychecker, also, doesn't like seeing
so many 'return' statements in one function. *grin*
"""
if expressions.isSelfEvaluating(exp):
return pogo.bounce(cont, exp)
if expressions.isVariable(exp):
return pogo.bounce(cont, environment.lookupVariableValue(exp, env))
if expressions.isQuoted(exp):
return evalQuoted(exp, env, cont)
if expressions.isAssignment(exp):
return evalAssignment(exp, env, cont)
if expressions.isDefinition(exp):
return evalDefinition(exp, env, cont)
if expressions.isIf(exp):
return evalIf(exp, env, cont)
if expressions.isLambda(exp):
return pogo.bounce(cont, expressions.makeProcedure
(expressions.lambdaParameters(exp),
expressions.lambdaBody(exp),
env))
if expressions.isBegin(exp):
return evalSequence(expressions.beginActions(exp), env, cont)
if expressions.isApplication(exp):
return evalApplication(exp, env, cont)
raise SchemeError, "Unknown expression type -- eval " + str(exp)
def apply(procedure, arguments, env, cont):
"""Applies a procedure on a list of arguments."""
if expressions.isPrimitiveProcedure(procedure):
return applyPrimitiveProcedure(procedure, arguments, env, cont)
elif expressions.isContinuationProcedure(procedure):
return applyContinuationProcedure(procedure, arguments)
if expressions.isCompoundProcedure(procedure):
newEnv = environment.extendEnvironment(
expressions.procedureParameters(procedure),
arguments,
expressions.procedureEnvironment(procedure))
return evalSequence(expressions.procedureBody(procedure), newEnv, cont)
raise SchemeError, "Unknown procedure type -- apply " + str(procedure)
def applyPrimitiveProcedure(proc, args, env, cont):
try:
return applyInUnderlyingPython(expressions.primitiveImplementation(proc),
[cont, env, pair.toPythonList(args)])
except Exception, e:
if isinstance(e, exceptions.SystemExit): raise e
raise SchemeError, e
def applyContinuationProcedure(proc, args):
try:
return applyInUnderlyingPython(expressions.continuationImplementation(proc),
pair.toPythonList(args))
except Exception, e:
if isinstance(e, exceptions.SystemExit): raise e
raise SchemeError, e
def evalRands(exps, env, cont):
"""Given a list of expressions, returns a new list containing the
values of evaluating each on of them. If the continuation is
given, then calls cont() on the evaluated operands instead."""
def c1(head_val):
def c2(tail_val):
return pogo.bounce(cont, pair.cons(head_val, tail_val))
return evalRands(expressions.restOperands(exps), env, c2)
if expressions.isNoOperands(exps):
return pogo.bounce(cont, pair.list())
return teval(expressions.firstOperand(exps), env, c1)
def evalIf(exp, env, cont):
def c(predicate_val):
if expressions.isTrue(predicate_val):
return teval(expressions.ifConsequent(exp), env, cont)
else:
return teval(expressions.ifAlternative(exp), env, cont)
return teval(expressions.ifPredicate(exp), env, c)
def evalSequence(exps, env, cont):
def c(val):
return evalSequence(expressions.restExps(exps), env, cont)
if expressions.isLastExp(exps):
return teval(expressions.firstExp(exps), env, cont)
else:
return teval(expressions.firstExp(exps), env, c)
def evalAssignment(exp, env, cont):
def c(val):
environment.setVariableValue(expressions.assignmentVariable(exp),
val, env)
return pogo.bounce(cont, Symbol("ok"))
return teval(expressions.assignmentValue(exp), env, c)
def evalDefinition(exp, env, cont):
def c(val):
environment.defineVariable(
expressions.definitionVariable(exp), val, env)
return pogo.bounce(cont, Symbol("ok"))
return teval(expressions.definitionValue(exp), env, c)
def evalApplication(exp, env, cont):
def c1(operator_val):
def c2(operands_val):
return apply(operator_val, operands_val, env, cont)
return evalRands(expressions.operands(exp), env, c2)
return teval(expressions.operator(exp), env, c1)
def evalQuoted(exp, env, cont):
"""Returns a quoted expression, using deepQuotedEval to look for
UNQUOTE.
Consequently, quoted elements are properly turned into cons pairs.
"""
text = expressions.textOfQuotation(exp)
if expressions.isQuasiquoted(exp):
expandedExp = expressions.expandQuasiquotation(text)
return pogo.bounce(teval,
expandedExp,
env,
cont)
else:
return pogo.bounce(cont, text)
|