1

Is there a way to match matching parentheses in vim regex?

For example, if I have a line like this:

function("test(with)parentheses"); // (comment)

Command

:.s/\(function\)(\(.*\))/\1[\2]/g

turns it into function["test(with]parentheses"); // (comment), and

:.s/\(function\)(\([^)]*\))/\1[\2]/g

turns it into function["test(with)parentheses"); // (comment].

How would I get function["test(with)parentheses"]; // (comment)?

Please, don't assume that the ) of interest is always followed by a ;.

4
  • for your given sample, you could use s/\(function\)(\("[^"]*"\))/\1[\2]/g but I suppose that is not always the case? – Sundeep May 26 '18 at 4:11
  • Right. I just made up the example on the spot. The question is really about identifying the closing parenthesis. – SU3 May 26 '18 at 5:02
  • 1
    okay :) if you are familiar with perl regex and your installation supports it, you could use perldo s/// – Sundeep May 26 '18 at 5:04
  • That's interesting. I'll look into that. – SU3 May 26 '18 at 5:25
4

TL;DR:

You can not use regexp's to match infinitely nested patterns in Vim.

Regular Languages

Regular expressions are modeled off a formal language theory, regular language. Some regular expression libraries even go a step further and use an NFA (Nondeterministic finite automaton are regular) as the actual underlying model their regular expression engine. Vim has support for a NFA based regex engine for example. See :h NFA.

Why is it important that Regexp are modeled off of Regular Languages?

First and foremost nested matching parenthesis is not regular. Although regex engines are often not purely regular in what they can match, they must support recursion to match nested patterns. So the question becomes can the given regex engine use recursion to overcome this limit? Perl's regex engine can do this for example. Sadly, Vim does not have a regex engine that can use recursion.

Workarounds?

You can anticipate the max depth and use branches to sort of accomplish this affect:

([^(]*([^)]*)[^)]*)\|([^)]*)

That pattern will match (foo) as well as (foo(bar)foo). It will not match (foo(bar(baz)bar)foo) as it is too deep. It will also fail if parens are not properly matched.

You may want to use something other than :substitute. Like use the % command or text objects.

f(mm%r]`mr[
f(yi)ca)[<c-r>0]<esc>

You may be able to use Tim Pope's surround.vim for this use case:

cs(]

This would "change the surrounding" ( pairs with ] pairs.

More advanced techniques would be to use searchpair() and/or sniffing Vim's syntax highlighting for a given word. See :h searchpair()

2
  • Thanks for the thorough explanation. As an aside, why is recursion necessary to identify matching parentheses? Wouldn't counting opening and closing parentheses within the string suffice, i.e. while the algorithm steps character by character through a string to see if (.*) can still include the next character. Sorry if this statement is unclear. There isn't enough room in a comment. – SU3 May 25 '18 at 23:47
  • Regular languages / NFA’s can’t count. That is why they can not handle such cases. Regexp engines must have special support to handle it. In regexp speak it is recursive patterns which sort of count. More like counting with a stack. This is more akin a push down automatons. I feel like this is getting too deep into Formal Languages and regexp engines. TL;DR: sorry you just can’t and if your engine can recurse, then you will need to support one super ugly and confusing pattern – Peter Rincker May 26 '18 at 0:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.