""" blisp - The best lisp implementation in the world! by Brendan O'Connor This file, all.py, is the big mish-mash of stuff that hasn't been modularized yet. It's what you run to start the interpreter. """ from __future__ import generators from copy import copy, deepcopy from Parser import ParseString, QuotedLispList try: import readline except ImportError: pass class LispException(Exception): pass class RequireException(LispException): pass def Unparse(ls): if isinstance(ls, str): return ls if not isinstance(ls, list): return repr(ls) if len(ls) >= 2 and ls[0] == 'quote': return "'" + Unparse(ls[1]) ret = ['('] for elem in ls: ret += Unparse(elem) ret += [' '] ret.pop() #kills the last space ret += [')'] return str.join('', ret) ################################################################# def EvalExpr(expr, ns): """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. """ print "EvalExpr begin: %s, type %s" %(expr, type(expr)) if isinstance(expr, str): if expr in ns: return ns[expr] else: return expr if expr[0] in builtin_macros: #We don't want to do any recursive evaluation! return builtin_macros[expr[0]](expr[1:]) startfrom = 1 Substitute(expr, ns, startfrom) print "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 'thinking about substitutions for %s...' %lispList[i] if lispList[i] in ns: print '\tsubstituting %s for %s!' %(ns[lispList[i]], lispList[i]) lispList[i] = ns[lispList[i]] else: print '\tnot subbing.' else: assert isinstance(lispList[i], list) def CallFunction(fnName, args): """Does function-namespaces lookup and then calls the correct function. It only exists because the global non-modularized mess that is the current namespace system. It's expected that the function's arguments have already been resolved, that is, you only call this from EvalExpr. """ #Get fn to be a function object that takes one argument, # the lisp function's args. if fnName in builtin_functions: fn = builtin_functions[fnName] elif fnName in defined_functions: fn = defined_functions[fnName].Call else: raise LispException, "Unknown function name '%s'!" %fnName return fn(args) class LispFunction(object): def __init__(self, params, evals_to, lispname="< no name ever given :( >"): self.params = params self.evals_to = evals_to self.lispname = lispname print "New LispFunction created." print " %(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!, I, %s, got %d args but want %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 "Call() begin: args=%s %s" %(args, type(args)) thisCallsNamespace = self.make_namespace(args) print "Call(): created namespace=%s" %thisCallsNamespace eval_result = EvalExpr(deepcopy(self.evals_to), thisCallsNamespace) print "Call(): returning %s %s" %(eval_result, type(eval_result)) return eval_result ############################## # The builtin functions and macros... The architecture here needs an overhaul. # I'm thinking, they should be subclassed from LispFunction, and there # should be a separate LispMacro tree... but nothing yet. # # You have to call them AFTER recursively evalling what will be their args.. # Except for macros, where you don't eval their args at all, but take them # literally. EvalExpr handles that. See also CallFunction. # def LispEval(lispargs): """\ Lisp usage: (eval '(+ 1 2)) Python usage: LispEval( ['+', '1', '2'] ) not quoted becaues it's called AFTER EvalExpr'ing away the quote """ assert len(lispargs) == 1 eval_this = lispargs[0] eval_this_copy = deepcopy(eval_this) return EvalExpr(eval_this_copy, {}) def LispDefun(lispargs): #This is a 'macro' as far as what I think a macro is. global defined_functions #xxx bad hack. Note it's used in other places too, without the tidy 'global' declaration. assert len(lispargs) == 3 name = lispargs[0]; assert isinstance(name, str) params = lispargs[1]; assert not isinstance(params, str) evals_to = lispargs[2] if name in defined_functions: raise LispExeption, \ "defun error: Function with name '%s' already exists" % name defined_functions[name] = LispFunction(params, evals_to, name) return 0 def LispQuote(lispargs): return lispargs[0] def LispAdd(lispargs): assert len(lispargs) > 1 for e in lispargs: assert isinstance(e, str), e numeric_sum = 0 for str_num in lispargs: numeric_sum += int(str_num) return str(numeric_sum) ## NOTE: Since there are no cons cells in blisp, we only support lisp-lists. ## This means there are no dotted pairs and related things I don't know about ## that lisp is supposed to have. def LispCons(lispargs): assert len(lispargs) == 2 return [lispargs[0]] + lispargs[1] def LispCdr(lispargs): assert len(lispargs) == 1 return lispargs[0][1:] def LispCar(lispargs): assert len(lispargs) == 1 return lispargs[0][0] builtin_functions = { 'eval' : LispEval, 'car' : LispCar, 'cdr' : LispCdr, 'cons' : LispCons, '+' : LispAdd } #Macros don't get their arguments evaluated first. Functions do. builtin_macros = {'defun' : LispDefun, 'quote' : LispQuote } defined_functions = {} #Maps name -> LispFunction print "all.py: All builtin functions:\n", builtin_functions print "all.py: All builtin macros:\n", builtin_macros def ProcessString(s): eval_lisparg = ParseString( "%s" %s ) print "Evalling: ",eval_lisparg eval_lispargs = [eval_lisparg] return LispEval( eval_lispargs ) def IILoop(): "Interactive Interpreter loop" print """ This is blisp. By Brendan O'Connor. Positively the best LISP ever! 'q' to quit.""" while 1: s = raw_input("blisp :-) ") if s == "" or s[0] == ";": continue if s == 'q': return try: print Unparse(ProcessString(s)) except LispException, e: print "Oops, error! Hopefully there's a helpful error message \n here -->",e if __name__=='__main__': IILoop()