4

I wish to create two macros: one that takes a known number, and another that takes an unknown number of arguments that are comma-separated and does something with them. There have been a few answers to this question before see (here and here), but they always use modern LaTeX packages such as xparse. For example, I could create a new fraction command (known number of args) by doing

\ExplSyntaxOn
\makeatletter
    \def\numargs{2}
    \NewDocumentCommand{\frc}{>{\SplitArgument{\numargs-1}{,}}m}{\@frc@#1}
    \NewDocumentCommand{\@frc@}{mm}{\frac{#1}{#2}}
\makeatother
\ExplSyntaxOff

and using it with \frc{1,2}, effectively doing the same as \frac{1}{2}. Another example would be to create a bolded list (unknown number of args) with

\ExplSyntaxOn
\NewDocumentCommand{\bflist}{m}{\clist_map_inline:nn{#1}{\@bflist@{##1}}}
\newcommand{\@bflist@}[1]{\bf #1}
\ExplSyntaxOff

and using it with \bflist{a, b, c, d, e, f}, etc, which would create the bolded list a b c d e f. Both of these solutions use modern LaTeX tools; I am wondering, is there a way to create these commands in plain TeX? Or at the very least, in LaTeX without using external packages?

2
  • You should be able to load xparse in plain TeX.
    – Gaussler
    May 26, 2022 at 6:48
  • noe you don't need any packages to use \NewDocumentCommand or \SplitArgument in latex (unless you have an old release) May 26, 2022 at 10:39

1 Answer 1

7

The solution in very simple in Plain TeX (or more precisely: using only TeX primitive commands)

% fixed number of args:
\def\frc#1{\frcA#1\end}
\def\frcA#1,#2\end{{#1\over #2}}

test: $\frc{a,b}$

% unknown number of args:
\def\boldlist#1{\boldlistA #1,\end,}
\def\boldlistA #1#2,{%
   \ifx\end#1\else {\bf#1#2}\expandafter \boldlistA\fi
}

test: \boldlist{a, b, c, d, e, f}

\bye

Note that \boldlistA uses first unseparated parameter #1 followed by second separated #2 in order to remove spaces after commas. We need not to do it in the \frc macro because it is intended for math mode where spaces are ignored by TeX.

Second example in more detail: Assume syntax: \boldlist{a,b,cxy,d} without spaces after commas. Then we can define:

\def\boldlist#1{\boldlistA #1,\end,}
\def\boldlistA #1,{%
   \ifx\end#1\else {\bf#1}\expandafter \boldlistA\fi
}

This is recursive call of \boldlistA. The parameter #1 is a in first call (because it is separated by comma), then it is b, then cxy, then d and finally, it is \end. The test \ifx\end#1 stops this recursion.

If you want to use optional spaces before parameters (after comma) but you want to ignore them, i.e. \boldlist{a, b, cxy,d} then the \boldlistA have #1 unseparated: the #1 is first non-space token, spaces are ignored by TeX. The #2 is separated by comma. It is empty in the first case: #1=a, #2=empty, it is empty in second case #1=b, space before it is ignored, #2=empty, but it is not empty in third case: #1=c, space before it is ignored, #2=xy.

7
  • 1
    Can you explain how the second one works? I don't quite get it. Also, why are there so many answers using xparse if the answer in plain TeX is simple?
    – btshepard
    May 26, 2022 at 6:16
  • 3
    I added more words to the second example. And to your second question: I don't know. LaTeX hides (unfortunately) TeX primitive constructs and offers more complicated solutions. Cf. petr.olsak.net/optex/optex-tricks.html
    – wipet
    May 26, 2022 at 6:40
  • @bshepard Afaik xparse attempts to handle the removal of spaces that surround an item of the (comma-)separated list while avoiding brace-removal. E.g., if the list-item is \macro{argument},, then \boldlistA of the 1st example yields \ifx\end\macro argument\else {\bf\macro argument}\expandafter \boldlistA\fi, i.e., braces surrounding \macro's argument are removed. As long as it is ensured that your comma-list does not contain items that form such edge cases, you can keep things simple. But xparse aims at a large user-base where you cannot predict what users will try to do. ;-) May 26, 2022 at 8:52
  • @bshepard There are more edge cases that might cause problems. E.g blank comma-list items. But trying to handle them all turns the code incomprehensible and hard to read. Therefore I might elaborate only if you specify exactly the nature of the comma-list-items you wish to process. E.g., might a comma-list item contain unbalanced \if or \else or \fi? Is it okay to rely on a certain token sequence not to occur in any item of the comma-separated list? Do you want removal of leading/trailing spaces? Shall outermost level of curly braces be removed so comma/surrounding spaces can be hidden? May 26, 2022 at 9:01
  • 2
    TeX intself is not good designed to such ambitious projects like doing new language level over it and hide it. But LaTeX developers are trying to do this, so their codes are very complicated. Second approach is to come to term with what TeX is doing and use TeX directly.
    – wipet
    May 26, 2022 at 9:29

You must log in to answer this question.

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