docs: def _container_handler(c, ctx):
      """Coroutine for container values. Delegates to other coroutines to tokenize all child values."""
      _, self = (yield None)
      queue = ctx.queue
      child_context = None
      is_field_name = ctx.ion_type is IonType.STRUCT
      delimiter_required = False
      complete = ctx.depth == 0
      can_flush = False
  
      def has_pending_symbol():
          return child_context and child_context.pending_symbol is not None
  
      def symbol_value_event():
          return child_context.event_transition(
              IonEvent, IonEventType.SCALAR, IonType.SYMBOL, _as_symbol(child_context.pending_symbol))
  
      def pending_symbol_value():
          if has_pending_symbol():
              assert not child_context.value
              if ctx.ion_type is IonType.STRUCT and child_context.field_name is None:
                  _illegal_character(c, ctx,
                                     'Encountered STRUCT value %s without field name.' % (child_context.pending_symbol,))
              return symbol_value_event()
          return None
  
      def is_value_decorated():
          return child_context is not None and (child_context.annotations or child_context.field_name is not None)
  
      def _can_flush():
          return child_context is not None and \
                 child_context.depth == 0 and \
                 (
                     (
                         child_context.ion_type is not None and
                         (
                             child_context.ion_type.is_numeric or
                             (child_context.ion_type.is_text and not ctx.quoted_text and not is_field_name)
                         )
                     ) or
                     (
                         child_context.line_comment and
                         not is_value_decorated()
                     )
                 )
  
      while True:
          # Loop over all values in this container.
          if c in ctx.container.end or c in ctx.container.delimiter or BufferQueue.is_eof(c):
              symbol_event = pending_symbol_value()
              if symbol_event is not None:
                  yield symbol_event
                  child_context = None
                  delimiter_required = ctx.container.is_delimited
              if c in ctx.container.end:
                  if not delimiter_required and is_value_decorated():
                      _illegal_character(c, child_context,
                                         'Dangling field name (%s) and/or annotation(s) (%r) at end of container.'
                                         % (child_context.field_name, child_context.annotations))
                  # Yield the close event and go to enclosing container. This coroutine instance will never resume.
                  yield Transition(
                      IonEvent(IonEventType.CONTAINER_END, ctx.ion_type, depth=ctx.depth-1),
                      ctx.whence
                  )
                  raise ValueError('Resumed a finished container handler.')
              elif c in ctx.container.delimiter:
                  if not delimiter_required:
                      _illegal_character(c, ctx.derive_child_context(None),
                                         'Encountered delimiter %s without preceding value.'
                                         % (_chr(ctx.container.delimiter[0]),))
                  is_field_name = ctx.ion_type is IonType.STRUCT
                  delimiter_required = False
                  c = None
              else:
                  assert BufferQueue.is_eof(c)
                  assert len(queue) == 0
                  yield ctx.read_data_event(self, complete=True)
                  c = None
          if c is not None and c not in _WHITESPACE:
              can_flush = False
              if c == _SLASH:
                  if child_context is None:
                      # This is the start of a new child value (or, if this is a comment, a new value will start after the
                      # comment ends).
                      child_context = ctx.derive_child_context(self)
                  if ctx.ion_type is IonType.SEXP:
                      handler = _sexp_slash_handler(c, child_context, pending_event=pending_symbol_value())
                  else:
                      handler = _comment_handler(c, child_context, self)
              elif delimiter_required:
                  # This is not the delimiter, or whitespace, or the start of a comment. Throw.
                  _illegal_character(c, ctx.derive_child_context(None), 'Delimiter %s not found after value.'
                                     % (_chr(ctx.container.delimiter[0]),))
              elif has_pending_symbol():
                  # A character besides whitespace, comments, and delimiters has been found, and there is a pending
                  # symbol. That pending symbol is either an annotation, a field name, or a symbol value.
                  if c == _COLON:
                      if is_field_name:
                          is_field_name = False
                          child_context.set_field_name()
                          c = None
                      else:
                          assert not ctx.quoted_text
                          if len(queue) == 0:
                              yield ctx.read_data_event(self)
                          c = queue.read_byte()
                          if c == _COLON:
                              child_context.set_annotation()
                              c = None  # forces another character to be read safely
                          else:
                              # Colon that doesn't indicate a field name or annotation.
                              _illegal_character(c, child_context)
                  else:
                      if is_field_name:
                          _illegal_character(c, child_context, 'Illegal character after field name %s.'
                                             % child_context.pending_symbol)
                      # It's a symbol value delimited by something other than a comma (i.e. whitespace or comment)
                      yield symbol_value_event()
                      child_context = None
                      delimiter_required = ctx.container.is_delimited
                  continue
              else:
                  if not is_value_decorated():
                      # This is the start of a new child value.
                      child_context = ctx.derive_child_context(self)
                  if is_field_name:
                      handler = _FIELD_NAME_START_TABLE[c](c, child_context)
                  else:
                      handler = _VALUE_START_TABLE[c](c, child_context)  # Initialize the new handler
                      can_flush = _IMMEDIATE_FLUSH_TABLE[c]
              container_start = c == _OPEN_BRACKET or \
                                c == _OPEN_PAREN  # _OPEN_BRACE might start a lob; that is handled elsewhere.
              quoted_start = c == _DOUBLE_QUOTE or c == _SINGLE_QUOTE
              while True:
                  # Loop over all characters in the current token. A token is either a non-symbol value or a pending
                  # symbol, which may end up being a field name, annotation, or symbol value.
                  if container_start:
                      c = None
                      container_start = False
                  else:
                      if child_context.quoted_text or quoted_start:
                          quoted_start = False
                          yield child_context.next_code_point(self)
                          c = child_context.code_point
                      else:
                          if len(queue) == 0:
                              yield ctx.read_data_event(self, can_flush=can_flush)
                          c = queue.read_byte()
                  trans = handler.send((c, handler))
                  if trans.event is not None:
                      is_self_delimiting = False
                      if child_context.is_composite:
                          # This is a composite transition, i.e. it is an event transition followed by an immediate
                          # transition to the handler coroutine for the next token.
                          next_transition = trans.next_transition
                          child_context = trans.next_context
                          assert next_transition is None or next_transition.event is None
                      else:
                          next_transition = None
                          is_self_delimiting = child_context.is_self_delimiting
                          child_context = None
                      # This child value is finished. c is now the first character in the next value or sequence.
                      # Hence, a new character should not be read; it should be provided to the handler for the next
                      # child context.
                      yield trans
                      event_ion_type = trans.event.ion_type  # None in the case of IVM event.
                      is_container = event_ion_type is not None and event_ion_type.is_container and \
                          trans.event.event_type is not IonEventType.SCALAR
                      if is_container:
                          assert next_transition is None
                          yield Transition(
                              None,
                              _container_handler(c, ctx.derive_container_context(trans.event.ion_type, self))
                          )
                      complete = ctx.depth == 0
                      can_flush = False
                      if is_container or is_self_delimiting:
                          # The end of the value has been reached, and c needs to be updated
                          assert not ctx.quoted_text
                          if len(queue) == 0:
                              yield ctx.read_data_event(self, complete, can_flush)
                          c = queue.read_byte()
                      delimiter_required = ctx.container.is_delimited
                      if next_transition is None:
                          break
                      else:
                          trans = next_transition
                  elif self is trans.delegate:
                      child_context.set_ion_type(None)  # The next token will determine the type.
                      complete = False
                      can_flush = _can_flush()
                      if is_field_name:
                          assert not can_flush
                          if c == _COLON or not child_context.is_self_delimiting:
                              break
                      elif has_pending_symbol():
                          can_flush = ctx.depth == 0
                          if not child_context.is_self_delimiting or child_context.line_comment:
                              break
                      elif child_context.is_self_delimiting:
                          # This is the end of a comment. If this is at the top level and is un-annotated,
                          # it may end the stream.
                          complete = ctx.depth == 0 and not is_value_decorated()
                      # This happens at the end of a comment within this container, or when a symbol token has been
                      # found. In both cases, an event should not be emitted. Read the next character and continue.
                      if len(queue) == 0:
                          yield ctx.read_data_event(self, complete, can_flush)
                      c = queue.read_byte()
                      break
                  # This is an immediate transition to a handler (may be the same one) for the current token.
                  can_flush = _can_flush()
                  handler = trans.delegate
          else:
              assert not ctx.quoted_text
              if len(queue) == 0:
                  yield ctx.read_data_event(self, complete, can_flush)
              c = queue.read_byte()