| 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 |