Created
June 2, 2020 15:14
-
-
Save n-yoda/23ab1d7f3c9e9b9971229464b8cbd773 to your computer and use it in GitHub Desktop.
calculator.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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