# A robust command for French-style intervals

In France, we usually write open or half-open intervals as ]a,b[ or [a,b[, not (a,b) or [a,b). I especially do not like (a,b) as it can be confused with an ordered pair. I have therefore defined an \interv command so that in \interv]a,b[, for instance, ] is considered as an opening delimiter and [ as a closing one. The comma between a and b may also be replaced by another symbol or be surrounded by more space to avoid confusion with a decimal separator, especially if the comma is declared as \mathord.

I know I could simply define a different command for each of the four possible intervals, but it would not be fun. The main difficulty is to find the closing delimiter after the comma, as it can either be ] or [. I am aware of the answers to how-to-read-a-stream-of-tokens-up-to-one-of-two-different-delimiters but is does not seem to solve my problem. My solution (see below) is to test each token after the comma and see if it is ] or [. Delimiters and commas inside brace-delimited list of tokens are ignored.

I also allow to put \lr between the \interv and the opening delimiter: this inserts \left and \right before the left and right delimiters, respectively. Another possibility is to put one of the \big (\Big, etc.) commands instead of \lr: this inserts \bigl and \bigr, etc. before left and right delimiters, respectively.

This works well, except in sectioning commands and the table of contents, and this only when \lr or \big is used. The simple \interv]a,b[ works in all cases because of the \protect in the definition of \interv, but what can I do to have \interv\lr]a,b[ and \inter\big]a,b[ also working in \section and the ToC? My commands are cumbersome, so feel free to suggest entirely different solutions, but with the same syntax for the user!

Here is my code:

\documentclass{article}

\makeatletter

% Main routine.
% Examples of use:
% -- "\interv[a,b]", where "," is replaced by "\intervsep";
% -- "\interv]-a,b[", where "]" is replaced by "\mathopen]" and "[" by
% "\mathclose[";
% -- "\interv\lr]a,b]", where the left "]" is replaced by "\left]" and the
% right one by "\right]";
% -- "\interv\Bigg[a,b[", where the left "[" is replaced by "\Biggl[" and the
% right one by "\Biggr[".
% -- "\interv[a,{f[x]}]", to hide brackets inside the braces so that the closing
% delimiter is the uttermost right "]".
\def\interv{\tok@list={}%
\protect\interv@I}

% Define the symbol and the spacing between <a> and <b>.
% Note: "\mathopen{}" is put after "\intervsep" in "\interv@V", in case <b>
% begins with "+" or "-", so that the operator is considered as unary.
\def\intervsep{\mathord{;}\mkern3mu}

% Search for the opening delimiter.
\def\interv@I{%
\@ifnextchar[%
{\interv@II}% "[" found.
{\@ifnextchar]%
{\interv@II}% "]" found.
{\@ifnextchar\lr%
{\interv@III}% "\lr" found.
{\interv@IV}% "\big", "\Big", etc. found.
}}}

% Left delimiter found just after "\interv".
\def\interv@II{%
\def\first{\mathopen}%
\def\last{\mathclose}%
\interv@V}

% "\lr" found between "\interv" and the opening delimiter.
\def\interv@III#1{%
\def\first{\left}%
\def\last{\right}%
\interv@V}

% "\big", "\Big", etc. found between "\interv" and the opening delimiter.
\def\interv@IV#1{%
\def\first{\csname\expandafter\@gobble\string#1l\endcsname}%
\def\last{\csname\expandafter\@gobble\string#1r\endcsname}%
\interv@V}

% Process everything left of ",".
% Note: "\lr" and "\big", etc. have been absorbed by "\interv@III" and
% "\interv@IV", so #1 is always "[" or "]".
\def\interv@V#1#2,{\first#1#2\intervsep\mathopen{}\interv@VI}

% Search for the closing delimiter and read all tokens up to it.
\def\interv@VI{%
\@ifnextchar]%
{\interv@VII}% "]" found.
{\@ifnextchar[%
{\interv@VII}% "[" found.
{\@ifnextchar\bgroup%
{\interv@VIII}% list of tokens between braces found.
{\interv@IX}% single token found.
}}}

% Process closing delimiter. "\last" is "\mathclose" or "\right".
\def\interv@VII#1{\the\tok@list\last#1}

% Do not search for the closing delimiter within brace-delimited lists of
% tokens. Append them with enclosing braces to the main list and look for the
% next token.
\def\interv@VIII#1{\append@tok{{#1}}\tok@list\interv@VI}

% Append a single token to the main list and look for the main token.
\def\interv@IX#1{\append@tok#1\tok@list\interv@VI}

% Append a token to a list of tokens.
\newtoks\tok@list
\newtoks\next@tok
\def\append@tok#1#2{\next@tok={#1}%
\edef\do@append{\noexpand#2={\the#2\the\next@tok}}%
\do@append}

\makeatother

\begin{document}
% To check the effects of "\mathopen", "\mathclose", "\left", "\right" on
% binary and unary operators.
\thinmuskip=20mu
\medmuskip=40mu

% Works.
\verb|A \cap \interv]-a, +b[ \mathord\setminus B|:
$A \cap \interv]-a, +b[ \mathord\setminus B$

% Works.
\verb|A \cap \interv]-1{,}2, +3{,}4[ \mathord\setminus B|:
$A \cap \interv]-1{,}2, +3{,}4[ \mathord\setminus B$

% Works.
\verb|A \cap \interv\lr]-a, +\frac{b}{c}] \mathord\setminus B|:
$A \cap \interv\lr]-a, +\frac{b}{c}] \mathord\setminus B$

