1# Copyright (c) 2016 Nuxi (https://nuxi.nl/) and contributors.
2#
3# SPDX-License-Identifier: BSD-2-Clause
4
5from .itf import read_itf, Node
6from .abi import *
7
8
9class AbiParser:
10    def parse_abi_file(self, file_name):
11        return self.parse_abi(read_itf(file_name))
12
13    def parse_abi(self, nodes):
14        abi = Abi()
15
16        abi.doc = self.pop_documentation(Node(text='ROOT', children=nodes))
17
18        for node in nodes:
19            decl = node.text.split()
20
21            doc = self.pop_documentation(node)
22
23            thing = None
24
25            if decl[0] in int_like_types:
26                t = self.parse_int_like_type(abi, decl, node.children)
27                abi.types[t.name] = t
28                thing = t
29
30            elif decl[0] == 'struct':
31                t = self.parse_struct(abi, decl, node.children)
32                abi.types[t.name] = t
33                thing = t
34
35            elif decl[0] == 'function':
36                t = self.parse_function(abi, decl, node.children)
37                abi.types[t.name] = t
38                thing = t
39
40            elif decl[0] == 'syscall':
41                s = self.parse_syscall(abi, decl, node.children)
42                abi.syscalls[s.name] = s
43                thing = s
44
45            else:
46                print('Invalid top level declaration: {}'.format(node.text))
47
48            thing.doc = doc
49
50        for type in abi.types.values():
51            type.used_by = {
52                t
53                for t in abi.types.values()
54                if type in getattr(t, 'dependencies', set())
55            }
56
57            type.used_by.update({
58                s
59                for s in abi.syscalls.values()
60                if type in getattr(s, 'dependencies', set())
61            })
62
63        return abi
64
65    def parse_int_like_type(self, abi, decl, children):
66        if len(decl) != 3:
67            raise Exception('Invalid {} declaration: {}'.format(
68                decl[0], ' '.join(decl)))
69
70        name = decl[2]
71        if name in abi.types:
72            raise Exception('Duplicate definition of {}'.format(name))
73
74        int_type = decl[1]
75        if int_type not in int_types:
76            raise Exception('Invalid int type: {}'.format(int_type))
77
78        values = []
79        attr = {}
80
81        for node in children:
82            value_decl = node.text.split()
83            if value_decl[0] == '@cprefix' and len(value_decl) <= 2:
84                self.__expect_no_children(node)
85                attr['cprefix'] = (value_decl[1]
86                                   if len(value_decl) == 2 else '')
87            elif len(value_decl) == 2:
88                v = SpecialValue(value_decl[1], int(value_decl[0], 0))
89                v.doc = self.pop_documentation(node)
90                self.__expect_no_children(node)
91                values.append(v)
92            else:
93                raise Exception('Invalid value: {}'.format(child.text))
94
95        return int_like_types[decl[0]](name, int_types[int_type], values,
96                                       **attr)
97
98    def parse_struct(self, abi, decl, children):
99        if len(decl) != 2:
100            raise Exception('Invalid struct declaration: {}'.format(
101                decl, ' '.join(decl)))
102
103        name = decl[1]
104        if name in abi.types:
105            raise Exception('Duplicate definition of {}'.format(name))
106
107        members = self.parse_struct_members(abi, children)
108
109        return StructType(name, members)
110
111    def parse_struct_members(self, abi, children):
112        members = []
113
114        for node in children:
115            mem_decl = node.text.split()
116            mem = None
117
118            if mem_decl[0] == 'variant' and len(mem_decl) == 2:
119                tag_member_name = mem_decl[1]
120                tag_member = None
121                for m in members:
122                    if m.name == tag_member_name:
123                        if (isinstance(m, SimpleStructMember)
124                                and (isinstance(m.type, EnumType)
125                                     or isinstance(m.type, AliasType))):
126                            tag_member = m
127                            break
128                        else:
129                            raise Exception(
130                                'Variant tag ({}) must be an enum or '
131                                'an alias type.'.format(m.name))
132                if tag_member is None:
133                    raise Exception('No such member to use as variant tag: '
134                                    '{}.'.format(tag_member_name))
135                mem = self.parse_variant(abi, tag_member, node.children)
136
137            elif mem_decl[0] in {'range', 'crange'}:
138                doc = self.pop_documentation(node)
139                self.__expect_no_children(node)
140                if len(mem_decl) < 3:
141                    raise Exception('Invalid range: {}'.format(node.text))
142                mem_type = self.parse_type(abi, mem_decl[1:-1])
143                mem_name = mem_decl[-1]
144                mem = RangeStructMember(mem_name, mem_decl[0] == 'crange',
145                                        mem_type)
146                mem.doc = doc
147                mem.raw_members[0].doc = doc
148
149            else:
150                mem_name = mem_decl[-1]
151                mem_type = self.parse_type(abi, mem_decl[:-1])
152                mem_vals = []
153                if isinstance(mem_type, IntLikeType):
154                    doc = self.pop_documentation(node, optional=True)
155                    for n in node.children:
156                        vname = n.text
157                        val = [v for v in mem_type.values if vname == v.name]
158                        if len(val) != 1:
159                            raise Exception(
160                                'Struct member type {} has no value {}'.format(
161                                    mem_type.name, vname))
162                        v = SpecialValue(val[0].name, val[0].value)
163                        v.doc = self.pop_documentation(n)
164                        self.__expect_no_children(n)
165                        mem_vals.append(v)
166                else:
167                    doc = self.pop_documentation(node)
168                    self.__expect_no_children(node)
169                mem = SimpleStructMember(mem_name, mem_type, mem_vals)
170                mem.doc = doc
171
172            members.append(mem)
173
174        return members
175
176    def parse_function(self, abi, decl, children):
177        if len(decl) != 2:
178            raise Exception('Invalid function declaration: {}'.format(
179                ' '.join(decl)))
180
181        name = decl[1]
182        if name in abi.types:
183            raise Exception('Duplicate definition of {}'.format(name))
184
185        parameters = StructType(None, [])
186        return_type = VoidType()
187
188        if len(children) > 0 and children[0].text == 'in':
189            param_spec = children.pop(0)
190            parameters = StructType(None,
191                                    self.parse_struct_members(
192                                        abi, param_spec.children))
193
194        if len(children) > 0 and children[0].text == 'out':
195            out_spec = children.pop(0)
196            doc = self.pop_documentation(out_spec)
197            if len(out_spec.children) != 1:
198                raise Exception('Expected a single return type in '
199                                '`out\' section of function.')
200            self.__expect_no_children(out_spec.children[0])
201            return_type = (self.parse_type(abi,
202                                           out_spec.children[0].text.split()))
203            return_type.doc = doc
204
205        return FunctionType(name, parameters, return_type)
206
207    def parse_syscall(self, abi, decl, children):
208        if len(decl) != 2:
209            raise Exception('Invalid declaration: {}'.format(' '.join(decl)))
210
211        name = decl[1]
212        if name in abi.syscalls:
213            raise Exception('Duplicate syscall name: {}'.format(name))
214
215        input = StructType('', [])
216        output = StructType('', [])
217        attr = {}
218
219        if len(children) > 0 and children[0].text == 'in':
220            in_spec = children.pop(0)
221            input = StructType(None,
222                               self.parse_struct_members(
223                                   abi, in_spec.children))
224
225        if len(children) > 0:
226            if children[0].text == 'out':
227                out_spec = children.pop(0)
228                output = StructType(None,
229                                    self.parse_struct_members(
230                                        abi, out_spec.children))
231
232            elif children[0].text == 'noreturn':
233                noreturn_spec = children.pop(0)
234                self.__expect_no_children(noreturn_spec)
235                attr['noreturn'] = True
236
237        if children != []:
238            raise Exception('Invalid node under syscall: {}'.format(
239                children[0].text))
240
241        syscall = Syscall(name, input, output, **attr)
242
243        return syscall
244
245    def parse_variant(self, abi, tag_member, children):
246        tag_type = tag_member.type
247        members = []
248
249        for node in children:
250            tag_value_names = node.text.split()
251            tag_values = []
252            for vname in tag_value_names:
253                val = [v for v in tag_type.values if vname == v.name]
254                if len(val) != 1:
255                    raise Exception(
256                        'Variant tag type {} has no value {}'.format(
257                            tag_type.name, vname))
258                tag_values.append(val[0])
259            if len(node.children) != 1:
260                raise Exception(
261                    'Excepted a single member in variant member `{}\'.'.format(
262                        node.text))
263            decl = node.children[0].text.split()
264            if len(decl) == 2 and decl[0] == 'struct':
265                name = decl[1]
266                spec = node.children[0]
267            else:
268                name = None
269                spec = node
270            type = StructType(None,
271                              self.parse_struct_members(abi, spec.children))
272            members.append(VariantMember(name, tag_values, type))
273
274        return VariantStructMember(tag_member, members)
275
276    def parse_type(self, abi, decl):
277        if decl == ['void']:
278            return VoidType()
279        elif len(decl) == 1:
280            if decl[0] in int_types:
281                return int_types[decl[0]]
282            if decl[0] in abi.types:
283                return abi.types[decl[0]]
284            raise Exception('Unknown type {}'.format(' '.join(decl)))
285        elif decl[:1] == ['array'] and len(decl) > 2:
286            return ArrayType(int(decl[1], 0), self.parse_type(abi, decl[2:]))
287        elif decl[:1] == ['ptr']:
288            return PointerType(self.parse_type(abi, decl[1:]))
289        elif decl[:1] == ['cptr']:
290            return PointerType(self.parse_type(abi, decl[1:]), const=True)
291        elif decl[:1] == ['atomic']:
292            return AtomicType(self.parse_type(abi, decl[1:]))
293        else:
294            raise Exception('Invalid type: {}'.format(' '.join(decl)))
295
296    def pop_documentation(self, node, optional=False):
297        doc = ''
298        while len(
299                node.children) > 0 and (node.children[0].text.startswith('| ')
300                                        or node.children[0].text == '|'):
301            n = node.children.pop(0)
302            if n.children != []:
303                raise Exception(
304                    'Documentation nodes should not have children.')
305            doc += n.text[2:] + '\n'
306        if doc == '' and not optional:
307            import sys
308            sys.stderr.write('Missing documentation for: {}\n'.format(
309                node.text))
310        return doc
311
312    @staticmethod
313    def __expect_no_children(node):
314        if len(node.children) > 0:
315            raise Exception('Unexpected node inside {}: {}'.format(
316                node.text, node.children[0].text))
317