Merge branch 'mauro' into docs-mw

The changes here basically addresses several issues discoverd by
Randy and by me and makes NestedMatch more similar to KernRe.

It also moves the transforms ruleset to a separate file, to make
easier to maintain kernel-doc code.
This commit is contained in:
Jonathan Corbet
2026-03-03 10:54:28 -07:00
5 changed files with 286 additions and 182 deletions

View File

@@ -4,6 +4,14 @@
Kernel-doc parser stage
=======================
C replacement rules used by the parser
======================================
.. automodule:: lib.python.kdoc.xforms_lists
:members:
:show-inheritance:
:undoc-members:
File handler classes
====================

View File

@@ -15,6 +15,7 @@ import os
import re
from kdoc.kdoc_parser import KernelDoc
from kdoc.xforms_lists import CTransforms
from kdoc.kdoc_output import OutputFormat
@@ -117,7 +118,7 @@ class KernelFiles():
if fname in self.files:
return
doc = KernelDoc(self.config, fname)
doc = KernelDoc(self.config, fname, CTransforms())
export_table, entries = doc.parse_kdoc()
self.export_table[fname] = export_table

View File

@@ -69,141 +69,10 @@ doc_begin_func = KernRe(str(doc_com) + # initial " * '
r'(?:[-:].*)?$', # description (not captured)
cache = False)
#
# Here begins a long set of transformations to turn structure member prefixes
# and macro invocations into something we can parse and generate kdoc for.
#
struct_args_pattern = r'([^,)]+)'
struct_xforms = [
# Strip attributes
(KernRe(r"__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)", flags=re.I | re.S, cache=False), ' '),
(KernRe(r'\s*__aligned\s*\([^;]*\)', re.S), ' '),
(KernRe(r'\s*__counted_by\s*\([^;]*\)', re.S), ' '),
(KernRe(r'\s*__counted_by_(le|be)\s*\([^;]*\)', re.S), ' '),
(KernRe(r'\s*__packed\s*', re.S), ' '),
(KernRe(r'\s*CRYPTO_MINALIGN_ATTR', re.S), ' '),
(KernRe(r'\s*__private', re.S), ' '),
(KernRe(r'\s*__rcu', re.S), ' '),
(KernRe(r'\s*____cacheline_aligned_in_smp', re.S), ' '),
(KernRe(r'\s*____cacheline_aligned', re.S), ' '),
(KernRe(r'\s*__cacheline_group_(begin|end)\([^\)]+\);'), ''),
#
# Unwrap struct_group macros based on this definition:
# __struct_group(TAG, NAME, ATTRS, MEMBERS...)
# which has variants like: struct_group(NAME, MEMBERS...)
# Only MEMBERS arguments require documentation.
#
# Parsing them happens on two steps:
#
# 1. drop struct group arguments that aren't at MEMBERS,
# storing them as STRUCT_GROUP(MEMBERS)
#
# 2. remove STRUCT_GROUP() ancillary macro.
#
# The original logic used to remove STRUCT_GROUP() using an
# advanced regex:
#
# \bSTRUCT_GROUP(\(((?:(?>[^)(]+)|(?1))*)\))[^;]*;
#
# with two patterns that are incompatible with
# Python re module, as it has:
#
# - a recursive pattern: (?1)
# - an atomic grouping: (?>...)
#
# I tried a simpler version: but it didn't work either:
# \bSTRUCT_GROUP\(([^\)]+)\)[^;]*;
#
# As it doesn't properly match the end parenthesis on some cases.
#
# So, a better solution was crafted: there's now a NestedMatch
# class that ensures that delimiters after a search are properly
# matched. So, the implementation to drop STRUCT_GROUP() will be
# handled in separate.
#
(KernRe(r'\bstruct_group\s*\(([^,]*,)', re.S), r'STRUCT_GROUP('),
(KernRe(r'\bstruct_group_attr\s*\(([^,]*,){2}', re.S), r'STRUCT_GROUP('),
(KernRe(r'\bstruct_group_tagged\s*\(([^,]*),([^,]*),', re.S), r'struct \1 \2; STRUCT_GROUP('),
(KernRe(r'\b__struct_group\s*\(([^,]*,){3}', re.S), r'STRUCT_GROUP('),
#
# Replace macros
#
# TODO: use NestedMatch for FOO($1, $2, ...) matches
#
# it is better to also move those to the NestedMatch logic,
# to ensure that parentheses will be properly matched.
#
(KernRe(r'__ETHTOOL_DECLARE_LINK_MODE_MASK\s*\(([^\)]+)\)', re.S),
r'DECLARE_BITMAP(\1, __ETHTOOL_LINK_MODE_MASK_NBITS)'),
(KernRe(r'DECLARE_PHY_INTERFACE_MASK\s*\(([^\)]+)\)', re.S),
r'DECLARE_BITMAP(\1, PHY_INTERFACE_MODE_MAX)'),
(KernRe(r'DECLARE_BITMAP\s*\(' + struct_args_pattern + r',\s*' + struct_args_pattern + r'\)',
re.S), r'unsigned long \1[BITS_TO_LONGS(\2)]'),
(KernRe(r'DECLARE_HASHTABLE\s*\(' + struct_args_pattern + r',\s*' + struct_args_pattern + r'\)',
re.S), r'unsigned long \1[1 << ((\2) - 1)]'),
(KernRe(r'DECLARE_KFIFO\s*\(' + struct_args_pattern + r',\s*' + struct_args_pattern +
r',\s*' + struct_args_pattern + r'\)', re.S), r'\2 *\1'),
(KernRe(r'DECLARE_KFIFO_PTR\s*\(' + struct_args_pattern + r',\s*' +
struct_args_pattern + r'\)', re.S), r'\2 *\1'),
(KernRe(r'(?:__)?DECLARE_FLEX_ARRAY\s*\(' + struct_args_pattern + r',\s*' +
struct_args_pattern + r'\)', re.S), r'\1 \2[]'),
(KernRe(r'DEFINE_DMA_UNMAP_ADDR\s*\(' + struct_args_pattern + r'\)', re.S), r'dma_addr_t \1'),
(KernRe(r'DEFINE_DMA_UNMAP_LEN\s*\(' + struct_args_pattern + r'\)', re.S), r'__u32 \1'),
]
#
# Regexes here are guaranteed to have the end delimiter matching
# the start delimiter. Yet, right now, only one replace group
# is allowed.
#
struct_nested_prefixes = [
(re.compile(r'\bSTRUCT_GROUP\('), r'\1'),
]
#
# Transforms for function prototypes
#
function_xforms = [
(KernRe(r"^static +"), ""),
(KernRe(r"^extern +"), ""),
(KernRe(r"^asmlinkage +"), ""),
(KernRe(r"^inline +"), ""),
(KernRe(r"^__inline__ +"), ""),
(KernRe(r"^__inline +"), ""),
(KernRe(r"^__always_inline +"), ""),
(KernRe(r"^noinline +"), ""),
(KernRe(r"^__FORTIFY_INLINE +"), ""),
(KernRe(r"__init +"), ""),
(KernRe(r"__init_or_module +"), ""),
(KernRe(r"__exit +"), ""),
(KernRe(r"__deprecated +"), ""),
(KernRe(r"__flatten +"), ""),
(KernRe(r"__meminit +"), ""),
(KernRe(r"__must_check +"), ""),
(KernRe(r"__weak +"), ""),
(KernRe(r"__sched +"), ""),
(KernRe(r"_noprof"), ""),
(KernRe(r"__always_unused *"), ""),
(KernRe(r"__printf\s*\(\s*\d*\s*,\s*\d*\s*\) +"), ""),
(KernRe(r"__(?:re)?alloc_size\s*\(\s*\d+\s*(?:,\s*\d+\s*)?\) +"), ""),
(KernRe(r"__diagnose_as\s*\(\s*\S+\s*(?:,\s*\d+\s*)*\) +"), ""),
(KernRe(r"DECL_BUCKET_PARAMS\s*\(\s*(\S+)\s*,\s*(\S+)\s*\)"), r"\1, \2"),
(KernRe(r"__attribute_const__ +"), ""),
(KernRe(r"__attribute__\s*\(\((?:[\w\s]+(?:\([^)]*\))?\s*,?)+\)\)\s+"), ""),
]
#
# Ancillary functions
#
def apply_transforms(xforms, text):
"""
Apply a set of transforms to a block of text.
"""
for search, subst in xforms:
text = search.sub(subst, text)
return text
multi_space = KernRe(r'\s\s+')
def trim_whitespace(s):
"""
@@ -382,11 +251,12 @@ class KernelDoc:
#: String to write when a parameter is not described.
undescribed = "-- undescribed --"
def __init__(self, config, fname):
def __init__(self, config, fname, xforms):
"""Initialize internal variables"""
self.fname = fname
self.config = config
self.xforms = xforms
# Initial state for the state machines
self.state = state.NORMAL
@@ -870,11 +740,8 @@ class KernelDoc:
# Go through the list of members applying all of our transformations.
#
members = trim_private_members(members)
members = apply_transforms(struct_xforms, members)
members = self.xforms.apply("struct", members)
nested = NestedMatch()
for search, sub in struct_nested_prefixes:
members = nested.sub(search, sub, members)
#
# Deal with embedded struct and union members, and drop enums entirely.
#
@@ -969,17 +836,9 @@ class KernelDoc:
"""
VAR_ATTRIBS = [
"extern",
"const",
]
OPTIONAL_VAR_ATTR = "^(?:" + "|".join(VAR_ATTRIBS) + ")?"
sub_prefixes = [
(KernRe(r"__read_mostly"), ""),
(KernRe(r"__ro_after_init"), ""),
(KernRe(r"(?://.*)$"), ""),
(KernRe(r"(?:/\*.*\*/)"), ""),
(KernRe(r";$"), ""),
(KernRe(r"=.*"), ""),
]
OPTIONAL_VAR_ATTR = r"^(?:\b(?:" +"|".join(VAR_ATTRIBS) +r")\b\s*)*"
#
# Store the full prototype before modifying it
@@ -1004,8 +863,7 @@ class KernelDoc:
# Drop comments and macros to have a pure C prototype
#
if not declaration_name:
for r, sub in sub_prefixes:
proto = r.sub(sub, proto)
proto = self.xforms.apply("var", proto)
proto = proto.rstrip()
@@ -1015,17 +873,17 @@ class KernelDoc:
default_val = None
r= KernRe(OPTIONAL_VAR_ATTR + r"\w.*\s+(?:\*+)?([\w_]+)\s*[\d\]\[]*\s*(=.*)?")
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+(?:\*+)?(?:[\w_]+)\s*[\d\]\[]*\s*(=.*)?")
if r.match(proto):
default_val = r.group(1)
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
@@ -1063,10 +921,7 @@ class KernelDoc:
found = func_macro = False
return_type = ''
decl_type = 'function'
#
# Apply the initial transformations.
#
prototype = apply_transforms(function_xforms, prototype)
#
# If we have a macro, remove the "#define" at the front.
#
@@ -1085,6 +940,11 @@ class KernelDoc:
declaration_name = r.group(1)
func_macro = True
found = True
else:
#
# Apply the initial transformations.
#
prototype = self.xforms.apply("func", prototype)
# Yes, this truly is vile. We are looking for:
# 1. Return type (may be nothing if we're looking at a macro)

