How to add closing parentheses when there are additional parentheses inside

Viewed 221

I am doing a "parser" which has to replace a keyword with another keyword

trim(<something>)  --> ltrim(rtrim(<something>))

I am using python regular expressions. The main issue is since in the <something> part there can be additional parentheses which makes forming a regex very difficult to me. Simply adding a ? to make it non-greedy doesn't really work, and putting greedy the regex will match with the parentheses at the end of the file 200 lines down. From the start I assume someone else had this issue, and I found these two questions.

How to correctly parse closing parentheses

How to add a missing closing parenthesis to a string in Python?

But neither help in my case, my code so far:

def run(path):
for root, dirs, files in os.walk(path):
    for fname in files:
        fpath = os.path.join(root, fname)
        with open(fpath, 'r') as f:
            s = f.read()
        s = s.replace("trim","ltrim(rtrim" )
        text = s
        text = re.sub(r'(\(.+(?!\))\b)', r"\1)", text)
        return text

The code just adds closing ) without including the text in between even though I places a + in my regex which should mean at least one appearance of any char.

Like this

trim(p_name) becomes ltrim(rtrim()p_name)

I need it to be ltrim(rtrim(p_name))

1 Answers

You cannot match recursive patterns with built-in Python re, so you will have to pip install regex to install the PyPi regex module and use

return regex.sub(r'\btrim(\((?:[^()]++|(?1))*\))', r'ltrim(r\g<0>)', text) )

See the regex demo.

Details:

  • \btrim - matches a whole word trim
  • (\((?:[^()]++|(?1))*\)) - Group 1:
    • \( - a ( char
    • (?:[^()]++|(?1))* - zero or more chars other than ( and ) or the whole Group 1 pattern (recursed)
    • \) - a ) char.

The replacement is ltrim(r\g<0>): ltrim(r + the whole match + ).

See the Python demo:

import regex
text = "trim(str(p_name))"
print( regex.sub(r'\btrim(\((?:[^()]++|(?1))*\))', r'ltrim(r\g<0>)', text) )
## => ltrim(rtrim(str(p_name)))
Related