def parse(tokens, rules, node_fn, postprocess_fn, verbose=False):
  """Run bottom up parser on QCFG target using target CFG.

  Args:
    tokens: List of strings for input.
    rules: List of TargetCfgRule instances.
    node_fn: Function with input arguments (span_begin, span_end, rule,
      children) and returns a list of "node".
    postprocess_fn: Function from a list of "nodes" to "nodes".
    verbose: Print debug output if True.

  Returns:
    A List of "node" objects for completed parses.
  """
  if verbose:
    print("tokens: %s" % (tokens,))
    print("rules:")
    for rule in rules:
      print(str(rule))
  terminals = [
      token for token in tokens
      if not token.startswith(qcfg_rule.NON_TERMINAL_PREFIX)
  ]

  # Convert rules.
  converter = cfg_converter.CFGRuleConverter()
  parser_rules = []
  idx_to_rule = {}
  rule_idx = 0
  for rule in rules:
    parser_rule = converter.convert_to_cfg_rule(
        lhs=rule.lhs,
        rhs=rule.rhs.split(" "),
        rule_idx=rule_idx,
        nonterminal_prefix=target_grammar.NON_TERMINAL_PREFIX,
        allowed_terminals=set(terminals))
    if parser_rule:
      parser_rules.append(parser_rule)
      idx_to_rule[rule_idx] = rule
      rule_idx += 1

  # Add rules for every target nonterminal and QCFG nonterminal
  target_nts = set(converter.nonterminals_to_ids.keys())
  qcfg_nts = set(qcfg_rule.get_nts(tokens))
  for target_nt in target_nts:
    for qcfg_nt in qcfg_nts:
      rule = target_grammar.TargetCfgRule(target_nt, _convert_qcfg_nt(qcfg_nt))
      parser_rule = converter.convert_to_cfg_rule(
          lhs=rule.lhs,
          rhs=rule.rhs.split(" "),
          rule_idx=rule_idx,
          nonterminal_prefix=target_grammar.NON_TERMINAL_PREFIX)
      parser_rules.append(parser_rule)
      idx_to_rule[rule_idx] = rule
      rule_idx += 1

  input_symbols = []
  for token in tokens:
    if qcfg_rule.is_nt(token):
      if token not in converter.nonterminals_to_ids:
        return []
      idx = converter.nonterminals_to_ids[token]
      input_symbols.append(cfg_rule.CFGSymbol(idx, cfg_rule.NON_TERMINAL))
    else:
      if token not in converter.terminals_to_ids:
        return []
      idx = converter.terminals_to_ids[token]
      input_symbols.append(cfg_rule.CFGSymbol(idx, cfg_rule.TERMINAL))

  # Wrap node_fn to pass original Rule instead of CFGRule.
  def populate_fn(span_begin, span_end, parser_rule, children):
    rule = idx_to_rule[parser_rule.idx]
    nodes = node_fn(span_begin, span_end, rule, children)
    return nodes

  nonterminals = set(converter.nonterminals_to_ids.values())
  if verbose:
    print("parser_rules: %s" % parser_rules)

  parses = cfg_parser.parse_symbols(
      input_symbols,
      parser_rules,
      nonterminals,
      nonterminals,
      populate_fn,
      postprocess_fn,
      max_single_nt_applications=0,
      verbose=verbose)
  return parses
Run bottom up parser on QCFG target using target CFG.

  Args:
    tokens: List of strings for input.
    rules: List of TargetCfgRule instances.
    node_fn: Function with input arguments (span_begin, span_end, rule,
      children) and returns a list of "node".
    postprocess_fn: Function from a list of "nodes" to "nodes".
    verbose: Print debug output if True.

  Returns:
    A List of "node" objects for completed parses.
  
language/compgen/csl/qcfg/qcfg_target_parser.py