xref: /linux/tools/lib/python/kdoc/kdoc_re.py (revision b2d231f4a77800661b3fb812d997841a548c6526)
1#!/usr/bin/env python3
2# SPDX-License-Identifier: GPL-2.0
3# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>.
4
5"""
6Regular expression ancillary classes.
7
8Those help caching regular expressions and do matching for kernel-doc.
9"""
10
11import re
12
13# Local cache for regular expressions
14re_cache = {}
15
16
17class KernRe:
18    """
19    Helper class to simplify regex declaration and usage.
20
21    It calls re.compile for a given pattern. It also allows adding
22    regular expressions and define sub at class init time.
23
24    Regular expressions can be cached via an argument, helping to speedup
25    searches.
26    """
27
28    def _add_regex(self, string, flags):
29        """
30        Adds a new regex or reuses it from the cache.
31        """
32        self.regex = re_cache.get(string, None)
33        if not self.regex:
34            self.regex = re.compile(string, flags=flags)
35            if self.cache:
36                re_cache[string] = self.regex
37
38    def __init__(self, string, cache=True, flags=0):
39        """
40        Compile a regular expression and initialize internal vars.
41        """
42
43        self.cache = cache
44        self.last_match = None
45
46        self._add_regex(string, flags)
47
48    def __str__(self):
49        """
50        Return the regular expression pattern.
51        """
52        return self.regex.pattern
53
54    def __repr__(self):
55        """
56        Returns a displayable version of the class init.
57        """
58
59        flag_map = {
60            re.IGNORECASE: "re.I",
61            re.MULTILINE: "re.M",
62            re.DOTALL: "re.S",
63            re.VERBOSE: "re.X",
64        }
65
66        flags = []
67        for flag, name in flag_map.items():
68            if self.regex.flags & flag:
69                flags.append(name)
70
71        flags_name = " | ".join(flags)
72
73        max_len = 60
74        pattern = ""
75        for pos in range(0, len(self.regex.pattern), max_len):
76            pattern += '"' + self.regex.pattern[pos:max_len + pos] + '" '
77
78        if flags_name:
79            return f'KernRe({pattern}, {flags_name})'
80        else:
81            return f'KernRe({pattern})'
82
83    def __add__(self, other):
84        """
85        Allows adding two regular expressions into one.
86        """
87
88        return KernRe(str(self) + str(other), cache=self.cache or other.cache,
89                  flags=self.regex.flags | other.regex.flags)
90
91    def match(self, string):
92        """
93        Handles a re.match storing its results.
94        """
95
96        self.last_match = self.regex.match(string)
97        return self.last_match
98
99    def search(self, string):
100        """
101        Handles a re.search storing its results.
102        """
103
104        self.last_match = self.regex.search(string)
105        return self.last_match
106
107    def finditer(self,  string):
108        """
109        Alias to re.finditer.
110        """
111
112        return self.regex.finditer(string)
113
114    def findall(self, string):
115        """
116        Alias to re.findall.
117        """
118
119        return self.regex.findall(string)
120
121    def split(self, string):
122        """
123        Alias to re.split.
124        """
125
126        return self.regex.split(string)
127
128    def sub(self, sub, string, count=0):
129        """
130        Alias to re.sub.
131        """
132
133        return self.regex.sub(sub, string, count=count)
134
135    def group(self, num):
136        """
137        Returns the group results of the last match.
138        """
139
140        return self.last_match.group(num)
141
142    def groups(self):
143        """
144        Returns the group results of the last match
145        """
146
147        return self.last_match.groups()
148