% Works.
\verb|A \cap \interv\Bigg[-a, +{b[c]}[ \mathord\setminus B|:
$A \cap \interv\Bigg[-a, +{b[c]}[ \mathord\setminus B$

\hrule\leavevmode

% Works.
\noindent\verb|A \cap \interv]-a, +b[ \mathord\setminus B|:
\section{$A \cap \interv]-a, +b[ \mathord\setminus B$}

% Complains that "\lr" is an undefined control sequence.
% "\left" and "\right" not used in the table of contents.
\verb|A \cap \interv\lr]-a, +\frac{b}{c}] \mathord\setminus B|:
\section{$\displaystyle A \cap \interv\lr]-a, +\frac{b}{c}] \mathord\setminus B$}

% Fails in the table of contents.
\verb|A \cap \interv\Bigg[-a, +{b[c]}[ \mathord\setminus B|:
\section{$A \cap \interv\Bigg[-a, +{b[c]}[ \mathord\setminus B$}

\tableofcontents
\end{document}


## 1 Answer

I don't like this way of doing things: it's fragile and error prone.

However, the protection for the object in moving arguments is easy to provide.

Since \interv is necessarily followed by one token, which is not a left brace, you can define it as

\def\interv#1{%
\ifx\protect\@typeset@protect
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
{\tok@list={}\interv@I#1}%
{\noexpand\interv\noexpand#1}%
}


so when LaTeX is not typesetting, it will see \interv and the first token after it and not expand them.

This is, more or less, how I do it. Of course I don't use the reverse brackets: I agree with Don Knuth it's a perversion (see exercise 18.14 of the TeXbook). ;-) I just adapted my macros to your preferences. Note that this way of inputting the code allows for much greater flexibility. Just change the delimiters in the definition and you'll change them in all the book.

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

\ExplSyntaxOn
\NewDocumentCommand{\interval}{O{}>{\SplitArgument{1}{,}}m}
{
\group_begin:
\keys_set:nn { calcolo/interval } { o, #1 }
\bool_if:NTF { \l_calcolo_interval_auto_bool }
{\mleft\l_calcolo_interval_left_tl}
{\mathopen{\l_calcolo_interval_size_tl\l_calcolo_interval_left_tl}}
\calcolo_interval_set:nn #2
\bool_if:NTF { \l_calcolo_interval_auto_bool }
{\mright\l_calcolo_interval_right_tl}
{\mathclose{\l_calcolo_interval_size_tl\l_calcolo_interval_right_tl}}
\group_end:
}
\cs_new_protected:Nn \calcolo_interval_set:nn
{
#1 \l_calcolo_interval_sep_tl #2
}

\keys_define:nn { calcolo/interval }
{
size .code:n =
\tl_if_eq:nnTF { #1 } { * }
{ \bool_set_true:N \l_calcolo_interval_auto_bool }
{ \tl_set:Nx \l_calcolo_interval_size_tl { \exp_not:c {#1} } },
o .code:n =
\tl_set:Nn \l_calcolo_interval_left_tl { ] }
\tl_set:Nn \l_calcolo_interval_right_tl { [ },
oo .code:n =
\tl_set:Nn \l_calcolo_interval_left_tl { ] }
\tl_set:Nn \l_calcolo_interval_right_tl { [ },
c .code:n =
\tl_set:Nn \l_calcolo_interval_left_tl { [ }
\tl_set:Nn \l_calcolo_interval_right_tl { ] },
cc .code:n =
\tl_set:Nn \l_calcolo_interval_left_tl { [ }
\tl_set:Nn \l_calcolo_interval_right_tl { ] },
oc .code:n =
\tl_set:Nn \l_calcolo_interval_left_tl { ] }
\tl_set:Nn \l_calcolo_interval_right_tl { ] },
co .code:n =
\tl_set:Nn \l_calcolo_interval_left_tl { [ }
\tl_set:Nn \l_calcolo_interval_right_tl { [ },
o .value_forbidden:n = true,
oo .value_forbidden:n = true,
c .value_forbidden:n = true,
cc .value_forbidden:n = true,
oc .value_forbidden:n = true,
co .value_forbidden:n = true,
}

\tl_new:N \l_calcolo_interval_left_tl
\tl_new:N \l_calcolo_interval_right_tl
\tl_new:N \l_calcolo_interval_size_tl
\tl_new:N \l_calcolo_interval_sep_tl
\bool_new:N \l_calcolo_interval_auto_bool

\tl_set:Nn \l_calcolo_interval_sep_tl { \mathpunct{;} }

\ExplSyntaxOff

\begin{document}

$\interval{a,b}$
$\interval[o]{a,b}$ $\interval[c]{a,b}$
$\interval[oo]{a,b}$ $\interval[cc]{a,b}$
$\interval[oc]{a,b}$ $\interval[co]{a,b}$

\bigskip

\verb|A \cap \interval{-a, +b} \setminus B|:
$A \cap \interval{-a, +b} \setminus B$

% Works.
\verb|A \cap \interval{-1{,}2, +3{,}4} \setminus B|:
$A \cap \interval{-1{,}2, +3{,}4} \setminus B$

% Works.
\verb|A \cap \interval[oc,size=*]{-a, +\frac{b}{c}} \setminus B|:
$A \cap \interval[oc,size=*]{-a, +\frac{b}{c}} \setminus B$

% Works.
\verb|A \cap \interval[co,size=Bigg]{-a, +b[c]} \setminus B|:
$A \cap \interval[co,size=Bigg]{-a, +b[c]} \setminus B$

\bigskip
\hrule
\bigskip

% Works.
\noindent\verb|A \cap \interval{-a, +b} \setminus B|:
\section{$A \cap \interval{-a, +b} \setminus B$}

\verb|A \cap \interval[oc,size=*]{-a, +\frac{b}{c}} \setminus B|:
\section{$\displaystyle A \cap \interval[oc,size=*]{-a, +\frac{b}{c}} \setminus B$}

\verb|A \cap \interval[co,size=Bigg]{-a, +b[c]} \setminus B|:
\section{$A \cap \interval[co,size=Bigg]{-a, +b[c]} \setminus B$}

\tableofcontents
\end{document}


• The syntax of \interval is less natural than mine, but your LaTeX2e redefinition of \interv works and I will use it from now on; I would never have found it, thank you! I knew the quote from D. Knuth. Actually, French mathematicians have other perversions, such as calling "positif", "supérieur" and "croissant" what English-speaking mathematicians would call "non negative", "greater than or equal" and "non decreasing". I find the French convention more convenient, but I agree that our use of \subset for non proper subsets is inconsistent with the use < for the strict inequality. – Michel Fioc Nov 20 '15 at 11:15
• @MichelFioc Maybe less natural, but surely more flexible; and it doesn't require bracing brackets. As the input separator one can also choose a different one, if commas appear frequently in the boundaries. – egreg Nov 20 '15 at 11:23