1# -*- coding: utf-8 -*-
2
3import sys
4import os
5
6try:
7    from pycparser import c_ast  # NOQA
8except ImportError:
9    sys.stderr.write(
10        '\nThe pycparser library is missing, '
11        'please run "pip install pycparser" to install it.\n'
12    )
13    sys.stderr.flush()
14    sys.exit(-500)
15
16from pycparser.c_generator import CGenerator
17from collections import OrderedDict
18
19
20generator = CGenerator()
21
22
23BASIC_TYPES = [
24    'float',
25    'double',
26    'long',
27    'ulong',
28    'unsigned long',
29    'long double',
30    'signed long double',
31    'unsigned long double',
32    'long long',
33    'signed long long',
34    'unsigned long long',
35    'int',
36    'uint',
37    'signed int',
38    'unsigned int',
39    'long int',
40    'signed long int',
41    'unsigned long int',
42    'short'
43    'ushort',
44    'signed short',
45    'unsigned short',
46    'void',
47    'char',
48    'uchar',
49    'signed char',
50    'unsigned char',
51    'bool'
52]
53
54STDLIB_TYPES = [
55    'size_t',
56    'uint8_t',
57    'uint16_t',
58    'uint32_t',
59    'uint64_t',
60    'int8_t',
61    'int16_t',
62    'int32_t',
63    'int64_t',
64    'va_list',
65    'uintptr_t',
66    'intptr_t',
67]
68
69enums = {}
70functions = {}
71structures = {}
72unions = {}
73typedefs = {}
74macros = {}
75
76FILTER_PRIVATE = False
77
78
79def filter_node(n):
80    if hasattr(n, 'coord') and n.coord is not None:
81        if 'fake_libc_include' in n.coord.file:
82            return True
83        if FILTER_PRIVATE and '_private.h' not in n.coord.file:
84            return True
85
86    return False
87
88
89class ArrayDecl(c_ast.ArrayDecl):
90
91    def process(self):
92        self.type.process()
93
94    def __init__(self, *args, **kwargs):
95        super().__init__(*args, **kwargs)
96        self._parent = None
97
98    @property
99    def name(self):
100        return None
101
102    @property
103    def parent(self):
104        return self._parent
105
106    @parent.setter
107    def parent(self, value):
108        self._parent = value
109        self.type.parent = self
110
111    def to_dict(self):
112        if filter_node(self):
113            return None
114
115        if self.dim is None:
116            dim = None
117        else:
118            dim = generator.visit(self.dim)
119
120        if isinstance(self.type, TypeDecl):
121            res = self.type.to_dict()
122            res['json_type'] = 'array'
123            res['dim'] = dim
124            res['quals'].extend(self.dim_quals)
125            return res
126
127        res = OrderedDict([
128            ('type', self.type.to_dict()),
129            ('json_type', 'array'),
130            ('dim', dim),
131            ('quals', self.dim_quals)
132        ])
133
134        return res
135
136
137class Constant(c_ast.Constant):
138
139    def process(self):
140        pass
141
142    def __init__(self, *args, **kwargs):
143        super().__init__(*args, **kwargs)
144        self._parent = None
145
146    @property
147    def parent(self):
148        return self._parent
149
150    @parent.setter
151    def parent(self, value):
152        self._parent = value
153        self.type.parent = self
154
155    def to_dict(self):
156        if filter_node(self):
157            return None
158
159        return self.value
160
161
162collected_types = []
163
164forward_decls = {}
165
166
167class Decl(c_ast.Decl):
168
169    def process(self):
170        self.type.process()
171
172    def __init__(self, *args, **kwargs):
173        super().__init__(*args, **kwargs)
174        self._parent = None
175
176    @property
177    def parent(self):
178        return self._parent
179
180    @parent.setter
181    def parent(self, value):
182        self._parent = value
183        self.type.parent = self
184
185    def to_dict(self):
186        if filter_node(self):
187            return None
188
189        if self.name and self.name == '_silence_gcc_warning':
190            return None
191
192        if not self.name:
193            try:
194                name = self.type.name
195            except AttributeError:
196                name = None
197
198            if name:
199                if name == '_silence_gcc_warning':
200                    return None
201        else:
202            name = self.name
203
204        if isinstance(self.parent, (Struct, Union)):
205            if self.bitsize:
206                bitsize = self.bitsize.to_dict()
207            else:
208                bitsize = None
209
210            res = OrderedDict([
211                ('name', name),
212                ('type', self.type.to_dict()),
213                ('json_type', 'field'),
214                ('bitsize', bitsize)
215            ])
216        elif isinstance(self.parent, FuncDecl):
217            res = OrderedDict([
218                ('name', name),
219                ('type', self.type.to_dict()),
220                ('json_type', 'arg'),
221            ])
222        elif isinstance(self.type, Enum):
223            res = self.type.to_dict()
224            res['name'] = name
225
226        elif isinstance(self.type, (FuncDef, FuncDecl)):
227            res = self.type.to_dict()
228            res['name'] = name
229
230        else:
231            if isinstance(self.type, (Struct, Union)):
232                res = self.type.to_dict()
233
234                if 'quals' in res:
235                    res['quals'].extend(self.quals)
236                else:
237                    res['quals'] = self.quals
238
239                if res['json_type'] == 'forward_decl':
240                    if res['name'] and res['name'] not in forward_decls:
241                        forward_decls[res['name']] = res
242
243                    return None
244
245                return res
246
247            if self.name:
248                name = self.name
249            else:
250                name = self.type.name
251
252            doc_search = get_var_docs(name)  # NOQA
253
254            if doc_search is None:
255                docstring = ''
256            else:
257                docstring = doc_search.description
258
259            if (
260                isinstance(self.type, PtrDecl) and
261                isinstance(self.type.type, FuncDecl)
262            ):
263                type_dict = self.type.type.to_dict()
264                type_dict['json_type'] = 'function_pointer'
265
266                if docstring:
267                    type_dict['docstring'] = docstring
268
269                if 'quals' in type_dict:
270                    type_dict['quals'].extend(self.quals)
271                else:
272                    type_dict['quals'] = self.quals
273
274                return type_dict
275
276            res = OrderedDict([
277                ('name', name),
278                ('type', self.type.to_dict()),
279                ('json_type', 'variable'),
280                ('docstring', docstring),
281                ('quals', self.quals),
282                ('storage', self.storage)
283            ])
284
285        return res
286
287
288class EllipsisParam(c_ast.EllipsisParam):
289
290    def process(self):
291        pass
292
293    def __init__(self, *args, **kwargs):
294        super().__init__(*args, **kwargs)
295        self._parent = None
296
297    @property
298    def parent(self):
299        return self._parent
300
301    @parent.setter
302    def parent(self, value):
303        self._parent = value
304
305    def to_dict(self):
306        if filter_node(self):
307            return None
308
309        res = OrderedDict([
310            ('name', '...'),
311            ('type', OrderedDict([
312                ('name', 'ellipsis'),
313                ('json_type', 'special_type')
314            ])),
315            ('json_type', 'arg'),
316            ('docstring', None)
317        ])
318
319        return res
320
321    @property
322    def name(self):
323        return '...'
324
325
326member_namespace = {}
327
328
329class Enum(c_ast.Enum):
330
331    def process(self):
332        name = self.name
333        parent = self.parent
334
335        while parent is not None and name is None:
336            try:
337                name = parent.name
338            except AttributeError:
339                pass
340
341            parent = parent.parent
342
343        if name and name not in collected_types:
344            collected_types.append(name)
345
346        for item in (self.values or []):
347            item.process()
348
349    def __init__(self, *args, **kwargs):
350        super().__init__(*args, **kwargs)
351        self._parent = None
352
353    @property
354    def parent(self):
355        return self._parent
356
357    @parent.setter
358    def parent(self, value):
359        self._parent = value
360        if self.values:
361            self.values.parent = self
362
363    def to_dict(self):
364        if filter_node(self):
365            return None
366
367        if self.name:
368            doc_search = get_enum_docs(self.name)  # NOQA
369
370            if doc_search is None:
371                docstring = ''
372            else:
373                docstring = doc_search.description
374        else:
375            docstring = ''
376
377        members = []
378        value_num = 0
379
380        for item in (self.values or []):
381            item_dict = item.to_dict()
382            try:
383                code = generator.visit(item.value)
384
385                try:
386                    value = eval("bytearray([b'" + code + "'])[0]")
387                except:  # NOQA
388                    index = code.find('L')
389
390                    while index >= 1:
391                        if code[index - 1].isdigit():
392                            code = list(code)
393                            code.pop(index)
394                            code = ''.join(code)
395
396                        index = code.find('L', index + 1)
397
398                    value = eval(code, member_namespace)
399
400                member_namespace[item_dict['name']] = value
401
402                value_num = value + 1
403
404                code = f'0x{hex(value)[2:].upper()}'
405                value = code
406            except:  # NOQA
407                value = f'0x{hex(value_num)[2:].upper()}'
408                member_namespace[item_dict['name']] = value_num
409                value_num += 1
410
411            item_dict['value'] = value
412            members.append(item_dict)
413
414        hex_len = len(hex(value_num)[2:])
415        for member in members:
416            member['value'] = (
417                f'0x{hex(int(member["value"], 16))[2:].zfill(hex_len).upper()}'
418            )
419
420        res = OrderedDict([
421            ('name', self.name),
422            ('type', OrderedDict([
423                ('name', 'int'),
424                ('json_type', 'primitive_type')
425            ])),
426            ('json_type', 'enum'),
427            ('docstring', docstring),
428            ('members', members)
429        ])
430
431        return res
432
433
434class Enumerator(c_ast.Enumerator):
435
436    def process(self):
437        pass
438
439    def __init__(self, *args, **kwargs):
440        super().__init__(*args, **kwargs)
441        self._parent = None
442
443    @property
444    def parent(self):
445        return self._parent
446
447    @parent.setter
448    def parent(self, value):
449        self._parent = value
450
451    def to_dict(self):
452        if filter_node(self):
453            return None
454
455        parent_name = self.parent.name
456        parent = self.parent
457
458        while parent is not None and parent_name is None:
459            try:
460                parent_name = parent.name
461            except AttributeError:
462                continue
463
464            parent = parent.parent
465
466        if parent_name and parent_name.startswith('_'):
467            if parent_name[1:] in collected_types:
468                type_ = OrderedDict([
469                    ('name', parent_name[1:]),
470                    ('json_type', 'lvgl_type')
471                ])
472            elif parent_name in collected_types:
473                type_ = OrderedDict([
474                    ('name', parent_name),
475                    ('json_type', 'lvgl_type')
476                ])
477            else:
478                type_ = OrderedDict([
479                    ('name', 'int'),
480                    ('json_type', 'primitive_type')
481                ])
482
483        elif parent_name and parent_name in collected_types:
484            type_ = OrderedDict([
485                ('name', parent_name),
486                ('json_type', 'lvgl_type')
487            ])
488        else:
489            type_ = OrderedDict([
490                ('name', 'int'),
491                ('json_type', 'primitive_type')
492            ])
493
494        doc_search = get_enum_item_docs(self.name)  # NOQA
495
496        if doc_search is None:
497            docstring = ''
498        else:
499            docstring = doc_search.description
500
501        res = OrderedDict([
502            ('name', self.name),
503            ('type', type_),
504            ('json_type', 'enum_member'),
505            ('docstring', docstring)
506        ])
507
508        return res
509
510
511class EnumeratorList(c_ast.EnumeratorList):
512
513    def process(self, indent):
514        pass
515
516    def __init__(self, *args, **kwargs):
517        super().__init__(*args, **kwargs)
518        self._parent = None
519
520    @property
521    def parent(self):
522        return self._parent
523
524    @parent.setter
525    def parent(self, value):
526        self._parent = value
527
528        for item in (self.enumerators or []):
529            item.parent = value
530
531    def to_dict(self):
532        if filter_node(self):
533            return None
534
535        pass
536
537
538def is_type(obj, type_):
539    if isinstance(obj, list):
540        return type_ == 'typedef'
541
542    return obj['json_type'] == type_
543
544
545found_types = {}
546
547get_enum_item_docs = None
548get_enum_docs = None
549get_func_docs = None
550get_var_docs = None
551get_union_docs = None
552get_struct_docs = None
553get_typedef_docs = None
554get_macro_docs = None
555get_macros = None
556
557
558_enums = {}
559_functions = {}
560_structures = {}
561_unions = {}
562_typedefs = {}
563_variables = {}
564_function_pointers = {}
565_forward_decls = {}
566
567
568class FileAST(c_ast.FileAST):
569
570    def __init__(self, *args, **kwargs):
571        super().__init__(*args, **kwargs)
572        self._parent = None
573
574    def setup_docs(self, no_docstrings, temp_directory):  # NOQA
575        global get_enum_item_docs
576        global get_enum_docs
577        global get_func_docs
578        global get_var_docs
579        global get_union_docs
580        global get_struct_docs
581        global get_typedef_docs
582        global get_macro_docs
583        global get_macros
584
585        if no_docstrings:
586
587            def dummy_list():
588                return []
589
590            def dummy_doc(_):
591                return None
592
593            get_enum_item_docs = dummy_doc
594            get_enum_docs = dummy_doc
595            get_func_docs = dummy_doc
596            get_var_docs = dummy_doc
597            get_union_docs = dummy_doc
598            get_struct_docs = dummy_doc
599            get_typedef_docs = dummy_doc
600            get_macro_docs = dummy_doc
601            get_macros = dummy_list
602
603        else:
604            import doc_builder  # NOQA
605
606            doc_builder.EMIT_WARNINGS = False
607            # doc_builder.DOXYGEN_OUTPUT = False
608
609            docs = doc_builder.XMLSearch(temp_directory)
610
611            get_enum_item_docs = docs.get_enum_item
612            get_enum_docs = docs.get_enum
613            get_func_docs = docs.get_function
614            get_var_docs = docs.get_variable
615            get_union_docs = docs.get_union
616            get_struct_docs = docs.get_structure
617            get_typedef_docs = docs.get_typedef
618            get_macro_docs = docs.get_macro
619            get_macros = docs.get_macros
620
621    @property
622    def name(self):
623        return None
624
625    @property
626    def parent(self):
627        return self._parent
628
629    @parent.setter
630    def parent(self, value):
631        self._parent = value
632
633    def to_dict(self):
634        items = []
635
636        # This code block is to handle how pycparser handles forward
637        # declarations and combining the forward declarations with the actual
638        # types so any information that is contained in the type gets properly
639        # attached to the forward declaration
640        forward_struct_decls = {}
641
642        for item in self.ext[:]:
643            if (
644                isinstance(item, Decl) and
645                item.name is None and
646                isinstance(
647                    item.type,
648                    (Struct, Union)
649                ) and
650                item.type.name is not None
651            ):
652                if item.type.decls is None:
653                    forward_struct_decls[item.type.name] = [item]
654                else:
655                    if item.type.name in forward_struct_decls:
656                        decs = forward_struct_decls[item.type.name]
657                        if len(decs) == 2:
658                            decl, td = decs
659
660                            if FILTER_PRIVATE:
661                                if (
662                                    '_private.h' not in decl.coord.file and
663                                    '_private.h' not in td.coord.file and
664                                    '_private.h' not in item.coord.file
665                                ):
666                                    continue
667
668                                if decl.type.decls and '_private.h' in decl.coord.file:
669                                    decl.name = decl.type.name
670                                    self.ext.remove(item)
671                                elif item.type.decls and '_private.h' in item.coord.file:
672                                    item.name = item.type.name
673                                    self.ext.remove(decl)
674
675                                self.ext.remove(td)
676                            else:
677                                td.type.type.decls = item.type.decls[:]
678
679                                self.ext.remove(decl)
680                                self.ext.remove(item)
681            elif (
682                isinstance(item, Typedef) and
683                isinstance(item.type, TypeDecl) and
684                item.name and
685                item.type.declname and
686                item.name == item.type.declname and
687                isinstance(
688                    item.type.type,
689                    (Struct, Union)
690                ) and
691                item.type.type.decls is None
692            ):
693                if item.type.type.name in forward_struct_decls:
694                    forward_struct_decls[item.type.type.name].append(item)
695        ############################
696
697        for item in self.ext:
698            if filter_node(item):
699                continue
700            try:
701                item.parent = self
702                items.append(item)
703            except AttributeError:
704                pass
705
706        enums = []  # NOQA
707        functions = []  # NOQA
708        structures = []  # NOQA
709        unions = []  # NOQA
710        typedefs = []  # NOQA
711        variables = []
712        function_pointers = []
713        forward_decl = []
714
715        no_enum_name_count = 1
716
717        for itm in items:
718            itm.process()
719            item = itm.to_dict()
720
721            if item is None:
722                continue
723
724            if is_type(item, 'typedef'):
725                typedefs.append(item)
726                _typedefs[itm.name] = item
727            elif is_type(item, 'function_pointer'):
728                function_pointers.append(item)
729                _function_pointers[item['name']] = item
730            elif is_type(item, 'function'):
731                functions.append(item)
732                _functions[item['name']] = item
733            elif is_type(item, 'struct'):
734                structures.append(item)
735                _structures[item['name']] = item
736            elif is_type(item, 'union'):
737                unions.append(item)
738                _unions[item['name']] = item
739            elif is_type(item, 'enum'):
740                enums.append(item)
741
742                if item['name'] is None:
743                    item['name'] = f'NO_NAME_{no_enum_name_count}'
744                    no_enum_name_count += 1
745
746                _enums[item['name']] = item
747            elif is_type(item, 'variable'):
748                variables.append(item)
749                _variables[item['name']] = item
750            elif is_type(item, 'forward_decl'):
751                forward_decl.append(item)
752                _forward_decls[item['name']] = item
753            else:
754                print('UNKNOWN TYPE:')
755                print(item)
756                print(item.to_dict())
757
758        for tdef_name in _typedefs.keys():
759            if '_' + tdef_name in _enums:
760                enum_dict = _enums['_' + tdef_name]
761                for member in enum_dict['members']:
762                    member['type']['name'] = tdef_name
763                    member['type']['json_type'] = 'lvgl_type'
764            else:
765                if tdef_name.endswith('_t'):
766                    td_name = tdef_name[:-2].upper()
767                else:
768                    td_name = tdef_name.upper()
769
770                for en_name, enum_dict in _enums.items():
771                    if not en_name.startswith('NO_NAME_'):
772                        continue
773
774                    member_names = [
775                        member['name']
776                        for member in enum_dict['members']
777                        if not member['name'].startswith('_')
778                    ]
779
780                    if not member_names:
781                        continue
782
783                    c_name = os.path.commonprefix(member_names)
784                    c_name = "_".join(c_name.split("_")[:-1])
785                    if c_name != td_name:
786                        continue
787
788                    for member in enum_dict['members']:
789                        member['type']['name'] = tdef_name
790                        member['type']['json_type'] = 'lvgl_type'
791                    break
792
793        for enm in enums:
794            if enm['name'].startswith('NO_NAME_'):
795                enm['name'] = None
796
797        res = {
798            'enums': enums,
799            'functions': functions,
800            'function_pointers': function_pointers,
801            'structures': structures,
802            'unions': unions,
803            'variables': variables,
804            'typedefs': [],
805            'forward_decls': forward_decl,
806            'macros': []
807        }
808
809        for typedef in typedefs:
810            if isinstance(typedef, list):
811                typedef, obj_dict = typedef
812                if obj_dict['json_type'] == 'struct':
813                    res['structures'].append(obj_dict)
814                elif obj_dict['json_type'] == 'union':
815                    res['unions'].append(obj_dict)
816                elif obj_dict['json_type'] == 'enum':
817                    res['enums'].append(obj_dict)
818
819            res['typedefs'].append(typedef)
820
821        for macro in get_macros():  # NOQA
822            macro_type = OrderedDict([
823                ('name', macro.name),
824                ('json_type', 'macro'),
825                ('docstring', macro.description),
826                ('params', macro.params),
827                ('initializer', macro.initializer)
828            ])
829
830            res['macros'].append(macro_type)
831
832        return res
833
834
835class FuncDecl(c_ast.FuncDecl):
836
837    @property
838    def name(self):
839        type_ = self.type
840        while isinstance(type_, PtrDecl):
841            type_ = type_.type
842
843        try:
844            name = type_.name
845        except AttributeError:
846            name = None
847            parent = self.parent
848            while parent is not None and name is None:
849                try:
850                    name = parent.name
851                except AttributeError:
852                    pass
853
854                parent = parent.parent
855
856        return name
857
858    def process(self):
859        name = self.name
860        if name and name not in collected_types:
861            collected_types.append(name)
862
863        for arg in (self.args or []):
864            arg.process()
865
866        self.type.process()
867
868    def __init__(self, *args, **kwargs):
869        super().__init__(*args, **kwargs)
870        self._parent = None
871
872    @property
873    def parent(self):
874        return self._parent
875
876    @parent.setter
877    def parent(self, value):
878        self._parent = value
879
880        for arg in (self.args or []):
881            arg.parent = self
882
883        self.type.parent = self
884
885    def to_dict(self):
886        if filter_node(self):
887            return None
888
889        if self.name:
890            doc_search = get_func_docs(self.name)  # NOQA
891            if doc_search is None:
892                docstring = ''
893                ret_docstring = ''
894            else:
895                docstring = doc_search.description
896                ret_docstring = doc_search.res_description
897
898                if docstring is None:
899                    docstring = ''
900                if ret_docstring is None:
901                    ret_docstring = ''
902
903        else:
904            doc_search = None
905            docstring = ''
906            ret_docstring = ''
907
908        args = []
909
910        for arg in (self.args or []):
911            arg = arg.to_dict()
912            if arg['name'] and doc_search is not None:
913                for doc_arg in doc_search.args:
914                    if doc_arg.name == arg['name']:
915                        if doc_arg.description is None:
916                            arg['docstring'] = ''
917                        else:
918                            arg['docstring'] = doc_arg.description
919                        break
920                else:
921                    arg['docstring'] = ''
922            else:
923                arg['docstring'] = ''
924
925            args.append(arg)
926
927        type_dict = OrderedDict([
928            ('type', self.type.to_dict()),
929            ('json_type', 'ret_type'),
930            ('docstring', ret_docstring)
931        ])
932
933        res = OrderedDict([
934            ('name', self.name),
935            ('type', type_dict),
936            ('json_type', 'function'),
937            ('docstring', docstring),
938            ('args', args)
939        ])
940
941        return res
942
943
944class FuncDef(c_ast.FuncDef):
945
946    @property
947    def name(self):
948        return None
949
950    def process(self):
951        self.decl.process()
952
953    def __init__(self, *args, **kwargs):
954        super().__init__(*args, **kwargs)
955        self._parent = None
956
957    @property
958    def parent(self):
959        return self._parent
960
961    @parent.setter
962    def parent(self, value):
963        self._parent = value
964        self.decl.parent = value
965
966    def to_dict(self):
967        if filter_node(self):
968            return None
969
970        return self.decl.to_dict()
971
972
973class IdentifierType(c_ast.IdentifierType):
974
975    def process(self):
976        pass
977
978    def __init__(self, *args, **kwargs):
979        super().__init__(*args, **kwargs)
980        self._parent = None
981
982    @property
983    def name(self):
984        return ' '.join(self.names)
985
986    @property
987    def parent(self):
988        return self._parent
989
990    @parent.setter
991    def parent(self, value):
992        self._parent = value
993
994    def to_dict(self):
995        if filter_node(self):
996            return None
997
998        name = ' '.join(self.names)
999
1000        if name in BASIC_TYPES:
1001            json_type = 'primitive_type'
1002        elif name in STDLIB_TYPES:
1003            json_type = 'stdlib_type'
1004        elif name in collected_types:
1005            json_type = 'lvgl_type'
1006        elif name.startswith('_') and name[1:] in collected_types:
1007            name = name[1:]
1008            json_type = 'lvgl_type'
1009        elif name.startswith('_lv_') or name.startswith('lv_'):
1010            json_type = 'lvgl_type'
1011        else:
1012            json_type = 'unknown_type'
1013
1014        res = OrderedDict([
1015            ('name', name),
1016            ('json_type', json_type),
1017        ])
1018
1019        return res
1020
1021
1022class ParamList(c_ast.ParamList):
1023
1024    def process(self):
1025        pass
1026
1027    def __init__(self, *args, **kwargs):
1028        super().__init__(*args, **kwargs)
1029        self._parent = None
1030
1031    @property
1032    def parent(self):
1033        return self._parent
1034
1035    @parent.setter
1036    def parent(self, value):
1037        self._parent = value
1038        for param in (self.params or []):
1039            param.parent = value
1040
1041    def to_dict(self):
1042        if filter_node(self):
1043            return None
1044
1045        pass
1046
1047
1048class PtrDecl(c_ast.PtrDecl):
1049
1050    @property
1051    def name(self):
1052        return None
1053
1054    def process(self):
1055        self.type.process()
1056
1057    def __init__(self, *args, **kwargs):
1058        super().__init__(*args, **kwargs)
1059        self._parent = None
1060
1061    @property
1062    def parent(self):
1063        return self._parent
1064
1065    @parent.setter
1066    def parent(self, value):
1067        self._parent = value
1068        self.type.parent = self
1069
1070    def to_dict(self):
1071        if filter_node(self):
1072            return None
1073
1074        if isinstance(self.type, FuncDecl):
1075            type_dict = self.type.to_dict()
1076            type_dict['json_type'] = 'function_pointer'
1077            res = type_dict
1078        else:
1079            res = OrderedDict([
1080                ('type', self.type.to_dict()),
1081                ('json_type', 'pointer')
1082            ])
1083
1084        if 'quals' in res:
1085            res['quals'].extend(self.quals)
1086        else:
1087            res['quals'] = self.quals
1088
1089        return res
1090
1091
1092class Struct(c_ast.Struct):
1093
1094    def process(self):
1095        for decl in (self.decls or []):
1096            decl.process()
1097
1098        name = self.name
1099        parent = self.parent
1100        while parent is not None and name is None:
1101            name = parent.name
1102            parent = parent.parent
1103
1104        if name and name not in collected_types:
1105            collected_types.append(name)
1106
1107    def __init__(self, *args, **kwargs):
1108        super().__init__(*args, **kwargs)
1109        self._parent = None
1110
1111    @property
1112    def parent(self):
1113        return self._parent
1114
1115    @parent.setter
1116    def parent(self, value):
1117        self._parent = value
1118
1119        for decl in (self.decls or []):
1120            decl.parent = self
1121
1122    def to_dict(self):
1123        if filter_node(self):
1124            return None
1125
1126        if not self.decls:
1127            name = self.name
1128            if not name:
1129                self.name = self.parent.name
1130
1131            if name:
1132                struct_doc = get_struct_docs(name)  # NOQA
1133                if struct_doc:
1134                    docstring = struct_doc.description
1135                else:
1136                    docstring = ''
1137            else:
1138                docstring = ''
1139
1140            res = OrderedDict([
1141                ('name', name),
1142                ('type', OrderedDict([
1143                    ('name', 'struct'),
1144                    ('json_type', 'primitive_type')
1145                ])),
1146                ('json_type', 'forward_decl'),
1147                ('docstring', docstring),
1148            ])
1149
1150        else:
1151            if self.name:
1152                struct_doc = get_struct_docs(self.name)  # NOQA
1153            elif self.parent.name:
1154                struct_doc = get_struct_docs(self.parent.name)  # NOQA
1155            else:
1156                struct_doc = None
1157
1158            if struct_doc is not None:
1159                docstring = struct_doc.description
1160            else:
1161                docstring = ''
1162
1163            fields = []
1164
1165            for field in self.decls:
1166                field = field.to_dict()
1167
1168                if struct_doc is not None:
1169                    for field_doc in struct_doc.fields:
1170                        if field_doc.name == field['name']:
1171                            field_docstring = field_doc.description
1172                            break
1173                    else:
1174                        field_docstring = ''
1175                else:
1176                    field_docstring = ''
1177
1178                field['docstring'] = field_docstring
1179                field['json_type'] = 'field'
1180                fields.append(field)
1181
1182            res = OrderedDict([
1183                ('name', self.name),
1184                ('type', OrderedDict([
1185                    ('name', 'struct'),
1186                    ('json_type', 'primitive_type')
1187                ])),
1188                ('json_type', 'struct'),
1189                ('docstring', docstring),
1190                ('fields', fields)
1191            ])
1192
1193        return res
1194
1195
1196class TypeDecl(c_ast.TypeDecl):
1197
1198    @property
1199    def name(self):
1200        return self.declname
1201
1202    def process(self):
1203        self.type.process()
1204
1205    def __init__(self, *args, **kwargs):
1206        super().__init__(*args, **kwargs)
1207        self._parent = None
1208
1209    @property
1210    def parent(self):
1211        return self._parent
1212
1213    @parent.setter
1214    def parent(self, value):
1215        parent = value
1216
1217        while parent is not None:
1218            try:
1219                if parent.declname == self.declname:
1220                    break
1221            except AttributeError:
1222                pass
1223
1224            try:
1225                if parent.name == self.declname:
1226                    break
1227            except AttributeError:
1228                pass
1229
1230            parent = parent.parent
1231
1232        if parent is None:
1233            self._parent = value
1234
1235            self.type.parent = self
1236        else:
1237            self.type.parent = parent
1238
1239    def to_dict(self):
1240        if filter_node(self):
1241            return None
1242
1243        if self.parent is None:
1244            res = self.type.to_dict()
1245            if self.declname is not None and not self.type.name:
1246                res['name'] = self.declname
1247
1248        elif isinstance(self.type, (Union, Struct)):
1249            res = self.type.to_dict()
1250
1251            if not self.type.name and self.declname:
1252                res['name'] = self.declname
1253        else:
1254            res = OrderedDict([
1255                ('name', self.declname),
1256                ('type', self.type.to_dict()),
1257                ('json_type', str(type(self))),
1258            ])
1259
1260        res['quals'] = self.quals
1261
1262        return res
1263
1264
1265class Typedef(c_ast.Typedef):
1266
1267    def process(self):
1268        if self._parent is None:
1269            self.type.parent = self
1270
1271        self.type.process()
1272
1273        if self.name and self.name not in collected_types:
1274            collected_types.append(self.name)
1275
1276    def __init__(self, *args, **kwargs):
1277        super().__init__(*args, **kwargs)
1278        self._parent = None
1279
1280    @property
1281    def parent(self):
1282        if filter_node(self):
1283            return None
1284
1285        return self._parent
1286
1287    @parent.setter
1288    def parent(self, value):
1289        self._parent = value
1290
1291        self.type.parent = self
1292
1293    @property
1294    def is_struct(self):
1295        return (
1296            isinstance(self.type, TypeDecl) and
1297            isinstance(self.type.type, Struct)
1298        )
1299
1300    @property
1301    def is_union(self):
1302        return (
1303            isinstance(self.type, TypeDecl) and
1304            isinstance(self.type.type, Union)
1305        )
1306
1307    def get_struct_union(self):
1308        res = self.type.type.to_dict()
1309        if not self.type.type.name and self.type.name:
1310            res['name'] = self.type.name
1311        elif not self.type.type.name:
1312            res['name'] = self.name
1313
1314        return res
1315
1316    def to_dict(self):
1317        doc_search = get_typedef_docs(self.name)  # NOQA
1318
1319        if doc_search is None:
1320            docstring = ''
1321        else:
1322            docstring = doc_search.description
1323
1324        if (
1325            isinstance(self.type, PtrDecl) and
1326            isinstance(self.type.type, FuncDecl)
1327        ):
1328            type_dict = self.type.type.to_dict()
1329            type_dict['json_type'] = 'function_pointer'
1330            type_dict['name'] = self.name
1331            if 'quals' in type_dict:
1332                type_dict['quals'].extend(self.quals)
1333            else:
1334                type_dict['quals'] = self.quals
1335
1336            if (
1337                'docstring' not in type_dict or
1338                not type_dict['docstring']
1339            ):
1340                type_dict['docstring'] = docstring
1341
1342            return type_dict
1343
1344        if isinstance(self.type, TypeDecl):
1345            type_dict = self.type.type.to_dict()
1346
1347            if type_dict['name'] is None:
1348                if self.name is not None:
1349                    type_dict['name'] = self.name
1350                else:
1351                    raise RuntimeError(str(type_dict))
1352
1353            if 'quals' in type_dict:
1354                type_dict['quals'].extend(self.quals)
1355            else:
1356                type_dict['quals'] = self.quals
1357
1358            type_dict['quals'].extend(self.type.quals)
1359
1360            if 'docstring' not in type_dict:
1361                type_dict['docstring'] = ''
1362
1363            if not type_dict['docstring']:
1364                type_dict['docstring'] = docstring
1365
1366            if type_dict['name'] in _structures:
1367                _structures[type_dict['name']]['name'] = self.name
1368
1369                if 'quals' in _structures[type_dict['name']]:
1370                    _structures[type_dict['name']]['quals'].extend(
1371                        type_dict['quals']
1372                    )
1373                else:
1374                    _structures[type_dict['name']]['quals'] = type_dict['quals']
1375
1376                if (
1377                    type_dict['docstring'] and
1378                    not _structures[type_dict['name']]['docstring']
1379                ):
1380                    _structures[type_dict['name']]['docstring'] = (
1381                        type_dict['docstring']
1382                    )
1383
1384                return None
1385
1386            if type_dict['name'] in _unions:
1387                _unions[type_dict['name']]['name'] = self.name
1388
1389                if 'quals' in _unions[type_dict['name']]:
1390                    _unions[type_dict['name']]['quals'].extend(
1391                        type_dict['quals']
1392                    )
1393                else:
1394                    _unions[type_dict['name']]['quals'] = type_dict['quals']
1395
1396                if (
1397                    type_dict['docstring'] and
1398                    not _structures[type_dict['name']]['docstring']
1399                ):
1400                    _structures[type_dict['name']]['docstring'] = (
1401                        type_dict['docstring']
1402                    )
1403
1404                return None
1405
1406            if type_dict['name'] in _enums:
1407                if self.name is not None:
1408                    type_dict = self.type.to_dict()
1409
1410                    res = OrderedDict(
1411                        [
1412                            ('name', self.name),
1413                            ('type', type_dict),
1414                            ('json_type', 'typedef'),
1415                            ('docstring', docstring),
1416                            ('quals', self.quals)
1417                        ]
1418                    )
1419                    return res
1420
1421                if 'quals' in _enums[type_dict['name']]:
1422                    _enums[type_dict['name']]['quals'].extend(
1423                        type_dict['quals']
1424                    )
1425                else:
1426                    _enums[type_dict['name']]['quals'] = type_dict['quals']
1427
1428                if (
1429                    type_dict['docstring'] and
1430                    not _enums[type_dict['name']]['docstring']
1431                ):
1432                    _enums[type_dict['name']]['docstring'] = (
1433                        type_dict['docstring']
1434                    )
1435
1436                return None
1437
1438            if not type_dict['name']:
1439                type_dict['name'] = self.name
1440                return type_dict
1441
1442            if type_dict['name'] and type_dict['name'][1:] == self.name:
1443                type_dict['name'] = self.name
1444                return type_dict
1445
1446            if type_dict['name'] and type_dict['name'] == self.name:
1447                return type_dict
1448
1449            if isinstance(self.type.type, (Struct, Union)):
1450                res = OrderedDict([
1451                    ('name', self.name),
1452                    ('type', OrderedDict([
1453                        ('name', self.type.type.name),
1454                        ('json_type', 'lvgl_type')
1455                    ])),
1456                    ('json_type', 'typedef'),
1457                    ('docstring', docstring),
1458                    ('quals', self.quals + self.type.quals)
1459                ])
1460
1461                return [res, self.type.type.to_dict()]
1462
1463            elif isinstance(self.type.type, Enum):
1464                if self.type.type.name:
1465                    type_dict = self.type.type.to_dict()
1466
1467                    if not type_dict['docstring']:
1468                        type_dict['docstring'] = docstring
1469                        docstring = ''
1470
1471                    if not type_dict['name']:
1472                        if self.type.name:
1473                            type_dict['name'] = self.type.name
1474                        else:
1475                            type_dict['name'] = self.name
1476
1477                    res = OrderedDict([
1478                        ('name', self.name),
1479                        ('type', OrderedDict([
1480                            ('name', type_dict['name']),
1481                            ('json_type', 'lvgl_type')
1482                        ])),
1483                        ('json_type', 'typedef'),
1484                        ('docstring', docstring),
1485                    ])
1486
1487                    return [res, type_dict]
1488
1489        type_dict = self.type.to_dict()
1490
1491        if 'quals' in type_dict:
1492            type_dict['quals'].extend(self.quals)
1493        else:
1494            type_dict['quals'] = self.quals
1495
1496        if (
1497            docstring and
1498            'docstring' in type_dict and
1499            not type_dict['docstring']
1500        ):
1501            type_dict['docstring'] = docstring
1502
1503        if 'name' in type_dict and type_dict['name']:
1504            if type_dict['name'] == self.name:
1505                return type_dict
1506            if type_dict['name'][1:] == self.name:
1507                type_dict['name'] = self.name
1508                return type_dict
1509
1510        quals = type_dict['quals']
1511        del type_dict['quals']
1512
1513        res = OrderedDict([
1514            ('name', self.name),
1515            ('type', type_dict),
1516            ('json_type', 'typedef'),
1517            ('docstring', docstring),
1518            ('quals', quals)
1519        ])
1520
1521        return res
1522
1523
1524class Typename(c_ast.Typename):
1525
1526    def process(self):
1527        self.type.process()
1528
1529    def __init__(self, *args, **kwargs):
1530        super().__init__(*args, **kwargs)
1531        self._parent = None
1532
1533    @property
1534    def parent(self):
1535        return self._parent
1536
1537    @parent.setter
1538    def parent(self, value):
1539        self._parent = value
1540
1541        self.type.parent = self
1542
1543    def to_dict(self):
1544        if filter_node(self):
1545            return None
1546
1547        if not self.name and isinstance(self.type, IdentifierType):
1548            res = self.type.to_dict()
1549            res['quals'] = self.quals
1550
1551        elif isinstance(self.parent, FuncDecl):
1552            if self.name and self.parent.name:
1553                func_docs = get_func_docs(self.parent.name)  # NOQA
1554
1555                if func_docs is not None:
1556                    for arg in func_docs.args:
1557                        if arg.name and arg.name == self.name:
1558                            docstring = arg.description
1559                            break
1560                    else:
1561                        docstring = ''
1562                else:
1563                    docstring = ''
1564            else:
1565                docstring = ''
1566
1567            res = OrderedDict([
1568                ('name', self.name),
1569                ('type', self.type.to_dict()),
1570                ('json_type', 'arg'),
1571                ('docstring', docstring),
1572                ('quals', self.quals)
1573            ])
1574        else:
1575            res = OrderedDict([
1576                ('name', self.name),
1577                ('type', self.type.to_dict()),
1578                ('json_type',  str(type(self))),
1579                ('quals', self.quals)
1580            ])
1581
1582        return res
1583
1584
1585class Union(c_ast.Union):
1586
1587    def process(self):
1588        for field in (self.decls or []):
1589            field.process()
1590
1591        name = self.name
1592        parent = self.parent
1593        while parent is not None and name is None:
1594            try:
1595                name = parent.name
1596            except AttributeError:
1597                pass
1598
1599            parent = parent.parent
1600
1601        if name and name not in collected_types:
1602            collected_types.append(name)
1603
1604    def __init__(self, *args, **kwargs):
1605        super().__init__(*args, **kwargs)
1606        self._parent = None
1607
1608    @property
1609    def parent(self):
1610        return self._parent
1611
1612    @parent.setter
1613    def parent(self, value):
1614        self._parent = value
1615
1616        for decl in (self.decls or []):
1617            decl.parent = self
1618
1619    def to_dict(self):
1620        if filter_node(self):
1621            return None
1622
1623        if not self.decls:
1624            name = self.name
1625            if not name:
1626                self.name = self.parent.name
1627
1628            if name:
1629                union_doc = get_union_docs(name)  # NOQA
1630                if union_doc:
1631                    docstring = union_doc.description
1632                else:
1633                    docstring = ''
1634            else:
1635                docstring = ''
1636
1637            res = OrderedDict([
1638                ('name', name),
1639                ('type', OrderedDict([
1640                    ('name', 'union'),
1641                    ('json_type', 'primitive_type')
1642                ])),
1643                ('json_type', 'forward_decl'),
1644                ('docstring', docstring),
1645            ])
1646        else:
1647            if self.name:
1648                union_doc = get_union_docs(self.name)  # NOQA
1649            elif self.parent.name:
1650                union_doc = get_union_docs(self.parent.name)  # NOQA
1651            else:
1652                union_doc = None
1653
1654            if union_doc is not None:
1655                docstring = union_doc.description
1656            else:
1657                docstring = ''
1658
1659            fields = []
1660
1661            for field in self.decls:
1662                field = field.to_dict()
1663
1664                if union_doc is not None:
1665                    for field_doc in union_doc.fields:
1666                        if field_doc.name == field['name']:
1667                            field_docstring = field_doc.description
1668                            break
1669                    else:
1670                        field_docstring = ''
1671                else:
1672                    field_docstring = ''
1673
1674                field['docstring'] = field_docstring
1675                field['json_type'] = 'field'
1676                fields.append(field)
1677
1678            res = OrderedDict([
1679                ('name', self.name),
1680                ('type', OrderedDict([
1681                    ('name', 'union'),
1682                    ('json_type', 'primitive_type')
1683                ])),
1684                ('json_type', 'union'),
1685                ('docstring', docstring),
1686                ('fields', fields)
1687            ])
1688
1689        return res
1690
1691
1692for cls in (
1693    ArrayDecl,
1694    Constant,
1695    Decl,
1696    EllipsisParam,
1697    Enum,
1698    Enumerator,
1699    EnumeratorList,
1700    EnumeratorList,
1701    FileAST,
1702    FuncDecl,
1703    FuncDef,
1704    IdentifierType,
1705    ParamList,
1706    PtrDecl,
1707    Struct,
1708    TypeDecl,
1709    Typedef,
1710    Typename,
1711    Union
1712):
1713    cls_name = cls.__name__
1714    setattr(getattr(sys.modules['pycparser.c_parser'], 'c_ast'), cls_name, cls)
1715