0

I am trying to compose \textit{abc} like this:

\documentclass{article}
\makeatletter

\def\test{}

\catcode123=12 % change cat code of "{" to "other"
\g@addto@macro\test{
\catcode123=1  % restore the cat code

\g@addto@macro\test{abc}

\catcode125=12 % change cat code of "}" to "other"
\g@addto@macro\test}
\catcode125=2  % restore the cat code

\begin{document}
    \test
\end{document}

However, issuing \test does not print an italic abc. How can I achieve what I am trying to do?

bers
  • 5,404
  • You need to give an initial (empty) definition to \test: \def\test{}. You are probably getting an Undefined control sequence error, right? – Phelype Oleinik Sep 01 '19 at 21:55
  • @PhelypeOleinik Sorry, that was just an error of my MWE that's fixed now. The error is that the output shows abc", non-italic. – bers Sep 01 '19 at 21:56
  • 2
    You can't (terms and conditions may apply) add a single catcode 1 (or 2) curly brace to a macro. The curly braces you add are catcode 12, so they don't work as argument delimiters for \textit. You can resort to a brace trick (like in the answer siracusa just posted) or add the pair at once. – Phelype Oleinik Sep 01 '19 at 22:49
  • @UlrichDiez it looks good certainly! I haven't tested it, though. I'm note sure it works when \test is not initialized to be empty, as well. – bers Sep 02 '19 at 11:57
  • What about \newcommand\globaladdbraces[1]{\expandafter\gdef\expandafter#1\expandafter{\expandafter{#1}}} ... \def\test{} ... \g@addto@macro\test{abc} ... \globaladdbraces\test ... \show\test ... \expandafter\textit\test ? – Ulrich Diez Sep 02 '19 at 11:59
  • Yes. \test must be initialized to empty. I deleted my comment and corrected this. ;-) I suppose your exploration of the limits of the concept of successively composing things via \g@addto@macro is not just a moot thing for the sake of having fun while playing around with (La)TeX but is an aspect of approaching a concrete task/of solving a concrete problem. Perhaps help can be improved when you reveal as many details of that task/problem as possible. ;-) – Ulrich Diez Sep 02 '19 at 12:07
  • By the way: In the very first line of your question you say you wish to compose \textit.... but in the example provided by you the sequence \textit does not occur at all. So my question is: Do you wish to successively compose the entire \textit{abc}-command, i.e., the token-sequence \textit, {, a, b, c, } or do you already have a sequence of tokens and the problem is just about passing that sequence as argument to \textit ? – Ulrich Diez Sep 02 '19 at 12:32
  • @UlrichDiez my use case (very briefly): create an environment that shows a only caption. Render \envbody invisible, but patch commands (\subfloat, \label etc) to store their arguments somewhere, ensuring correct nesting. Some more general info here: https://tex.stackexchange.com/questions/506424. My specific questions revolves around the need to properly account for \subfloat{\label{a}\label{b}\label{c}...} - doesn't make sense, but someone might do it. (Except for that tabularx problem that I circumvent by tabular*, I do have a solution now.) – bers Sep 02 '19 at 15:09

3 Answers3

5

Although expl3's regex engine (which is loaded by regexpatch) is an excellent tool (and my job description is to merchandise expl3), in this case it's like using a shotgun to kill a fly :-)

You can achieve the same effect using a macro to define another. A simplistic definition of a \MakeItalic macro would be \def\MakeItalic#1{\edef#1{\noexpand\textit{#1}}}. The \noexpand would make sure that \textit doesn't blow up inside the \edef (you could omit the \noexpand if you used \protected@edef instead). The #1 would be your macro that contains abc, which would expand into abc.

A more robust definition of \MakeItalic would require e-TeX (you're using LaTeX, so that is the default anyway):

\newcommand{\MakeItalic}[1]{%
  \edef#1{\noexpand\textit{\unexpanded\expandafter{#1}}}}

The difference from the previous version is that instead of completely expanding #1, we expand it only once with \unexpanded\expandafter{#1}. This makes sure that if your \test macro contains other macros or active characters it won't explode.

A test document:

\documentclass{article}
\newcommand{\MakeItalic}[1]{%
  \edef#1{\noexpand\textit{\unexpanded\expandafter{#1}}}}
% Complicated definition of \test
\def\test{abc}
% Add \textit{...}
\MakeItalic\test
\begin{document}
\texttt{\meaning\test} = \test
\end{document}

produces:

enter image description here

  • I like this answer, and I have learnt a lot. I have accepted the other answer though, as it is closer to what I have been asking. – bers Sep 01 '19 at 23:15
  • @bers No worries :-) Indeed, with siracusa's answer you can compose the token list sort of step-by-step, whereas my approach puts everything in place with one blow. – Phelype Oleinik Sep 02 '19 at 00:31
3

You could use the following patterns to build your macro's replacement text. The basic idea is to not build the final result directly but to first create a sequence of commands that yields the final result when it is fully expanded in an \edef. For building the sequence you can use the \g@addto@macro macro, adding material as your algorithm goes.

Here are the sequence patterns to be added for various aspects (see this answer for an explanation of the brace patterns):

  • Add a macro name: \noexpand\macroname
  • Add general material that should occur unexpanded: \unexpanded{...}
  • Add a left brace: {\iffalse}\fi
  • Add a right brace: \iffalse{\fi}
  • All unexpandable material or \protect'ed macros can be used directly

From those pattern we can build the macro mentioned in your question:

\documentclass{article}
\makeatletter

\def\test{}

\g@addto@macro\test{\noexpand\textit}
\g@addto@macro\test{{\iffalse}\fi}
\g@addto@macro\test{abc}
\g@addto@macro\test{\iffalse{\fi}}

%\show\test
\edef\test{\test}
%\show\test

\begin{document}
    \test
\end{document}

If you uncomment the \show calls in this example, you can see the macro definition before

> \test=macro:
->\noexpand \textit {\iffalse }\fi abc\iffalse {\fi }.

and after the expansion

> \test=macro:
->\textit {abc}.
siracusa
  • 13,411
0

One answer could look like this:

\documentclass{article}
\usepackage{regexpatch}

\def\test{abc}
\regexpatchcmd{\test}{.*}{\c{textit}\cB\{\0\cE\}}{}{}

\begin{document}
    \test
\end{document}

A variant:

\documentclass{article}
\usepackage{regexpatch}
\makeatletter

\def\test{\textit XXX}
\g@addto@macro\test{abc}
\g@addto@macro\test{XXX}

\regexpatchcmd{\test}{XXX(.*)XXX}{\cB\{\1\cE\}}{}{}

\begin{document}
    \test
\end{document}
bers
  • 5,404
  • Oh, you just want "italic abc", not "italic {abc}". Then why not simply \def\test{\textit{abc}}? – Phelype Oleinik Sep 01 '19 at 22:05
  • @PhelypeOleinik I want to compose \test programmatically, like in a loop that has a, b, c (which are commands that are more complex than that, of course). – bers Sep 01 '19 at 22:10
  • 1
    Then why not \def\deftest#1#2{\def#1{\textit{#2}}} and then \deftest\test{abd}? Sorry to be such a pain, but I think you are over-complicating something rather simple :-) – Phelype Oleinik Sep 01 '19 at 22:15