Comma-separated list
Here some macro which reads the lines as macro arguments.
It got inspired by this question.
It would be possible to change the "interface" to LaTeX environments instead, but the TeX syntax (without \begin
and \end
) is easier.
\documentclass{article}
\makeatletter
\newcommand*\commasep[2][, ]{%
\begingroup
\def\commasepsep{#1\space}% store separator e.g. ','
\def\commasependsep{#2\space}% store last separator e.g. 'and'
\@commasep
}
\newcommand*\@commasep[1][.]{%
\def\commasepend{#1}% store end marker e.g. '.'
\@ifnextchar\endcommasep{}{% check for empty list
\catcode\endlinechar\active% make end-of-line active
\commasepfirstelement% read first element
}%
}%
\begingroup
\catcode\endlinechar\active%
% The `~` character represents the end-of-line character in the
% code below:
\lccode`\~=\endlinechar%
\lowercase{%
% Read first element
\gdef\commasepfirstelement#1~}{%
#1%
\@ifnextchar\endcommasep{%
\commasepend% remove this if you don't want one with only one element
}{%
\commasepelement% no comma here!
}%
}%
\lowercase{%
\endgroup%
% Read all other elements
\gdef\commasepelement#1~}{%
\@ifnextchar\endcommasep{% Stop at `\endcommasep`
\commasependsep#1\commasepend%
}{%
\commasepsep#1\commasepelement%
}%
}%
\def\endcommasep{%
\@gobble{endcommasep}% be unique (for `\@ifnextchar`)
\endgroup
}%
\makeatother
\begin{document}
% Usage: \commasep[<separator>]{<last separator>}[<end marker>]
xxx\commasep[,]{and}[.]
a
b
c
d
\endcommasep yyy
xxx\commasep{and}
a
\endcommasep yyy
xxx\commasep{and}
\endcommasep yyy
xxx\commasep{or}
a
b
\endcommasep yyy
\end{document}
Please also note that there is a parselines
package which might be used. However, it doesn't support handling the last entry different. Nevertheless, its code might be a good read for people interested in this kind of parsing.
Items are delimited by ,,
, and the list is ended with ..
. In fact, each ,,
should be followed by a space or the end of a line (without %
). For very long lists (a few paragraphs?), this might get slow, since we grab the whole list as an argument at every step.
(Note that I "declare" each command with \newcommand\command{}
to make sure they don't exist before I define them using \def
.)
The whole construction relies on a generic macro to insert the desired separators between list elements, with some configuration possible. See e.g., \commasepitem
below, which writes the list in an itemize
environment.
\documentclass{article}
\makeatletter
% #1 at the beginning
% #2 between each element
% #3 between the last two
% #4 after the last
% #5 is the first item
\newcommand*\commasep@generic{}
\def\commasep@generic#1#2#3#4 #5,, {#1#5\commasep@next{#2}{#3}{#4}}
\newcommand*\commasep@next{}
\def\commasep@next#1#2#3#4,, #5..{%
% #1 is the list separator,
% #2 is the last list separator,
% #3 is the end text
% #4 the item,
% #5 the rest.
\expandafter\ifx\expandafter a\detokenize{#5}a% test if #3 is empty.
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
{#2#4#3}%
{#1#4\commasep@next{#1}{#2}{#3}#5..}%
}%
\newcommand*\commasep[1]{\commasep@generic{}{, }{, #1 }{\ignorespaces}}
\newcommand*\commasepitem{%
\commasep@generic{\begin{itemize}\item}{\item}{\item[(last)]}{\end{itemize}} }
\makeatother
\begin{document}
xx\commasep{and}
a,,
b,,
c,,
d,,
..
yyyy
xxx\commasepitem
``London bridge is falling down'' is much more frightening!,,
c,,
``Mary had a little lamb, little lamb, little lamb'' is a nice
little song that my mom used to sing,,
d,,
..
yyyy
\end{document}
With the help of enumitem it is possible.
\usepackage[inline]{enumitem}
\begin{itemize*}[label={}, itemjoin={,}, itemjoin*={, and}]
\item one
\item two
\item three
\end{itemize*}
Output: