docs: def predict_and_complete(self, i, to_scan, columns, transitives):
          """The core Earley Predictor and Completer.
  
          At each stage of the input, we handling any completed items (things
          that matched on the last cycle) and use those to predict what should
          come next in the input stream. The completions and any predicted
          non-terminals are recursively processed until we reach a set of,
          which can be added to the scan list for the next scanner cycle."""
          # Held Completions (H in E.Scotts paper).
          node_cache = {}
          held_completions = {}
  
          column = columns[i]
          # R (items) = Ei (column.items)
          items = deque(column)
          while items:
              item = items.pop()    # remove an element, A say, from R
  
              ### The Earley completer
              if item.is_complete:   ### (item.s == string)
                  if item.node is None:
                      label = (item.s, item.start, i)
                      item.node = node_cache[label] if label in node_cache else node_cache.setdefault(label, SymbolNode(*label))
                      item.node.add_family(item.s, item.rule, item.start, None, None)
  
                  # create_leo_transitives(item.rule.origin, item.start)
  
                  ###R Joop Leo right recursion Completer
                  if item.rule.origin in transitives[item.start]:
                      transitive = transitives[item.start][item.s]
                      if transitive.previous in transitives[transitive.column]:
                          root_transitive = transitives[transitive.column][transitive.previous]
                      else:
                          root_transitive = transitive
  
                      new_item = Item(transitive.rule, transitive.ptr, transitive.start)
                      label = (root_transitive.s, root_transitive.start, i)
                      new_item.node = node_cache[label] if label in node_cache else node_cache.setdefault(label, SymbolNode(*label))
                      new_item.node.add_path(root_transitive, item.node)
                      if new_item.expect in self.TERMINALS:
                          # Add (B :: aC.B, h, y) to Q
                          to_scan.add(new_item)
                      elif new_item not in column:
                          # Add (B :: aC.B, h, y) to Ei and R
                          column.add(new_item)
                          items.append(new_item)
                  ###R Regular Earley completer
                  else:
                      # Empty has 0 length. If we complete an empty symbol in a particular
                      # parse step, we need to be able to use that same empty symbol to complete
                      # any predictions that result, that themselves require empty. Avoids
                      # infinite recursion on empty symbols.
                      # held_completions is 'H' in E.Scott's paper.
                      is_empty_item = item.start == i
                      if is_empty_item:
                          held_completions[item.rule.origin] = item.node
  
                      originators = [originator for originator in columns[item.start] if originator.expect is not None and originator.expect == item.s]
                      for originator in originators:
                          new_item = originator.advance()
                          label = (new_item.s, originator.start, i)
                          new_item.node = node_cache[label] if label in node_cache else node_cache.setdefault(label, SymbolNode(*label))
                          new_item.node.add_family(new_item.s, new_item.rule, i, originator.node, item.node)
                          if new_item.expect in self.TERMINALS:
                              # Add (B :: aC.B, h, y) to Q
                              to_scan.add(new_item)
                          elif new_item not in column:
                              # Add (B :: aC.B, h, y) to Ei and R
                              column.add(new_item)
                              items.append(new_item)
  
              ### The Earley predictor
              elif item.expect in self.NON_TERMINALS: ### (item.s == lr0)
                  new_items = []
                  for rule in self.predictions[item.expect]:
                      new_item = Item(rule, 0, i)
                      new_items.append(new_item)
  
                  # Process any held completions (H).
                  if item.expect in held_completions:
                      new_item = item.advance()
                      label = (new_item.s, item.start, i)
                      new_item.node = node_cache[label] if label in node_cache else node_cache.setdefault(label, SymbolNode(*label))
                      new_item.node.add_family(new_item.s, new_item.rule, new_item.start, item.node, held_completions[item.expect])
                      new_items.append(new_item)
  
                  for new_item in new_items:
                      if new_item.expect in self.TERMINALS:
                          to_scan.add(new_item)
                      elif new_item not in column:
                          column.add(new_item)
                          items.append(new_item)