""" def __init__(self, regex, delim="("): self.regex = KernRe("^" + regex + r"\b") self.start_delim = delim def _search(self, tokenizer): """ Finds paired blocks for a regex that ends with a delimiter. The suggestion of using finditer to match pairs came from: https://stackoverflow.com/questions/5454322/python-how-to-match-nested-parentheses-with-regex but I ended using a different implementation to align all three types of delimiters and seek for an initial regular expression. The algorithm seeks for open/close paired delimiters and places them into a stack, yielding a start/stop position of each match when the stack is zeroed. The algorithm should work fine for properly paired lines, but will silently ignore end delimiters that precede a start delimiter. This should be OK for kernel-doc parser, as unaligned delimiters would cause compilation errors. So, we don't need to raise exceptions to cover such issues. """ start = None started = False import sys stack = [] for i, tok in enumerate(tokenizer.tokens): if start is None: if tok.kind == CToken.NAME and self.regex.match(tok.value): start = i stack.append((start, tok.level)) started = False continue if not started: if tok.kind == CToken.SPACE: continue if tok.kind == CToken.BEGIN and tok.value == self.start_delim: started = True continue # Name only token without BEGIN/END if i > start: i -= 1 yield start, i start = None if tok.kind == CToken.END and tok.level == stack[-1][1]: start, level = stack.pop() yield start, i start = None # # If an END zeroing levels is not there, return remaining stuff # This is meant to solve cases where the caller logic might be # picking an incomplete block. # if start and stack: if started: s = str(tokenizer) log.warning(f"can't find a final end at {s}") yield start, len(tokenizer.tokens) def search(self, source): """ This is similar to re.search: It matches a regex that it is followed by a delimiter, returning occurrences only if all delimiters are paired. """ if isinstance(source, CTokenizer): tokenizer = source is_token = True else: tokenizer = CTokenizer(source) is_token = False for start, end in self._search(tokenizer): new_tokenizer = CTokenizer(tokenizer.tokens[start:end + 1]) if is_token: yield new_tokenizer else: yield str(new_tokenizer) def sub(self, sub_str, source, count=0): """ This is similar to re.sub: It matches a regex that it is followed by a delimiter, replacing occurrences only if all delimiters are paired. if the sub argument contains:: r'\0' it will work just like re: it places there the matched paired data with the delimiter stripped. If count is different than zero, it will replace at most count items. """ if isinstance(source, CTokenizer): is_token = True tokenizer = source else: is_token = False tokenizer = CTokenizer(source) # Detect if sub_str contains sub arguments args_match = CTokenArgs(sub_str) new_tokenizer = CTokenizer() pos = 0 n = 0 # # NOTE: the code below doesn't consider overlays at sub. # We may need to add some extra unit tests to check if those # would cause problems. When replacing by "", this should not # be a problem, but other transformations could be problematic # for start, end in self._search(tokenizer): new_tokenizer.tokens += tokenizer.tokens[pos:start] new = CTokenizer(tokenizer.tokens[start:end + 1]) new_tokenizer.tokens += args_match.tokens(new) pos = end + 1 n += 1 if count and n >= count: break new_tokenizer.tokens += tokenizer.tokens[pos:] if not is_token: return str(new_tokenizer) return new_tokenizer def __repr__(self): """ Returns a displayable version of the class init. """ return f'CMatch("{self.regex.regex.pattern}")'Lines 501–662 of 662. Previous lines
""" def __init__(self, regex, delim="("): self.regex = KernRe("^" + regex + r"\b") self.start_delim = delim def _search(self, tokenizer): """ Finds paired blocks for a regex that ends with a delimiter. The suggestion of using finditer to match pairs came from: https://stackoverflow.com/questions/5454322/python-how-to-match-nested-parentheses-with-regex but I ended using a different implementation to align all three types of delimiters and seek for an initial regular expression. The algorithm seeks for open/close paired delimiters and places them into a stack, yielding a start/stop position of each match when the stack is zeroed. The algorithm should work fine for properly paired lines, but will silently ignore end delimiters that precede a start delimiter. This should be OK for kernel-doc parser, as unaligned delimiters would cause compilation errors. So, we don't need to raise exceptions to cover such issues. """ start = None started = False import sys stack = [] for i, tok in enumerate(tokenizer.tokens): if start is None: if tok.kind == CToken.NAME and self.regex.match(tok.value): start = i stack.append((start, tok.level)) started = False continue if not started: if tok.kind == CToken.SPACE: continue if tok.kind == CToken.BEGIN and tok.value == self.start_delim: started = True continue # Name only token without BEGIN/END if i > start: i -= 1 yield start, i start = None if tok.kind == CToken.END and tok.level == stack[-1][1]: start, level = stack.pop() yield start, i start = None # # If an END zeroing levels is not there, return remaining stuff # This is meant to solve cases where the caller logic might be # picking an incomplete block. # if start and stack: if started: s = str(tokenizer) log.warning(f"can't find a final end at {s}") yield start, len(tokenizer.tokens) def search(self, source): """ This is similar to re.search: It matches a regex that it is followed by a delimiter, returning occurrences only if all delimiters are paired. """ if isinstance(source, CTokenizer): tokenizer = source is_token = True else: tokenizer = CTokenizer(source) is_token = False for start, end in self._search(tokenizer): new_tokenizer = CTokenizer(tokenizer.tokens[start:end + 1]) if is_token: yield new_tokenizer else: yield str(new_tokenizer) def sub(self, sub_str, source, count=0): """ This is similar to re.sub: It matches a regex that it is followed by a delimiter, replacing occurrences only if all delimiters are paired. if the sub argument contains:: r'\0' it will work just like re: it places there the matched paired data with the delimiter stripped. If count is different than zero, it will replace at most count items. """ if isinstance(source, CTokenizer): is_token = True tokenizer = source else: is_token = False tokenizer = CTokenizer(source) # Detect if sub_str contains sub arguments args_match = CTokenArgs(sub_str) new_tokenizer = CTokenizer() pos = 0 n = 0 # # NOTE: the code below doesn't consider overlays at sub. # We may need to add some extra unit tests to check if those # would cause problems. When replacing by "", this should not # be a problem, but other transformations could be problematic # for start, end in self._search(tokenizer): new_tokenizer.tokens += tokenizer.tokens[pos:start] new = CTokenizer(tokenizer.tokens[start:end + 1]) new_tokenizer.tokens += args_match.tokens(new) pos = end + 1 n += 1 if count and n >= count: break new_tokenizer.tokens += tokenizer.tokens[pos:] if not is_token: return str(new_tokenizer) return new_tokenizer def __repr__(self): """ Returns a displayable version of the class init. """ return f'CMatch("{self.regex.regex.pattern}")'