8

I'm trying to make an automatized meeting log generator using pdflatex. I want to create a table with the n rows, and fill them up with comma separated typein inputs. I've got the myTable macro from this answer, and it works fine, if I directly write in the text. (1) But if I use the \def\variable or \typein[\variable]{variable} (2) it fails with the error shown below.

As I understand I probably should use \expandafter somewhere in some way, but I couldn't figured it out myself.

\documentclass[a4paper,11pt]{article}

\usepackage{etoolbox}

\def\variable{Person 1/Active/Online/X,Person 2/Active/-/-,Person 3/Intern/Office/X}

\def\addtablerow#1/#2/#3/#4!{\textbf{#1} & #2 & #3 & #4\\}
\newcommand{\myTable}[1]{
    \renewcommand*\do[1]{\addtablerow##1!}
    \begin{tabular}{|p{0.4\textwidth}|p{0.2\textwidth} | p{0.2\textwidth} |        p{0.2\textwidth}|}
        \hline
        Name & Role & Presence & Present \\ \hline
        \docsvlist{#1} \hline
    \end{tabular}
}


\begin{document}
    % 1
    \myTable{Person 1/Developer/Online/X,Person 2/Developer/-/-,Person 3/Intern/Office/X}
    % 2
    \myTable{\variable}
\end{document}

The full error:

\variable !\etb@lst@docsvlist \@nil \etb@lst@q@end ,& \hline \end {ta\ETC.
! File ended while scanning use of \addtablerow.
<inserted text> 
                \par 
<*> test.tex
            
! Emergency stop.
<*> test.tex

2 Answers 2

8

The call \myTable{\variable} passes a list of length one. You need to access at the value of \variable before \myTable does its job.

So, as the other answer suggests is right, although there's no need to add an \expandafter in front of \addtablerow.

I suggest a different method, with expl3 that avoids polluting the code with \def, using the analog of a namespace.

The \printlist macro takes a * if you want to use a list defined previously.

If all lists you need are like this, you can avoid the * variant, changing the code should be easy.

The working is much like that of \docsvlist, but with some advantages: spaces around the comma delimiters are ignored, for instance. Here \clist_map_inline:nN has a role similar to \docsvlist and instead of redefining \do you define a set of two macros, the first of which is the analog of \addtablerow, but has to be split into an external and an internal part.

The table preamble has been fixed to avoid overfull: your table is longer than the text width, because you aren't taking into account the intercolumn spaces.

\documentclass[a4paper,11pt]{article}

\ExplSyntaxOn

\NewDocumentCommand{\definelist}{mm}
 {
  \clist_clear_new:c { l_fauszt_list_#1_clist }
  \clist_set:cn { l_fauszt_list_#1_clist } { #2 }
 }

\NewDocumentCommand{\printlist}{sm}
 {% the star variant uses a list name
  % otherwise a full list is passed
  \IfBooleanTF{#1}
   {
    \fauszt_list_print:v { l_fauszt_list_#2_clist }
   }
   {
    \fauszt_list_print:n { #2 }
   }
 }

\cs_new_protected:Nn \fauszt_list_print:n
 {
  \begin{tabular}{
    |p{\dimexpr0.4\textwidth-2\tabcolsep}
    |p{\dimexpr0.2\textwidth-2\tabcolsep}
    |p{\dimexpr0.2\textwidth-2\tabcolsep}
    |p{\dimexpr0.2\textwidth-2\tabcolsep}|
  }
  \hline
  Name & Role & Presence & Present \\
  \hline
  \clist_map_function:nN { #1 } \__fauszst_list_print_row:n
  \end{tabular}
 }
\cs_generate_variant:Nn \fauszt_list_print:n { v }

\cs_new_protected:Nn \__fauszst_list_print_row:n
 {
  \__fauszt_list_print_row:w #1 \q_stop
 }
\cs_new_protected:Npn \__fauszt_list_print_row:w #1 / #2 / #3 / #4 \q_stop
 {
  \textbf{#1} & #2 & #3 & #4 \\ \hline
 }

\ExplSyntaxOff

\definelist{variable}{
  Person 1/Active/Online/X,
  Person 2/Active/-/-,
  Person 3/Intern/Office/X
}

\begin{document}

\noindent
\printlist{Person 1/Developer/Online/X,Person 2/Developer/-/-,Person 3/Intern/Office/X}

\bigskip

\noindent
\printlist*{variable}

\end{document}

enter image description here

Further generalizations are possible.


For completeness, I'll prove my statement that, in the solution proposed by user237299, the token \expandafter in \expandafter\addtablerow##1 can be removed as it does nothing useful (and it can even do evil in corner cases).

Let's just follow the expansions. When \variable is defined as in the question and the call is

\noindent\expandafter\myTable\expandafter{\variable}

(first proposed solution), this is just the same as calling

\noindent\myTable{Person 1/Developer/Online/X,Person 2/Developer/-/-,Person 3/Intern/Office/X}

Then \docsvlist will determine the first item and pass it as a braced argument to \do, so the first cycle in the loop will perform

\do{Person 1/Developer/Online/X}

which would become

\expandafter\addtablerow Person 1/Developer/Online/X!

and the \expandafter is clearly useless.

In the second solution, the call is \myTable{\variable} and then the main bit is

\expandafter\docsvlist\expandafter{\variable}

but it's just a variation on the first one and the working is exactly the same.

Then user237299 purports that the \expandafter I'm referring to will be useful in case the call is \myTable{\variableTwo}, where there has been

\def\variableTwo{\variable}

Unfortunately, this is not true. Indeed, using the first solution (but using the second would be exactly the same), one has

\expandafter\myTable\expandafter{\variableTwo}

that becomes

\myTable{\variable}

which calls \docsvlist{\variable}. The argument has no comma, so a single cycle is performed and the call becomes

\expandafter\addtablerow\variable

Nothing useful, because we'd get

\addtablerow Person 1/Developer/Online/X,Person 2/Developer/-/-,Person 3/Intern/Office/X!

Of course, doing \def\variableTwo{\variable} is quite meaningless in this context. If one wants to alias \variable, maybe for augmenting it while having the previous list still available, one should do \let\variableTwo=\variable.

2
  • 1
    This is very interesting, I'll give it a shot for sure. Thank you!
    – Fauszt
    Mar 13, 2021 at 23:25
  • There in fact is an input in which that second \expandafter could make sense, which is when one stores a complete person inside a macro, something like \printlist{\personA,\personB,\personC} (but I'd use a \tl_count:n to only expand the first token when the input contains just a single token).
    – Skillmon
    Mar 16, 2021 at 7:58
7

You only need some \expandafters.

\documentclass[a4paper,11pt]{article}

\usepackage{etoolbox}

\def\variable{Person 1/Active/Online/X,Person 2/Active/-/-,Person 3/Intern/Office/X}

\def\addtablerow#1/#2/#3/#4!{\textbf{#1} & #2 & #3 & #4\\}
\newcommand{\myTable}[1]{
    \renewcommand*\do[1]{\expandafter\addtablerow##1!}
    \begin{tabular}{|p{0.4\textwidth}|p{0.2\textwidth} | p{0.2\textwidth} |        p{0.2\textwidth}|}
        \hline
        Name & Role & Presence & Present \\ \hline
        \docsvlist{#1} \hline
    \end{tabular}
}


\begin{document}
    % 1
    \noindent\myTable{Person 1/Developer/Online/X,Person 2/Developer/-/-,Person 3/Intern/Office/X}
    % 2
    \par
    \noindent\expandafter\myTable\expandafter{\variable}
\end{document}

enter image description here

Or

\documentclass[a4paper,11pt]{article}

\usepackage{etoolbox}

\def\variable{Person 1/Active/Online/X,Person 2/Active/-/-,Person 3/Intern/Office/X}

\def\addtablerow#1/#2/#3/#4!{\textbf{#1} & #2 & #3 & #4\\}
\newcommand{\myTable}[1]{%
    \renewcommand*\do[1]{\expandafter\addtablerow##1!}
    \begin{tabular}{|p{0.4\textwidth}|p{0.2\textwidth} | p{0.2\textwidth} |        p{0.2\textwidth}|}
        \hline
        Name & Role & Presence & Present \\ \hline
        \expandafter\docsvlist\expandafter{#1} \hline
    \end{tabular}
}


\begin{document}
    % 1
    \noindent\myTable{Person 1/Developer/Online/X,Person 2/Developer/-/-,Person 3/Intern/Office/X}
    % 2
    \par
    \noindent\myTable{\variable}
\end{document}
1
  • Thank you guys! This is a tremendous help for me!
    – Fauszt
    Mar 13, 2021 at 23:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.