text: # Python - lab 9
  # var 7
  # Group 2
  # 8/11/2019
  
  # print("Python - Lab 9")
  # print("The simplest Python interpreter ever")
  # print("See more at https://vadimcpp.ru/python/laba9.html")
  
  #
  # This is Backus–Naur form
  #
  # print('''
  # Syntax:
  # <expression> ::= <digit>|<digit><operation><expression>
  # <operation> ::= +|-
  # <digit> ::= 0|1|2|3|4|5|6|7|8|9
  # ''')
  import random
  
  
  # It's works too
  # class BaseInterpretError(Exception):
  #     def __init__(self, message):
  #         super().__init__(message)
  
  
  class InterpretError(Exception):
      def __init__(self, message, index=None):
          super().__init__(message)
          self.message = message
          self.index = index
  
      def __str__(self):
          msg = f"Error at position {self.index}.\n"
          msg += f"{self.message}"
          return msg
  
  
  def test_print_exception_msg():
      try:
          # raise BaseInterpretError("some error msg")
          raise InterpretError("some error msg", 1)
      except Exception as e:
          print(e)
  
  
  # test_print_exception_msg()
  
  
  class SimpleInterpreter:
      def __init__(self):
          self.digits = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
          self.operations = ["+", "-"]
  
      def __is_contains_in_digits(self, digit) -> bool:
          return digit in self.digits
  
      def __is_contains_in_operations(self, operation) -> bool:
          return operation in self.operations
  
      def __processDigit(self, digit) -> None:
          if not self.__is_contains_in_digits(digit):
              msg = f"Expected {self.digits} get {digit}"
              raise InterpretError(msg)
          if self.operation == "+":
              self.result += int(digit)
          elif self.operation == "-":
              self.result -= int(digit)
  
      def __loadProgram(self, program):
          self.result = 0
          self.program = program
          self.index = 0
          self.operation = "+"
  
      # eop - End of Program
      def __eop(self):
          return self.index >= len(self.program)
  
      # readDigit - try to read digit
      # returns digit or raise exception
      def __readDigit(self):
          if self.__eop():
              msg = "End of program occurred, expected a digit"
              raise InterpretError(msg, self.index)
          digit = self.program[self.index]
  
          if not digit.isdigit():
              msg = "Expected a digit"
              raise InterpretError(msg, self.index)
  
          self.index += 1
          return digit
  
      #
      # readOperation - try to read operation
      #
      # returns operation or raise exception
      #
      def __readOperation(self):
          if self.__eop():
              msg = "End of program occured, expected an operation"
              raise InterpretError(msg, self.index)
  
          operation = self.program[self.index]
  
          if not operation == "+" and not operation == "-":
              msg = "Expected an operation"
              raise InterpretError(msg, self.index)
  
          self.index += 1
          self.operation = operation
  
      #
      # <expression> ::= <digit>|<digit><operation><expression>
      #
      def __readExpression(self):
          self.__processDigit(self.__readDigit())
  
          if not self.__eop():
              self.__readOperation()
              self.__readExpression()
  
      def __interpret(self):
          self.__readExpression()
  
      def __printResult(self):
          print('Result is {}'.format(self.result))
  
      def runProgram(self, program):
          self.__loadProgram(program)
          try:
              self.__interpret()
              self.__printResult()
          except Exception as e:
              #
              # OPTIONAL:
              # 1. Try to pass error string to exception and print it out
              #
              print(e)
  
  
  def generate_correct_program(interpreter: SimpleInterpreter, length: int) -> str:
      # if length % 2 == 0:
      #     raise Exception(f"Wrong length: {length} % 2 != 1")
      program = []
      for i in range(length):
          if i % 2 == 0:
              program += random.choice(interpreter.digits)
          else:
              program += random.choice(interpreter.operations)
      program = "".join(program)
      return program
  
  
  def generate_incorrect_program(interpreter: SimpleInterpreter, length: int) -> str:
      program: str = ""
      program += generate_correct_program(interpreter, length)
  
      def del_first_symbol(program: str) -> str:
          return program[1:]
  
      def double_some_symbol(program: str) -> str:
          rndi = random.randint(0, len(program))
          return program[:rndi + 1] + program[rndi:]
  
      func = random.choice([del_first_symbol, double_some_symbol])
      program = func(program)
      return program
  
  
  def generate_programs(interpreter: SimpleInterpreter, count: int = 10, min_len: int = 1, max_len: int = 100) -> list:
      programs = []
      for i in range(count):
          length = random.randint(min_len, max_len)
          # length = length + 1 - (length % 2)
          func_generators = [generate_correct_program, generate_incorrect_program]
          gen = random.choice(func_generators)
          p = gen(interpreter, length)
          programs.append(p)
      return programs
  
  
  if __name__ == "__main__":
      programs = [
          "",  # 0
          "1",  # 1
          "2+2",  # 2
          "1+2-3+4",  # 3
          "1*2-4/2",  # 4 wrong syntax
          "12-34",  # 5 wrong syntax
          "-2+7",  # 6 wrong syntax
          "1+2+3+4+5",  # 7
      ]
  
      interpreter = SimpleInterpreter()
  
      # __debug__ cannot be reassigned
      # This constant is true if Python was not started with an -O option
      # print(__debug__)
      # assert False, "testtext"
      # test_print_exception_msg()
  
      programs = generate_programs(interpreter)
  
      for i in range(len(programs)):
          print(f"{i}: {programs[i]}")
          interpreter.runProgram(programs[i])
  