newcommand with comma separated argument, and optional arguments
The following example uses \comma@parse
of package kvsetkeys
to parse the comma separated argument lists.
\documentclass{report}
\usepackage{kvsetkeys}% provides \comma@parse
\usepackage{etexcmds}% provides \etex@unexpanded
\makeatletter
\newcount\arg@count
\newcommand{\arg@parser}[1]{%
\advance\arg@count\@ne
\expandafter\let\csname arg\romannumeral\arg@count\endcsname\comma@entry
}
% \mycmd[opt arg1, opt arg2]{arg 3, arg4, arg5}
\newcommand\mycmd[2][]{% Default is empty and will be configured later
% Set default values
\arg@count=\z@
\comma@parse{default1, default2}\arg@parser % Default values
% Parse optional argument
\arg@count=\z@
\comma@parse{#1}\arg@parser
\ifnum\arg@count>2 %
\@latex@error{Too many optional arguments}{%
The macro \string\mycmd\space got \the\arg@count\space
optional arguments,\MessageBreak
but expected are 2 optional arguments.\MessageBreak
\@ehd
}%
\fi
% Mandatory arguments
\arg@count=2
\comma@parse{#2}\arg@parser
\ifnum\arg@count=5 %
\else
\@latex@error{Wrong number of mandatory arguments}{%
The macro \string\mycmd\space got \the\numexpr\arg@count-2\relax\space
mandatory arguments,\MessageBreak
but expected are 3 mandatory arguments.\MessageBreak
\@ehd
}%
\fi
% Either using \argi, \argii, \argiii, \argiv, \argv
% or
% \@mycmd\argi\argii\argiii\argiv\argv
% or
\edef\process@me{%
\noexpand\@mycmd
{\etex@unexpanded\expandafter{\argi}}%
{\etex@unexpanded\expandafter{\argii}}%
{\etex@unexpanded\expandafter{\argiii}}%
{\etex@unexpanded\expandafter{\argiv}}%
{\etex@unexpanded\expandafter{\argv}}%
}%
\process@me
}
\newcommand{\@mycmd}[5]{%
\noindent
optional: #1 -- #2\\
mandatory: #3 -- #4 -- #5%
}
\makeatother
\begin{document}
\mycmd[optional arg]{third arg,fourth arg, fifth arg }
\end{document}
Remarks:
The list of optional arguments can be shorter, the omitted arguments gets default values.
\comma@parse
trims the arguments by removing leading and trailing space.\comma@parse
removes empty entries. A workaround for empty arguments is using\relax
or\empty
.
This solution works with two light-weight comma parsers defined in the solution itself (\p@rse@csl@opt
and \p@rse@csl@mnd
). What is executed on the parsed arguments is layed out to \domycmdopt
and \domycmdmnd
(resp.):
\documentclass{article}
\makeatletter
\newcommand\mycmd[2][]{%
\if\relax\detokenize{#1}\relax\else\p@rse@csl@opt#1,\@nil\fi
\p@rse@csl@mnd#2,\@nil
}
\def\p@rse@csl@opt#1,#2{%
\domycmdopt{#1}%
\ifx#2\@nil\else\expandafter\p@rse@csl@opt#2\fi
}
\def\p@rse@csl@mnd#1,#2{%
\domycmdmnd{#1}%
\ifx#2\@nil\else\expandafter\p@rse@csl@mnd#2\fi
}
\def\domycmdopt#1{opt-arg:#1\par}
\def\domycmdmnd#1{mnd-arg:#1\par}
\makeatletter
\begin{document}
\def\abc{def}
\mycmd[f00,bar]{hell0,w0rld}
\mycmd{1,2,3,$\alpha$,\abc}
\end{document}
A variant of jon's answer, where the original setup is used. It's mainly for showing how expl3
makes things easy.
I'll assume you want to separate the prefix the optional arguments by “Optional:”, then typeset them on a line, separated by “space, en-dash, space; the mandatory arguments are prefixed by “Mandatory” and separated by “space, em-dash, space”.
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\mycmd}{om}
{
\IfValueT{#1}
{
Optional:~\guest_print_list:nn { #1 } { ~--~ } \\*
}
Mandatory:~\guest_print_list:nn { #2 } { ~---~ }
}
\seq_new:N \l_guest_list_seq
\cs_new_protected:Nn \guest_print_list:nn
{
\seq_set_from_clist:Nn \l_guest_list_seq { #1 }
\seq_use:Nn \l_guest_list_seq { #2 }
}
\ExplSyntaxOff
\setlength{\parindent}{0pt} % just for the example
\begin{document}
\mycmd{a, b , c}
\mycmd{a}
\mycmd[A]{a,b,c}
\mycmd[A, B]{a,b,c}
\end{document}
Quite straightforward, with no code duplication, by reusing the \guest_print_list:nn
function, which takes the list of arguments to be printed as the first argument and the separator as the second argument. Note that spaces around the commas in the input are ignored.