-
-
Save technillogue/5887092 to your computer and use it in GitHub Desktop.
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 |
Hello!
I was wondering if I have your permission to use your calculator in my project. All credit will go to you, along with a link to your GitHub. The file will be modified slightly to allow math equations similar to 4(8)
, and of course small spelling differences as well. Is this okay with you?
Uh, sure?
Uh, sure?
Thank you very much!
Hello!
I was wondering if I have your permission to use your calculator in my project. All credit will go to you, along with a link to your GitHub. The file will be modified slightly to allow math equations similar to4(8)
, and of course small spelling differences as well. Is this okay with you?
How did you get the 4(8+8) to work?
Hello, i have fixed your project. There was a issue with exponential calculation:
Input? 2^2
Traceback (most recent call last):
File "/home/kuflierl/Software/Test/py/recrusive-calc.py", line 89, in <module>
print(calc(input("Input? ")))
File "/home/kuflierl/Software/Test/py/recrusive-calc.py", line 86, in calc
return evaluate(lex(expr))
File "/home/kuflierl/Software/Test/py/recrusive-calc.py", line 69, in evaluate
return func(leftTerm, rightTerm)
File "/home/kuflierl/Software/Test/py/recrusive-calc.py", line 11, in <lambda>
("^", lambda x, y: x ^ y)
TypeError: unsupported operand type(s) for ^: 'float' and 'float'
In python we use ** for exponential calculations. This should fix it:
#!/bin/python3
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)
while 1:
print(evaluate(lex(input("Input? "))))
BTW i shortened your project a bit.
thanks for your code very much
("^", lambda x, y: x ^ y) should be ("^", lambda x, y: x**y)
(Also if you enter a string into the input the code doesn't work.)