import logging import os.path import sys from c_common.scriptutil import ( CLIArgSpec as Arg, add_verbosity_cli, add_traceback_cli, add_kind_filtering_cli, add_files_cli, add_commands_cli, process_args_by_key, configure_logger, get_prog, main_for_filenames, ) from .preprocessor import get_preprocessor from .preprocessor.__main__ import ( add_common_cli as add_preprocessor_cli, ) from .info import KIND from . import parse_file as _iter_parsed logger = logging.getLogger(__name__) def _format_vartype(vartype): if isinstance(vartype, str): return vartype data = vartype try: vartype = data['vartype'] except KeyError: storage, typequal, typespec, abstract = vartype.values() else: storage = data.get('storage') if storage: _, typequal, typespec, abstract = vartype.values() else: storage, typequal, typespec, abstract = vartype.values() vartype = f'{typespec} {abstract}' if typequal: vartype = f'{typequal} {vartype}' if storage: vartype = f'{storage} {vartype}' return vartype def _get_preprocessor(filename, **kwargs): return get_processor(filename, log_err=print, **kwargs ) ####################################### # the formats def fmt_raw(filename, item, *, showfwd=None): yield str(tuple(item)) def fmt_summary(filename, item, *, showfwd=None): if item.filename and item.filename != os.path.join('.', filename): yield f'> {item.filename}' if showfwd is None: LINE = ' {lno:>5} {kind:10} {funcname:40} {fwd:1} {name:40} {data}' else: LINE = ' {lno:>5} {kind:10} {funcname:40} {name:40} {data}' lno = kind = funcname = fwd = name = data = '' MIN_LINE = len(LINE.format(**locals())) fileinfo, kind, funcname, name, data = item lno = fileinfo.lno if fileinfo and fileinfo.lno >= 0 else '' funcname = funcname or ' --' name = name or ' --' isforward = False if kind is KIND.FUNCTION: storage, inline, params, returntype, isforward = data.values() returntype = _format_vartype(returntype) data = returntype + params if inline: data = f'inline {data}' if storage: data = f'{storage} {data}' elif kind is KIND.VARIABLE: data = _format_vartype(data) elif kind is KIND.STRUCT or kind is KIND.UNION: if data is None: isforward = True else: fields = data data = f'({len(data)}) {{ ' indent = ',\n' + ' ' * (MIN_LINE + len(data)) data += ', '.join(f.name for f in fields[:5]) fields = fields[5:] while fields: data = f'{data}{indent}{", ".join(f.name for f in fields[:5])}' fields = fields[5:] data += ' }' elif kind is KIND.ENUM: if data is None: isforward = True else: names = [d if isinstance(d, str) else d.name for d in data] data = f'({len(data)}) {{ ' indent = ',\n' + ' ' * (MIN_LINE + len(data)) data += ', '.join(names[:5]) names = names[5:] while names: data = f'{data}{indent}{", ".join(names[:5])}' names = names[5:] data += ' }' elif kind is KIND.TYPEDEF: data = f'typedef {data}' elif kind == KIND.STATEMENT: pass else: raise NotImplementedError(item) if isforward: fwd = '*' if not showfwd and showfwd is not None: return elif showfwd: return kind = kind.value yield LINE.format(**locals()) def fmt_full(filename, item, *, showfwd=None): raise NotImplementedError FORMATS = { 'raw': fmt_raw, 'summary': fmt_summary, 'full': fmt_full, } def add_output_cli(parser): parser.add_argument('--format', dest='fmt', default='summary', choices=tuple(FORMATS)) parser.add_argument('--showfwd', action='store_true', default=None) parser.add_argument('--no-showfwd', dest='showfwd', action='store_false', default=None) def process_args(args): pass return process_args ####################################### # the commands def _cli_parse(parser, excluded=None, **prepr_kwargs): process_output = add_output_cli(parser) process_kinds = add_kind_filtering_cli(parser) process_preprocessor = add_preprocessor_cli(parser, **prepr_kwargs) process_files = add_files_cli(parser, excluded=excluded) return [ process_output, process_kinds, process_preprocessor, process_files, ] def cmd_parse(filenames, *, fmt='summary', showfwd=None, iter_filenames=None, **kwargs ): if 'get_file_preprocessor' not in kwargs: kwargs['get_file_preprocessor'] = _get_preprocessor() try: do_fmt = FORMATS[fmt] except KeyError: raise ValueError(f'unsupported fmt {fmt!r}') for filename in main_for_filenames(filenames, iter_filenames): for item in _iter_parsed(filename, **kwargs): for line in do_fmt(filename, item, showfwd=showfwd): print(line) def _cli_data(parser): ... return [] def cmd_data(filenames, **kwargs ): # XXX raise NotImplementedError COMMANDS = { 'parse': ( 'parse the given C source & header files', [_cli_parse], cmd_parse, ), 'data': ( 'check/manage local data (e.g. excludes, macros)', [_cli_data], cmd_data, ), } ####################################### # the script def parse_args(argv=sys.argv[1:], prog=sys.argv[0], *, subset='parse'): import argparse parser = argparse.ArgumentParser( prog=prog or get_prog, ) processors = add_commands_cli( parser, commands={k: v[1] for k, v in COMMANDS.items()}, commonspecs=[ add_verbosity_cli, add_traceback_cli, ], subset=subset, ) args = parser.parse_args(argv) ns = vars(args) cmd = ns.pop('cmd') verbosity, traceback_cm = process_args_by_key( args, processors[cmd], ['verbosity', 'traceback_cm'], ) return cmd, ns, verbosity, traceback_cm def main(cmd, cmd_kwargs): try: run_cmd = COMMANDS[cmd][0] except KeyError: raise ValueError(f'unsupported cmd {cmd!r}') run_cmd(**cmd_kwargs) if __name__ == '__main__': cmd, cmd_kwargs, verbosity, traceback_cm = parse_args() configure_logger(verbosity) with traceback_cm: main(cmd, cmd_kwargs)