summaryrefslogtreecommitdiffstats
path: root/Tools/c-analyzer/table-file.py
blob: ba3dd29055e03d5f339522bb2040d460e315b451 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133

def iter_clean_lines(lines):
    lines = iter(lines)
    for line in lines:
        line = line.strip()
        if line.startswith('# XXX'):
            continue
        yield line


def parse_table_lines(lines):
    lines = iter_clean_lines(lines)

    for line in lines:
        if line.startswith(('####', '#----')):
            kind = 0 if line[1] == '#' else 1
            try:
                line = next(lines).strip()
            except StopIteration:
                line = ''
            if not line.startswith('# '):
                raise NotImplementedError(line)
            yield kind, line[2:].lstrip()
            continue

        maybe = None
        while line.startswith('#'):
            if line != '#' and line[1] == ' ':
                maybe = line[2:].lstrip()
            try:
                line = next(lines).strip()
            except StopIteration:
                return
            if not line:
                break
        else:
            if line:
                if maybe:
                    yield 2, maybe
                yield 'row', line


def iter_sections(lines):
    header = None
    section = []
    for kind, value in parse_table_lines(lines):
        if kind == 'row':
            if not section:
                if header is None:
                    header = value
                    continue
                raise NotImplementedError(value)
            yield tuple(section), value
        else:
            if header is None:
                header = False
            section[kind:] = [value]


def collect_sections(lines):
    sections = {}
    for section, row in iter_sections(lines):
        if section not in sections:
            sections[section] = [row]
        else:
            sections[section].append(row)
    return sections


def collate_sections(lines):
    collated = {}
    for section, rows in collect_sections(lines).items():
        parent = collated
        current = ()
        for name in section:
            current += (name,)
            try:
                child, secrows, totalrows = parent[name]
            except KeyError:
                child = {}
                secrows = []
                totalrows = []
                parent[name] = (child, secrows, totalrows)
            parent = child
            if current == section:
                secrows.extend(rows)
            totalrows.extend(rows)
    return collated


#############################
# the commands

def cmd_count_by_section(lines):
    total = 0
    def render_tree(root, depth=0):
        nonlocal total
        indent = '    ' * depth
        for name, data in root.items():
            subroot, rows, totalrows = data
            sectotal = f'({len(totalrows)})' if totalrows != rows else ''
            count = len(rows) if rows else ''
            yield f'{sectotal:>7} {count:>4}  {indent}{name}'
            yield from render_tree(subroot, depth+1)
            total += len(rows)
    sections = collate_sections(lines)
    yield from render_tree(sections)
    yield f'(total: {total})'


#############################
# the script

def parse_args(argv=None, prog=None):
    import argparse
    parser = argparse.ArgumentParser(prog=prog)
    parser.add_argument('filename')

    args = parser.parse_args(argv)
    ns = vars(args)

    return ns


def main(filename):
    with open(filename) as infile:
        for line in cmd_count_by_section(infile):
            print(line)


if __name__ == '__main__':
    kwargs = parse_args()
    main(**kwargs)