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