(root)/
Python-3.12.0/
Tools/
c-analyzer/
table-file.py
       1  
       2  KINDS = [
       3      'section-major',
       4      'section-minor',
       5      'section-group',
       6      'row',
       7  ]
       8  
       9  
      10  def iter_clean_lines(lines):
      11      lines = iter(lines)
      12      for rawline in lines:
      13          line = rawline.strip()
      14          if line.startswith('#') and not rawline.startswith('##'):
      15              continue
      16          yield line, rawline
      17  
      18  
      19  def parse_table_lines(lines):
      20      lines = iter_clean_lines(lines)
      21  
      22      group = None
      23      prev = ''
      24      for line, rawline in lines:
      25          if line.startswith('## '):
      26              assert not rawline.startswith(' '), (line, rawline)
      27              if group:
      28                  assert prev, (line, rawline)
      29                  kind, after, _ = group
      30                  assert kind and kind != 'section-group', (group, line, rawline)
      31                  assert after is not None, (group, line, rawline)
      32              else:
      33                  assert not prev, (prev, line, rawline)
      34                  kind, after = group = ('section-group', None)
      35              title = line[3:].lstrip()
      36              assert title, (line, rawline)
      37              if after is not None:
      38                  try:
      39                      line, rawline = next(lines)
      40                  except StopIteration:
      41                      line = None
      42                  if line != after:
      43                      raise NotImplementedError((group, line, rawline))
      44              yield kind, title
      45              group = None
      46          elif group:
      47              raise NotImplementedError((group, line, rawline))
      48          elif line.startswith('##---'):
      49              assert line.rstrip('-') == '##', (line, rawline)
      50              group = ('section-minor', '', line)
      51          elif line.startswith('#####'):
      52              assert not line.strip('#'), (line, rawline)
      53              group = ('section-major', '', line)
      54          elif line:
      55              yield 'row', line
      56          prev = line
      57  
      58  
      59  def iter_sections(lines):
      60      header = None
      61      section = []
      62      for kind, value in parse_table_lines(lines):
      63          if kind == 'row':
      64              if not section:
      65                  if header is None:
      66                      header = value
      67                      continue
      68                  raise NotImplementedError(repr(value))
      69              yield tuple(section), value
      70          else:
      71              if header is None:
      72                  header = False
      73              start = KINDS.index(kind)
      74              section[start:] = [value]
      75  
      76  
      77  def collect_sections(lines):
      78      sections = {}
      79      for section, row in iter_sections(lines):
      80          if section not in sections:
      81              sections[section] = [row]
      82          else:
      83              sections[section].append(row)
      84      return sections
      85  
      86  
      87  def collate_sections(lines):
      88      collated = {}
      89      for section, rows in collect_sections(lines).items():
      90          parent = collated
      91          current = ()
      92          for name in section:
      93              current += (name,)
      94              try:
      95                  child, secrows, totalrows = parent[name]
      96              except KeyError:
      97                  child = {}
      98                  secrows = []
      99                  totalrows = []
     100                  parent[name] = (child, secrows, totalrows)
     101              parent = child
     102              if current == section:
     103                  secrows.extend(rows)
     104              totalrows.extend(rows)
     105      return collated
     106  
     107  
     108  #############################
     109  # the commands
     110  
     111  def cmd_count_by_section(lines):
     112      div = ' ' + '-' * 50
     113      total = 0
     114      def render_tree(root, depth=0):
     115          nonlocal total
     116          indent = '    ' * depth
     117          for name, data in root.items():
     118              subroot, rows, totalrows = data
     119              sectotal = f'({len(totalrows)})' if totalrows != rows else ''
     120              count = len(rows) if rows else ''
     121              if depth == 0:
     122                  yield div
     123              yield f'{sectotal:>7} {count:>4}  {indent}{name}'
     124              yield from render_tree(subroot, depth+1)
     125              total += len(rows)
     126      sections = collate_sections(lines)
     127      yield from render_tree(sections)
     128      yield div
     129      yield f'(total: {total})'
     130  
     131  
     132  #############################
     133  # the script
     134  
     135  def parse_args(argv=None, prog=None):
     136      import argparse
     137      parser = argparse.ArgumentParser(prog=prog)
     138      parser.add_argument('filename')
     139  
     140      args = parser.parse_args(argv)
     141      ns = vars(args)
     142  
     143      return ns
     144  
     145  
     146  def main(filename):
     147      with open(filename) as infile:
     148          for line in cmd_count_by_section(infile):
     149              print(line)
     150  
     151  
     152  if __name__ == '__main__':
     153      kwargs = parse_args()
     154      main(**kwargs)