code: def generate_edges(formula):
      """Parses an edge specification from the head of the given
      formula part and yields the following:
  
        - startpoint(s) of the edge by vertex names
        - endpoint(s) of the edge by names or an empty list if the vertices are
          isolated
        - a pair of bools to denote whether we had arrowheads at the start and end
          vertices
      """
      if formula == "":
          yield [], [""], [False, False]
          return
  
      name_tokens = {token.NAME, token.NUMBER, token.STRING}
      edge_chars = "<>-+"
      start_names, end_names, arrowheads = [], [], [False, False]
      parsing_vertices = True
  
      # Tokenize the formula
      token_gen = tokenize.generate_tokens(StringIO(formula).__next__)
      for token_info in token_gen:
          # Do the state transitions
          token_type, tok, _, _, _ = token_info
          if parsing_vertices:
              if (
                  all(ch in edge_chars for ch in tok)
                  and token_type == token.OP
              ):
                  parsing_vertices = False
                  # Check the edge we currently have
                  if start_names and end_names:
                      # We have a whole edge
                      yield start_names, end_names, arrowheads
                  start_names, end_names = end_names, []
                  arrowheads = [False, False]
          else:
              if any(ch not in edge_chars for ch in tok):
                  parsing_vertices = True
  
          if parsing_vertices:
              # We are parsing vertex names at the moment
              if token_type in name_tokens:
                  # We found a vertex name
                  if token_type == token.STRING:
                      end_names.append(eval(tok))
                  else:
                      end_names.append(str(tok))
              elif tok == ":" and token_type == token.OP:
                  # Separating semicolon between vertex names, we just go on
                  continue
              elif token_type == token.NEWLINE:
                  # Newlines are fine
                  pass
              elif token_type == token.ENDMARKER:
                  # End markers are fine
                  pass
              else:
                  msg = (
                      "invalid token found in edge specification: %s; "
                      "token_type=%r; tok=%r"
                      % (formula, token_type, tok)
                  )
                  raise SyntaxError(msg)
          else:
              # We are parsing an edge operator
              if "<" in tok:
                  if ">" in tok:
                      arrowheads = [True, True]
                  else:
                      arrowheads[0] = True
              elif ">" in tok:
                  arrowheads[1] = True
              elif "+" in tok:
                  if tok[0] == "+":
                      arrowheads[0] = True
                  if len(tok) > 1 and tok[-1] == "+":
                      arrowheads[1] = True
  
      # The final edge
      yield start_names, end_names, arrowheads
  
imports: from io import StringIO
  from igraph.datatypes import UniqueIdGenerator
  import re
  import tokenize
  import token
label_desc: Use image node_id 1 for calling a global function with example usage: generate_edges(formula) without return types