View File

@@ -52,7 +52,28 @@ class KernRe:
return self.regex.pattern
def __repr__(self):
return f're.compile("{self.regex.pattern}")'
"""
Returns a displayable version of the class init.
"""
flag_map = {
re.IGNORECASE: "re.I",
re.MULTILINE: "re.M",
re.DOTALL: "re.S",
re.VERBOSE: "re.X",
}
flags = []
for flag, name in flag_map.items():
if self.regex.flags & flag:
flags.append(name)
flags_name = " | ".join(flags)
if flags_name:
return f'KernRe("{self.regex.pattern}", {flags_name})'
else:
return f'KernRe("{self.regex.pattern}")'
def __add__(self, other):
"""
@@ -78,6 +99,13 @@ class KernRe:
self.last_match = self.regex.search(string)
return self.last_match
def finditer(self, string):
"""
Alias to re.finditer.
"""
return self.regex.finditer(string)
def findall(self, string):
"""
Alias to re.findall.
@@ -106,6 +134,23 @@ class KernRe:
return self.last_match.group(num)
def groups(self):
"""
Returns the group results of the last match
"""
return self.last_match.groups()
#: Nested delimited pairs (brackets and parenthesis)
DELIMITER_PAIRS = {
'{': '}',
'(': ')',
'[': ']',
}
#: compiled delimiters
RE_DELIM = KernRe(r'[\{\}\[\]\(\)]')
class NestedMatch:
"""
@@ -143,7 +188,7 @@ class NestedMatch:
# except that the content inside the match group is delimiter-aligned.
#
# The content inside parentheses is converted into a single replace
# group (e.g. r`\1').
# group (e.g. r`\0').
#
# It would be nice to change such definition to support multiple
# match groups, allowing a regex equivalent to:
@@ -155,15 +200,10 @@ class NestedMatch:
#
# FOO(arg1, arg2, arg3)
DELIMITER_PAIRS = {
'{': '}',
'(': ')',
'[': ']',
}
def __init__(self, regex):
self.regex = KernRe(regex)
RE_DELIM = re.compile(r'[\{\}\[\]\(\)]')
def _search(self, regex, line):
def _search(self, line):
"""
Finds paired blocks for a regex that ends with a delimiter.
@@ -185,24 +225,42 @@ class NestedMatch:
stack = []
for match_re in regex.finditer(line):
for match_re in self.regex.finditer(line):
start = match_re.start()
offset = match_re.end()
string_char = None
escape = False
d = line[offset - 1]
if d not in self.DELIMITER_PAIRS:
if d not in DELIMITER_PAIRS:
continue
end = self.DELIMITER_PAIRS[d]
end = DELIMITER_PAIRS[d]
stack.append(end)
for match in self.RE_DELIM.finditer(line[offset:]):
for match in RE_DELIM.finditer(line[offset:]):
pos = match.start() + offset
d = line[pos]
if d in self.DELIMITER_PAIRS:
end = self.DELIMITER_PAIRS[d]
if escape:
escape = False
continue
if string_char:
if d == '\\':
escape = True
elif d == string_char:
string_char = None
continue
if d in ('"', "'"):
string_char = d
continue
if d in DELIMITER_PAIRS:
end = DELIMITER_PAIRS[d]
stack.append(end)
continue
@@ -215,7 +273,7 @@ class NestedMatch:
yield start, offset, pos + 1
break
def search(self, regex, line):
def search(self, line):
"""
This is similar to re.search:
@@ -223,12 +281,12 @@ class NestedMatch:
returning occurrences only if all delimiters are paired.
"""
for t in self._search(regex, line):
for t in self._search(line):
yield line[t[0]:t[2]]
def sub(self, regex, sub, line, count=0):
r"""
def sub(self, sub, line, count=0):
"""
This is similar to re.sub:
It matches a regex that it is followed by a delimiter,
@@ -236,7 +294,7 @@ class NestedMatch:
if the sub argument contains::
r'\1'
r'\0'
it will work just like re: it places there the matched paired data
with the delimiter stripped.
@@ -249,20 +307,20 @@ class NestedMatch:
cur_pos = 0
n = 0
for start, end, pos in self._search(regex, line):
for start, end, pos in self._search(line):
out += line[cur_pos:start]
# Value, ignoring start/end delimiters
value = line[end:pos - 1]
# replaces \1 at the sub string, if \1 is used there
# replaces \0 at the sub string, if \0 is used there
new_sub = sub
new_sub = new_sub.replace(r'\1', value)
new_sub = new_sub.replace(r'\0', value)
out += new_sub
# Drop end ';' if any
if line[pos] == ';':
if pos < len(line) and line[pos] == ';':
pos += 1
cur_pos = pos
@@ -276,3 +334,10 @@ class NestedMatch:
out += line[cur_pos:l]
return out
def __repr__(self):
"""
Returns a displayable version of the class init.
"""
return f'NestedMatch("{self.regex.regex.pattern}")'

