"""Contains most of the real work of evaluating expressions & calling functions""" import interpreter from lisp_excs import * from debug import dbgs from copy import * def EvalExpr(expr, ns, interper=None): """That's expression, namespace, where ns is a table of string-to-atom mappings. expr can be a string, a LispList It gets recursively evaluated, the first elt getting called. And namespace substitution happens, too. THIS FUNCTION IS NOT COPY SAFE needs a writable copy of expr!!! But ns is read-only. """ if interper==None: interper=interpreter.the_interper print>>dbgs, "EvalExpr begin: %s, %s and namespace=%s" %(expr, type(expr), ns) if isinstance(expr, str): if expr in ns: return ns[expr] else: return expr if expr[0] in interper.builtin_macros: #We don't want to do any recursive evaluation! print>>dbgs, "EvalExpr: '%s' is a macro, not recursively evaluating elements."\ %expr[0] return interper.builtin_macros[expr[0]].Call(expr[1:]) startfrom = 1 Substitute(expr, ns, startfrom) print>>dbgs, "EvalExpr: After substitution: %s" %expr for i in range(startfrom, len(expr)): expr[i] = EvalExpr(expr[i], ns) expr = CallFunction(expr[0], expr[1:]) return expr def Substitute(lispList, ns, startfrom=1): "Does namespace substitution on lispList, in-place." for i in range(startfrom, len(lispList)): #We iterate over indexes because iterating over elements won't do in-place substitution on the list. if isinstance(lispList[i], str): print>>dbgs, 'thinking about substitutions for %s...' %lispList[i] if lispList[i] in ns: print>>dbgs, '\tsubstituting %s for %s!' %(ns[lispList[i]], lispList[i]) lispList[i] = ['quote', ns[lispList[i]]] else: print>>dbgs, '\tnot subbing.' else: assert isinstance(lispList[i], list) def CallFunction(fnName, args, interper=None): """Does function-namespaces lookup and then calls the correct function. It's expected that the function's arguments have already been resolved, that is, you only call this from EvalExpr. """ if interper==None: interper = interpreter.the_interper if fnName in interper.builtin_functions: fn = interper.builtin_functions[fnName] elif fnName in interper.defined_functions: fn = interper.defined_functions[fnName] else: raise LispException, "Unknown function name '%s'!" %fnName return fn.Call(args) class LispFunction(object): """This is a function defined in Lisp, as opposed to a BuiltinFunction that's implemented in Python. However, they both have the same interface.""" def __init__(self, params, evals_to, lispname="< no name ever given :( >"): self.params = params self.evals_to = evals_to self.lispname = lispname print>>dbgs, "New LispFunction created." print>>dbgs, " %(lispname)s, params=%(params)s, evals_to=%(evals_to)s" %vars() def checkargs(self, args): "Helper for Call()" if len(args) != len(self.params): error_msg = "Call died!, '%s' got %d args but wants %d!" \ % (self.lispname, len(args), len(self.params)) raise LispException( error_msg ) def make_namespace(self, args): """Helper for Call() Example: If params=['a','b'] and args=[1, 2], then we return { 'a':1, 'b':2 } """ items = zip(self.params, args) return dict(items) def Call(self, args): "Precondition: args have already been recursively evaluated!" self.checkargs(args) print>>dbgs, "%s's Call() begin: args=%s %s" %(self.lispname, args, type(args)) thisCallsNamespace = self.make_namespace(args) print>>dbgs, "%s's Call(): created namespace=%s" %(self.lispname, thisCallsNamespace) eval_result = EvalExpr(deepcopy(self.evals_to), thisCallsNamespace) print>>dbgs, "%s's Call(): returning %s %s" %(self.lispname, eval_result, type(eval_result)) return eval_result