1  import re
       2  
       3  from ._regexes import (
       4      _ind,
       5      STRING_LITERAL,
       6      VAR_DECL as _VAR_DECL,
       7  )
       8  
       9  
      10  def log_match(group, m):
      11      from . import _logger
      12      _logger.debug(f'matched <{group}> ({m.group(0)})')
      13  
      14  
      15  #############################
      16  # regex utils
      17  
      18  def set_capture_group(pattern, group, *, strict=True):
      19      old = f'(?:  # <{group}>'
      20      if strict and f'(?:  # <{group}>' not in pattern:
      21          raise ValueError(f'{old!r} not found in pattern')
      22      return pattern.replace(old, f'(  # <{group}>', 1)
      23  
      24  
      25  def set_capture_groups(pattern, groups, *, strict=True):
      26      for group in groups:
      27          pattern = set_capture_group(pattern, group, strict=strict)
      28      return pattern
      29  
      30  
      31  #############################
      32  # syntax-related utils
      33  
      34  _PAREN_RE = re.compile(rf'''
      35      (?:
      36          (?:
      37              [^'"()]*
      38              {_ind(STRING_LITERAL, 3)}
      39           )*
      40          [^'"()]*
      41          (?:
      42              ( [(] )
      43              |
      44              ( [)] )
      45           )
      46       )
      47      ''', re.VERBOSE)
      48  
      49  
      50  def match_paren(text, depth=0):
      51      pos = 0
      52      while (m := _PAREN_RE.match(text, pos)):
      53          pos = m.end()
      54          _open, _close = m.groups()
      55          if _open:
      56              depth += 1
      57          else:  # _close
      58              depth -= 1
      59              if depth == 0:
      60                  return pos
      61      else:
      62          raise ValueError(f'could not find matching parens for {text!r}')
      63  
      64  
      65  VAR_DECL = set_capture_groups(_VAR_DECL, (
      66      'STORAGE',
      67      'TYPE_QUAL',
      68      'TYPE_SPEC',
      69      'DECLARATOR',
      70      'IDENTIFIER',
      71      'WRAPPED_IDENTIFIER',
      72      'FUNC_IDENTIFIER',
      73  ))
      74  
      75  
      76  def parse_var_decl(decl):
      77      m = re.match(VAR_DECL, decl, re.VERBOSE)
      78      (storage, typequal, typespec, declarator,
      79       name,
      80       wrappedname,
      81       funcptrname,
      82       ) = m.groups()
      83      if name:
      84          kind = 'simple'
      85      elif wrappedname:
      86          kind = 'wrapped'
      87          name = wrappedname
      88      elif funcptrname:
      89          kind = 'funcptr'
      90          name = funcptrname
      91      else:
      92          raise NotImplementedError
      93      abstract = declarator.replace(name, '')
      94      vartype = {
      95          'storage': storage,
      96          'typequal': typequal,
      97          'typespec': typespec,
      98          'abstract': abstract,
      99      }
     100      return (kind, name, vartype)
     101  
     102  
     103  #############################
     104  # parser state utils
     105  
     106  # XXX Drop this or use it!
     107  def iter_results(results):
     108      if not results:
     109          return
     110      if callable(results):
     111          results = results()
     112  
     113      for result, text in results():
     114          if result:
     115              yield result, text