docs: def _parse_variable(self, parent, patch_nml=None):
          """Parse a variable and return its name and values."""
          if not patch_nml:
              patch_nml = Namelist()
  
          v_name = self.prior_token
          v_values = []
  
          # Patch state
          patch_values = None
  
          # Derived type parent index (see notes below)
          dt_idx = None
  
          if self.token == '(':
  
              v_idx_bounds = self._parse_indices()
              v_idx = FIndex(v_idx_bounds, self.global_start_index)
  
              # Update starting index against namelist record
              if v_name.lower() in parent.start_index:
                  p_idx = parent.start_index[v_name.lower()]
  
                  for idx, pv in enumerate(zip(p_idx, v_idx.first)):
                      if all(i is None for i in pv):
                          i_first = None
                      else:
                          i_first = min(i for i in pv if i is not None)
  
                      v_idx.first[idx] = i_first
  
                  # Resize vector based on starting index
                  for i_p, i_v in zip(p_idx, v_idx.first):
                      if i_p is not None and i_v is not None and i_v < i_p:
                          pad = [None for _ in range(i_p - i_v)]
                          parent[v_name] = pad + parent[v_name]
  
              else:
                  # If variable already existed without an index, then assume a
                  #   1-based index
                  # FIXME: Need to respect undefined `None` starting indexes?
                  if v_name in parent:
                      v_idx.first = [self.default_start_index
                                     for _ in v_idx.first]
  
              parent.start_index[v_name.lower()] = v_idx.first
  
              self._update_tokens()
  
              # Derived type parent check
              # NOTE: This assumes single-dimension derived type vectors
              #       (which I think is the only case supported in Fortran)
              if self.token == '%':
                  assert v_idx_bounds[0][1] - v_idx_bounds[0][0] == 1
                  dt_idx = v_idx_bounds[0][0] - v_idx.first[0]
  
                  # NOTE: This is the sensible play to call `parse_variable`
                  # but not yet sure how to implement it, so we currently pass
                  # along `dt_idx` to the `%` handler.
  
          else:
              v_idx = None
  
              # If indexed variable already exists, then re-index this new
              #   non-indexed variable using the global start index
  
              if v_name in parent.start_index:
                  p_start = parent.start_index[v_name.lower()]
                  v_start = [self.default_start_index for _ in p_start]
  
                  # Resize vector based on new starting index
                  for i_p, i_v in zip(p_start, v_start):
                      if i_v < i_p:
                          pad = [None for _ in range(i_p - i_v)]
                          parent[v_name] = pad + parent[v_name]
  
                  parent.start_index[v_name.lower()] = v_start
  
          if self.token == '%':
  
              # Resolve the derived type
  
              # Check for value in patch
              v_patch_nml = None
              if v_name in patch_nml:
                  v_patch_nml = patch_nml.pop(v_name.lower())
  
              if parent:
                  vpar = parent.get(v_name.lower())
                  if vpar and isinstance(vpar, list):
                      # If new element is not a list, then assume it's the first
                      # element of the list.
                      if dt_idx is None:
                          dt_idx = self.default_start_index
  
                      try:
                          v_parent = vpar[dt_idx]
                      except IndexError:
                          v_parent = Namelist()
                  elif vpar:
                      v_parent = vpar
                  else:
                      v_parent = Namelist()
              else:
                  v_parent = Namelist()
                  parent[v_name] = v_parent
  
              self._update_tokens()
              self._update_tokens()
  
              v_att, v_att_vals = self._parse_variable(
                  v_parent,
                  patch_nml=v_patch_nml
              )
  
              next_value = Namelist()
              next_value[v_att] = v_att_vals
              self._append_value(v_values, next_value, v_idx)
  
          else:
              # Construct the variable array
  
              assert self.token == '='
              n_vals = None
  
              self._update_tokens()
  
              # Check if value is in the namelist patch
              # TODO: Edit `Namelist` to support case-insensitive `pop` calls
              #       (Currently only a problem in PyPy2)
              if v_name in patch_nml:
                  patch_values = patch_nml.pop(v_name.lower())
  
                  if not isinstance(patch_values, list):
                      patch_values = [patch_values]
  
                  p_idx = 0
  
              # Add variables until next variable trigger
              while (self.token not in ('=', '(', '%') or
                     (self.prior_token, self.token) in (('=', '('), (',', '('))):
  
                  # Check for repeated values
                  if self.token == '*':
                      n_vals = self._parse_value()
                      assert isinstance(n_vals, int)
                      self._update_tokens()
                  elif not n_vals:
                      n_vals = 1
  
                  # First check for implicit null values
                  if self.prior_token in ('=', '%', ','):
                      if (self.token in (',', '/', '&', '$') and
                              not (self.prior_token == ',' and
                                   self.token in ('/', '&', '$'))):
                          self._append_value(v_values, None, v_idx, n_vals)
  
                  elif self.prior_token == '*':
  
                      if self.token not in ('/', '&', '$'):
                          self._update_tokens()
  
                      if (self.token == '=' or (self.token in ('/', '&', '$') and
                                                self.prior_token == '*')):
                          next_value = None
                      else:
                          next_value = self._parse_value()
  
                      self._append_value(v_values, next_value, v_idx, n_vals)
  
                  else:
                      next_value = self._parse_value()
                      self._append_value(v_values, next_value, v_idx, n_vals)
  
                  # Reset default repeat factor for subsequent values
                  n_vals = 1
  
                  # Exit for end of nml group (/, &, $) or null broadcast (=)
                  if self.token in ('/', '&', '$', '='):
                      break
                  else:
                      # Get the remaining length of the unpatched vector?
  
                      # NOTE: it is probably very inefficient to keep re-creating
                      # iterators upon every element; this solution reflects the
                      # absence of mature lookahead in the script.
                      #
                      # This is a temporary fix to address errors caused by
                      # patches of different length from the original value, and
                      # represents a direction to fully rewrite the parser using
                      # `tee`.
  
                      self.tokens, lookahead = itertools.tee(self.tokens)
                      n_vals_remain = count_values(lookahead)
  
                      if patch_values:
                          # XXX: The (p_idx - 1) <= n_vals_remain test is dodgy
                          # and does not really make sense to me, but it appears
                          # to work.
  
                          # TODO: Patch indices that are not set in the namelist
  
                          if (p_idx < len(patch_values) and
                                  (p_idx - 1) <= n_vals_remain and
                                  len(patch_values) > 0 and self.token != ','):
                              p_val = patch_values[p_idx]
                              p_repr = patch_nml._f90repr(patch_values[p_idx])
                              p_idx += 1
                              self._update_tokens(override=p_repr)
                              if isinstance(p_val, complex):
                                  # Skip over the complex content
                                  # NOTE: Assumes input and patch are complex
                                  self._update_tokens(write_token=False)
                                  self._update_tokens(write_token=False)
                                  self._update_tokens(write_token=False)
                                  self._update_tokens(write_token=False)
                          else:
                              # Skip any values beyond the patch size
                              skip = (p_idx >= len(patch_values))
                              self._update_tokens(patch_skip=skip)
                      else:
                          self._update_tokens()
  
          if patch_values:
              v_values = patch_values
  
          if not v_idx:
              v_values = delist(v_values)
  
          return v_name, v_values