17db96d56Sopenharmony_ci
27db96d56Sopenharmony_cidef iter_clean_lines(lines):
37db96d56Sopenharmony_ci    lines = iter(lines)
47db96d56Sopenharmony_ci    for line in lines:
57db96d56Sopenharmony_ci        line = line.strip()
67db96d56Sopenharmony_ci        if line.startswith('# XXX'):
77db96d56Sopenharmony_ci            continue
87db96d56Sopenharmony_ci        yield line
97db96d56Sopenharmony_ci
107db96d56Sopenharmony_ci
117db96d56Sopenharmony_cidef parse_table_lines(lines):
127db96d56Sopenharmony_ci    lines = iter_clean_lines(lines)
137db96d56Sopenharmony_ci
147db96d56Sopenharmony_ci    for line in lines:
157db96d56Sopenharmony_ci        if line.startswith(('####', '#----')):
167db96d56Sopenharmony_ci            kind = 0 if line[1] == '#' else 1
177db96d56Sopenharmony_ci            try:
187db96d56Sopenharmony_ci                line = next(lines).strip()
197db96d56Sopenharmony_ci            except StopIteration:
207db96d56Sopenharmony_ci                line = ''
217db96d56Sopenharmony_ci            if not line.startswith('# '):
227db96d56Sopenharmony_ci                raise NotImplementedError(line)
237db96d56Sopenharmony_ci            yield kind, line[2:].lstrip()
247db96d56Sopenharmony_ci            continue
257db96d56Sopenharmony_ci
267db96d56Sopenharmony_ci        maybe = None
277db96d56Sopenharmony_ci        while line.startswith('#'):
287db96d56Sopenharmony_ci            if line != '#' and line[1] == ' ':
297db96d56Sopenharmony_ci                maybe = line[2:].lstrip()
307db96d56Sopenharmony_ci            try:
317db96d56Sopenharmony_ci                line = next(lines).strip()
327db96d56Sopenharmony_ci            except StopIteration:
337db96d56Sopenharmony_ci                return
347db96d56Sopenharmony_ci            if not line:
357db96d56Sopenharmony_ci                break
367db96d56Sopenharmony_ci        else:
377db96d56Sopenharmony_ci            if line:
387db96d56Sopenharmony_ci                if maybe:
397db96d56Sopenharmony_ci                    yield 2, maybe
407db96d56Sopenharmony_ci                yield 'row', line
417db96d56Sopenharmony_ci
427db96d56Sopenharmony_ci
437db96d56Sopenharmony_cidef iter_sections(lines):
447db96d56Sopenharmony_ci    header = None
457db96d56Sopenharmony_ci    section = []
467db96d56Sopenharmony_ci    for kind, value in parse_table_lines(lines):
477db96d56Sopenharmony_ci        if kind == 'row':
487db96d56Sopenharmony_ci            if not section:
497db96d56Sopenharmony_ci                if header is None:
507db96d56Sopenharmony_ci                    header = value
517db96d56Sopenharmony_ci                    continue
527db96d56Sopenharmony_ci                raise NotImplementedError(value)
537db96d56Sopenharmony_ci            yield tuple(section), value
547db96d56Sopenharmony_ci        else:
557db96d56Sopenharmony_ci            if header is None:
567db96d56Sopenharmony_ci                header = False
577db96d56Sopenharmony_ci            section[kind:] = [value]
587db96d56Sopenharmony_ci
597db96d56Sopenharmony_ci
607db96d56Sopenharmony_cidef collect_sections(lines):
617db96d56Sopenharmony_ci    sections = {}
627db96d56Sopenharmony_ci    for section, row in iter_sections(lines):
637db96d56Sopenharmony_ci        if section not in sections:
647db96d56Sopenharmony_ci            sections[section] = [row]
657db96d56Sopenharmony_ci        else:
667db96d56Sopenharmony_ci            sections[section].append(row)
677db96d56Sopenharmony_ci    return sections
687db96d56Sopenharmony_ci
697db96d56Sopenharmony_ci
707db96d56Sopenharmony_cidef collate_sections(lines):
717db96d56Sopenharmony_ci    collated = {}
727db96d56Sopenharmony_ci    for section, rows in collect_sections(lines).items():
737db96d56Sopenharmony_ci        parent = collated
747db96d56Sopenharmony_ci        current = ()
757db96d56Sopenharmony_ci        for name in section:
767db96d56Sopenharmony_ci            current += (name,)
777db96d56Sopenharmony_ci            try:
787db96d56Sopenharmony_ci                child, secrows, totalrows = parent[name]
797db96d56Sopenharmony_ci            except KeyError:
807db96d56Sopenharmony_ci                child = {}
817db96d56Sopenharmony_ci                secrows = []
827db96d56Sopenharmony_ci                totalrows = []
837db96d56Sopenharmony_ci                parent[name] = (child, secrows, totalrows)
847db96d56Sopenharmony_ci            parent = child
857db96d56Sopenharmony_ci            if current == section:
867db96d56Sopenharmony_ci                secrows.extend(rows)
877db96d56Sopenharmony_ci            totalrows.extend(rows)
887db96d56Sopenharmony_ci    return collated
897db96d56Sopenharmony_ci
907db96d56Sopenharmony_ci
917db96d56Sopenharmony_ci#############################
927db96d56Sopenharmony_ci# the commands
937db96d56Sopenharmony_ci
947db96d56Sopenharmony_cidef cmd_count_by_section(lines):
957db96d56Sopenharmony_ci    div = ' ' + '-' * 50
967db96d56Sopenharmony_ci    total = 0
977db96d56Sopenharmony_ci    def render_tree(root, depth=0):
987db96d56Sopenharmony_ci        nonlocal total
997db96d56Sopenharmony_ci        indent = '    ' * depth
1007db96d56Sopenharmony_ci        for name, data in root.items():
1017db96d56Sopenharmony_ci            subroot, rows, totalrows = data
1027db96d56Sopenharmony_ci            sectotal = f'({len(totalrows)})' if totalrows != rows else ''
1037db96d56Sopenharmony_ci            count = len(rows) if rows else ''
1047db96d56Sopenharmony_ci            if depth == 0:
1057db96d56Sopenharmony_ci                yield div
1067db96d56Sopenharmony_ci            yield f'{sectotal:>7} {count:>4}  {indent}{name}'
1077db96d56Sopenharmony_ci            yield from render_tree(subroot, depth+1)
1087db96d56Sopenharmony_ci            total += len(rows)
1097db96d56Sopenharmony_ci    sections = collate_sections(lines)
1107db96d56Sopenharmony_ci    yield from render_tree(sections)
1117db96d56Sopenharmony_ci    yield div
1127db96d56Sopenharmony_ci    yield f'(total: {total})'
1137db96d56Sopenharmony_ci
1147db96d56Sopenharmony_ci
1157db96d56Sopenharmony_ci#############################
1167db96d56Sopenharmony_ci# the script
1177db96d56Sopenharmony_ci
1187db96d56Sopenharmony_cidef parse_args(argv=None, prog=None):
1197db96d56Sopenharmony_ci    import argparse
1207db96d56Sopenharmony_ci    parser = argparse.ArgumentParser(prog=prog)
1217db96d56Sopenharmony_ci    parser.add_argument('filename')
1227db96d56Sopenharmony_ci
1237db96d56Sopenharmony_ci    args = parser.parse_args(argv)
1247db96d56Sopenharmony_ci    ns = vars(args)
1257db96d56Sopenharmony_ci
1267db96d56Sopenharmony_ci    return ns
1277db96d56Sopenharmony_ci
1287db96d56Sopenharmony_ci
1297db96d56Sopenharmony_cidef main(filename):
1307db96d56Sopenharmony_ci    with open(filename) as infile:
1317db96d56Sopenharmony_ci        for line in cmd_count_by_section(infile):
1327db96d56Sopenharmony_ci            print(line)
1337db96d56Sopenharmony_ci
1347db96d56Sopenharmony_ci
1357db96d56Sopenharmony_ciif __name__ == '__main__':
1367db96d56Sopenharmony_ci    kwargs = parse_args()
1377db96d56Sopenharmony_ci    main(**kwargs)
138