| import re |
| |
| from ._regexes import ( |
| _ind, |
| STRING_LITERAL, |
| VAR_DECL as _VAR_DECL, |
| ) |
| |
| |
| def log_match(group, m, depth_before=None, depth_after=None): |
| from . import _logger |
| |
| if m is not None: |
| text = m.group(0) |
| if text.startswith(('(', ')')) or text.endswith(('(', ')')): |
| _logger.debug(f'matched <{group}> ({text!r})') |
| else: |
| _logger.debug(f'matched <{group}> ({text})') |
| |
| elif depth_before is not None or depth_after is not None: |
| if depth_before is None: |
| depth_before = '???' |
| elif depth_after is None: |
| depth_after = '???' |
| _logger.log(1, f'depth: %s -> %s', depth_before, depth_after) |
| |
| else: |
| raise NotImplementedError('this should not have been hit') |
| |
| |
| ############################# |
| # regex utils |
| |
| def set_capture_group(pattern, group, *, strict=True): |
| old = f'(?: # <{group}>' |
| if strict and f'(?: # <{group}>' not in pattern: |
| raise ValueError(f'{old!r} not found in pattern') |
| return pattern.replace(old, f'( # <{group}>', 1) |
| |
| |
| def set_capture_groups(pattern, groups, *, strict=True): |
| for group in groups: |
| pattern = set_capture_group(pattern, group, strict=strict) |
| return pattern |
| |
| |
| ############################# |
| # syntax-related utils |
| |
| _PAREN_RE = re.compile(rf''' |
| (?: |
| (?: |
| [^'"()]* |
| {_ind(STRING_LITERAL, 3)} |
| )* |
| [^'"()]* |
| (?: |
| ( [(] ) |
| | |
| ( [)] ) |
| ) |
| ) |
| ''', re.VERBOSE) |
| |
| |
| def match_paren(text, depth=0): |
| pos = 0 |
| while (m := _PAREN_RE.match(text, pos)): |
| pos = m.end() |
| _open, _close = m.groups() |
| if _open: |
| depth += 1 |
| else: # _close |
| depth -= 1 |
| if depth == 0: |
| return pos |
| else: |
| raise ValueError(f'could not find matching parens for {text!r}') |
| |
| |
| VAR_DECL = set_capture_groups(_VAR_DECL, ( |
| 'STORAGE', |
| 'TYPE_QUAL', |
| 'TYPE_SPEC', |
| 'DECLARATOR', |
| 'IDENTIFIER', |
| 'WRAPPED_IDENTIFIER', |
| 'FUNC_IDENTIFIER', |
| )) |
| |
| |
| def parse_var_decl(decl): |
| m = re.match(VAR_DECL, decl, re.VERBOSE) |
| (storage, typequal, typespec, declarator, |
| name, |
| wrappedname, |
| funcptrname, |
| ) = m.groups() |
| if name: |
| kind = 'simple' |
| elif wrappedname: |
| kind = 'wrapped' |
| name = wrappedname |
| elif funcptrname: |
| kind = 'funcptr' |
| name = funcptrname |
| else: |
| raise NotImplementedError |
| abstract = declarator.replace(name, '') |
| vartype = { |
| 'storage': storage, |
| 'typequal': typequal, |
| 'typespec': typespec, |
| 'abstract': abstract, |
| } |
| return (kind, name, vartype) |
| |
| |
| ############################# |
| # parser state utils |
| |
| # XXX Drop this or use it! |
| def iter_results(results): |
| if not results: |
| return |
| if callable(results): |
| results = results() |
| |
| for result, text in results(): |
| if result: |
| yield result, text |