View File

@@ -0,0 +1,170 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0
# Copyright(c) 2026: Mauro Carvalho Chehab <mchehab@kernel.org>.
import re
from kdoc.kdoc_re import KernRe, NestedMatch
struct_args_pattern = r'([^,)]+)'
class CTransforms:
"""
Data class containing a long set of transformations to turn
structure member prefixes, and macro invocations and variables
into something we can parse and generate kdoc for.
"""
#: Transforms for structs and unions.
struct_xforms = [
# Strip attributes
(KernRe(r"__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)", flags=re.I | re.S, cache=False), ' '),
(KernRe(r'\s*__aligned\s*\([^;]*\)', re.S), ' '),
(KernRe(r'\s*__counted_by\s*\([^;]*\)', re.S), ' '),
(KernRe(r'\s*__counted_by_(le|be)\s*\([^;]*\)', re.S), ' '),
(KernRe(r'\s*__guarded_by\s*\([^\)]*\)', re.S), ' '),
(KernRe(r'\s*__pt_guarded_by\s*\([^\)]*\)', re.S), ' '),
(KernRe(r'\s*__packed\s*', re.S), ' '),
(KernRe(r'\s*CRYPTO_MINALIGN_ATTR', re.S), ' '),
(KernRe(r'\s*__private', re.S), ' '),
(KernRe(r'\s*__rcu', re.S), ' '),
(KernRe(r'\s*____cacheline_aligned_in_smp', re.S), ' '),
(KernRe(r'\s*____cacheline_aligned', re.S), ' '),
(KernRe(r'\s*__cacheline_group_(begin|end)\([^\)]+\);'), ''),
#
# Unwrap struct_group macros based on this definition:
# __struct_group(TAG, NAME, ATTRS, MEMBERS...)
# which has variants like: struct_group(NAME, MEMBERS...)
# Only MEMBERS arguments require documentation.
#
# Parsing them happens on two steps:
#
# 1. drop struct group arguments that aren't at MEMBERS,
# storing them as STRUCT_GROUP(MEMBERS)
#
# 2. remove STRUCT_GROUP() ancillary macro.
#
# The original logic used to remove STRUCT_GROUP() using an
# advanced regex:
#
# \bSTRUCT_GROUP(\(((?:(?>[^)(]+)|(?1))*)\))[^;]*;
#
# with two patterns that are incompatible with
# Python re module, as it has:
#
# - a recursive pattern: (?1)
# - an atomic grouping: (?>...)
#
# I tried a simpler version: but it didn't work either:
# \bSTRUCT_GROUP\(([^\)]+)\)[^;]*;
#
# As it doesn't properly match the end parenthesis on some cases.
#
# So, a better solution was crafted: there's now a NestedMatch
# class that ensures that delimiters after a search are properly
# matched. So, the implementation to drop STRUCT_GROUP() will be
# handled in separate.
#
(KernRe(r'\bstruct_group\s*\(([^,]*,)', re.S), r'STRUCT_GROUP('),
(KernRe(r'\bstruct_group_attr\s*\(([^,]*,){2}', re.S), r'STRUCT_GROUP('),
(KernRe(r'\bstruct_group_tagged\s*\(([^,]*),([^,]*),', re.S), r'struct \1 \2; STRUCT_GROUP('),
(KernRe(r'\b__struct_group\s*\(([^,]*,){3}', re.S), r'STRUCT_GROUP('),
#
# Replace macros
#
# TODO: use NestedMatch for FOO($1, $2, ...) matches
#
# it is better to also move those to the NestedMatch logic,
# to ensure that parentheses will be properly matched.
#
(KernRe(r'__ETHTOOL_DECLARE_LINK_MODE_MASK\s*\(([^\)]+)\)', re.S),
r'DECLARE_BITMAP(\1, __ETHTOOL_LINK_MODE_MASK_NBITS)'),
(KernRe(r'DECLARE_PHY_INTERFACE_MASK\s*\(([^\)]+)\)', re.S),
r'DECLARE_BITMAP(\1, PHY_INTERFACE_MODE_MAX)'),
(KernRe(r'DECLARE_BITMAP\s*\(' + struct_args_pattern + r',\s*' + struct_args_pattern + r'\)',
re.S), r'unsigned long \1[BITS_TO_LONGS(\2)]'),
(KernRe(r'DECLARE_HASHTABLE\s*\(' + struct_args_pattern + r',\s*' + struct_args_pattern + r'\)',
re.S), r'unsigned long \1[1 << ((\2) - 1)]'),
(KernRe(r'DECLARE_KFIFO\s*\(' + struct_args_pattern + r',\s*' + struct_args_pattern +
r',\s*' + struct_args_pattern + r'\)', re.S), r'\2 *\1'),
(KernRe(r'DECLARE_KFIFO_PTR\s*\(' + struct_args_pattern + r',\s*' +
struct_args_pattern + r'\)', re.S), r'\2 *\1'),
(KernRe(r'(?:__)?DECLARE_FLEX_ARRAY\s*\(' + struct_args_pattern + r',\s*' +
struct_args_pattern + r'\)', re.S), r'\1 \2[]'),
(KernRe(r'DEFINE_DMA_UNMAP_ADDR\s*\(' + struct_args_pattern + r'\)', re.S), r'dma_addr_t \1'),
(KernRe(r'DEFINE_DMA_UNMAP_LEN\s*\(' + struct_args_pattern + r'\)', re.S), r'__u32 \1'),
(KernRe(r'VIRTIO_DECLARE_FEATURES\(([\w_]+)\)'), r'union { u64 \1; u64 \1_array[VIRTIO_FEATURES_U64S]; }'),
(NestedMatch(r"__cond_acquires\s*\("), ""),
(NestedMatch(r"__cond_releases\s*\("), ""),
(NestedMatch(r"__acquires\s*\("), ""),
(NestedMatch(r"__releases\s*\("), ""),
(NestedMatch(r"__must_hold\s*\("), ""),
(NestedMatch(r"__must_not_hold\s*\("), ""),
(NestedMatch(r"__must_hold_shared\s*\("), ""),
(NestedMatch(r"__cond_acquires_shared\s*\("), ""),
(NestedMatch(r"__acquires_shared\s*\("), ""),
(NestedMatch(r"__releases_shared\s*\("), ""),
(NestedMatch(r'\bSTRUCT_GROUP\('), r'\0'),
]
#: Transforms for function prototypes.
function_xforms = [
(KernRe(r"^static +"), ""),
(KernRe(r"^extern +"), ""),
(KernRe(r"^asmlinkage +"), ""),
(KernRe(r"^inline +"), ""),
(KernRe(r"^__inline__ +"), ""),
(KernRe(r"^__inline +"), ""),
(KernRe(r"^__always_inline +"), ""),
(KernRe(r"^noinline +"), ""),
(KernRe(r"^__FORTIFY_INLINE +"), ""),
(KernRe(r"__init +"), ""),
(KernRe(r"__init_or_module +"), ""),
(KernRe(r"__exit +"), ""),
(KernRe(r"__deprecated +"), ""),
(KernRe(r"__flatten +"), ""),
(KernRe(r"__meminit +"), ""),
(KernRe(r"__must_check +"), ""),
(KernRe(r"__weak +"), ""),
(KernRe(r"__sched +"), ""),
(KernRe(r"_noprof"), ""),
(KernRe(r"__always_unused *"), ""),
(KernRe(r"__printf\s*\(\s*\d*\s*,\s*\d*\s*\) +"), ""),
(KernRe(r"__(?:re)?alloc_size\s*\(\s*\d+\s*(?:,\s*\d+\s*)?\) +"), ""),
(KernRe(r"__diagnose_as\s*\(\s*\S+\s*(?:,\s*\d+\s*)*\) +"), ""),
(KernRe(r"DECL_BUCKET_PARAMS\s*\(\s*(\S+)\s*,\s*(\S+)\s*\)"), r"\1, \2"),
(KernRe(r"__no_context_analysis\s*"), ""),
(KernRe(r"__attribute_const__ +"), ""),
(KernRe(r"__attribute__\s*\(\((?:[\w\s]+(?:\([^)]*\))?\s*,?)+\)\)\s+"), ""),
]
#: Transforms for variable prototypes.
var_xforms = [
(KernRe(r"__read_mostly"), ""),
(KernRe(r"__ro_after_init"), ""),
(KernRe(r'\s*__guarded_by\s*\([^\)]*\)', re.S), ""),
(KernRe(r'\s*__pt_guarded_by\s*\([^\)]*\)', re.S), ""),
(KernRe(r"LIST_HEAD\(([\w_]+)\)"), r"struct list_head \1"),
(KernRe(r"(?://.*)$"), ""),
(KernRe(r"(?:/\*.*\*/)"), ""),
(KernRe(r";$"), ""),
]
#: Transforms main dictionary used at apply_transforms().
xforms = {
"struct": struct_xforms,
"func": function_xforms,
"var": var_xforms,
}
def apply(self, xforms_type, text):
"""
Apply a set of transforms to a block of text.
"""
if xforms_type not in self.xforms:
return text
for search, subst in self.xforms[xforms_type]:
text = search.sub(subst, text)
return text