# How to use \@ifnextchar to create a “table of references” macro?

At the bottom of every section in a big policy manual I need to include a table of references to a procedures manual like this:

\begin{table}[H]
\begin{tabular}{ |c|c| }
\hline
\multicolumn{2}{|c|}{Procedures} \\
\hline
%fyi PM = Procedures Manual
PM \ref{PM-tyinglaces} & \nameref{PM-tyinglaces} \\
PM \ref{PM-polishshoes & \nameref{PM-polishshoes} \\
PM \ref{PM-ironshirt} & \nameref{PM-ironshirt} \\
\hline
\end{tabular}
\end{table}


Of course since every table is formatted the same I'd really like a macro with syntax similar to:

%\procedurelist{firstlabel}{nextlabel}...{nthlabel}
%
\procedurelist{PM-tyinglaces}{PM-polishshoes}{PM-ironshirt}


A single comma deliminated parameter would be okay too. My attempt is found below. I just can't get it to close the table, somehow it breaks when I include the closing \hline.

\makeatletter
\newcommand{\procedurelist}[1]{%
\begin{table}[H]
\begin{tabular}{ |c|c| }
\hline
\multicolumn{2}{|c|}{Procedures} \\
\hline
PM \ref{#1} & \nameref{#1} \\%
\checknextarg
}
\newcommand{\checknextarg}{%
\@ifnextchar\bgroup{%
\consumenextarg
}{%
\hline  %causes error?
\end{tabular}
\end{table}
}
}
\newcommand{\consumenextarg}[1]{%
PM \ref{#1} & \nameref{#1} \\%
\@ifnextchar\bgroup{%
\consumenextarg
}{%
\hline %causes error?
\end{tabular}
\end{table}
}
}
\makeatother


How do I achieve my goal?

What am I doing wrong in my macro?

• The error of the \hline is caused because you used \checknextarg which isn't expandable as it uses \@ifnextchar which is not expandable. Then you want to issue a \noalign which is not the first token in that row. – Skillmon Sep 18 '18 at 19:33

I would not recommend using a syntax such as

\procedurelist{a}{b}{c}...{z}


and much prefer something like

\procedurelist{a,b,c,...,z}


It is not really difficult to do the way you imagined. The trick is to build the table body in advance.

\documentclass{article}
\usepackage{hyperref}

\makeatletter
\newcommand{\procedurelist}{%
\proclist@body={}%
\proclist@start
}

\newtoks\proclist@body
\newcommand{\proclist@start}{%
\@ifnextchar\bgroup\proclist@absorb\proclist@finish
}
\newcommand{\proclist@absorb}[1]{%
\proclist@body=\expandafter{%
\the\proclist@body
PM~\ref{#1} & \nameref{#1} \\%
}%
\proclist@start
}
\newcommand{\proclist@finish}{%
\begin{tabular}{|c|c|}
\hline
\the\proclist@body
\hline
\end{tabular}%
}
\makeatother

\begin{document}

\section{Tie laces}\label{PM-tyinglaces}
\section{Polish shoes}\label{PM-polishshoes}
\section{Iron shirt}\label{PM-ironshirt}

\section{End}

\procedurelist{PM-tyinglaces}{PM-polishshoes}{PM-ironshirt}

\end{document}


The other approach with a comma separated list is easier with expl3, which already has features for processing comma separated lists and cycling over them.

\documentclass{article}
\usepackage{xparse}
\usepackage{hyperref}

\ExplSyntaxOn

\NewDocumentCommand{\procedurelist}{m}
{
\seq_clear:N \l__kjc_procedurelist_seq
\clist_map_inline:nn { #1 }
{
\seq_put_right:Nn \l__kjc_procedurelist_seq
{
PM~\ref{##1} & \nameref{##1}
}
}
\begin{tabular}{|c|c|}
\hline
\seq_use:Nn \l__kjc_procedurelist_seq { \\ } \\
\hline
\end{tabular}
}

\seq_new:N \l__kjc_procedurelist_seq

\ExplSyntaxOff

\begin{document}

\section{Tie laces}\label{PM-tyinglaces}
\section{Polish shoes}\label{PM-polishshoes}
\section{Iron shirt}\label{PM-ironshirt}

\section{End}

\procedurelist{PM-tyinglaces,PM-polishshoes,PM-ironshirt}

\end{document}


A better table:

\documentclass{article}
\usepackage{xparse,booktabs}
\usepackage{hyperref}

\ExplSyntaxOn

\NewDocumentCommand{\procedurelist}{m}
{
\seq_clear:N \l__kjc_procedurelist_seq
\clist_map_inline:nn { #1 }
{
\seq_put_right:Nn \l__kjc_procedurelist_seq
{
\ref{##1} & \nameref{##1}
}
}
\begin{tabular}{cl}
\toprule
\multicolumn{1}{c}{\textbf{PM}} & \textbf{Name} \\
\midrule
\seq_use:Nn \l__kjc_procedurelist_seq { \\ } \\
\bottomrule
\end{tabular}
}

\seq_new:N \l__kjc_procedurelist_seq

\ExplSyntaxOff

\begin{document}

\section{Tie laces}\label{PM-tyinglaces}
\section{Polish shoes}\label{PM-polishshoes}
\section{Iron shirt}\label{PM-ironshirt}

\section{End}

\procedurelist{PM-tyinglaces,PM-polishshoes,PM-ironshirt}

\end{document}


A simpler implementation, once one gets the hang of it:

\documentclass{article}
\usepackage{xparse,booktabs}
\usepackage{hyperref}

\ExplSyntaxOn

\NewDocumentCommand{\procedurelist}{m}
{
\cs_set:Nn \__kjc_procedurelist_row:n { \ref{##1} & \nameref{##1} \\ }
\begin{tabular}{cl}
\toprule
\multicolumn{1}{c}{\textbf{PM}} & \textbf{Name} \\
\midrule
\clist_map_function:nN { #1 } \__kjc_procedurelist_row:n
\bottomrule
\end{tabular}
}

\ExplSyntaxOff

\begin{document}

\section{Tie laces}\label{PM-tyinglaces}
\section{Polish shoes}\label{PM-polishshoes}
\section{Iron shirt}\label{PM-ironshirt}

\section{End}

\procedurelist{PM-tyinglaces,PM-polishshoes,PM-ironshirt}

\end{document}


With this approach the \__kjc_procedurelist_row:n function could be defined outside the main command. However one can also improve this by allowing to change the default on the fly:

\documentclass{article}
\usepackage{xparse,booktabs}
\usepackage{hyperref}

\ExplSyntaxOn

\NewDocumentCommand{\procedurelist}{om}
{
\IfValueT{#1}
{
\cs_set:Nn \__kjc_procedurelist_row:n { #1 }
}
\begin{tabular}{cl}
\toprule
\multicolumn{1}{c}{\textbf{PM}} & \textbf{Name} \\
\midrule
\clist_map_function:nN { #2 } \__kjc_procedurelist_row:n
\bottomrule
\end{tabular}
}
\cs_new:Nn \__kjc_procedurelist_row:n { \ref{#1} & \nameref{#1} \\ }
\ExplSyntaxOff

\begin{document}

\section{Tie laces}\label{PM-tyinglaces}
\section{Polish shoes}\label{PM-polishshoes}
\section{Iron shirt}\label{PM-ironshirt}

\section{End}

\procedurelist{
PM-tyinglaces,
PM-polishshoes,
PM-ironshirt
}
\procedurelist[\ref{#1} & \textit{\nameref{#1}} \\]{
PM-tyinglaces,
PM-polishshoes,
PM-ironshirt
}

\end{document}


• Why do you define \__kjc_procedurelist_tmp:n every time \procedurelist is called in your last implementation? Should be better performing if set only once (assuming \procedurelist is called more than once). – Skillmon Sep 18 '18 at 22:05
• @Skillmon Sure. Let me add something. – egreg Sep 18 '18 at 22:32

The trick is to first grab all those arguments and store them somewhere (here in \procedurelist@content). After you grabbed all arguments you can then output the formatted table. This way you don't have any unexpandable tests inside of the table blocking any \noaligns.

Example code (I removed the \namerefs and the surrounding table as that doesn't make sense if the only allowed placement is H and you don't use a caption):

\documentclass[]{article}

\makeatletter
\newcommand*\procedurelist@content{}
\newcommand*\procedurelist@checknext
{%
\@ifnextchar\bgroup
{\procedurelist@eatnext}
{\procedurelist@shipout}%
}
\newcommand\procedurelist[1]
{%
\begingroup
\def\procedurelist@content{PM \ref{#1} & #1 \\}%
\procedurelist@checknext
}
\newcommand\procedurelist@eatnext[1]
{%
\edef\procedurelist@content
{\unexpanded\expandafter{\procedurelist@content PM \ref{#1} & #1 \\}}%
\procedurelist@checknext
}
\newcommand*\procedurelist@shipout
{%
\begin{tabular}{|c|c|}
\hline
\multicolumn{2}{|c|}{Procedures} \\
\hline
\procedurelist@content
\hline
\end{tabular}%
\endgroup
}
\makeatother

\begin{document}
\procedurelist{arg1}{arg2}{arg3}{arg4}
\end{document}


Using \xintFor (which is not expandable loop, but nevertheless works in tabulars...)

\documentclass{article}
\usepackage{booktabs}
\usepackage{hyperref}
\usepackage{xinttools}

\newcommand{\procedurelist}[1]{%
\begin{tabular}{cl}
\toprule
\multicolumn{1}{c}{\textbf{PM}} & \textbf{Name} \\
\midrule
\xintFor ##1 in {#1}\do {\ref{##1} & \nameref{##1} \\ }
\bottomrule
\end{tabular}
}

\begin{document}

\section{Tie laces}\label{PM-tyinglaces}
\section{Polish shoes}\label{PM-polishshoes}
\section{Iron shirt}\label{PM-ironshirt}

\section{End}

\procedurelist{PM-tyinglaces,PM-polishshoes,PM-ironshirt}

\end{document}


• With mark-up such as \procedurelist{{PM-tyinglaces}{PM-polishshoes}{PM-ironshirt}}, same code as in my answer with \xintFor ##1 in {#1} replaced by \xintFor* #1 in {##1}. (slightly more efficient as any way \xintFor converts the comma separated values into the braced values expected by \xintFor*; but with only 3 values...) – user4686 Sep 18 '18 at 21:21