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, 2018 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? Feb 1, 2018 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, 2018 at 15:08
  • @egreg I was trying to avoid expl3, not to undermind the project or your efforts. Feb 1, 2018 at 15:13
  • What's the connection between \appendtocslist and \readcslist? I see none.
    – egreg
    Feb 1, 2018 at 15:14

2 Answers 2

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. Feb 28, 2018 at 14:49
  • @JonathanKomar Yes, that would be necessary if #2 contains “dangerous” items.
    – egreg
    Feb 28, 2018 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. Feb 3, 2018 at 17:15

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .