• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# !/usr/bin/env python3
2# coding=utf-8
3"""
4* Copyright (c) 2022 Shenzhen Kaihong Digital Industry Development Co., Ltd.
5*
6* HDF is dual licensed: you can use it either under the terms of
7* the GPL, or the BSD license, at your option.
8* See the LICENSE file in the root of this repository for complete details.
9"""
10
11import json
12import os
13import re
14from shutil import copyfile, move
15
16import CppHeaderParser
17
18
19class HeaderParser:
20    """
21    Extract file path, file name, includes, enums, unions, structs, interfaces and callbacks
22    from CppHeaderParser
23    """
24
25    def __init__(self):
26        self._header_dict = {
27            "name": "",
28            "path": "",
29            "import": [],
30            "enum": [],
31            "union": [],
32            "struct": [],
33            "typedef": [],
34            "interface": [],
35            "callback": []
36        }
37        self._rand_name_count = 0
38
39    def parse(self, header_file):
40        try:
41            hjson = json.loads(CppHeaderParser.CppHeader(header_file).toJSON())
42        except CppHeaderParser.CppParseError:
43            back_file = self._pre_handle(header_file)
44            hjson = json.loads(CppHeaderParser.CppHeader(header_file).toJSON())
45            move(back_file, header_file)
46
47        try:
48            self._extract_path_and_file(header_file)
49            self._extract_include(hjson["includes"])
50            self._extract_enum(hjson["enums"])
51            for i in hjson["classes"]:
52                self._extract_union(hjson["classes"][i])
53                self._extract_struct(hjson["classes"][i])
54                self._extract_interface(hjson["classes"][i])
55            self._extract_typedef(hjson["typedefs"])
56            return self._header_dict
57        except KeyError:
58            pass
59
60    @staticmethod
61    def _pre_handle(header_file):
62        back_file = header_file + ".back"
63        copyfile(header_file, back_file)
64        f = open(header_file, 'r')
65        new_lines = ""
66        for line in f:
67            tt = re.match(r".*enum *((\w+) *\*) *\w* *;", line)
68            if tt:
69                new_line = line.replace(tt[1], tt[2] + "_ENUM_POINTER ")
70                new_lines += new_line
71                continue
72            tt = re.match(r".*(\[\w* *\(.*\) *]) *", line)
73            if tt:
74                new_line = line.replace(tt[1], "[]")
75                new_lines += new_line
76                continue
77            else:
78                new_lines += line
79        f.close()
80        f = open(header_file, 'w')
81        f.writelines(new_lines)
82        f.close()
83        return back_file
84
85    @staticmethod
86    def _has_function_pointer(jvs):
87        for jv in jvs:
88            if jv["function_pointer"] > 0:
89                return True
90        return False
91
92    def _checkout_function_pointer_param(self, params):
93        if params == '':
94            return []
95        tt = re.match(r"(typedef )* *\w+ \( \* \) \(( .* )\)", params)
96        if tt:
97            params = tt[2].strip() + ","
98        else:
99            print("error cant analyze function pointer params: ", params)
100            return []
101        ret = []
102        while params:
103            tt = re.match(" *(const )*(struct |enum |union )* *", params)
104            if tt:  # 去掉结构类型头
105                params = params[tt.regs[0][1]:]
106                tt = re.match(r"((unsigned )*[a-zA-Z0-9_:]+( *\** *\**)) *", params)
107                if tt:
108                    param_type = params[tt.regs[1][0]:tt.regs[1][1]]  # 参数类型
109                    params = params[tt.regs[0][1]:]
110                    tt = re.match("([a-zA-Z0-9_]+) *(,)", params)
111                    if tt:
112                        param_name = params[tt.regs[1][0]:tt.regs[1][1]]
113                        params = params[tt.regs[0][1]:]
114                    else:  # 没有定义变量名的,设置一个默认变量名
115                        param_name = "rand_name_%d" % self._rand_name_count
116                        self._rand_name_count += 1
117                        params = params[params.index(",") + 1:]
118                    ret.append({"name": param_name, "type": param_type.strip()})
119                else:
120                    ret.append({"name": '', "type": params.strip(',').strip()})
121                    break
122            else:
123                print("error cant analyze param :[%s]" % params)
124                break
125        return ret
126
127    def _extract_path_and_file(self, path):
128        self._header_dict["path"], self._header_dict["name"] = os.path.split(path)
129
130    def _extract_include(self, includes):
131        """
132        Extract imports from includes
133        """
134        for include in includes:
135            if '<' not in include:  # ignore system files
136                self._header_dict.get("import").append(include[1:-1])
137
138    def _extract_enum(self, enums):
139        """
140        Extract enums from enums
141        """
142        for enm in enums:
143            if "name" in enm:
144                enum_dict = {"name": enm["name"], "members": []}
145            else:
146                enum_dict = {"name": "LostName_%d" % self._rand_name_count, "members": []}
147                self._rand_name_count += 1
148            for value in enm["values"]:
149                v_value = value["value"]
150                if not isinstance(v_value, int):
151                    tt = re.match(r"0x|0X|\(|-", v_value)
152                    if tt:
153                        errmsg = "unexpected '%s'" % tt[0]
154                        v_value = v_value + " // " + errmsg
155                        print("[HeaderParser]: %s[line %d] " % (enm["filename"], enm["line_number"]), errmsg)
156                enum_dict["members"].append({"name": value["name"], "value": v_value})
157            self._header_dict.get("enum").append(enum_dict)
158
159    def _extract_union(self, stack):
160        """
161        Extract unions from classes
162        """
163        union_dict = {}
164        if stack["declaration_method"] == "union":
165            union_dict["name"] = stack["name"].split(" ")[-1]
166            union_dict["type"] = "union"
167            union_dict["members"] = []
168            file_name = self._header_dict.get("path") + "/" + self._header_dict.get("name")
169            for mb in stack["members"]:
170                union_dict.get("members").append({"file_name": file_name,
171                                                  "line_number": mb["line_number"],
172                                                  "name": mb["name"], "type": mb["type"]})
173            self._header_dict.get("union").append(union_dict)
174
175    def _extract_struct(self, stack):
176        """
177        Extract structs from structs or classes that have no methods
178        """
179        struct_dict = {}
180        if stack["declaration_method"] in ["struct", "class"]:
181            if len(stack["methods"]["public"]) == 0:  # 不带函数
182                if not self._has_function_pointer(stack["properties"]["public"]):  # 变量中没有函数指针
183                    struct_dict["name"] = stack["name"]
184                    struct_dict["type"] = "struct"
185                    struct_dict["members"] = []
186                    file_name = self._header_dict.get("path") + "/" + self._header_dict.get("name")
187                    for mb in stack["properties"]["public"]:
188                        if "enum_type" in mb:
189                            struct_dict.get("members").append({"file_name": file_name,
190                                                               "line_number": mb["line_number"],
191                                                               "name": mb["name"],
192                                                               "type": mb["enum_type"]["name"]})
193                        elif mb["array"] == 1:
194                            struct_dict.get("members").append({"file_name": file_name,
195                                                               "line_number": mb["line_number"],
196                                                               "name": mb["name"],
197                                                               "type": mb["type"] + " *"})
198                        else:
199                            struct_dict.get("members").append({"file_name": file_name,
200                                                               "line_number": mb["line_number"],
201                                                               "name": mb["name"],
202                                                               "type": mb["type"]})
203                    self._header_dict.get("struct").append(struct_dict)
204
205    def _extract_interface(self, stack):
206        """
207        Extract interfaces from structs or classes which have some methods
208        """
209        interface_dict = {}
210        if stack["declaration_method"] in ["struct", "class"]:
211            # 带函数,或变量中包含函数指针
212            if len(stack["methods"]["public"]) > 0 or self._has_function_pointer(stack["properties"]["public"]):
213                interface_dict["name"] = stack["name"]
214                interface_dict["members"] = []
215                for mb in stack["methods"]["public"]:
216                    if mb["name"] in (stack["name"], "DECLARE_INTERFACE_DESCRIPTOR"):
217                        continue
218                    params = []
219                    for param in mb["parameters"]:
220                        para_name = param["name"]
221                        if para_name == '':
222                            para_name = "rand_name_%d" % self._rand_name_count
223                            self._rand_name_count += 1
224                        params.append({"name": para_name, "type": param["type"]})
225                    interface_dict.get("members").append(
226                        {"name": mb["name"],
227                         "params": params,
228                         "file_name":
229                             self._header_dict.get("path") + "/" + self._header_dict.get("name"),
230                         "line_number": mb["line_number"]})
231                for mb in stack["properties"]["public"]:
232                    if mb["function_pointer"] > 0:
233                        interface_dict.get("members").append(
234                            {"name": mb["name"],
235                             "params": self._checkout_function_pointer_param(mb["type"]),
236                             "file_name":
237                                 self._header_dict.get("path") + "/" + self._header_dict.get("name"),
238                             "line_number": mb["line_number"]})
239                self._header_dict.get("interface").append(interface_dict)
240
241    def _extract_typedef(self, typedefs):
242        """
243        Extract typedef from global typedefs
244        e.g.
245        "typedefs": {
246            "HotPlugCallback": "typedef void ( * ) ( uint32_t devId , bool connected , void * data )",
247            "AudioHandle": "void *"
248        }
249        """
250        for td in typedefs:
251            if "typedef" in typedefs[td]:
252                self._header_dict.get("typedef").append({"name": td,
253                                                         "type": "/* unsupported function pointer type: " + td + " */"})
254            else:
255                self._header_dict.get("typedef").append({"name": td, "type": typedefs[td]})
256