(root)/
Python-3.11.7/
Tools/
c-analyzer/
c_parser/
parser/
_func_body.py
       1  import re
       2  
       3  from ._regexes import (
       4      LOCAL as _LOCAL,
       5      LOCAL_STATICS as _LOCAL_STATICS,
       6  )
       7  from ._common import (
       8      log_match,
       9      parse_var_decl,
      10      set_capture_groups,
      11      match_paren,
      12  )
      13  from ._compound_decl_body import DECL_BODY_PARSERS
      14  
      15  
      16  LOCAL = set_capture_groups(_LOCAL, (
      17      'EMPTY',
      18      'INLINE_LEADING',
      19      'INLINE_PRE',
      20      'INLINE_KIND',
      21      'INLINE_NAME',
      22      'STORAGE',
      23      'VAR_DECL',
      24      'VAR_INIT',
      25      'VAR_ENDING',
      26      'COMPOUND_BARE',
      27      'COMPOUND_LABELED',
      28      'COMPOUND_PAREN',
      29      'BLOCK_LEADING',
      30      'BLOCK_OPEN',
      31      'SIMPLE_STMT',
      32      'SIMPLE_ENDING',
      33      'BLOCK_CLOSE',
      34  ))
      35  LOCAL_RE = re.compile(rf'^ \s* {LOCAL}', re.VERBOSE)
      36  
      37  
      38  # Note that parse_function_body() still has trouble with a few files
      39  # in the CPython codebase.
      40  
      41  def parse_function_body(source, name, anon_name):
      42      # XXX
      43      raise NotImplementedError
      44  
      45  
      46  def parse_function_body(name, text, resolve, source, anon_name, parent):
      47      raise NotImplementedError
      48      # For now we do not worry about locals declared in for loop "headers".
      49      depth = 1;
      50      while depth > 0:
      51          m = LOCAL_RE.match(text)
      52          while not m:
      53              text, resolve = continue_text(source, text or '{', resolve)
      54              m = LOCAL_RE.match(text)
      55          text = text[m.end():]
      56          (
      57           empty,
      58           inline_leading, inline_pre, inline_kind, inline_name,
      59           storage, decl,
      60           var_init, var_ending,
      61           compound_bare, compound_labeled, compound_paren,
      62           block_leading, block_open,
      63           simple_stmt, simple_ending,
      64           block_close,
      65           ) = m.groups()
      66  
      67          if empty:
      68              log_match('', m)
      69              resolve(None, None, None, text)
      70              yield None, text
      71          elif inline_kind:
      72              log_match('', m)
      73              kind = inline_kind
      74              name = inline_name or anon_name('inline-')
      75              data = []  # members
      76              # We must set the internal "text" from _iter_source() to the
      77              # start of the inline compound body,
      78              # Note that this is effectively like a forward reference that
      79              # we do not emit.
      80              resolve(kind, None, name, text, None)
      81              _parse_body = DECL_BODY_PARSERS[kind]
      82              before = []
      83              ident = f'{kind} {name}'
      84              for member, inline, text in _parse_body(text, resolve, source, anon_name, ident):
      85                  if member:
      86                      data.append(member)
      87                  if inline:
      88                      yield from inline
      89              # un-inline the decl.  Note that it might not actually be inline.
      90              # We handle the case in the "maybe_inline_actual" branch.
      91              text = f'{inline_leading or ""} {inline_pre or ""} {kind} {name} {text}'
      92              # XXX Should "parent" really be None for inline type decls?
      93              yield resolve(kind, data, name, text, None), text
      94          elif block_close:
      95              log_match('', m)
      96              depth -= 1
      97              resolve(None, None, None, text)
      98              # XXX This isn't great.  Calling resolve() should have
      99              # cleared the closing bracket.  However, some code relies
     100              # on the yielded value instead of the resolved one.  That
     101              # needs to be fixed.
     102              yield None, text
     103          elif compound_bare:
     104              log_match('', m)
     105              yield resolve('statement', compound_bare, None, text, parent), text
     106          elif compound_labeled:
     107              log_match('', m)
     108              yield resolve('statement', compound_labeled, None, text, parent), text
     109          elif compound_paren:
     110              log_match('', m)
     111              try:
     112                  pos = match_paren(text)
     113              except ValueError:
     114                  text = f'{compound_paren} {text}'
     115                  #resolve(None, None, None, text)
     116                  text, resolve = continue_text(source, text, resolve)
     117                  yield None, text
     118              else:
     119                  head = text[:pos]
     120                  text = text[pos:]
     121                  if compound_paren == 'for':
     122                      # XXX Parse "head" as a compound statement.
     123                      stmt1, stmt2, stmt3 = head.split(';', 2)
     124                      data = {
     125                          'compound': compound_paren,
     126                          'statements': (stmt1, stmt2, stmt3),
     127                      }
     128                  else:
     129                      data = {
     130                          'compound': compound_paren,
     131                          'statement': head,
     132                      }
     133                  yield resolve('statement', data, None, text, parent), text
     134          elif block_open:
     135              log_match('', m)
     136              depth += 1
     137              if block_leading:
     138                  # An inline block: the last evaluated expression is used
     139                  # in place of the block.
     140                  # XXX Combine it with the remainder after the block close.
     141                  stmt = f'{block_open}{{<expr>}}...;'
     142                  yield resolve('statement', stmt, None, text, parent), text
     143              else:
     144                  resolve(None, None, None, text)
     145                  yield None, text
     146          elif simple_ending:
     147              log_match('', m)
     148              yield resolve('statement', simple_stmt, None, text, parent), text
     149          elif var_ending:
     150              log_match('', m)
     151              kind = 'variable'
     152              _, name, vartype = parse_var_decl(decl)
     153              data = {
     154                  'storage': storage,
     155                  'vartype': vartype,
     156              }
     157              after = ()
     158              if var_ending == ',':
     159                  # It was a multi-declaration, so queue up the next one.
     160                  _, qual, typespec, _ = vartype.values()
     161                  text = f'{storage or ""} {qual or ""} {typespec} {text}'
     162              yield resolve(kind, data, name, text, parent), text
     163              if var_init:
     164                  _data = f'{name} = {var_init.strip()}'
     165                  yield resolve('statement', _data, None, text, parent), text
     166          else:
     167              # This should be unreachable.
     168              raise NotImplementedError
     169  
     170  
     171  #############################
     172  # static local variables
     173  
     174  LOCAL_STATICS = set_capture_groups(_LOCAL_STATICS, (
     175      'INLINE_LEADING',
     176      'INLINE_PRE',
     177      'INLINE_KIND',
     178      'INLINE_NAME',
     179      'STATIC_DECL',
     180      'STATIC_INIT',
     181      'STATIC_ENDING',
     182      'DELIM_LEADING',
     183      'BLOCK_OPEN',
     184      'BLOCK_CLOSE',
     185      'STMT_END',
     186  ))
     187  LOCAL_STATICS_RE = re.compile(rf'^ \s* {LOCAL_STATICS}', re.VERBOSE)
     188  
     189  
     190  def parse_function_statics(source, func, anon_name):
     191      # For now we do not worry about locals declared in for loop "headers".
     192      depth = 1;
     193      while depth > 0:
     194          for srcinfo in source:
     195              m = LOCAL_STATICS_RE.match(srcinfo.text)
     196              if m:
     197                  break
     198          else:
     199              # We ran out of lines.
     200              if srcinfo is not None:
     201                  srcinfo.done()
     202              return
     203          for item, depth in _parse_next_local_static(m, srcinfo,
     204                                                      anon_name, func, depth):
     205              if callable(item):
     206                  parse_body = item
     207                  yield from parse_body(source)
     208              elif item is not None:
     209                  yield item
     210  
     211  
     212  def _parse_next_local_static(m, srcinfo, anon_name, func, depth):
     213      (inline_leading, inline_pre, inline_kind, inline_name,
     214       static_decl, static_init, static_ending,
     215       _delim_leading,
     216       block_open,
     217       block_close,
     218       stmt_end,
     219       ) = m.groups()
     220      remainder = srcinfo.text[m.end():]
     221  
     222      if inline_kind:
     223          log_match('func inline', m)
     224          kind = inline_kind
     225          name = inline_name or anon_name('inline-')
     226          # Immediately emit a forward declaration.
     227          yield srcinfo.resolve(kind, name=name, data=None), depth
     228  
     229          # un-inline the decl.  Note that it might not actually be inline.
     230          # We handle the case in the "maybe_inline_actual" branch.
     231          srcinfo.nest(
     232              remainder,
     233              f'{inline_leading or ""} {inline_pre or ""} {kind} {name}'
     234          )
     235          def parse_body(source):
     236              _parse_body = DECL_BODY_PARSERS[kind]
     237  
     238              data = []  # members
     239              ident = f'{kind} {name}'
     240              for item in _parse_body(source, anon_name, ident):
     241                  if item.kind == 'field':
     242                      data.append(item)
     243                  else:
     244                      yield item
     245              # XXX Should "parent" really be None for inline type decls?
     246              yield srcinfo.resolve(kind, data, name, parent=None)
     247  
     248              srcinfo.resume()
     249          yield parse_body, depth
     250  
     251      elif static_decl:
     252          log_match('local variable', m)
     253          _, name, data = parse_var_decl(static_decl)
     254  
     255          yield srcinfo.resolve('variable', data, name, parent=func), depth
     256  
     257          if static_init:
     258              srcinfo.advance(f'{name} {static_init} {remainder}')
     259          elif static_ending == ',':
     260              # It was a multi-declaration, so queue up the next one.
     261              _, qual, typespec, _ = data.values()
     262              srcinfo.advance(f'static {qual or ""} {typespec} {remainder}')
     263          else:
     264              srcinfo.advance('')
     265  
     266      else:
     267          log_match('func other', m)
     268          if block_open:
     269              depth += 1
     270          elif block_close:
     271              depth -= 1
     272          elif stmt_end:
     273              pass
     274          else:
     275              # This should be unreachable.
     276              raise NotImplementedError
     277          srcinfo.advance(remainder)
     278          yield None, depth