"""
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()                      

