docs: def pre_parse(code: str) -> Tuple[ClassTypes, str]:
      """
      Re-formats a vyper source string into a python source string and performs
      some validation.  More specifically,
  
      * Translates "contract" and "struct" keyword into python "class" keyword
      * Validates "@version" pragma against current compiler version
      * Prevents direct use of python "class" keyword
      * Prevents use of python semi-colon statement separator
  
      Also returns a mapping of detected contract and struct names to their
      respective vyper class types ("contract" or "struct").
  
      :param code: The vyper source code to be re-formatted.
      :return: A tuple including the class type mapping and the reformatted python
          source string.
      """
      result = []
      previous_keyword = None
      class_types: ClassTypes = {}
  
      try:
          code_bytes = code.encode('utf-8')
          g = tokenize(io.BytesIO(code_bytes).readline)
  
          for token in g:
              toks = [token]
  
              typ = token.type
              string = token.string
              start = token.start
              end = token.end
              line = token.line
  
              if typ == COMMENT and "@version" in string:
                  validate_version_pragma(string[1:], start)
  
              if typ == NAME and string == "class" and start[1] == 0:
                  raise StructureException(
                      "The `class` keyword is not allowed. Perhaps you meant `contract` or `struct`?",
                      start,
                  )
  
              # Make note of contract or struct name along with the type keyword
              # that preceded it
              if typ == NAME and previous_keyword is not None:
                  class_types[string] = previous_keyword
                  previous_keyword = None
  
              # Translate vyper-specific class keywords into python "class"
              # keyword
              if typ == NAME and string in VYPER_CLASS_TYPES and start[1] == 0:
                  toks = [TokenInfo(NAME, "class", start, end, line)]
                  previous_keyword = string
  
              if (typ, string) == (OP, ";"):
                  raise StructureException("Semi-colon statements not allowed.", start)
  
              result.extend(toks)
      except TokenError as e:
          raise StructureException(e.args[0], e.args[1]) from e
  
      return class_types, untokenize(result).decode('utf-8')