Skip to content

Instantly share code, notes, and snippets.

@ksylvan
Last active March 13, 2020 23:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ksylvan/3dd8e3d8e594a478e2709addecfcecbc to your computer and use it in GitHub Desktop.
Save ksylvan/3dd8e3d8e594a478e2709addecfcecbc to your computer and use it in GitHub Desktop.
Simple Calculator
# You are building an educational website and want
# to create a simple calculator for students to use.
# The calculator will only allow addition and subtraction
# of non-negative integers.
# We also want to allow parentheses in our input.
# Given an expression string using
# the "+", "-", "(", and ")"
# operators like "5+(16-2)", write a function to parse the
# string and evaluate the result.
import re
class Calculate:
__NUMS = re.compile(r"\d+")
@classmethod
def tokens(cls, s):
pos = 0
n = len(s)
while pos < n:
m = Calculate.__NUMS.match(s, pos)
if m:
yield int(m.group(0))
pos += len(m.group(0))
if pos < n:
yield s[pos]
pos += 1
def __init__(self, s):
self.__tokens = []
for t in Calculate.tokens(s):
self.__tokens.append(t)
class Context:
def __init__(self):
self.__stack = []
self.__op = None
def __str__(self):
return f"Context({self.stack}){'OP: ' + str(self.op) if self.op else ''}"
@property
def op(self):
return self.__op
@op.setter
def op(self, v):
self.__op = v
@property
def stack(self):
return self.__stack
def nonEmpty(self):
return len(self.stack) != 0
@property
def val(self):
if self.nonEmpty():
return self.stack[-1]
return None
def __deal_with_val_in_context(self, cx, v):
if cx.op:
left = cx.stack.pop()
cx.stack.append(cx.op(left, v))
cx.op = None
else:
cx.stack.append(v)
def value(self):
l = []
cx = Calculate.Context()
for t in self.__tokens:
if type(t) is int:
self.__deal_with_val_in_context(cx, t)
continue
if t == '(':
l.append(cx)
cx = Calculate.Context()
continue
if t == ')':
my_v = cx.val
cx = l.pop()
if my_v is not None:
self.__deal_with_val_in_context(cx, my_v)
continue
if t == '+':
cx.op = lambda a, b: a + b
continue
if t == '-':
cx.op = lambda a, b: a -b
return cx.val
if __name__ == '__main__':
tests = [
("6+9-12", 3),
("1+2-3+4-5+6-7", -2),
("100+200+300", 600),
("1-2-3-0", -4),
("255", 255),
("5+16-((9-6)-(4-2))+1", 21),
("22+(2-4)", 20),
("6+9-12", 3),
("((1024))", 1024),
("1+(2+3)-(4-5)+6", 13),
("255", 255)]
for t in tests:
expr = t[0]
correct = t[1]
v = Calculate(expr).value()
res = "OK" if v == correct else "FAIL"
print(f"{res} Calculate({expr}) = {v} [expected {correct}]")
# OK Calculate(6+9+12) = 3 [expected 3]
# OK Calculate(1+2-3+4-5+6-7) = -2 [expected -2]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment