Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
allendowney
GitHub Repository: allendowney/cpython
Path: blob/main/Tools/c-analyzer/c_parser/__main__.py
12 views
1
import logging
2
import sys
3
4
from c_common.scriptutil import (
5
add_verbosity_cli,
6
add_traceback_cli,
7
add_kind_filtering_cli,
8
add_files_cli,
9
add_commands_cli,
10
process_args_by_key,
11
configure_logger,
12
get_prog,
13
main_for_filenames,
14
)
15
from .preprocessor.__main__ import (
16
add_common_cli as add_preprocessor_cli,
17
)
18
from .info import KIND
19
from . import parse_file as _iter_parsed
20
21
22
logger = logging.getLogger(__name__)
23
24
25
def _format_vartype(vartype):
26
if isinstance(vartype, str):
27
return vartype
28
29
data = vartype
30
try:
31
vartype = data['vartype']
32
except KeyError:
33
storage, typequal, typespec, abstract = vartype.values()
34
else:
35
storage = data.get('storage')
36
if storage:
37
_, typequal, typespec, abstract = vartype.values()
38
else:
39
storage, typequal, typespec, abstract = vartype.values()
40
41
vartype = f'{typespec} {abstract}'
42
if typequal:
43
vartype = f'{typequal} {vartype}'
44
if storage:
45
vartype = f'{storage} {vartype}'
46
return vartype
47
48
49
def _get_preprocessor(filename, **kwargs):
50
return get_processor(filename,
51
log_err=print,
52
**kwargs
53
)
54
55
56
#######################################
57
# the formats
58
59
def fmt_raw(filename, item, *, showfwd=None):
60
yield str(tuple(item))
61
62
63
def fmt_summary(filename, item, *, showfwd=None):
64
if item.filename != filename:
65
yield f'> {item.filename}'
66
67
if showfwd is None:
68
LINE = ' {lno:>5} {kind:10} {funcname:40} {fwd:1} {name:40} {data}'
69
else:
70
LINE = ' {lno:>5} {kind:10} {funcname:40} {name:40} {data}'
71
lno = kind = funcname = fwd = name = data = ''
72
MIN_LINE = len(LINE.format(**locals()))
73
74
fileinfo, kind, funcname, name, data = item
75
lno = fileinfo.lno if fileinfo and fileinfo.lno >= 0 else ''
76
funcname = funcname or ' --'
77
name = name or ' --'
78
isforward = False
79
if kind is KIND.FUNCTION:
80
storage, inline, params, returntype, isforward = data.values()
81
returntype = _format_vartype(returntype)
82
data = returntype + params
83
if inline:
84
data = f'inline {data}'
85
if storage:
86
data = f'{storage} {data}'
87
elif kind is KIND.VARIABLE:
88
data = _format_vartype(data)
89
elif kind is KIND.STRUCT or kind is KIND.UNION:
90
if data is None:
91
isforward = True
92
else:
93
fields = data
94
data = f'({len(data)}) {{ '
95
indent = ',\n' + ' ' * (MIN_LINE + len(data))
96
data += ', '.join(f.name for f in fields[:5])
97
fields = fields[5:]
98
while fields:
99
data = f'{data}{indent}{", ".join(f.name for f in fields[:5])}'
100
fields = fields[5:]
101
data += ' }'
102
elif kind is KIND.ENUM:
103
if data is None:
104
isforward = True
105
else:
106
names = [d if isinstance(d, str) else d.name
107
for d in data]
108
data = f'({len(data)}) {{ '
109
indent = ',\n' + ' ' * (MIN_LINE + len(data))
110
data += ', '.join(names[:5])
111
names = names[5:]
112
while names:
113
data = f'{data}{indent}{", ".join(names[:5])}'
114
names = names[5:]
115
data += ' }'
116
elif kind is KIND.TYPEDEF:
117
data = f'typedef {data}'
118
elif kind == KIND.STATEMENT:
119
pass
120
else:
121
raise NotImplementedError(item)
122
if isforward:
123
fwd = '*'
124
if not showfwd and showfwd is not None:
125
return
126
elif showfwd:
127
return
128
kind = kind.value
129
yield LINE.format(**locals())
130
131
132
def fmt_full(filename, item, *, showfwd=None):
133
raise NotImplementedError
134
135
136
FORMATS = {
137
'raw': fmt_raw,
138
'summary': fmt_summary,
139
'full': fmt_full,
140
}
141
142
143
def add_output_cli(parser):
144
parser.add_argument('--format', dest='fmt', default='summary', choices=tuple(FORMATS))
145
parser.add_argument('--showfwd', action='store_true', default=None)
146
parser.add_argument('--no-showfwd', dest='showfwd', action='store_false', default=None)
147
148
def process_args(args, *, argv=None):
149
pass
150
return process_args
151
152
153
#######################################
154
# the commands
155
156
def _cli_parse(parser, excluded=None, **prepr_kwargs):
157
process_output = add_output_cli(parser)
158
process_kinds = add_kind_filtering_cli(parser)
159
process_preprocessor = add_preprocessor_cli(parser, **prepr_kwargs)
160
process_files = add_files_cli(parser, excluded=excluded)
161
return [
162
process_output,
163
process_kinds,
164
process_preprocessor,
165
process_files,
166
]
167
168
169
def cmd_parse(filenames, *,
170
fmt='summary',
171
showfwd=None,
172
iter_filenames=None,
173
relroot=None,
174
**kwargs
175
):
176
if 'get_file_preprocessor' not in kwargs:
177
kwargs['get_file_preprocessor'] = _get_preprocessor()
178
try:
179
do_fmt = FORMATS[fmt]
180
except KeyError:
181
raise ValueError(f'unsupported fmt {fmt!r}')
182
for filename, relfile in main_for_filenames(filenames, iter_filenames, relroot):
183
for item in _iter_parsed(filename, **kwargs):
184
item = item.fix_filename(relroot, fixroot=False, normalize=False)
185
for line in do_fmt(relfile, item, showfwd=showfwd):
186
print(line)
187
188
189
def _cli_data(parser):
190
...
191
192
return []
193
194
195
def cmd_data(filenames,
196
**kwargs
197
):
198
# XXX
199
raise NotImplementedError
200
201
202
COMMANDS = {
203
'parse': (
204
'parse the given C source & header files',
205
[_cli_parse],
206
cmd_parse,
207
),
208
'data': (
209
'check/manage local data (e.g. excludes, macros)',
210
[_cli_data],
211
cmd_data,
212
),
213
}
214
215
216
#######################################
217
# the script
218
219
def parse_args(argv=sys.argv[1:], prog=sys.argv[0], *, subset='parse'):
220
import argparse
221
parser = argparse.ArgumentParser(
222
prog=prog or get_prog,
223
)
224
225
processors = add_commands_cli(
226
parser,
227
commands={k: v[1] for k, v in COMMANDS.items()},
228
commonspecs=[
229
add_verbosity_cli,
230
add_traceback_cli,
231
],
232
subset=subset,
233
)
234
235
args = parser.parse_args(argv)
236
ns = vars(args)
237
238
cmd = ns.pop('cmd')
239
240
verbosity, traceback_cm = process_args_by_key(
241
args,
242
argv,
243
processors[cmd],
244
['verbosity', 'traceback_cm'],
245
)
246
247
return cmd, ns, verbosity, traceback_cm
248
249
250
def main(cmd, cmd_kwargs):
251
try:
252
run_cmd = COMMANDS[cmd][0]
253
except KeyError:
254
raise ValueError(f'unsupported cmd {cmd!r}')
255
run_cmd(**cmd_kwargs)
256
257
258
if __name__ == '__main__':
259
cmd, cmd_kwargs, verbosity, traceback_cm = parse_args()
260
configure_logger(verbosity)
261
with traceback_cm:
262
main(cmd, cmd_kwargs)
263
264