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)