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