# Replacing certain delimiters in an existing document: P( ) to P[ ]

What is the best strategy to replace the delimters of a Probability measure \mathbb{P} automatically in a very long existing file.

Assume the existing LaTeX file used notation like \mathbb{P}(A), \mathbb{P}(B) and \mathbb{P}(...), but it should use the notation \mathbb{P}\lbrack ...\rbrack.

I guess many LaTeX users are facing these kind of problems, but maybe ic can be better solved outside LaTeX - maybe by using regular expressions in the code editor? (I have very little experience with regex.)

It is important that not all round parenthesis are replaced. Only the opening parenthesis directly after \mathbb{P} and its corresponding closing parenthesis should be replaced by squared ones.

It would be nice if probabilities of intervalls would work to - eg. \mathbb{P}(\lbrack a, b)) to \mathbb{P}\lbrack\lbrack a, b)\rbrack, but if one percent of the replacements are wrong it would be still helpful.

PS: Of course it would be better to define a command for that in the first place like \newcommand{\PP}[1]{\mathbb{P}\left\lbrack{#1}\right\rbrack}, but I am talking about an existing document where \mathbb{P}(...) is hard coded in the text.

PPS:MWE:

\documentclass{article}
\usepackage{amssymb}

\begin{document}
Basic examples: $\mathbb{P}(A)$,
nested: $\mathbb{P}(A\cup(A\cap B))$, intervall-notation: $\mathbb{P}(X\in[a,b))$,
left/right: $\mathbb{P}\left(X\in\left[a,(-b)^k\right)\right)$
$\mathbb{P} ... (these\ parenthesis\ should\ not\ be\ affacted)$.%because they are not directly after P.

Often $($ and $]$ are matched---e.g.:
$\mathbb{P}(X\in[a,\infty))$,
$\mathbb{P}\left( Y\in[b,c)\cup[d,e)\right)$ and often $[$ and $)$ are matched---e.g.:
$\mathbb{P}\left( Y\in[b,c)\cup(d,e]\right)$

Sometimes \texttt{left} and \texttt{right} are used and sometimes not. %\left and \right
And nesting can be quite deep.
$\mathbb{P}\left( X\in \left( [a,b)\cup\left(c,3\left(a+(-1)^k(d+e)\right)\right] \right)\setminus (B\cap ( \lbrack k,k+1) \cup C )) \right)$
There could be comments involved too:
$\mathbb{P}( X\in \left( [a,b)\cup\left(c,3\left(a+(-1)(d+e)\right)\right] \right)\setminus (B\cap ( \lbrack k,k+1)%k+2) \cup C )) )$
\end{document}


