1#!/usr/bin/env python 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 10import json 11import os 12import re 13import stat 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 finally: 47 pass 48 49 try: 50 self._extract_path_and_file(header_file) 51 self._extract_include(hjson["includes"]) 52 self._extract_enum(hjson["enums"]) 53 for i in hjson["classes"]: 54 self._extract_union(hjson["classes"][i]) 55 self._extract_struct(hjson["classes"][i]) 56 self._extract_interface(hjson["classes"][i]) 57 self._extract_typedef(hjson["typedefs"]) 58 return self._header_dict 59 except KeyError: 60 pass 61 finally: 62 pass 63 64 @staticmethod 65 def _pre_handle(header_file): 66 back_file = header_file + ".back" 67 copyfile(header_file, back_file) 68 with open(header_file, 'r') as f: 69 new_lines = "" 70 for line in f: 71 tt = re.match(r".*enum *((\w+) *\*) *\w* *;", line) 72 if tt: 73 new_line = line.replace(tt[1], tt[2] + "_ENUM_POINTER ") 74 new_lines += new_line 75 continue 76 tt = re.match(r".*(\[\w* *\(.*\) *]) *", line) 77 if tt: 78 new_line = line.replace(tt[1], "[]") 79 new_lines += new_line 80 continue 81 else: 82 new_lines += line 83 flags = os.O_RDWR | os.O_CREAT 84 modes = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IROTH 85 with os.fdopen(os.open(header_file, flags, modes), 86 "w", encoding="utf-8") as file: 87 file.write(new_lines) 88 return back_file 89 90 @staticmethod 91 def _has_function_pointer(jvs): 92 for jv in jvs: 93 if jv["function_pointer"] > 0: 94 return True 95 return False 96 97 def _checkout_function_pointer_param(self, params): 98 if params == '': 99 return [] 100 tt = re.match(r"(typedef )* *\w+ \( \* \) \(( .* )\)", params) 101 if tt: 102 params = tt[2].strip() + "," 103 else: 104 print("error cant analyze function pointer params: ", params) 105 return [] 106 ret = [] 107 while params: 108 tt = re.match(" *(const )*(struct |enum |union )* *", params) 109 110 if not tt: 111 print("error cant analyze param :[%s]" % params) 112 break 113 114 params = params[tt.regs[0][1]:] 115 tt = re.match(r"((unsigned )*[a-zA-Z0-9_:]+( *\** *\**)) *", params) 116 if not tt: 117 ret.append({"name": '', "type": params.strip(',').strip()}) 118 break 119 120 param_type = params[tt.regs[1][0]:tt.regs[1][1]] # 参数类型 121 params = params[tt.regs[0][1]:] 122 tt = re.match("([a-zA-Z0-9_]+) *(,)", params) 123 if tt: 124 param_name = params[tt.regs[1][0]:tt.regs[1][1]] 125 params = params[tt.regs[0][1]:] 126 else: # 没有定义变量名的,设置一个默认变量名 127 param_name = "rand_name_%d" % self._rand_name_count 128 self._rand_name_count += 1 129 params = params[params.index(",") + 1:] 130 ret.append({"name": param_name, "type": param_type.strip()}) 131 return ret 132 133 def _extract_path_and_file(self, path): 134 self._header_dict["path"], self._header_dict["name"] = os.path.split(path) 135 136 def _extract_include(self, includes): 137 """ 138 Extract imports from includes 139 """ 140 for include in includes: 141 if '<' not in include: # ignore system files 142 self._header_dict.get("import").append(include[1:-1]) 143 144 def _extract_enum(self, enums): 145 """ 146 Extract enums from enums 147 """ 148 for enm in enums: 149 if "name" in enm: 150 enum_dict = {"name": enm["name"], "members": []} 151 else: 152 enum_dict = {"name": "LostName_%d" % self._rand_name_count, "members": []} 153 self._rand_name_count += 1 154 for value in enm["values"]: 155 v_value = value["value"] 156 if isinstance(v_value, int): 157 enum_dict["members"].append({"name": value["name"], "value": v_value}) 158 continue 159 tt = re.match(r"0x|0X|\(|-", v_value) 160 if tt: 161 errmsg = "unexpected '%s'" % tt[0] 162 v_value = v_value + " // " + errmsg 163 print("[HeaderParser]: %s[line %d] " % (enm["filename"], enm["line_number"]), errmsg) 164 enum_dict["members"].append({"name": value["name"], "value": v_value}) 165 self._header_dict.get("enum").append(enum_dict) 166 167 def _extract_union(self, stack): 168 """ 169 Extract unions from classes 170 """ 171 union_dict = {} 172 if stack["declaration_method"] == "union": 173 union_dict["name"] = stack["name"].split(" ")[-1] 174 union_dict["type"] = "union" 175 union_dict["members"] = [] 176 file_name = self._header_dict.get("path") + "/" + self._header_dict.get("name") 177 for mb in stack["members"]: 178 union_dict.get("members").append({ 179 "file_name": file_name, 180 "line_number": mb["line_number"], 181 "name": mb["name"], "type": mb["type"] 182 }) 183 self._header_dict.get("union").append(union_dict) 184 185 def _extract_struct(self, stack): 186 """ 187 Extract structs from structs or classes that have no methods 188 """ 189 struct_dict = {} 190 if stack["declaration_method"] not in ["struct", "class"]: 191 return 192 if len(stack["methods"]["public"]) != 0: 193 return 194 if self._has_function_pointer(stack["properties"]["public"]): 195 return 196 struct_dict["name"] = stack["name"] 197 struct_dict["type"] = "struct" 198 struct_dict["members"] = [] 199 file_name = self._header_dict.get("path") + "/" + self._header_dict.get("name") 200 for mb in stack["properties"]["public"]: 201 if "enum_type" in mb: 202 struct_dict.get("members").append({ 203 "file_name": file_name, 204 "line_number": mb["line_number"], 205 "name": mb["name"], 206 "type": mb["enum_type"]["name"] 207 }) 208 elif mb["array"] == 1: 209 struct_dict.get("members").append({ 210 "file_name": file_name, 211 "line_number": mb["line_number"], 212 "name": mb["name"], 213 "type": mb["type"] + " *" 214 }) 215 else: 216 struct_dict.get("members").append({ 217 "file_name": file_name, 218 "line_number": mb["line_number"], 219 "name": mb["name"], 220 "type": mb["type"] 221 }) 222 self._header_dict.get("struct").append(struct_dict) 223 224 def _extract_interface(self, stack): 225 """ 226 Extract interfaces from structs or classes which have some methods 227 """ 228 interface_dict = {} 229 if stack["declaration_method"] not in ["struct", "class"]: 230 return 231 # 带函数,或变量中包含函数指针 232 if len(stack["methods"]["public"]) <= 0 and not self._has_function_pointer(stack["properties"]["public"]): 233 return 234 interface_dict["name"] = stack["name"] 235 interface_dict["members"] = [] 236 for mb in stack["methods"]["public"]: 237 if mb["name"] in (stack["name"], "DECLARE_INTERFACE_DESCRIPTOR"): 238 continue 239 params = [] 240 for param in mb["parameters"]: 241 para_name = param["name"] 242 if para_name == '': 243 para_name = "rand_name_%d" % self._rand_name_count 244 self._rand_name_count += 1 245 params.append({"name": para_name, "type": param["type"]}) 246 interface_dict.get("members").append({ 247 "name": mb["name"], 248 "params": params, 249 "file_name": self._header_dict.get("path") + "/" + self._header_dict.get("name"), 250 "line_number": mb["line_number"]}) 251 for mb in stack["properties"]["public"]: 252 if mb["function_pointer"] <= 0: 253 continue 254 interface_dict.get("members").append({ 255 "name": mb["name"], 256 "params": self._checkout_function_pointer_param(mb["type"]), 257 "file_name": self._header_dict.get("path") + "/" + self._header_dict.get("name"), 258 "line_number": mb["line_number"]}) 259 self._header_dict.get("interface").append(interface_dict) 260 261 def _extract_typedef(self, typedefs): 262 """ 263 Extract typedef from global typedefs 264 e.g. 265 "typedefs": { 266 "HotPlugCallback": "typedef void ( * ) ( uint32_t devId , bool connected , void * data )", 267 "AudioHandle": "void *" 268 } 269 """ 270 for td in typedefs: 271 if "typedef" in typedefs[td]: 272 error_comment = "/* unsupported function pointer type: %s */" % (td) 273 self._header_dict.get("typedef").append({"name": td, "type": error_comment}) 274 else: 275 self._header_dict.get("typedef").append({"name": td, "type": typedefs[td]}) 276