Skip to content

Instantly share code, notes, and snippets.

@n-yoda
Created June 2, 2020 15:14
Show Gist options
  • Save n-yoda/23ab1d7f3c9e9b9971229464b8cbd773 to your computer and use it in GitHub Desktop.
Save n-yoda/23ab1d7f3c9e9b9971229464b8cbd773 to your computer and use it in GitHub Desktop.
calculator.py
#!/usr/bin/python3
class Token(object):
symbol = None
@classmethod
def can_read(cls, line, index):
return line[index] == cls.symbol
@classmethod
def read(cls, line, index):
return cls(), index + 1
class Number(Token):
def __init__(self, number):
self.number = number
@classmethod
def can_read(cls, line, index):
return line[index].isdigit()
@classmethod
def read(cls, line, index):
number = 0
while index < len(line) and line[index].isdigit():
number = number * 10 + int(line[index])
index += 1
if index < len(line) and line[index] == ".":
index += 1
keta = 0.1
while index < len(line) and line[index].isdigit():
number += int(line[index]) * keta
keta /= 10
index += 1
token = Number(number)
return token, index
class Operator(Token):
def apply(self, a, b):
raise NotImplementedError()
class WeakOperator(Operator):
pass
class StrongOperator(Operator):
pass
class Plus(WeakOperator):
symbol = "+"
def apply(self, a, b):
return a + b
class Minus(WeakOperator):
symbol = "-"
def apply(self, a, b):
return a - b
class Mult(StrongOperator):
symbol = "*"
def apply(self, a, b):
return a * b
class Div(StrongOperator):
symbol = "/"
def apply(self, a, b):
return a / b
class ParenStart(Token):
symbol = "("
class ParenEnd(Token):
symbol = ")"
def tokenize(line):
tokens = []
index = 0
while index < len(line):
for token_type in [Number, Plus, Minus, Mult, Div, ParenStart, ParenEnd]:
if token_type.can_read(line, index):
(token, index) = token_type.read(line, index)
break
else:
print("Invalid character found: " + line[index])
exit(1)
tokens.append(token)
return tokens
def evaluate_num_or_paren(tokens, index):
if isinstance(tokens[index], Number):
return tokens[index].number, index + 1
elif isinstance(tokens[index], ParenStart):
(answer, index) = evaluate_plus_minus(tokens, index + 1)
if isinstance(tokens[index], ParenEnd):
return answer, index + 1
print("Invalid syntax. Number or '(' are expected.", index)
exit(1)
def evaluate_mul_div(tokens, index):
(answer, index) = evaluate_num_or_paren(tokens, index)
while index < len(tokens):
if isinstance(tokens[index], StrongOperator):
operator = tokens[index]
(result, index) = evaluate_num_or_paren(tokens, index + 1)
answer = operator.apply(answer, result)
elif isinstance(tokens[index], WeakOperator):
return answer, index
elif isinstance(tokens[index], ParenEnd):
return answer, index
else:
print("Invalid syntax. Operators or ')' are expected.", index)
exit(1)
return answer, index
def evaluate_plus_minus(tokens, index):
(answer, index) = evaluate_mul_div(tokens, index)
while index < len(tokens):
if isinstance(tokens[index], WeakOperator):
operator = tokens[index]
(result, index) = evaluate_mul_div(tokens, index + 1)
answer = operator.apply(answer, result)
elif isinstance(tokens[index], ParenEnd):
return answer, index
else:
print("Invalid syntax. Plus, minus, or ')' are expected.", index)
exit(1)
return answer, index
def evaluate(tokens):
(answer, index) = evaluate_plus_minus(tokens, 0)
if index < len(tokens):
print("Invalid syntax. ')' is not expected here.", index)
exit(1)
return answer
def test(line):
tokens = tokenize(line)
actualAnswer = evaluate(tokens)
expectedAnswer = eval(line)
if abs(actualAnswer - expectedAnswer) < 1e-8:
print("PASS! (%s = %f)" % (line, expectedAnswer))
else:
print(
"FAIL! (%s should be %f but was %f)" % (line, expectedAnswer, actualAnswer)
)
# Add more tests to this function :)
def runTest():
print("==== Test started! ====")
test("1+2")
test("1.0+2.1-3")
test("5*(1+2*3)-10/2")
test("((((1+2+3+4))))/(((1-2*4)))")
print("==== Test finished! ====\n")
runTest()
while True:
print("> ", end="")
line = input()
tokens = tokenize(line)
answer = evaluate(tokens)
print("answer = %f\n" % answer)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment