from numbers import Number as Number
# "as Number" allows writing 
# Number
# instead of 
# numbers.Number


# Students! You're not expected to be comfortable with this yet.
# It's just a preview. We'll cover recursion properly in the coming
# weeks. 
def arithEval(expr):
    """
    Evaluate an expression from a simple list-based language of 
    arithmetic expressions (called "A-expressions" here), defined 
    *recursively* as follows:
    An A-expression is either:
        1. A number, or
        2. A list of the form [x,'+',y] or [x,'*',y], where x and y
            are smaller A-expressions.
    Define an A-expression corresponding to (5 + 0.5) * (2 + 4)
    >>> e1 = [[5,'+',0.5], '*', [2,'+',4]]
    >>> arithEval(e1)
    33.0
    """
    if isinstance(expr,list):
        if len(expr) != 3:
            raise Exception(
                'Bad input: {} should have length 3'.format(str(expr)))
        if expr[1] == '+':
            return arithEval(expr[0]) + arithEval(expr[2])
        elif expr[1] == '*':
            return arithEval(expr[0]) * arithEval(expr[2])    
        else:
            raise Exception(
            """Bad input: operand (second element) of {} should be '+'      
            or '*'""".format(str(expr)))
            
    elif isinstance(expr,Number):
        return expr
    else:
        raise Exception(
            'Bad input: {} is not a number or list'.format(str(expr)))
        
        
# this version of the function does the same as the previous version
# when the input is well-formed (is a valid expression; defined in
# the comment at the top of the file)
# note how it gives uninformative Exceptions when you give it a bad
# input, e.g. [1,'+',3,'+',4] or "bad input" or [1,'/',2] 
def arithEvalUnchecked(expr):
    print(arithEvalUnchecked(expr))
    if isinstance(expr,list):
        if expr[1] == '+':
            return arithEvalUnchecked(expr[0]) + arithEvalUnchecked(expr[2])
        elif expr[1] == '*':
            return arithEvalUnchecked(expr[0]) * arithEvalUnchecked(expr[2])    
        else:   raise Exception()
    elif isinstance(expr,Number):
        return expr
    else: raise Exception()



# Exercise: try writing a function equivalent to arithEval without
# using recursion, e.g. using for and while loops.
# But don't try for too long (I haven't done it)! It is always 
# possible to do without recursion. The point of this exercise is
# to give you an appreciation of its usefulness.


# Try this in the shell:
# >>> import recursion_arith_expr as rae
# >>> rae.arithEval([1,'+',2])
# Now insert some examples directly into this file, underneath
# this block-comment.
# e.g. insert the line:
# arithEval([[7,'+',0.5], '*', [2,'+',4]])
# Now, in the same shell:
# >>> import imp
# >>> imp.reload(rae)

