1

I'd like to know how to create a macro that can append new elements/items to a macro list (token list) BUT also adds { and } around each added item.

Example I'd like it to save each new item like this:

\appendtocslist{tokenlist}{newitem}

yields {newitem} in the list.

Why Because I can easily parse the list with \readcslist.

I am not sure if also separating each value with a comma (or some other separator) is necessary.

Append to Command Sequence (CS) List

  • Smart enough to initiate a new macro if none exist iwth does not exist.
  • It uses csname to allow for dynamic macro names.
  • It currently uses a trailing comma character , as a separator/delimiter.

Code

\long\def\appendtocslist#1#2{% #1 = List ID (no backslash) #2=New Item to Append
   % I chose this complicated csname form of the list-making because it can accept dynamic csnames using counters
   \expandafter\ifx\csname#1\endcsname\relax\expandafter\xdef\csname#1\endcsname{#2,}\else% Macro Existence Check
   \expandafter\xdef\csname#1\expandafter\expandafter\expandafter\endcsname
   \expandafter\expandafter\expandafter
   {\csname #1\endcsname#2,}\fi%Append expansion of #1 with #2, delimit with ,. ^^J appears as newline in terminal or space when typeset and does not work as a delimiter
}%

Code

\documentclass{article}
\usepackage{fontspec}% xelatex

\makeatletter

\long\def\appendtocslist#1#2{% #1 = List ID (no backslash) #2=New Item to Append
   % I chose this complicated csname form of the list-making because it can accept dynamic csnames using counters
   \expandafter\ifx\csname#1\endcsname\relax\expandafter\xdef\csname#1\endcsname{#2,}\else% Macro Existence Check
   \expandafter\xdef\csname#1\expandafter\expandafter\expandafter\endcsname
   \expandafter\expandafter\expandafter
   {\csname #1\endcsname#2,}\fi%Append expansion of #1 with #2, delimit with ,. ^^J appears as newline in terminal or space when typeset and does not work as a delimiter
}%

\long\def\readcslist#1{%
  \@tempcnta=1%
  \checknextarg%
}
\long\def\checknextarg{%
  % My job is to check for another arg and trigger a recursive cycle.
  % for \@ifnextchar to check, so we say \bgroup, which means literal {
  % I am only important if there are exactly two args.
  \@ifnextchar\bgroup{\recursivecycle}{\advance\@tempcnta1\finalcall}% \@ifnextchar ignores spaces
}
\long\def\recursivecycle#1{%
  % My job is to eat each argument recursively until I
  % cannot find any more { characters. I ignore spaces.
  % If I reach the end, I run whatever is in false area of \@ifnextchar
  \advance\@tempcnta by 1% add one for next arg
  \@ifnextchar\bgroup{\recursivecycle}{\finalcall}}
\def\finalcall{Total n of args: \the\@tempcnta}
\makeatother
\begin{document}
Hello.
\appendtocslist{mylist}{a}
\appendtocslist{mylist}{b}
\appendtocslist{mylist}{c}

\readcslist{d}{e}{f}

% \expandafter\readargs\expandafter{\mylist} % wouldn't this be nice!%

\end{document}
10
  • Do you want also the comma? – egreg Feb 1 '18 at 15:03
  • @egreg Glad you asked first. What do you recommend from your knowledge of dealing with these things? Is it necessary if I have everything surrounded in braces for future manipulation e.g. element extraction or prepending/appending new elements? – Jonathan Komar Feb 1 '18 at 15:03
  • 1
    I would avoid to reinvent the wheel and use expl3. And definitely not absorb items until no more { are found. This is very dangerous and useless: \readcslist{{a}{b}{c}} is much simpler and safer. – egreg Feb 1 '18 at 15:08
  • @egreg I was trying to avoid expl3, not to undermind the project or your efforts. – Jonathan Komar Feb 1 '18 at 15:13
  • What's the connection between \appendtocslist and \readcslist? I see none. – egreg Feb 1 '18 at 15:14
1
\documentclass{article}

\makeatletter
% syntactic sugar
\def\expandtwice{\unexpanded\expandafter\expandafter\expandafter}
% append to list (or create it)
\newcommand\appendtocslist[2]{%
  % #1 = Macro Name without \ (list name), #2=New Item to Append
  \ifcsname#1\endcsname
    \expandafter\xdef\csname#1\endcsname{%
      % the previous items
      \expandtwice{\csname#1\endcsname}%
      % the new item
      {#2}%
    }%
  \else
    % the list doesn't exist yet, create it
    \expandafter\gdef\csname#1\endcsname{{#2}}
  \fi
}
% read n-argument types (ignoring spaces)
\long\def\readcslist#1{%
  \@tempcnta=1
  \checknextarg
}
\long\def\checknextarg{%
  \@ifnextchar\bgroup{\recursivecycle}{\finalcall}%
}
\long\def\recursivecycle#1{%
  \advance\@tempcnta by 1 % add one for next arg
  \checknextarg
}
\def\finalcall{Total n of args: \the\@tempcnta}
\makeatother
\begin{document}
Hello.
\appendtocslist{mylist}{a}
\appendtocslist{mylist}{b}
\appendtocslist{mylist}{c}

\texttt{\expandafter\meaning\csname mylist\endcsname}

\readcslist{d}{e}{f}

\expandafter\readcslist\mylist

\end{document}

enter image description here

2
  • I just realized that \xdef should be \protected@xdef to maintain the effect of \protect when appending things like \textit or \textbf. Obviously, you used my minimal example, but I wanted to note this here for posterity. – Jonathan Komar Feb 28 '18 at 14:49
  • @JonathanKomar Yes, that would be necessary if #2 contains “dangerous” items. – egreg Feb 28 '18 at 17:31
1

In response to the question "I'd like to know how to create a macro that can append new elements/items to a macro list (token list) BUT also adds { and } around each added item."

\documentclass{article}
\usepackage[T1]{fontenc}
\newtoks\mylist
\newcommand\addtotoklist[2]{#1\expandafter{\the#1{#2}}}
\begin{document}
\addtotoklist\mylist{a}
\addtotoklist\mylist{b}
\addtotoklist\mylist{cde}
\detokenize\expandafter{\the\mylist}
\end{document}

enter image description here

1
  • Good stuff. I think it helps to know that this is shortened form of \newcommand\addtotoklist[2]{#1=\expandafter{\the#1{#2}}} . In other words, skip ahead to expand the list as it is first, then put #2 after it, then the whole line of junk (tokens) will be assigned to #1. – Jonathan Komar Feb 3 '18 at 17:15

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.