• Are you willing to entertain a LuaLaTeX-based solution? – Mico Feb 13 at 22:20
• Would be nice to have an Overleaf-compatible document at the end, but I have no problem to use other software to manipulate the latex code. – Jakob Feb 13 at 22:26
• Use the facilities of your editor (or some other editor which can do regex) and do some search and replace. Finding matching braces can be hard, even more if they are actually not balanced, so you will probably have to check everyone before the replacement. – Ulrike Fischer Feb 13 at 22:26
• This is not trivial --- regex are normally bad at finding matched brackets (see for example stackoverflow.com/questions/5139300/… where a small Perl script is proposed instead). I (as a vim user) I would use a macro to search and position on the first ( and then use a command like change surronding delimiter or the equivalent for your mode, checking the changes one by one. – Rmano Feb 14 at 9:02

A simple search/replace would be to replace \mathbb{P}( with \PP{P}(. Then, assuming that all occurances of \mathbb{P} were of the form \mathbb{P}(...) the following should work:

If the content within the () also has round paren those will need an extra {} surrounding them. That is, somehitng like \PP{P}({AB)) will need to be changed to \PP{P}({(AB)}).

\documentclass{article}
\usepackage{amssymb}

\newcommand*{\PP}{}% Ensure we are not overwriting an existing macro
\def\PP#1(#2){\mathbb{#1}[#2]}

\begin{document}
%$\mathbb{P}(A)$% Replaced "\mathbb{P}" with "\PP{P}"
$\PP{P}(A)$
\end{document}

• +1. Of course, if you search and replace, you could just do a regular expression search and replace, and change the parentheses as needed. – Schrödinger's cat Feb 14 at 2:03
• @Schrödinger'scat: Allowing for nested () can get complicated. But if one is able to do a proper regex search and replace that would be ideal. – Peter Grill Feb 14 at 2:04
• Yes, I agree, but nested () will also be tricky with the macro approach (unless I am missing something and/or the OP was careful enough to add braces around inner parentheses). – Schrödinger's cat Feb 14 at 2:07
• $\PP{P}((A))$ does not work but $\PP{P}({(A)})$ would work as mentioned by @Schrödinger'scat. Do you think I would get any problems if I first replace EVERY occurrence of ( by ({, [ by [{, ] by }] and ) by }). This could work for half-open intervalls as well: P(X\in[a,\infty)). Additionally maybe \lbrack by \lbrack{, \rbrack by }\rbrack. – Jakob Feb 14 at 11:26
• \mathbb{P}\left( ... \right) will not be translated to square brackets automatically using this approach. – Jakob Feb 14 at 12:34

Only the opening parenthesis directly after \mathbb{P} and its corresponding closing parenthesis should be replaced by square brackets.

Here's a LuaLaTeX-based solution. It consists of (a) a Lua function called paren2brack which employs Lua's powerful string.gsub function and (b) a couple of LaTeX utility macros -- called \ParenBrackOn and \ParenBrackOff -- which switch the Lua function on and off. The parentheses-to-brackets substitution is performed "on the fly", i.e., the tex file itself is not edited or modified.

The solution can handle not just \mathbb{P}; in fact, the argument of \mathbb is allowed to be any uppercase letter.

Addendum: I've added a second string.gsub operation to the Lua function, so that it can process cases such as \mathbb{P}\left(A\right).

% !TEX TS-program = lualatex
\documentclass{article}
\usepackage{amssymb} % for '\mathbb' macro
\usepackage{luacode} % for 'luacode' env.

\begin{luacode}

function paren2brack ( s )
s = s:gsub( "(\\mathbb{%u})(%b())" , function ( x , y )
return x .. "[" .. y:sub(2,-2) .. "]"
end )
s = s:gsub( "(\\mathbb{%u})\\left%((.-)\\right%)" , function ( x , y )
return x .. "\\left[" .. y .. "\\right]"
end )
return s
end

\end{luacode}

%% Define a couple of LaTeX utility macros:
"process_input_buffer" , paren2brack , "paren2brack" )}}
\newcommand\ParenBrackOff{\directlua{luatexbase.remove_from_callback (
"process_input_buffer" , "paren2brack" )}}

\begin{document}

$\mathbb{P}(A)$, $\mathbb{Q}(B\cup(C\cap D))$, $\mathbb{P}\left(E\right)$
--- Lua function not activated

\ParenBrackOn % assign the Lua function to LuaTeX's 'process_input_buffer' callback
$\mathbb{P}(A)$, $\mathbb{Q}(B\cup(C\cap D))$, $\mathbb{P}\left(E\right)$
--- Lua function is activated

\end{document}

• Is there a way to hard code round parenthesis e.g. in the case of P(\left[a,b\right) ). Maybe with the help of something like tex.stackexchange.com/q/528372/128042 – Jakob Feb 13 at 23:31
• the code would be easier to understand if you could add a version, where only \mathbb{P} effects the parenthesis and not \mathbb{Q}. A version where [ is counted as opening parenthesis too and ] as closing parenthesis when finding the matching parenthesis without differentiating between them. – Jakob Feb 13 at 23:43
• @Jakob - to restrict the operation of the substitution operation to just \mathbb{P}, simply change \\mathbb{%u} to \\mathbb{P} in the search part of the gsub function. The mathtools package provides the macros \lparen and \rparen, which evaluate to ( and ), respectively. The macros and environments defined in mathtools are a superset of the macros and environments defined in amsmath. – Mico Feb 14 at 0:00
• how can I replace %b() by something like %b\left(\right)? In other words: how can I use strings instead of characters in %b()? – Jakob Feb 14 at 0:34
• @Jakob - I've augmented the Lua function so that it can handle \mathbb{P}\left(A\right)-type cases. – Mico Feb 14 at 14:23

Matching nested braces/brackets is not trivial; I propose the following which is a regular expression that plugs into perl via latexindent and its yaml interface. There's nothing to stop you from incorporating this into your own perl script.

The command that I used to generate the output is

latexindent -rr -l=jakob.yaml myfile.tex -o=output.tex


## output.tex



\documentclass{article}
\usepackage{amssymb}

\begin{document}
Basic examples: $\mathbb{P}\left\lbrack A\right\rbrack$,
nested: $\mathbb{P}\left\lbrack A\cup(A\cap B)\right\rbrack$, intervall-notation: $\mathbb{P}\left\lbrack X\in[a,b)\right\rbrack$,
left/right: $\mathbb{P}\left\lbrack X\in\left[a,(-b)^k\right)\right\rbrack$
$\mathbb{P} ... (these\ parenthesis\ should\ not\ be\ affacted)$.%because they are not directly after P.

Often $($ and $]$ are matched---e.g.:
$\mathbb{P}\left\lbrack X\in[a,\infty)\right\rbrack$,
$\mathbb{P}\left\lbrack Y\in[b,c)\cup[d,e)\right\rbrack$ and often $[$ and $)$ are matched---e.g.:
$\mathbb{P}\left\lbrack Y\in[b,c)\cup(d,e]\right\rbrack$

Sometimes \texttt{left} and \texttt{right} are used and sometimes not. %\left and \right
And nesting can be quite deep.
$\mathbb{P}\left\lbrack X\in \left( [a,b)\cup\left(c,3\left(a+(-1)^k(d+e)\right)\right] \right)\setminus (B\cap ( \lbrack k,k+1) \cup C )) \right\rbrack$
There could be comments involved too:
$\mathbb{P}\left\lbrack X\in \left( [a,b)\cup\left(c,3\left(a+(-1)(d+e)\right)\right] \right)\setminus (B\cap ( \lbrack k,k+1)%k+2) \cup C )\right\rbrack )$
\end{document}


and here's jakob.yaml; a summary of the approach is:

• for each $...$ and $...$ process the body by finding and storing all but the outer-most occurrences of \left(...\right], (...], \left[...\right), [...), \left(...\right), (...)

• for the outer-most occurrences of \left(...\right) and (...) replace them with \left\lbrack...\right\rbrack

See what you think. Doubtless others could do this more efficiently.

## jakob.yaml

replacements:
-
substitution: |-
s/((?<!\\)\$|\\$) # opening between either or \[ (.*?) # body between ... or \[...$ ((?<!\\)\$|\\\])/   # closing $or \] my$begin = $1; my$body = $2; my$end = $3; # operate on the body, only if it contains \mathbb if($body =~ m|\\mathbb|){
my $storageToken = "jakob-paren-token"; my$storageCount = 0;
my @storageArray = ();
# \left(...\right]
# \left(...\right]
# \left(...\right]
my $leftRightRegEx = qr|( \\left$$# \left( (?: (?! (?:\\left\() # anything except \left( ). )*? \\right\] # up to \right] )|xs; while( body =~ m|leftRightRegEx|xs ){ storageCount++; body =~ s|leftRightRegEx| push(@storageArray,{id=>storageToken.storageCount,value=>1}); storageToken.storageCount;|xse; } # (a,b] # (a,b] # (a,b] my intervalRegEx = qr|( \( # ( (?: (?! (?<!\$$$$# anything except ( ). )*? (?<!\$$ \] # up to ] )|xs; while($body =~ m|$intervalRegEx|xs ){$storageCount++;
$body =~ s|$intervalRegEx|
push(@storageArray,{id=>$storageToken.$storageCount,value=>$1});$storageToken.$storageCount;|xse; } # \left[...\right) # \left[...\right) # \left[...\right)$leftRightRegEx =
qr|(
\\left\[         # \left[
(?:
(?!
(?:\\left\[)  # anything except \left[
).
)*?
\\right\)        # up to \right)
)|xs;
while( $body =~ m|$leftRightRegEx|xs ){
$storageCount++;$body =~ s|$leftRightRegEx| push(@storageArray,{id=>$storageToken.$storageCount,value=>$1});
$storageToken.$storageCount;|xse;
}
# [a,b)
# [a,b)
# [a,b)
$intervalRegEx = qr|( \[ # [ (?: (?! (?<!\\)\[ # anything except [ ). )*? (?<!\\) \) # up to ) )|xs; while($body =~ m|$intervalRegEx|xs ){$storageCount++;
$body =~ s|$intervalRegEx|
push(@storageArray,{id=>$storageToken.$storageCount,value=>$1});$storageToken.$storageCount;|xse; } # \left(...\right) # \left(...\right) # \left(...\right) my$untilNoNestedLeftRightRegEx = qr|\\left$$(?: (?! (?<!\\right$$)
).
)*?
(?<!\\)
\\left$$|xs; leftRightRegEx = qr|( \\left\( # \left( (?: (?! (?:\\left\() # anything except \left( ). )*? \\right$$        # up to \right)
)|xs;
while( $body =~ m|$untilNoNestedLeftRightRegEx|xs ){
$storageCount++;$body =~ s|$leftRightRegEx| push(@storageArray,{id=>$storageToken.$storageCount,value=>$1});
$storageToken.$storageCount;|xse;
}
# now store all matched (...)
my $untilNoNestedParenRegEx = qr|$$(?: (?! (?<!\$$\) ). )*? (?<!\\) $$|xs; my parenRegEx = qr|( \( (?: (?! (?<!\$$$$). )*? (?<!\$$ \))|xs; while($body =~ m|$untilNoNestedParenRegEx|){$storageCount++;
$body =~ s|$parenRegEx|
push(@storageArray,{id=>$storageToken.$storageCount,value=>$1});$storageToken.$storageCount;|xse; } # now make the substitution: # \mathbb{(.*?)}$$(.*?)$$ # into # \mathbb{$1}\lbrack $2 \rbrack$body =~ s|(\\mathbb\{.*?\})\h*$$(.*?)$$|$1\\left\\lbrack$2\\right\\rbrack|sg;
$body =~ s|(\\mathbb\{.*?\})\h*\\left$$(.*?)\\right$$|$1\\left\\lbrack $2\\right\\rbrack|sg; # finally, put the nested braces/intervals # back in to the body while(@storageArray){ my$entry = pop(@storageArray);
$body =~ s|${$entry}{id}|${$entry}{value}|s; } }$begin.$body.$end;/sgxe

• I just tried to run your code without understanding it, but when I typed the command in the linux terminal I got "Unknown option: rr" and more error messages. I just coppied the latexindent folder int the same directory where the LaTeX-file and the yaml-file is or do I have to install this somehow? – Jakob Mar 22 at 20:40
• latexindent should ship with your tex distribution. Is your up to date? Current release is 3.8 – cmhughes Mar 22 at 20:44
• 3.4.2, 2018-04-07. How do I update? – Jakob Mar 22 at 21:05
• – cmhughes Mar 23 at 7:54
• Any luck with this? – cmhughes Mar 28 at 6:54