summaryrefslogtreecommitdiffstats
path: root/Tools/c-analyzer/c_parser/parser/_compound_decl_body.py
blob: eb5bc67607bb19a955d67aba6cd5f22a06fa715d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
import re

from ._regexes import (
    STRUCT_MEMBER_DECL as _STRUCT_MEMBER_DECL,
    ENUM_MEMBER_DECL as _ENUM_MEMBER_DECL,
)
from ._common import (
    log_match,
    parse_var_decl,
    set_capture_groups,
)


#############################
# struct / union

STRUCT_MEMBER_DECL = set_capture_groups(_STRUCT_MEMBER_DECL, (
    'COMPOUND_TYPE_KIND',
    'COMPOUND_TYPE_NAME',
    'SPECIFIER_QUALIFIER',
    'DECLARATOR',
    'SIZE',
    'ENDING',
    'CLOSE',
))
STRUCT_MEMBER_RE = re.compile(rf'^ \s* {STRUCT_MEMBER_DECL}', re.VERBOSE)


def parse_struct_body(source, anon_name, parent):
    done = False
    while not done:
        done = True
        for srcinfo in source:
            m = STRUCT_MEMBER_RE.match(srcinfo.text)
            if m:
                break
        else:
            # We ran out of lines.
            if srcinfo is not None:
                srcinfo.done()
            return
        for item in _parse_struct_next(m, srcinfo, anon_name, parent):
            if callable(item):
                parse_body = item
                yield from parse_body(source)
            else:
                yield item
            done = False


def _parse_struct_next(m, srcinfo, anon_name, parent):
    (inline_kind, inline_name,
     qualspec, declarator,
     size,
     ending,
     close,
     ) = m.groups()
    remainder = srcinfo.text[m.end():]

    if close:
        log_match('compound close', m)
        srcinfo.advance(remainder)

    elif inline_kind:
        log_match('compound inline', m)
        kind = inline_kind
        name = inline_name or anon_name('inline-')
        # Immediately emit a forward declaration.
        yield srcinfo.resolve(kind, name=name, data=None)

        # un-inline the decl.  Note that it might not actually be inline.
        # We handle the case in the "maybe_inline_actual" branch.
        srcinfo.nest(
            remainder,
            f'{kind} {name}',
        )
        def parse_body(source):
            _parse_body = DECL_BODY_PARSERS[kind]

            data = []  # members
            ident = f'{kind} {name}'
            for item in _parse_body(source, anon_name, ident):
                if item.kind == 'field':
                    data.append(item)
                else:
                    yield item
            # XXX Should "parent" really be None for inline type decls?
            yield srcinfo.resolve(kind, data, name, parent=None)

            srcinfo.resume()
        yield parse_body

    else:
        # not inline (member)
        log_match('compound member', m)
        if qualspec:
            _, name, data = parse_var_decl(f'{qualspec} {declarator}')
            if not name:
                name = anon_name('struct-field-')
            if size:
#                data = (data, size)
                data['size'] = int(size)
        else:
            # This shouldn't happen (we expect each field to have a name).
            raise NotImplementedError
            name = sized_name or anon_name('struct-field-')
            data = int(size)

        yield srcinfo.resolve('field', data, name, parent)  # XXX Restart?
        if ending == ',':
            remainder = rf'{qualspec} {remainder}'
        srcinfo.advance(remainder)


#############################
# enum

ENUM_MEMBER_DECL = set_capture_groups(_ENUM_MEMBER_DECL, (
    'CLOSE',
    'NAME',
    'INIT',
    'ENDING',
))
ENUM_MEMBER_RE = re.compile(rf'{ENUM_MEMBER_DECL}', re.VERBOSE)


def parse_enum_body(source, _anon_name, _parent):
    ending = None
    while ending != '}':
        for srcinfo in source:
            m = ENUM_MEMBER_RE.match(srcinfo.text)
            if m:
                break
        else:
            # We ran out of lines.
            if srcinfo is not None:
                srcinfo.done()
            return
        remainder = srcinfo.text[m.end():]

        (close,
         name, init, ending,
         ) = m.groups()
        if close:
            ending = '}'
        else:
            data = init
            yield srcinfo.resolve('field', data, name, _parent)
        srcinfo.advance(remainder)


#############################

DECL_BODY_PARSERS = {
    'struct': parse_struct_body,
    'union': parse_struct_body,
    'enum': parse_enum_body,
}