code: def interpret(self, data, getattr=getattr):
      tokenizer = self.tokenizer = PSTokenizer(data, self.encoding)
      getnexttoken = tokenizer.getnexttoken
      do_token = self.do_token
      handle_object = self.handle_object
      try:
          while 1:
              tokentype, token = getnexttoken()
              if not token:
                  break
              if tokentype:
                  handler = getattr(self, tokentype)
                  object = handler(token)
              else:
                  object = do_token(token)
              if object is not None:
                  handle_object(object)
          tokenizer.close()
          self.tokenizer = None
      except:
          if self.tokenizer is not None:
              log.debug(
                  "ps error:\n"
                  "- - - - - - -\n"
                  "%s\n"
                  ">>>\n"
                  "%s\n"
                  "- - - - - - -",
                  self.tokenizer.buf[
                      self.tokenizer.pos - 50 : self.tokenizer.pos
                  ],
                  self.tokenizer.buf[
                      self.tokenizer.pos : self.tokenizer.pos + 50
                  ],
              )
          raise
  
imports: from fontTools.misc.textTools import bytechr, byteord, bytesjoin, tobytes, tostr
  from fontTools.misc import eexec
  from .psOperators import PSOperators, ps_StandardEncoding, ps_array, ps_boolean, ps_dict, ps_integer, ps_literal, ps_mark, ps_name, ps_operator, ps_procedure, ps_procmark, ps_real, ps_string
  import re
  from collections.abc import Callable
  from string import whitespace
  import logging
label_desc: Use image node_id 4 for calling the PSInterpreter obj's underlying member method code with example usage: obj.interpret(data, getattr) without return types