# # The array-of-pointers case. Dig the parameter name out from the middle # of the declaration. # elif KernRe(r'\(.+\)\s*\[').search(arg): r = KernRe(r'[^\(]+\(\s*\*\s*' # Up to "(" and maybe "*" r'([\w.]*?)' # The actual pointer name r'\s*(\[\s*\w+\s*\]\s*)*\)') # The [array portion] if r.match(arg): param = r.group(1) else: self.emit_msg(ln, f"Invalid param: {arg}") param = arg dtype = arg.replace(param, '') self.push_parameter(ln, decl_type, param, dtype, arg, declaration_name) elif arg: # # Clean up extraneous spaces and split the string at commas; the first # element of the resulting list will also include the type information. # arg = KernRe(r'\s*:\s*').sub(":", arg) arg = KernRe(r'\s*\[').sub('[', arg) args = KernRe(r'\s*,\s*').split(arg) args[0] = re.sub(r'(\*+)\s*', r' \1', args[0]) # # args[0] has a string of "type a". If "a" includes an [array] # declaration, we want to not be fooled by any white space inside # the brackets, so detect and handle that case specially. # r = KernRe(r'^([^[\]]*\s+)(.*)$') if r.match(args[0]): args[0] = r.group(2) dtype = r.group(1) else: # No space in args[0]; this seems wrong but preserves previous behavior dtype = '' bitfield_re = KernRe(r'(.*?):(\w+)') for param in args: # # For pointers, shift the star(s) from the variable name to the # type declaration. # r = KernRe(r'^(\*+)\s*(.*)') if r.match(param): self.push_parameter(ln, decl_type, r.group(2), f"{dtype} {r.group(1)}", arg, declaration_name) # # Perform a similar shift for bitfields. # elif bitfield_re.search(param): if dtype != "": # Skip unnamed bit-fields self.push_parameter(ln, decl_type, bitfield_re.group(1), f"{dtype}:{bitfield_re.group(2)}", arg, declaration_name) else: self.push_parameter(ln, decl_type, param, dtype, arg, declaration_name) def check_sections(self, ln, decl_name, decl_type): """ Check for errors inside sections, emitting warnings if not found parameters are described. """ for section in self.entry.sections: if section not in self.entry.parameterlist and \ not known_sections.search(section): if decl_type == 'function': dname = f"{decl_type} parameter" else: dname = f"{decl_type} member" self.emit_msg(ln, f"Excess {dname} '{section}' description in '{decl_name}'") # # Check that documented parameter names (from doc comments, including # inline ``/** @member: */`` tags) actually match real members in # the declaration. This catches mismatched or stale kernel-doc # member tags that don't correspond to any actual struct/union # member or function parameter. # for param_name, desc in self.entry.parameterdescs.items(): # Skip auto-generated entries from push_parameter() if desc == self.undescribed: continue if desc in ("no arguments", "anonymous\n", "variable arguments"): continue if param_name.startswith("{unnamed_"): continue if param_name in self.entry.parameterlist: continue if decl_type == 'function': dname = f"{decl_type} parameter" else: dname = f"{decl_type} member" self.emit_msg(ln, f"Excess {dname} '{param_name}' description in '{decl_name}'") def check_return_section(self, ln, declaration_name, return_type): """ If the function doesn't return void, warns about the lack of a return description. """ if not self.config.wreturn: return # Ignore an empty return type (It's a macro) # Ignore functions with a "void" return type (but not "void *") if not return_type or KernRe(r'void\s*\w*\s*$').search(return_type): return if not self.entry.sections.get("Return", None): self.emit_msg(ln, f"No description found for return value of '{declaration_name}'") def split_struct_proto(self, proto): """ Split apart a structure prototype; returns (struct|union, name, members) or ``None``. """ type_pattern = r'(struct|union)' qualifiers = [ "__attribute__", "__packed", "__aligned", "____cacheline_aligned_in_smp", "____cacheline_aligned", ] definition_body = r'\{(.*)\}\s*' + "(?:" + '|'.join(qualifiers) + ")?" r = KernRe(type_pattern + r'\s+(\w+)\s*' + definition_body) if r.search(proto): return (r.group(1), r.group(2), r.group(3)) else: r = KernRe(r'typedef\s+' + type_pattern + r'\s*' + definition_body + r'\s*(\w+)\s*;') if r.search(proto): return (r.group(1), r.group(3), r.group(2)) return None def rewrite_struct_members(self, members): """ Process ``struct``/``union`` members from the most deeply nested outward. Rewrite the members of a ``struct`` or ``union`` for easier formatting later on. Among other things, this function will turn a member like:: struct { inner_members; } foo; into:: struct foo; inner_members; """ # # The trick is in the ``^{`` below - it prevents a match of an outer # ``struct``/``union`` until the inner one has been munged # (removing the ``{`` in the process). # struct_members = KernRe(r'(struct|union)' # 0: declaration type r'([^\{\};]+)' # 1: possible name r'(\{)' r'([^\{\}]*)' # 3: Contents of declaration r'(\})' r'([^\{\};]*)(;)') # 5: Remaining stuff after declaration tuples = struct_members.findall(members) while tuples: for t in tuples: newmember = "" oldmember = "".join(t) # Reconstruct the original formatting dtype, name, lbr, content, rbr, rest, semi = t # # Pass through each field name, normalizing the form and formatting. # for s_id in rest.split(','): s_id = s_id.strip() newmember += f"{dtype} {s_id}; " # # Remove bitfield/array/pointer info, getting the bare name. # s_id = KernRe(r'[:\[].*').sub('', s_id) s_id = KernRe(r'^\s*\**(\S+)\s*').sub(r'\1', s_id) # # Pass through the members of this inner structure/union. # for arg in content.split(';'): arg = arg.strip() # # Look for (type)(*name)(args) - pointer to function # r = KernRe(r'^([^\(]+\(\*?\s*)([\w.]*)(\s*\).*)') if r.match(arg): dtype, name, extra = r.group(1), r.group(2), r.group(3) # Pointer-to-function if not s_id: # Anonymous struct/union newmember += f"{dtype}{name}{extra}; " else: newmember += f"{dtype}{s_id}.{name}{extra}; " # # Otherwise a non-function member. # else: # # Remove bitmap and array portions and spaces around commas # arg = KernRe(r':\s*\d+\s*').sub('', arg) arg = KernRe(r'\[.*\]').sub('', arg) arg = KernRe(r'\s*,\s*').sub(',', arg) # # Look for a normal decl - "type name[,name...]" # r = KernRe(r'(.*)\s+([\S+,]+)') if r.search(arg): for name in r.group(2).split(','): name = KernRe(r'^\s*\**(\S+)\s*').sub(r'\1', name) if not s_id: # Anonymous struct/union newmember += f"{r.group(1)} {name}; " else: newmember += f"{r.group(1)} {s_id}.{name}; " else: newmember += f"{arg}; " # # At the end of the s_id loop, replace the original declaration with # the munged version. # members = members.replace(oldmember, newmember) # # End of the tuple loop - search again and see if there are outer members # that now turn up. # tuples = struct_members.findall(members) return members def format_struct_decl(self, declaration): """ Format the ``struct`` declaration into a standard form for inclusion in the resulting docs. """ # # Insert newlines, get rid of extra spaces. # declaration = KernRe(r'([\{;])').sub(r'\1\n', declaration) declaration = KernRe(r'\}\s+;').sub('};', declaration) # # Format inline enums with each member on its own line. # r = KernRe(r'(enum\s+\{[^\}]+),([^\n])') while r.search(declaration): declaration = r.sub(r'\1,\n\2', declaration) # # Now go through and supply the right number of tabs # for each line. # def_args = declaration.split('\n') level = 1 declaration = "" for clause in def_args: clause = KernRe(r'\s+').sub(' ', clause.strip(), count=1) if clause: if '}' in clause and level > 1: level -= 1 if not clause.startswith('#'): declaration += "\t" * level declaration += "\t" + clause + "\n" if "{" in clause and "}" not in clause: level += 1 return declaration def dump_struct(self, ln, proto, source): """ Store an entry for a ``struct`` or ``union`` """ # # Do the basic parse to get the pieces of the declaration. # source = source proto = trim_private_members(proto) struct_parts = self.split_struct_proto(proto) if not struct_parts: self.emit_msg(ln, f"{proto} error: Cannot parse struct or union!") return decl_type, declaration_name, members = struct_parts if self.entry.identifier != declaration_name: self.emit_msg(ln, f"expecting prototype for {decl_type} {self.entry.identifier}. " f"Prototype was for {decl_type} {declaration_name} instead\n") return # # Go through the list of members applying all of our transformations. # members = self.xforms.apply("struct", members) # # Deal with embedded struct and union members, and drop enums entirely. # declaration = members members = self.rewrite_struct_members(members) members = re.sub(r'(\{[^\{\}]*\})', '', members) # # Output the result and we are done. # self.create_parameter_list(ln, decl_type, members, ';', declaration_name) self.check_sections(ln, declaration_name, decl_type) self.output_declaration(decl_type, declaration_name, source=source, definition=self.format_struct_decl(declaration), purpose=self.entry.declaration_purpose) def dump_enum(self, ln, proto, source): """ Store an ``enum`` inside self.entries array. """ # # Strip preprocessor directives. Note that this depends on the # trailing semicolon we added in process_proto_type(). # source = source proto = trim_private_members(proto) proto = KernRe(r'#\s*((define|ifdef|if)\s+|endif)[^;]*;', flags=re.S).sub('', proto) # # Parse out the name and members of the enum. Typedef form first. # r = KernRe(r'typedef\s+enum\s*\{(.*)\}\s*(\w*)\s*;') if r.search(proto): declaration_name = r.group(2) members = r.group(1) # # Failing that, look for a straight enum # else: r = KernRe(r'enum\s+(\w*)\s*\{(.*)\}') if r.match(proto): declaration_name = r.group(1) members = r.group(2) # # OK, this isn't going to work. # else: self.emit_msg(ln, f"{proto}: error: Cannot parse enum!") return # # Make sure we found what we were expecting. # if self.entry.identifier != declaration_name: if self.entry.identifier == "": self.emit_msg(ln, f"{proto}: wrong kernel-doc identifier on prototype") else: self.emit_msg(ln, f"expecting prototype for enum {self.entry.identifier}. " f"Prototype was for enum {declaration_name} instead") return if not declaration_name: declaration_name = "(anonymous)" # # Parse out the name of each enum member, and verify that we # have a description for it. # member_set = set() members = KernRe(r'\([^;)]*\)').sub('', members) for arg in members.split(','): arg = KernRe(r'^\s*(\w+).*').sub(r'\1', arg) if not arg.strip(): continue self.entry.parameterlist.append(arg) if arg not in self.entry.parameterdescs: self.entry.parameterdescs[arg] = self.undescribed self.emit_msg(ln, f"Enum value '{arg}' not described in enum '{declaration_name}'") member_set.add(arg) # # Ensure that every described member actually exists in the enum. # for k in self.entry.parameterdescs: if k not in member_set: self.emit_msg(ln, f"Excess enum value '@{k}' description in '{declaration_name}'") self.output_declaration('enum', declaration_name, source=source, purpose=self.entry.declaration_purpose) def dump_var(self, ln, proto, source): """ Store variables that are part of kAPI. """ VAR_ATTRIBS = [ "extern", "const", ] OPTIONAL_VAR_ATTR = r"^(?:\b(?:" +"|".join(VAR_ATTRIBS) +r")\b\s*)*" # # Store the full prototype before modifying it # source = source full_proto = proto declaration_name = None # # Handle macro definitions # macro_prefixes = [ KernRe(r"DEFINE_[\w_]+\s*\(([\w_]+)\)"), ] for r in macro_prefixes: match = r.search(proto) if match: declaration_name = match.group(1) break # # Drop comments and macros to have a pure C prototype # if not declaration_name: proto = self.xforms.apply("var", proto) proto = proto.rstrip() # # Variable name is at the end of the declaration # default_val = None r= KernRe(OPTIONAL_VAR_ATTR + r"\s*[\w_\s]*\s+(?:\*+)?([\w_]+)\s*[\d\]\[]*\s*(=.*)?") if r.match(proto): if not declaration_name: declaration_name = r.group(1) default_val = r.group(2) else: r= KernRe(OPTIONAL_VAR_ATTR + r"(?:[\w_\s]*)?\s+(?:\*+)?(?:[\w_]+)\s*[\d\]\[]*\s*(=.*)?") if r.match(proto): default_val = r.group(1) if not declaration_name: self.emit_msg(ln,f"{proto}: can't parse variable") return if default_val: default_val = default_val.lstrip("=").strip() self.output_declaration("var", declaration_name, source=source, full_proto=full_proto, default_val=default_val, purpose=self.entry.declaration_purpose) def dump_declaration(self, ln, prototype, source): """ Store a data declaration inside self.entries array. """ if self.entry.decl_type == "enum": self.dump_enum(ln, prototype, source) elif self.entry.decl_type == "typedef": self.dump_typedef(ln, prototype, source) elif self.entry.decl_type in ["union", "struct"]: self.dump_struct(ln, prototype, source) elif self.entry.decl_type == "var": self.dump_var(ln, prototype, source) else: # This would be a bug self.emit_message(ln, f'Unknown declaration type: {self.entry.decl_type}') def dump_function(self, ln, prototype, source): """ Store a function or function macro inside self.entries array. """ source = source found = func_macro = False return_type = '' decl_type = 'function' # # If we have a macro, remove the "#define" at the front. # new_proto = KernRe(r"^#\s*define\s+").sub("", prototype) if new_proto != prototype: prototype = new_proto # # Dispense with the simple "#define A B" case here; the key # is the space after the name of the symbol being defined. # NOTE that the seemingly misnamed "func_macro" indicates a # macro *without* arguments. #Lines 501–1000 of 1731. Previous lines Load more
# # The array-of-pointers case. Dig the parameter name out from the middle # of the declaration. # elif KernRe(r'\(.+\)\s*\[').search(arg): r = KernRe(r'[^\(]+\(\s*\*\s*' # Up to "(" and maybe "*" r'([\w.]*?)' # The actual pointer name r'\s*(\[\s*\w+\s*\]\s*)*\)') # The [array portion] if r.match(arg): param = r.group(1) else: self.emit_msg(ln, f"Invalid param: {arg}") param = arg dtype = arg.replace(param, '') self.push_parameter(ln, decl_type, param, dtype, arg, declaration_name) elif arg: # # Clean up extraneous spaces and split the string at commas; the first # element of the resulting list will also include the type information. # arg = KernRe(r'\s*:\s*').sub(":", arg) arg = KernRe(r'\s*\[').sub('[', arg) args = KernRe(r'\s*,\s*').split(arg) args[0] = re.sub(r'(\*+)\s*', r' \1', args[0]) # # args[0] has a string of "type a". If "a" includes an [array] # declaration, we want to not be fooled by any white space inside # the brackets, so detect and handle that case specially. # r = KernRe(r'^([^[\]]*\s+)(.*)$') if r.match(args[0]): args[0] = r.group(2) dtype = r.group(1) else: # No space in args[0]; this seems wrong but preserves previous behavior dtype = '' bitfield_re = KernRe(r'(.*?):(\w+)') for param in args: # # For pointers, shift the star(s) from the variable name to the # type declaration. # r = KernRe(r'^(\*+)\s*(.*)') if r.match(param): self.push_parameter(ln, decl_type, r.group(2), f"{dtype} {r.group(1)}", arg, declaration_name) # # Perform a similar shift for bitfields. # elif bitfield_re.search(param): if dtype != "": # Skip unnamed bit-fields self.push_parameter(ln, decl_type, bitfield_re.group(1), f"{dtype}:{bitfield_re.group(2)}", arg, declaration_name) else: self.push_parameter(ln, decl_type, param, dtype, arg, declaration_name) def check_sections(self, ln, decl_name, decl_type): """ Check for errors inside sections, emitting warnings if not found parameters are described. """ for section in self.entry.sections: if section not in self.entry.parameterlist and \ not known_sections.search(section): if decl_type == 'function': dname = f"{decl_type} parameter" else: dname = f"{decl_type} member" self.emit_msg(ln, f"Excess {dname} '{section}' description in '{decl_name}'") # # Check that documented parameter names (from doc comments, including # inline ``/** @member: */`` tags) actually match real members in # the declaration. This catches mismatched or stale kernel-doc # member tags that don't correspond to any actual struct/union # member or function parameter. # for param_name, desc in self.entry.parameterdescs.items(): # Skip auto-generated entries from push_parameter() if desc == self.undescribed: continue if desc in ("no arguments", "anonymous\n", "variable arguments"): continue if param_name.startswith("{unnamed_"): continue if param_name in self.entry.parameterlist: continue if decl_type == 'function': dname = f"{decl_type} parameter" else: dname = f"{decl_type} member" self.emit_msg(ln, f"Excess {dname} '{param_name}' description in '{decl_name}'") def check_return_section(self, ln, declaration_name, return_type): """ If the function doesn't return void, warns about the lack of a return description. """ if not self.config.wreturn: return # Ignore an empty return type (It's a macro) # Ignore functions with a "void" return type (but not "void *") if not return_type or KernRe(r'void\s*\w*\s*$').search(return_type): return if not self.entry.sections.get("Return", None): self.emit_msg(ln, f"No description found for return value of '{declaration_name}'") def split_struct_proto(self, proto): """ Split apart a structure prototype; returns (struct|union, name, members) or ``None``. """ type_pattern = r'(struct|union)' qualifiers = [ "__attribute__", "__packed", "__aligned", "____cacheline_aligned_in_smp", "____cacheline_aligned", ] definition_body = r'\{(.*)\}\s*' + "(?:" + '|'.join(qualifiers) + ")?" r = KernRe(type_pattern + r'\s+(\w+)\s*' + definition_body) if r.search(proto): return (r.group(1), r.group(2), r.group(3)) else: r = KernRe(r'typedef\s+' + type_pattern + r'\s*' + definition_body + r'\s*(\w+)\s*;') if r.search(proto): return (r.group(1), r.group(3), r.group(2)) return None def rewrite_struct_members(self, members): """ Process ``struct``/``union`` members from the most deeply nested outward. Rewrite the members of a ``struct`` or ``union`` for easier formatting later on. Among other things, this function will turn a member like:: struct { inner_members; } foo; into:: struct foo; inner_members; """ # # The trick is in the ``^{`` below - it prevents a match of an outer # ``struct``/``union`` until the inner one has been munged # (removing the ``{`` in the process). # struct_members = KernRe(r'(struct|union)' # 0: declaration type r'([^\{\};]+)' # 1: possible name r'(\{)' r'([^\{\}]*)' # 3: Contents of declaration r'(\})' r'([^\{\};]*)(;)') # 5: Remaining stuff after declaration tuples = struct_members.findall(members) while tuples: for t in tuples: newmember = "" oldmember = "".join(t) # Reconstruct the original formatting dtype, name, lbr, content, rbr, rest, semi = t # # Pass through each field name, normalizing the form and formatting. # for s_id in rest.split(','): s_id = s_id.strip() newmember += f"{dtype} {s_id}; " # # Remove bitfield/array/pointer info, getting the bare name. # s_id = KernRe(r'[:\[].*').sub('', s_id) s_id = KernRe(r'^\s*\**(\S+)\s*').sub(r'\1', s_id) # # Pass through the members of this inner structure/union. # for arg in content.split(';'): arg = arg.strip() # # Look for (type)(*name)(args) - pointer to function # r = KernRe(r'^([^\(]+\(\*?\s*)([\w.]*)(\s*\).*)') if r.match(arg): dtype, name, extra = r.group(1), r.group(2), r.group(3) # Pointer-to-function if not s_id: # Anonymous struct/union newmember += f"{dtype}{name}{extra}; " else: newmember += f"{dtype}{s_id}.{name}{extra}; " # # Otherwise a non-function member. # else: # # Remove bitmap and array portions and spaces around commas # arg = KernRe(r':\s*\d+\s*').sub('', arg) arg = KernRe(r'\[.*\]').sub('', arg) arg = KernRe(r'\s*,\s*').sub(',', arg) # # Look for a normal decl - "type name[,name...]" # r = KernRe(r'(.*)\s+([\S+,]+)') if r.search(arg): for name in r.group(2).split(','): name = KernRe(r'^\s*\**(\S+)\s*').sub(r'\1', name) if not s_id: # Anonymous struct/union newmember += f"{r.group(1)} {name}; " else: newmember += f"{r.group(1)} {s_id}.{name}; " else: newmember += f"{arg}; " # # At the end of the s_id loop, replace the original declaration with # the munged version. # members = members.replace(oldmember, newmember) # # End of the tuple loop - search again and see if there are outer members # that now turn up. # tuples = struct_members.findall(members) return members def format_struct_decl(self, declaration): """ Format the ``struct`` declaration into a standard form for inclusion in the resulting docs. """ # # Insert newlines, get rid of extra spaces. # declaration = KernRe(r'([\{;])').sub(r'\1\n', declaration) declaration = KernRe(r'\}\s+;').sub('};', declaration) # # Format inline enums with each member on its own line. # r = KernRe(r'(enum\s+\{[^\}]+),([^\n])') while r.search(declaration): declaration = r.sub(r'\1,\n\2', declaration) # # Now go through and supply the right number of tabs # for each line. # def_args = declaration.split('\n') level = 1 declaration = "" for clause in def_args: clause = KernRe(r'\s+').sub(' ', clause.strip(), count=1) if clause: if '}' in clause and level > 1: level -= 1 if not clause.startswith('#'): declaration += "\t" * level declaration += "\t" + clause + "\n" if "{" in clause and "}" not in clause: level += 1 return declaration def dump_struct(self, ln, proto, source): """ Store an entry for a ``struct`` or ``union`` """ # # Do the basic parse to get the pieces of the declaration. # source = source proto = trim_private_members(proto) struct_parts = self.split_struct_proto(proto) if not struct_parts: self.emit_msg(ln, f"{proto} error: Cannot parse struct or union!") return decl_type, declaration_name, members = struct_parts if self.entry.identifier != declaration_name: self.emit_msg(ln, f"expecting prototype for {decl_type} {self.entry.identifier}. " f"Prototype was for {decl_type} {declaration_name} instead\n") return # # Go through the list of members applying all of our transformations. # members = self.xforms.apply("struct", members) # # Deal with embedded struct and union members, and drop enums entirely. # declaration = members members = self.rewrite_struct_members(members) members = re.sub(r'(\{[^\{\}]*\})', '', members) # # Output the result and we are done. # self.create_parameter_list(ln, decl_type, members, ';', declaration_name) self.check_sections(ln, declaration_name, decl_type) self.output_declaration(decl_type, declaration_name, source=source, definition=self.format_struct_decl(declaration), purpose=self.entry.declaration_purpose) def dump_enum(self, ln, proto, source): """ Store an ``enum`` inside self.entries array. """ # # Strip preprocessor directives. Note that this depends on the # trailing semicolon we added in process_proto_type(). # source = source proto = trim_private_members(proto) proto = KernRe(r'#\s*((define|ifdef|if)\s+|endif)[^;]*;', flags=re.S).sub('', proto) # # Parse out the name and members of the enum. Typedef form first. # r = KernRe(r'typedef\s+enum\s*\{(.*)\}\s*(\w*)\s*;') if r.search(proto): declaration_name = r.group(2) members = r.group(1) # # Failing that, look for a straight enum # else: r = KernRe(r'enum\s+(\w*)\s*\{(.*)\}') if r.match(proto): declaration_name = r.group(1) members = r.group(2) # # OK, this isn't going to work. # else: self.emit_msg(ln, f"{proto}: error: Cannot parse enum!") return # # Make sure we found what we were expecting. # if self.entry.identifier != declaration_name: if self.entry.identifier == "": self.emit_msg(ln, f"{proto}: wrong kernel-doc identifier on prototype") else: self.emit_msg(ln, f"expecting prototype for enum {self.entry.identifier}. " f"Prototype was for enum {declaration_name} instead") return if not declaration_name: declaration_name = "(anonymous)" # # Parse out the name of each enum member, and verify that we # have a description for it. # member_set = set() members = KernRe(r'\([^;)]*\)').sub('', members) for arg in members.split(','): arg = KernRe(r'^\s*(\w+).*').sub(r'\1', arg) if not arg.strip(): continue self.entry.parameterlist.append(arg) if arg not in self.entry.parameterdescs: self.entry.parameterdescs[arg] = self.undescribed self.emit_msg(ln, f"Enum value '{arg}' not described in enum '{declaration_name}'") member_set.add(arg) # # Ensure that every described member actually exists in the enum. # for k in self.entry.parameterdescs: if k not in member_set: self.emit_msg(ln, f"Excess enum value '@{k}' description in '{declaration_name}'") self.output_declaration('enum', declaration_name, source=source, purpose=self.entry.declaration_purpose) def dump_var(self, ln, proto, source): """ Store variables that are part of kAPI. """ VAR_ATTRIBS = [ "extern", "const", ] OPTIONAL_VAR_ATTR = r"^(?:\b(?:" +"|".join(VAR_ATTRIBS) +r")\b\s*)*" # # Store the full prototype before modifying it # source = source full_proto = proto declaration_name = None # # Handle macro definitions # macro_prefixes = [ KernRe(r"DEFINE_[\w_]+\s*\(([\w_]+)\)"), ] for r in macro_prefixes: match = r.search(proto) if match: declaration_name = match.group(1) break # # Drop comments and macros to have a pure C prototype # if not declaration_name: proto = self.xforms.apply("var", proto) proto = proto.rstrip() # # Variable name is at the end of the declaration # default_val = None r= KernRe(OPTIONAL_VAR_ATTR + r"\s*[\w_\s]*\s+(?:\*+)?([\w_]+)\s*[\d\]\[]*\s*(=.*)?") if r.match(proto): if not declaration_name: declaration_name = r.group(1) default_val = r.group(2) else: r= KernRe(OPTIONAL_VAR_ATTR + r"(?:[\w_\s]*)?\s+(?:\*+)?(?:[\w_]+)\s*[\d\]\[]*\s*(=.*)?") if r.match(proto): default_val = r.group(1) if not declaration_name: self.emit_msg(ln,f"{proto}: can't parse variable") return if default_val: default_val = default_val.lstrip("=").strip() self.output_declaration("var", declaration_name, source=source, full_proto=full_proto, default_val=default_val, purpose=self.entry.declaration_purpose) def dump_declaration(self, ln, prototype, source): """ Store a data declaration inside self.entries array. """ if self.entry.decl_type == "enum": self.dump_enum(ln, prototype, source) elif self.entry.decl_type == "typedef": self.dump_typedef(ln, prototype, source) elif self.entry.decl_type in ["union", "struct"]: self.dump_struct(ln, prototype, source) elif self.entry.decl_type == "var": self.dump_var(ln, prototype, source) else: # This would be a bug self.emit_message(ln, f'Unknown declaration type: {self.entry.decl_type}') def dump_function(self, ln, prototype, source): """ Store a function or function macro inside self.entries array. """ source = source found = func_macro = False return_type = '' decl_type = 'function' # # If we have a macro, remove the "#define" at the front. # new_proto = KernRe(r"^#\s*define\s+").sub("", prototype) if new_proto != prototype: prototype = new_proto # # Dispense with the simple "#define A B" case here; the key # is the space after the name of the symbol being defined. # NOTE that the seemingly misnamed "func_macro" indicates a # macro *without* arguments. #