# 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]