diff options
Diffstat (limited to 'Tools/c-analyzer/c_parser/__main__.py')
-rw-r--r-- | Tools/c-analyzer/c_parser/__main__.py | 261 |
1 files changed, 261 insertions, 0 deletions
diff --git a/Tools/c-analyzer/c_parser/__main__.py b/Tools/c-analyzer/c_parser/__main__.py new file mode 100644 index 0000000000..1752a703f6 --- /dev/null +++ b/Tools/c-analyzer/c_parser/__main__.py @@ -0,0 +1,261 @@ +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) |