(root)/
Python-3.12.0/
Tools/
c-analyzer/
c_parser/
__main__.py
       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)