1import os.path
2
3from c_common import fsutil
4import c_common.tables as _tables
5import c_parser.info as _info
6
7
8BASE_COLUMNS = [
9    'filename',
10    'funcname',
11    'name',
12    'kind',
13]
14END_COLUMNS = {
15    'parsed': 'data',
16    'decls': 'declaration',
17}
18
19
20def _get_columns(group, extra=None):
21    return BASE_COLUMNS + list(extra or ()) + [END_COLUMNS[group]]
22    #return [
23    #    *BASE_COLUMNS,
24    #    *extra or (),
25    #    END_COLUMNS[group],
26    #]
27
28
29#############################
30# high-level
31
32def read_parsed(infile):
33    # XXX Support other formats than TSV?
34    columns = _get_columns('parsed')
35    for row in _tables.read_table(infile, columns, sep='\t', fix='-'):
36        yield _info.ParsedItem.from_row(row, columns)
37
38
39def write_parsed(items, outfile):
40    # XXX Support other formats than TSV?
41    columns = _get_columns('parsed')
42    rows = (item.as_row(columns) for item in items)
43    _tables.write_table(outfile, columns, rows, sep='\t', fix='-')
44
45
46def read_decls(infile, fmt=None):
47    if fmt is None:
48        fmt = _get_format(infile)
49    read_all, _ = _get_format_handlers('decls', fmt)
50    for decl, _ in read_all(infile):
51        yield decl
52
53
54def write_decls(decls, outfile, fmt=None, *, backup=False):
55    if fmt is None:
56        fmt = _get_format(infile)
57    _, write_all = _get_format_handlers('decls', fmt)
58    write_all(decls, outfile, backup=backup)
59
60
61#############################
62# formats
63
64def _get_format(file, default='tsv'):
65    if isinstance(file, str):
66        filename = file
67    else:
68        filename = getattr(file, 'name', '')
69    _, ext = os.path.splitext(filename)
70    return ext[1:] if ext else default
71
72
73def _get_format_handlers(group, fmt):
74    # XXX Use a registry.
75    if group != 'decls':
76        raise NotImplementedError(group)
77    if fmt == 'tsv':
78        return (_iter_decls_tsv, _write_decls_tsv)
79    else:
80        raise NotImplementedError(fmt)
81
82
83# tsv
84
85def iter_decls_tsv(infile, extracolumns=None, relroot=fsutil.USE_CWD):
86    if relroot and relroot is not fsutil.USE_CWD:
87        relroot = os.path.abspath(relroot)
88    for info, extra in _iter_decls_tsv(infile, extracolumns):
89        decl = _info.Declaration.from_row(info)
90        decl = decl.fix_filename(relroot, formatted=False, fixroot=False)
91        yield decl, extra
92
93
94def write_decls_tsv(decls, outfile, extracolumns=None, *,
95                    relroot=fsutil.USE_CWD,
96                    **kwargs
97                    ):
98    if relroot and relroot is not fsutil.USE_CWD:
99        relroot = os.path.abspath(relroot)
100    decls = (d.fix_filename(relroot, fixroot=False) for d in decls)
101    # XXX Move the row rendering here.
102    _write_decls_tsv(decls, outfile, extracolumns, kwargs)
103
104
105def _iter_decls_tsv(infile, extracolumns=None):
106    columns = _get_columns('decls', extracolumns)
107    for row in _tables.read_table(infile, columns, sep='\t'):
108        if extracolumns:
109            declinfo = row[:4] + row[-1:]
110            extra = row[4:-1]
111        else:
112            declinfo = row
113            extra = None
114        # XXX Use something like tables.fix_row() here.
115        declinfo = [None if v == '-' else v
116                    for v in declinfo]
117        yield declinfo, extra
118
119
120def _write_decls_tsv(decls, outfile, extracolumns, kwargs):
121    columns = _get_columns('decls', extracolumns)
122    if extracolumns:
123        def render_decl(decl):
124            if type(row) is tuple:
125                decl, *extra = decl
126            else:
127                extra = ()
128            extra += ('???',) * (len(extraColumns) - len(extra))
129            *row, declaration = _render_known_row(decl)
130            row += extra + (declaration,)
131            return row
132    else:
133        render_decl = _render_known_decl
134    _tables.write_table(
135        outfile,
136        header='\t'.join(columns),
137        rows=(render_decl(d) for d in decls),
138        sep='\t',
139        **kwargs
140    )
141
142
143def _render_known_decl(decl, *,
144                       # These match BASE_COLUMNS + END_COLUMNS[group].
145                       _columns = 'filename parent name kind data'.split(),
146                       ):
147    if not isinstance(decl, _info.Declaration):
148        # e.g. Analyzed
149        decl = decl.decl
150    rowdata = decl.render_rowdata(_columns)
151    return [rowdata[c] or '-' for c in _columns]
152    # XXX
153    #return _tables.fix_row(rowdata[c] for c in columns)
154