"""
* Copyright (c) 2022 Shenzhen Kaihong Digital Industry Development Co., Ltd.
*
* HDF is dual licensed: you can use it either under the terms of
* the GPL, or the BSD license, at your option.
* See the LICENSE file in the root of this repository for complete details.
"""
import json
import os
import re
from shutil import copyfile, move
import CppHeaderParser
class HeaderParser:
"""
Extract file path, file name, includes, enums, unions, structs, interfaces and callbacks
from CppHeaderParser
"""
def __init__(self):
self._header_dict = {
"name": "",
"path": "",
"import": [],
"enum": [],
"union": [],
"struct": [],
"typedef": [],
"interface": [],
"callback": []
}
self._rand_name_count = 0
def parse(self, header_file):
try:
hjson = json.loads(CppHeaderParser.CppHeader(header_file).toJSON())
except CppHeaderParser.CppParseError:
back_file = self._pre_handle(header_file)
hjson = json.loads(CppHeaderParser.CppHeader(header_file).toJSON())
move(back_file, header_file)
try:
self._extract_path_and_file(header_file)
self._extract_include(hjson["includes"])
self._extract_enum(hjson["enums"])
for i in hjson["classes"]:
self._extract_union(hjson["classes"][i])
self._extract_struct(hjson["classes"][i])
self._extract_interface(hjson["classes"][i])
self._extract_typedef(hjson["typedefs"])
return self._header_dict
except KeyError:
pass
@staticmethod
def _pre_handle(header_file):
back_file = header_file + ".back"
copyfile(header_file, back_file)
f = open(header_file, 'r')
new_lines = ""
for line in f:
tt = re.match(r".*enum *((\w+) *\*) *\w* *;", line)
if tt:
new_line = line.replace(tt[1], tt[2] + "_ENUM_POINTER ")
new_lines += new_line
continue
tt = re.match(r".*(\[\w* *\(.*\) *]) *", line)
if tt:
new_line = line.replace(tt[1], "[]")
new_lines += new_line
continue
else:
new_lines += line
f.close()
f = open(header_file, 'w')
f.writelines(new_lines)
f.close()
return back_file
@staticmethod
def _has_function_pointer(jvs):
for jv in jvs:
if jv["function_pointer"] > 0:
return True
return False
def _checkout_function_pointer_param(self, params):
if params == '':
return []
tt = re.match(r"(typedef )* *\w+ \( \* \) \(( .* )\)", params)
if tt:
params = tt[2].strip() + ","
else:
print("error cant analyze function pointer params: ", params)
return []
ret = []
while params:
tt = re.match(" *(const )*(struct |enum |union )* *", params)
if tt:
params = params[tt.regs[0][1]:]
tt = re.match(r"((unsigned )*[a-zA-Z0-9_:]+( *\** *\**)) *", params)
if tt:
param_type = params[tt.regs[1][0]:tt.regs[1][1]]
params = params[tt.regs[0][1]:]
tt = re.match("([a-zA-Z0-9_]+) *(,)", params)
if tt:
param_name = params[tt.regs[1][0]:tt.regs[1][1]]
params = params[tt.regs[0][1]:]
else:
param_name = "rand_name_%d" % self._rand_name_count
self._rand_name_count += 1
params = params[params.index(",") + 1:]
ret.append({"name": param_name, "type": param_type.strip()})
else:
ret.append({"name": '', "type": params.strip(',').strip()})
break
else:
print("error cant analyze param :[%s]" % params)
break
return ret
def _extract_path_and_file(self, path):
self._header_dict["path"], self._header_dict["name"] = os.path.split(path)
def _extract_include(self, includes):
"""
Extract imports from includes
"""
for include in includes:
if '<' not in include:
self._header_dict.get("import").append(include[1:-1])
def _extract_enum(self, enums):
"""
Extract enums from enums
"""
for enm in enums:
if "name" in enm:
enum_dict = {"name": enm["name"], "members": []}
else:
enum_dict = {"name": "LostName_%d" % self._rand_name_count, "members": []}
self._rand_name_count += 1
for value in enm["values"]:
v_value = value["value"]
if not isinstance(v_value, int):
tt = re.match(r"0x|0X|\(|-", v_value)
if tt:
errmsg = "unexpected '%s'" % tt[0]
v_value = v_value + " // " + errmsg
print("[HeaderParser]: %s[line %d] " % (enm["filename"], enm["line_number"]), errmsg)
enum_dict["members"].append({"name": value["name"], "value": v_value})
self._header_dict.get("enum").append(enum_dict)
def _extract_union(self, stack):
"""
Extract unions from classes
"""
union_dict = {}
if stack["declaration_method"] == "union":
union_dict["name"] = stack["name"].split(" ")[-1]
union_dict["type"] = "union"
union_dict["members"] = []
file_name = self._header_dict.get("path") + "/" + self._header_dict.get("name")
for mb in stack["members"]:
union_dict.get("members").append({"file_name": file_name,
"line_number": mb["line_number"],
"name": mb["name"], "type": mb["type"]})
self._header_dict.get("union").append(union_dict)
def _extract_struct(self, stack):
"""
Extract structs from structs or classes that have no methods
"""
struct_dict = {}
if stack["declaration_method"] in ["struct", "class"]:
if len(stack["methods"]["public"]) == 0:
if not self._has_function_pointer(stack["properties"]["public"]):
struct_dict["name"] = stack["name"]
struct_dict["type"] = "struct"
struct_dict["members"] = []
file_name = self._header_dict.get("path") + "/" + self._header_dict.get("name")
for mb in stack["properties"]["public"]:
if "enum_type" in mb:
struct_dict.get("members").append({"file_name": file_name,
"line_number": mb["line_number"],
"name": mb["name"],
"type": mb["enum_type"]["name"]})
elif mb["array"] == 1:
struct_dict.get("members").append({"file_name": file_name,
"line_number": mb["line_number"],
"name": mb["name"],
"type": mb["type"] + " *"})
else:
struct_dict.get("members").append({"file_name": file_name,
"line_number": mb["line_number"],
"name": mb["name"],
"type": mb["type"]})
self._header_dict.get("struct").append(struct_dict)
def _extract_interface(self, stack):
"""
Extract interfaces from structs or classes which have some methods
"""
interface_dict = {}
if stack["declaration_method"] in ["struct", "class"]:
if len(stack["methods"]["public"]) > 0 or self._has_function_pointer(stack["properties"]["public"]):
interface_dict["name"] = stack["name"]
interface_dict["members"] = []
for mb in stack["methods"]["public"]:
if mb["name"] in (stack["name"], "DECLARE_INTERFACE_DESCRIPTOR"):
continue
params = []
for param in mb["parameters"]:
para_name = param["name"]
if para_name == '':
para_name = "rand_name_%d" % self._rand_name_count
self._rand_name_count += 1
params.append({"name": para_name, "type": param["type"]})
interface_dict.get("members").append(
{"name": mb["name"],
"params": params,
"file_name":
self._header_dict.get("path") + "/" + self._header_dict.get("name"),
"line_number": mb["line_number"]})
for mb in stack["properties"]["public"]:
if mb["function_pointer"] > 0:
interface_dict.get("members").append(
{"name": mb["name"],
"params": self._checkout_function_pointer_param(mb["type"]),
"file_name":
self._header_dict.get("path") + "/" + self._header_dict.get("name"),
"line_number": mb["line_number"]})
self._header_dict.get("interface").append(interface_dict)
def _extract_typedef(self, typedefs):
"""
Extract typedef from global typedefs
e.g.
"typedefs": {
"HotPlugCallback": "typedef void ( * ) ( uint32_t devId , bool connected , void * data )",
"AudioHandle": "void *"
}
"""
for td in typedefs:
if "typedef" in typedefs[td]:
self._header_dict.get("typedef").append({"name": td,
"type": "/* unsupported function pointer type: " + td + " */"})
else:
self._header_dict.get("typedef").append({"name": td, "type": typedefs[td]})