from operator import add, mul from rlist import * from ucb import main def tokenize(s): """Splits the provided string into a list of tokens.""" s = s.replace('(', ' ( ') s = s.replace(')', ' ) ') return s.split() def numberize(exp): """Converts exp to a number if possible, otherwise leaves it alone.""" try: return int(exp) except ValueError: try: return float(exp) except ValueError: return exp def read_exp(tokens): """Given a list of tokens, returns the first calculator expression (either a number, operator, or combination).""" if not tokens: raise SyntaxError('unexpected end of input') token = tokens.pop(0) if token == '(': exp = read_tail(tokens) if exp is nil: raise SyntaxError('empty combination') return exp elif token == ')': raise SyntaxError('unexpected )') else: return numberize(token) def read_tail(tokens): """Reads up to and including the first mismatched close parenthesis, then forms a combination out all of the values read up to that point.""" if tokens[0] == ')': tokens.pop(0) return nil return Pair(read_exp(tokens), read_tail(tokens)) def calc_eval(exp): """Evaluates a calculator expression.""" if isinstance(exp, Pair): return calc_apply(exp.car, map_rlist(calc_eval, exp.cdr)) else: return exp def calc_apply(op, args): """Applies an operator to an rlist of arguments.""" length = len_rlist(args) if op == '+': return reduce_rlist(add, 0, args) elif op == '*': return reduce_rlist(mul, 1, args) elif op == '-': if length == 0: raise TypeError('not enough arguments') elif length == 1: return -args.car else: return args.car - reduce_rlist(add, 0, args.cdr) elif op == '/': if length == 0: raise TypeError('not enough arguments') elif length == 1: return 1 / args.car else: return args.car / reduce_rlist(mul, 1, args.cdr) else: raise NameError('unknown operator {}'.format(op)) @main def repl(): while True: try: line = input('> ') exp = read_exp(tokenize(line)) print(calc_eval(exp)) except (KeyboardInterrupt, EOFError): print('Calculation complete.') return except (SyntaxError, TypeError, NameError, ValueError) as e: print(type(e).__name__ + ':', e)