Implement biblatex’s \cites command with LaTeX3

citingexpl3latex3xparse

I'm the author of zepinglee/citeproc-lua and
which already has \cite[⟨prenote⟩][⟨postnote⟩]{⟨key⟩} that takes optional ⟨prenote⟩ and ⟨postnote⟩ for a single cite item.
I'm planning to implement a command for multi-item citations like “[See 21: p. 21, 77, 22: p. 3]” where each item may have a corresponding prenote and/or postnote.
The \cites from biblatex command is in the form \cites[⟨prenote⟩][⟨postnote⟩]{⟨key⟩}...[⟨prenote⟩][⟨postnote⟩]{⟨key⟩} and it receives undetermined number of arguments.
However there are too too many expansion control commands in its implementation for me to understand.
How to implement such command with expl3/xparse?

Best Answer

You can organize parsing arguments like this:

\documentclass{article}
\usepackage{xparse}

\makeatletter
\NewDocumentCommand{\cites}{oom}{'#1','#3','#2'\next@cites}
\NewDocumentCommand{\next@cites}{oog}{%
  \IfNoValueF{#3}{; '#1','#3','#2'\next@cites}%
}
\makeatother

\begin{document}
\cites{key1}

\cites[1]{key2}

\cites[1][2]{key3}[4]{key5}

\cites[1][2]{key3}[4]{key5}[6][7]{key8}{key9}
\end{document}

The code above uses optional braced argument g which is not recommended in the xparse documentation. IfNoValueF prints the next group of arguments if it finds them and adds \next@cites to search for more. If there's no braced argument ahead, it stops. For the sake of simplicity, the current code silently ignores bracketed arguments if they are present but there's no the braced one.

Related Question