6

Once in a while I try to understand how TeX works... Hitherto now success, but sometimes I manage to define a macro. For example,

\def\appendto#1#2{\expandafter\def\expandafter#1\expandafter{#1#2}}%

makes it possible to typeset text by e.g.

\def\txt{}

\appendto{\txt}{Hello}
\appendto{\txt}{ }
\appendto{\txt}{world!}

%% Expand it.

\txt

My question is then, how can a corresponding insert macro be defined? A corresponding insert macro should expand to

Hello world!

when the input is

\def\txt{}
\insert{\txt}{world!}
\insert{\txt}{ }
\insert{\txt}{Hello}
\txt
9
  • Welcome to TeX-sx! Could you elaborate on what you mean by a 'insert macro': I think an example of the input and result would help.
    – Joseph Wright
    Feb 7 '15 at 19:38
  • BTW, the first argument to \appendto has to be a single token (a macro) so normally the braces would not be added here when talking about TeX. (LaTeX's documented syntax always uses braces for arguments, but that's not the focus here.)
    – Joseph Wright
    Feb 7 '15 at 19:41
  • A corresponding insert macro should expand to "Hello world!" when the input is \def\txt{} \insert{\txt}{world!} \insert{\txt}{ } \insert{\txt}{Hello}\txt Feb 7 '15 at 19:49
  • \def\prependto#1#2{\edef#1{\unexpanded{#2}\unexpanded\expandafter{#1}}
    – Manuel
    Feb 7 '15 at 20:01
  • @Manuel I'm guessing we are not allowed e-TeX so have to use the 'traditional' toks-based approach, although I suspect there is a dupe for this.
    – Joseph Wright
    Feb 7 '15 at 20:02
5

Macro based approach

\def\prependto#1{%
  \expandafter\prependtohelper\expandafter{#1}#1%
}
\long\def\prependtohelper#1#2#3{%
  % #1: contents of macro
  % #2: macro
  % #3: text for prepending
  \def#2{#3#1}%
}

% Testing
\def\txt{}
\prependto{\txt}{world!}
\prependto{\txt}{ }
\prependto{\txt}{Hello}

\immediate\write16{\meaning\txt}

\csname @@end\endcsname\end

Remarks:

  • I have added \long for marcro \prependhelper, which reads the inserted text. Then the text may also contain \par tokens (empty lines, whole paragraphs).

  • Disadvantage of the macro based approach is that # tokens cause trouble. This is solved by the following token or e-TeX based approaches.

Token based approach

\def\space{ }% already defined in plain TeX and LaTeX

\long\def\prependto#1#2{%
  \begingroup
    \toks0={#2}%
    \toks0=\expandafter{\the\toks0\expandafter\space#1}%
    \xdef\prependhelper{\the\toks0}%
  \endgroup
  \let#1\prependhelper
}

\def\txt{}
\prependto{\txt}{world!}
\prependto{\txt}{ }
\prependto{\txt}{Hello}

\immediate\write16{\meaning\txt}

\csname @@end\endcsname\end

Remarks:

  • There is a specialty if token registers are expanded in an \edef (or the global working \xdef). The token register is output, but the output tokens are not further expanded.

  • The group has the purpose to keep the changes of token register 0 local. The global macro \prependhelper transports the new definition of the macro outside the group.

  • The macro definition uses some expansion tricks. Before the token register assignment sees the opening curly brace, it still expands macros, thus the \expandafter can go there and do not need to start from before the assignment.

  • \toks0\expandafter\space is another expansion trick. If TeX parses a number, then it expands until it finds a non-digit. Also a space serves as end, this space is then ignored.

If a scratch register can be used such as \toks@, then the definition could be make smaller (with \makeatletter or \catcode`@=11):

\long\def\prependto#1#2{%
  \toks@={#2}%
  \toks@\expandafter{\the\expandafter\toks@ #1}%
}

e-TeX based approach

The e-TeX extensions provide \unexpanded, which can be used to avoid the circumvention with the token registers:

\long\def\prependto#1#2{%
  \edef#1{\unexpanded{#2}\unexpanded\expandafter{#1}}%
}

\def\txt{}
\prependto{\txt}{world!}
\prependto{\txt}{ }
\prependto{\txt}{Hello}

\immediate\write16{\meaning\txt}

\csname @@end\endcsname\end

Remarks:

  • Again, \expandafter before \unexpanded can be dropped, because e-TeX is expanding after \unexpanded until it finds the opening curly brace.
6
  • I'd avoid \xdef in the first token-based approach as you don't need a global definition: two definitions and an expansion chain will do the same thing.
    – Joseph Wright
    Feb 7 '15 at 20:44
  • @JosephWright And how do you avoid the problem with #? Feb 7 '15 at 20:50
  • In an e-TeX-free solution using # inside a macro isn't on as we can't do \edef\foo{\unexpanded{<content>}}. Thus I'd say it's not allowed in classical TeX: global assignments should in my view only be used when they are meant to be global.
    – Joseph Wright
    Feb 7 '15 at 20:53
  • @JosephWright Since the token bases approach is meant to be independent from e-TeX, there is no choice to a global assigment here. The alternative with a scratch register I have already provided. AFAIK there isn't a solution, which is free from side-effects. Either a global macro or a token register is used or changed. Feb 7 '15 at 20:57
  • My point as that you cannot provide a solution for classical TeX that can accept # tokens as they can't in general be stored in a macro, in contrast to e-TeX where they can. Abusing a global assignment is not on, so that is just how it is with Knuth's TeX. (I guess what I mean is that with the 'rules' I feel have to be applied to TeX programming there is not a complete solution for classical TeX here.)
    – Joseph Wright
    Feb 7 '15 at 21:00

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.