r"""Retrieves all the text between matching open and close parentheses. Given a string of lines and a regular expression string, retrieve all the text following the expression and between opening punctuation symbols like (, [, or {, and the matching close-punctuation symbol. This proper
(text, start_pattern)
| 5947 | |
| 5948 | |
| 5949 | def _GetTextInside(text, start_pattern): |
| 5950 | r"""Retrieves all the text between matching open and close parentheses. |
| 5951 | |
| 5952 | Given a string of lines and a regular expression string, retrieve all the text |
| 5953 | following the expression and between opening punctuation symbols like |
| 5954 | (, [, or {, and the matching close-punctuation symbol. This properly nested |
| 5955 | occurrences of the punctuation, so for the text like |
| 5956 | printf(a(), b(c())); |
| 5957 | a call to _GetTextInside(text, r'printf\(') will return 'a(), b(c())'. |
| 5958 | start_pattern must match string having an open punctuation symbol at the end. |
| 5959 | |
| 5960 | Args: |
| 5961 | text: The lines to extract text. Its comments and strings must be elided. |
| 5962 | It can be single line and can span multiple lines. |
| 5963 | start_pattern: The regexp string indicating where to start extracting |
| 5964 | the text. |
| 5965 | Returns: |
| 5966 | The extracted text. |
| 5967 | None if either the opening string or ending punctuation could not be found. |
| 5968 | """ |
| 5969 | # TODO(google): Audit cpplint.py to see what places could be profitably |
| 5970 | # rewritten to use _GetTextInside (and use inferior regexp matching today). |
| 5971 | |
| 5972 | # Give opening punctuation to get the matching close-punctuation. |
| 5973 | matching_punctuation = {"(": ")", "{": "}", "[": "]"} |
| 5974 | closing_punctuation = set(dict.values(matching_punctuation)) |
| 5975 | |
| 5976 | # Find the position to start extracting text. |
| 5977 | match = re.search(start_pattern, text, re.MULTILINE) |
| 5978 | if not match: # start_pattern not found in text. |
| 5979 | return None |
| 5980 | start_position = match.end(0) |
| 5981 | |
| 5982 | assert start_position > 0, "start_pattern must ends with an opening punctuation." |
| 5983 | assert text[start_position - 1] in matching_punctuation, ( |
| 5984 | "start_pattern must ends with an opening punctuation." |
| 5985 | ) |
| 5986 | # Stack of closing punctuation we expect to have in text after position. |
| 5987 | punctuation_stack = [matching_punctuation[text[start_position - 1]]] |
| 5988 | position = start_position |
| 5989 | while punctuation_stack and position < len(text): |
| 5990 | if text[position] == punctuation_stack[-1]: |
| 5991 | punctuation_stack.pop() |
| 5992 | elif text[position] in closing_punctuation: |
| 5993 | # A closing punctuation without matching opening punctuation. |
| 5994 | return None |
| 5995 | elif text[position] in matching_punctuation: |
| 5996 | punctuation_stack.append(matching_punctuation[text[position]]) |
| 5997 | position += 1 |
| 5998 | if punctuation_stack: |
| 5999 | # Opening punctuation left without matching close-punctuation. |
| 6000 | return None |
| 6001 | # punctuation match. |
| 6002 | return text[start_position : position - 1] |
| 6003 | |
| 6004 | |
| 6005 | # Patterns for matching call-by-reference parameters. |