Pessoalmente, se minhas expressões regulares estivessem se aproximando desse nível de complexidade, eu mudaria toda a operação para Perl. Este lida com um número arbitrário de chaves abertas / parênteses / chaves:
$ perl -ne '@open=/[\[({]/g; @close=/[)\]}]/g;
if($#close == $#open){s/(.+?)\.is/($1).is/} print' file
Ou mais compacto:
$ perl -pne 's/(.+?)\.is/($1).is/ if $#{/[\[({]/g} == $#{/[)\]}]/g}' file
Ou, mais completo, este pode lidar com casos como [}
(mas ainda falha em casos como )(
):
$ perl -pne '@osqb=/\[/g; @csqb=/\]/g;
@ocb=/\{/g; @ccb=/\}/g;
@op=/\(/g; @cp=/\)/g;
if($#osqb == $#csqb && $#ocb==$#ccb && $#op == $#cp){
s/(.+?)\.is/($1).is/
}' file
Quando executado no seu exemplo, isso será impresso
(this_thing).is 24
(that).is 50
(a[23]).is == 10
(a).is true
(this_thing).is 24
this_thing.is (24
((that).is 50
(a[23].is == 10
a.is ( true
(this_thing.is 24
a{.is true
this_thing{.is 24
a[.is true
this_thing[.is 24
Explicação
-
perl -ne
: processa o arquivo de entrada linha a linha (-n
) e executa o script fornecido por-e
. -
@open=/[\[({]/g;
: encontre todos os glifos de abertura e salve o resultado em uma matriz chamada@open
. -
@close=/[)\]}]/g;
: como acima, mas para o fechamento de glifos. -
if($#close == $#open)
: se o número de glifos de abertura for igual ao número de glifos de fechamento (se, em outras palavras, houver parênteses pendentes, etc.) ... -
s/(.+?)\.is/($1).is/
: ... então, substitua a cadeia mais curta que termina em.is
por si mesma entre parênteses. - O último
print
está fora dos colchetes e será executado se houve uma substituição ou não.