Skip to content

Instantly share code, notes, and snippets.

@technillogue
Created June 28, 2013 18:53
Show Gist options
  • Save technillogue/5887092 to your computer and use it in GitHub Desktop.
Save technillogue/5887092 to your computer and use it in GitHub Desktop.
A more complicated recursive python calculator
from collections import OrderedDict
#reverse order of operations
#I didn't have to use an OrderedDict, but it's cute
operations = OrderedDict([
("+", lambda x, y: x + y),
("-", lambda x, y: x - y),
("/", lambda x, y: x / y),
("*", lambda x, y: x * y),
("^", lambda x, y: x ^ y)
])
symbols = operations.keys()
def lex(expr):
"""
seperates numbers from symbols, recursively nests parens
"""
tokens = []
while expr:
char, *expr = expr
#char is equal to the first charecter of the expression, expr is equal
#to the rest of it
if char == "#":
#the rest of the line is a comment
break
if char == "(":
try:
paren, expr = lex(expr)
tokens.append(paren)
#expr is what's after the end of the paren, we'll just continue
#lexing after that''
except ValueError:
raise Exception("paren mismatch")
elif char == ")":
return tokens, expr
#returns the tokens leading up to the to the paren and the rest of
#the expression after it
elif char.isdigit() or char == ".":
#number
try:
if tokens[-1] in symbols:
tokens.append(char) #start a new num
elif type(tokens[-1]) is list:
raise Exception("parens cannot be followed by numbers")
#no support for 5(1+1) yet
else:
tokens[-1] += char #add to last num
except IndexError:
#if tokens is empty
tokens.append(char) #start first num
elif char in symbols:
tokens.append(char)
elif char.isspace():
pass
else:
raise Exception("invalid charecter: " + char)
return tokens
def evaluate(tokens):
for symbol, func in operations.items():
#try to find an operation to eval in order
try:
pos = tokens.index(symbol)
#split the tokens by the operation and eval that
leftTerm = evaluate(tokens[:pos])
rightTerm = evaluate(tokens[pos + 1:])
return func(leftTerm, rightTerm)
#incidentially, return immediatly breaks all loops within the
# function
except ValueError:
pass
#index raises ValueError when it's not found
if len(tokens) is 1:
try:
#it must be a number
return float(tokens[0])
except TypeError:
#if it's not a number
return evaluate(tokens[0])
else:
raise Exception("bad expression: " + tokens)
def calc(expr):
return evaluate(lex(expr))
while 1:
print(calc(input("Input? ")))
$ python3 calc.py
Input? 1 + 1 #simple operations, comments
2.0
Input? 0.1-0.5#floats and negatives, irrelevant whitespace
-0.4
Input? 4 + 4 / 2 - 1 #order of operations is respected
5.0
Input? (4-4 + (3-2) * (((((((7)))))))) # complex parens
7.0
@thanhhung0112
Copy link

thanks for your code very much

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment