# How to define a command that surronds an argument with parenthesis if it contains a comma?

I have a very simple command in which one of the arguments can contain commas, in which case the output may not be easily readable since the text in which it is used may contain other commas, making it non-obvious where that notation ends.

Unfortunately I often forgot to surround this argument with parenthesis, hence the confusion.

How can I redefine that command so that all its occurrences produce the good-looking output(i.e. exactly one pair of parenthesis is used if it contains commas)?

The particular command is:

\newcommand{\SubSeq}[3][\empty]{{#2 \upharpoonright #3}^{#1}}


And the argument that might contain a comma is #3.

Edit: I don't really care for more complicated situations like (a,b),c,d, you can assume that the input either has parenthesis surrounding the whole argument, or it doesn't have any parenthesis.

However, I'd agree that in ideal solution I'd like to have parenthesis every time that there's a comma outside delimiters in the argument. (And you can definitely assume delimiters are well matched)

Thanks to all who contributed answers! I decided to accept Qrrbrbirlbel's answer because is the most simple solution that achieve the functionality I wanted, but I'm really grateful to all of you.

## Code

\documentclass[varwidth]{standalone}
\usepackage{amsmath,amssymb,xstring}
\newcommand{\SubSeq}[3][]{%
{#2 \upharpoonright
\IfSubStr{#3}{,}{%
\IfBeginWith{#3}{(}{%
\IfEndWith{#3}{)}{#3}{(#3)}%
}{(#3)}%
}{#3}}%
\if\relax\detokenize{#1}\relax\else^{#1}\fi}
\begin{document}
$\SubSeq{a}{b}$              \par
$\SubSeq{a}{c, d}$           \par
$\SubSeq{a}{(e, f)}$         \par
$\SubSeq{a}{(g, h), i}$      \par
$\SubSeq{a}{((j, k), l)}$    \par
$\SubSeq{a}{[m, n]}$         \par
$\SubSeq{a}{(o, p), (q, r)}$ \par
\end{document}


## Output

• This produces double-parenthesis when used as \SubSeq{a}{(b,c)}, which would ruin the output in the cases where I did remember of this issue. – Bakuriu Sep 25 '13 at 13:39
• @Bakuriu Ah, I didn’t get that that should be caught. What output should \SubSeq[3]{f}{(g,h), i, k} give? – Qrrbrbirlbel Sep 25 '13 at 14:10
• Well, I don't really care because I don't have occurrences of it in my text, but ideally I want parenthesis whenever there is a comma not surrounded by parenthesis in the argument. – Bakuriu Sep 25 '13 at 14:22
• I guess no parentheses should be added in the square brackets case... – user4686 Sep 25 '13 at 14:28
• @Bakuriu I have updated my answer. Though, you will need some form of intelligent parsing to catch all possible uses of delimiters like the last two examples. – Qrrbrbirlbel Sep 25 '13 at 14:30

This avoids adding extra parentheses, also when the material is within square brackets or other delimiter.

\documentclass{article}

\makeatletter
\ifin@\expandafter\@thirdofthree
\else\expandafter\@firstofone
\fi
{\in@{,}{#1#2}%
\ifin@\expandafter\@firstoftwo
\else\expandafter\@secondoftwo
\fi}%
{(#1#2)}{#1#2}}
\makeatother

\begin{document}\thispagestyle{empty}
$\AddParenMaybe{b} \quad \AddParenMaybe{c,d} \quad \AddParenMaybe{(c,d)} \quad \AddParenMaybe{X^{X^X},d} \quad \AddParenMaybe{\left(X^{X^X},d\right)} \quad$
\end{document}


This may be more or less daunting than Qrrbrbirlbel's answer, depending on the background. ;-) But checking first for an open parentheses and then for a comma is easier.

\documentclass{article}
\usepackage{amsmath,amssymb,xparse}

\ExplSyntaxOn
\NewDocumentCommand{\SubSeq}{o m m}
{
\bakuriu_subseq:nn {#2}{#3}
\IfValueT{#1}{^{#1}}
}
\cs_new_protected:Npn \bakuriu_subseq:nn #1 #2
{
#1 \upharpoonright \nolinebreak
\tl_if_in:nnTF { #2 } { ( }
{ #2 } % there is ( so we do nothing
{ % there is no (, check if a comma is present
\tl_if_in:nnTF { #2 } { , }
{ (#2) }
{ #2 }
}
}
\ExplSyntaxOff
\begin{document}
$\SubSeq{a}{b} \quad \SubSeq{b}{c,d} \quad \SubSeq[3]{f}{(g,h)}$
\end{document}


The following extension should not be taken too seriously. ;-)

\documentclass{article}
\usepackage{amsmath,amssymb,xparse,l3regex}

\ExplSyntaxOn
\NewDocumentCommand{\SubSeq}{o m m}
{
\bakuriu_subseq:nn {#2}{#3}
\IfValueT{#1}{^{#1}}
}
\cs_new_protected:Npn \bakuriu_subseq:nn #1 #2
{
#1 \upharpoonright \nolinebreak
\tl_if_in:nnTF { #2 } { , }
% there is a comma, so we do a more complicated check
{ \bakuriu_check_parens:n { #2 } }
% no comma, print the argument as is
{ #2 }
}
\cs_new_protected:Npn \bakuriu_check_parens:n #1
{
\regex_match:nnTF { \A $$.*$$ \Z } { #1 }
% we have parentheses at start and end
{
\regex_match:nnTF { \A $$.* (\(|$$) .* \) \Z } { #1 }
% we have parentheses inside, add a pair of braces
{
% check for unbalanced parentheses if we remove the first
\regex_match:nnTF { \A $$(.*?\(.*?$$.*).* \) } { #1 }
{ #1 }
{ ( #1 ) }
}
% no inner parentheses
{ #1 }
}
% no outer parentheses
{ ( #1 ) }
}
\ExplSyntaxOff
\begin{document}
$\SubSeq{a}{b} \quad \SubSeq{b}{c,d} \quad \SubSeq[3]{f}{(g,h)}$

$\SubSeq{a}{(b,c)}\quad \SubSeq{a}{(b,c),d}$

$\SubSeq{a}{(b,c),(d,e)}\quad \SubSeq{a}{((b,c),(d,e))}$

$\SubSeq[2]{a}{(b,c),d,(e,f)}$

$\SubSeq{a}{a,(b,c),e}\quad \SubSeq{a}{(a,(b,c),e)}$

$\SubSeq{a}{a,(b,(c,d)),e}\quad \SubSeq{a}{(a,(b,(c,d)),e)}$
\end{document}


One might allow different inner delimiters, by first normalizing them to parentheses with a suitable regex substitution, this is left as an exercise. ;-)

• I don’t know if this even makes sense as input and output but what about \SubSeq[3]{f}{(g,h), i, k}? I expect there to get ((g, h), i, k) in the final output. – Qrrbrbirlbel Sep 25 '13 at 14:08
• egreg, before correcting, you will want to note that his next objection will be that your code does not handle (g,h),(i,k) as input. – Ryan Reich Sep 25 '13 at 14:11
• @Qrrbrbirlbel This can be accomplished, with a more complicated parsing. Let's wait for Bakuriu telling something about it. – egreg Sep 25 '13 at 14:12
• @RyanReich ooh I like a challenge:-) – David Carlisle Sep 25 '13 at 16:32

\documentclass{article}

\usepackage{amssymb}

\makeatletter
\newcommand{\SubSeq}[3][\empty]{{#2 \upharpoonright \sbox0{$\zz#3$}\zzl#3\zzr}^{#1}}
\def\zz{\global\let\zzl\relax\global\let\zzr\relax\count@\z@
\zzdef$${\advance\count@\@ne}% \zzdef$${\advance\count@\m@ne}%
\zzdef\,{\ifnum\count@=\z@\global\let\zzl(\global\let\zzr)\fi}%
}
\def\zzdef#1{\mathcode#1"8000\begingroup\lccode~#1\lowercase{\endgroup\def~}}
\makeatother

\begin{document}

egreg, before correcting, you will want to note that his next objection will be
that your code does not handle (g,h),(i,k) as input. - Ryan Reich 1 hour ago

$\SubSeq{a}{(g,h),(i,k)}$

$\SubSeq{a}{b} \quad \SubSeq{b}{c,d} \quad \SubSeq[3]{f}{(g,h)}$

$\SubSeq{a}{(b,c)}\quad \SubSeq{a}{(b,c),d}$

$\SubSeq{a}{(b,c),(d,e)}\quad \SubSeq{a}{((b,c),(d,e))}$

$\SubSeq[2]{a}{(b,c),d,(e,f)}$

$\SubSeq{a}{a,(b,c),e}\quad \SubSeq{a}{(a,(b,c),e)}$

$\SubSeq{a}{a,(b,(c,d)),e}\quad \SubSeq{a}{(a,(b,(c,d)),e)}$
\end{document}

• I like your \zzdef macro. It's very elegant (I mean, aside from the standard cleverness of \lowercase). – Ryan Reich Sep 25 '13 at 18:00

Thanks to David's doing the hard work, I can give a version of his answer that does not use count registers. Instead, it just lets TeX count the nesting:

\documentclass{article}
\usepackage{amssymb}
\makeatletter

\newcommand*\SubSeq[3][\empty]{%
{#2 \upharpoonright \sbox0{$\zz#3\zzg$}\zzp(#3\zzp)}^{#1}%
}

\def\zzdef#1{%
\mathcode#1"8000\begingroup
\lccode~#1%
\lowercase{\endgroup\def~}%
}

\def\zz{%
\zzdef\,{\let\zzp\relax}%
\zzdef$${\begingroup}% \zzdef$${\endgroup}%
\let\zzp\@gobble
}

\def\zzg{%
\global\let\zzp\zzp
}

\makeatother
\begin{document}
$\SubSeq{a}{(g,h),(i,k)}$

$\SubSeq{a}{b} \quad \SubSeq{a}{c,d} \quad \SubSeq[3]{f}{(g,h)}$

$\SubSeq{a}{(b,c)} \quad \SubSeq{a}{(b,c),d}$

$\SubSeq{a}{(b,c),(d,e)} \quad \SubSeq{a}{((b,c),(d,e))}$

$\SubSeq[2]{a}{(b,c),d,(e,f)}$

$\SubSeq{a}{a,(b,c),e}\quad \SubSeq{a}{(a,(b,c),e)}$

$\SubSeq{a}{a,(b,(c,d)),e} \quad \SubSeq{a}{(a,(b,(c,d)),e)}$
\end{document}


The output is the same.

• In my favor, I dropped it in the first place :) – Ryan Reich Sep 26 '13 at 20:13