5

Bottom line up front: what is a convenient way to input a particular character string into a \newcommand call so it can be manipulated to create multiple macro names? In particular, input (say) ABC so that a savebox \ABCbox is created, from which a macro \ABC is then defined using \ABCbox, and returning that macro for repeated use later on. I suspect I would prefer a Lua suggestion, but am a complete novice in how Lua interacts with LaTeX, so will likely need some hand-holding.


As a group theorist, I use "k" for conjugacy classes, but want one that is (in spirit) an overlay of $\ell$ on $\mathit k$ to have a loopy "k" in the same way \ell is a loopy "l". Ultimately, I wrote tikz code that created the glyph I wanted, and wrote a \newcommand to name the macro \kay that printed that glyph. However, I naively included the tikz code directly in the \newcommand call, which meant that the tikz code was newly compiled every time \kay was called, which was a few hundred times; compile times increased considerably. Due to the answer given at Long Compilation Time: Tikz Accents, I discovered saveboxes, and compile times were demonstrably faster.

As an academic exercise, I then decided to create other personal glyphs using similar methods, and found myself reusing the same "boxing" code over and over in creating the corresponding saveboxes. Hence, I wrote the following macro which would take as input the tikzpicture (and some other parameters) and output the corresponding savebox.

\newcommand\createABCbox[5]{%
\newsavebox#1
\savebox#1{%
\mathalpha{
\hspace{#2pt}
\raisebox{#3pt}{#5}%
\hspace{#4pt}}}}

The code would be used in the following way (consider this an MWE, the actual tikz code is much more elaborate):

\createABCbox{\ehhbox}{1}{1}{.5}
{
\begin{tikzpicture}
\node at (0,0){\(h\)};
\end{tikzpicture}
}
\newcommand \ehh{\usebox{\ehhbox}}

This got me to thinking. While this greatly reduced the amount of work I needed by putting it in a macro call, there should be a way to get the obvious additional savings. (As well, I suspect a solution to this problem will prove useful to another three situations I face.) What I would really like is to make a command \createABC that takes as its first input just the character string "ehh" and somehow manipulates it to create the character strings "\ehhbox" and "\ehh", with the newly developed strings then being used at the LaTeX level as the actual macro names being defined. Hence, running \createABC with input string "XYZ" would create the macro call \XYZ and the savebox \XYZbox.

As for my efforts in this direction, I have looked at the packages stringstrings and xstring, but both seem to deal more with strings which will be printed, rather than creating something that is recognized by a LuaLaTeX compiler as a macro name. I do admit I didn't delve too deeply into either of these, so I may have missed something.

Ideally, I would like to perform this task in Lua (at which I am a complete novice), but

  • I don't know the correct way to place an argument (in this example, #1) into a form Lua recognizes as a string, and

  • every time I try to insert Lua code into a LaTeX \newcommand, I get an error.

As an example of the second point, using the package luacode, the code

\begin{document}

\begin{luacode}
tex.sprint("hello world")
\end{luacode}

\end{document}

compiles correctly. However, if I wrap the Lua portion in a newcommand call such as

\begin{document}

\newcommand\AAA
{
\begin{luacode}
tex.sprint("hello world")
\end{luacode}
}

\AAA
\end{document}

I get the following error message.

Runaway argument?
! File ended while scanning use of \luacode@grab@lines.

For completeness, the entire preamble presently is...

\documentclass{article}
\special{papersize=8.5in,11in}
\pagestyle{empty}
\usepackage{bm,comment,luacode,mathtools,scalefnt,tikz}
\usetikzlibrary{arrows.meta,calc}
4
  • 2
    The basic answer to your question is yes, and the basic method is to use \csname ... \endcsname or methods that use that. See What exactly do \csname and \endcsname do? for an introduction.
    – Alan Munn
    Dec 1 '21 at 18:42
  • When in Lua, you could use token.set_macro("AAA","hello world") or token.set_macro("AAA","hello world","global") when no arguments are required Dec 1 '21 at 18:46
  • 1
    Wouldn't borrowing the k from another font be simpler? tex.stackexchange.com/questions/385574/math-font-with-special-k?
    – egreg
    Dec 1 '21 at 21:07
  • How to adjust things when using different font sizes? E.g., in normal size you might want spaces #2pt / #3pt / #4pt. What spaces do you want, e.g., when \huge or \scriptsize is in effect? Dec 4 '21 at 13:21
6

This is essentially the same as @gernot's answer but reduces the number of \expandafters and \csnames making the code more readable, imho.

The approach uses two steps of processing, the first step just reads in the first argument (the others are curried) and creates the macro names from it, resulting in two new arguments. The next step then gets all the arguments.

\documentclass[]{article}

\usepackage{tikz}

\makeatletter
\newcommand\newABC[1]
  {%
    % #1: name for macro and box
    % #2: before   (curried)
    % #3: raise    (curried)
    % #4: after    (curried)
    % #5: contents (curried)
    \expandafter\newABC@\csname #1\expandafter\endcsname\csname #1box\endcsname
  }
\newcommand\newABC@[6]
  {%
    % #1: macro
    % #2: box-macro
    % #3: before
    % #4: raise
    % #5: after
    % #6: contents
    \newsavebox#2%
    \sbox#2{\mathalpha{\hspace{#3pt}\raisebox{#4pt}{#6}\hspace{#5pt}}}%
    \newcommand#1{\usebox#2}%
  }
\makeatother

\newABC{ehh}{1}{1}{.5}
{%
\begin{tikzpicture}
\node at (0,0){\(h\)};
\end{tikzpicture}%
}

\begin{document}
A\ehh b
\end{document}

enter image description here


As requested a few explanations on the code:

  • \csname expands all following tokens until it finds an \endcsname and the result is turned into a control sequence.

  • \expandafter steps over the next token (regardless which kind of token, an opening brace for instance is a token as well which could be stepped over with this) and expands the token after that one once (if that token isn't expandable nothing happens).

So \expandafter\stuff\csname foo\endcsname will result in \stuff being stepped over and \csname being expanded once. Within a single step of expansion \csname expands all following tokens until it finds an \endcsname and leaves everything in between as the name of a control sequence. In this case it'll find foo (letters don't expand further), and so after \csname is done the \expandafter will be removed from the input stream and \stuff put back, so the input will now contain \stuff\foo.

We can utilize the fact that \csname expands everything until it finds an \endcsname to build two control sequences at once (in the following the next thing TeX will evaluate will be preceded by |> and the tokens stored to be put back because TeX stepped over them will be preceded by || -- this is the same style the unravel package would use, though my steps might not be the same that package would show):

|> \expandafter\stuff\csname foo\expandafter\endcsname\csname bar\endcsname

will first step over \stuff, so the input will look like this (this is less than one step of expansion, more a step of processing):

|| \expandafter\stuff
|> \csname foo\expandafter\endcsname\csname bar\endcsname

Now \csname will start grabbing/expanding tokens, and because of \expandafter the \endcsname will not be found, instead TeX steps over it and expands what follows:

|| \expandafter\stuff
|| \csname foo\expandafter\endcsname
|> \csname bar\endcsname

Now the second \csname grabs/expands tokens until it finds \endcsname and turn the found string into a control sequence:

|| \expandafter\stuff
|| \csname foo\expandafter\endcsname
|| \csname bar
|> \endcsname

and

|| \expandafter\stuff
|| \csname foo\expandafter\endcsname
|| \bar

Now the second \csname is done with one step of expansion and the second \expandafter will be removed and the token which followed it put back, so the next step of processing would look like

|| \expandafter\stuff
|| \csname foo
|> \endcsname\bar

The first \csname finally finds its \endcsname and this will become

|| \expandafter\stuff
|| \foo\bar

Now also the first \csname had its step of expansion from \expandafter, so it'll be removed and \stuff put back, so this eventually becomes

|> \stuff\foo\bar

and now \stuff can do stuff.

Even though the above was visualized in many small steps of processing when we look at expansion steps this is all done in a single step, because \expandafter will in a single step expand the \csname and that will fully expand the remaining stuff in this one step.

3
  • @JohnMcVey I agree, his answer is preferable for those reasons (and since he was faster to add these I didn't add them to mine).
    – Skillmon
    Dec 2 '21 at 11:07
  • @gernot no, it isn't. But as it currently stands your answer is the better one, even though the complete code might be less intelligible.
    – Skillmon
    Dec 2 '21 at 18:29
  • @gernot finally done.
    – Skillmon
    Dec 3 '21 at 8:57
0

You can use macros with argument-specifier c provided by the expl3-programming-interface indicating that as argument the user provides a set of tokens whose total expansion yields the name of a control-sequence-token which during expansion is to be used in place of the corresponding parameter of the replacement-text:

\documentclass{article}

\ExplSyntaxOn
\cs_new_protected:Npn \newABC #1#2#3#4#5 {%
  % #1: name of to-be-defined macro /
  %     prefix of name of to-be-allocated box-register
  %     ; for forming the entire name of the box-register 
  %       the phrase "box" is appended to the prefix
  % #2: measure related to unit pt of horizontal space prepended 
  %     to user-specified content of box
  % #3: measure related to unit pt of vertical space to raise 
  %     user-specified content of box upwards
  % #4: measure related to unit pt of horizontal space appended
  %     to user-specified content of box
  % #5: user-specified content of box
  \box_new:c{#1box}%
  \hbox_set:cn{#1box}{\mathalpha{\hspace{#2pt}\raisebox{#3pt}{#5}\hspace{#4pt}}}%
  \cs_new_protected:cpn{#1}{\box_use:c{#1box}}%
}%
\ExplSyntaxOff

\usepackage{tikz}

\newABC{ehh}{1}{1}{.5}{%
   \begin{tikzpicture}%
   \node at (0,0){\(h\)};%
   \end{tikzpicture}%
}%

\begin{document}
A\ehh b
\end{document}

enter image description here


If for whatsoever reason you don't like to use the expl3-programming-interface, I can offer a macro \CsNameToCsToken to create, e.g., the control-word-token \ehh from the character-token-sequence ehh or the control-word-token \ehhbox from the character-token-sequence ehhbox.

Syntax:

\CsNameToCsToken⟨stuff not in braces⟩{⟨NameOfCs⟩}

⟨stuff not in braces⟩\NameOfCs

(⟨stuff not in braces⟩ may be empty.)

(Due to \romannumeral-expansion the result is obtained by triggering two expansion-steps, e.g., by having two "hits" with \expandafter.)

With such a macro you are not bound to specific definition commands:

\CsNameToCsToken{foo}\foo  .

(\CsNameToCsToken{foo} just triggers creation of the control-word-token \foo which after its creation will be processed by TeX in the usual ways.)

\CsNameToCsToken\newcommand{foo}\newcommand\foo  .

\CsNameToCsToken\DeclareRobustCommand{foo}\DeclareRobustCommand\foo  .

\CsNameToCsToken\global\long\outer\def{foo}\global\long\outer\def\foo  .

\CsNameToCsToken\expandafter{foo}\bar\expandafter\foo\bar  .

\CsNameToCsToken\let{foo}=\bar\let\foo=\bar  .

\CsNameToCsToken\CsNameToCsToken\let{foo}={bar}\CsNameToCsToken\let\foo={bar}\let\foo=\bar  .

\CsNameToCsToken\string{foo}\string\foo  .

\CsNameToCsToken\meaning{foo}\meaning\foo  .

\CsNameToCsToken\NewDocumentCommand{foo}...\NewDocumentCommand\foo...  .

You can use \CsNameToCsToken for doing something like the following:

\documentclass{article}

\makeatletter
%%===============================================================================
%% Obtain control sequence token from name of control sequence token:
%%===============================================================================
%% \CsNameToCsToken<stuff not in braces>{NameOfCs}
%% ->  <stuff not in braces>\NameOfCs
%% (<stuff not in braces> may be empty.)
\@ifdefinable\CsNameToCsToken{%
  \long\def\CsNameToCsToken#1#{\romannumeral\InnerCsNameToCsToken{#1}}%
}%
\newcommand\InnerCsNameToCsToken[2]{%
  \expandafter\UD@exchange\expandafter{\csname#2\endcsname}{\UD@stopromannumeral#1}%
}%
\newcommand\UD@exchange[2]{#2#1}%
% End \romannumeral-driven expansion safely:
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
\makeatother


\newcommand\newABC[5]{%
  % #1: name of to-be-defined macro /
  %     prefix of name of to-be-allocated box-register
  %     ; for forming the entire name of the box-register 
  %       the phrase "box" is appended to the prefix
  % #2: measure related to unit pt of horizontal space prepended 
  %     to user-specified content of box
  % #3: measure related to unit pt of vertical space to raise 
  %     user-specified content of box upwards
  % #4: measure related to unit pt of horizontal space appended
  %     to user-specified content of box
  % #5: user-specified content of box
  \CsNameToCsToken\newsavebox{#1box}%
  \CsNameToCsToken\sbox{#1box}{\mathalpha{\hspace{#2pt}\raisebox{#3pt}{#5}\hspace{#4pt}}}%
  \CsNameToCsToken\newcommand*{#1}{\CsNameToCsToken\usebox{#1box}%
}%

\usepackage{tikz}

\newABC{ehh}{1}{1}{.5}{%
  \begin{tikzpicture}%
  \node at (0,0){\(h\)};%
  \end{tikzpicture}%
}%

\begin{document}
A\ehh b
\end{document}

enter image description here


The problem with the environment luacode is the same as with environments like verbatim or lstlisting:

It expects the phrase \end{luacode} to be tokenized under some non-standard-catcode-régime, yielding a set of tokens differing from the set of tokens one would obtain by tokenizing under standard-catcode-régime.

If coming from the replacement-text of a macro \AAA whose definition-text was tokenized under standard-catcode-régime, then the phrase \end{luacode} won't be recognized as the end of the environment luacode.

As a workaround I can offer a routine

\DefineVerbatimToScantokens{⟨control-word-token⟩}{⟨xparse-argument-specifiers⟩}{%
  ⟨verbatim-material to be passed to \scantokens⟩
}%

⟨verbatim-material to be passed to \scantokens⟩ is read and tokenized under verbatim-catcode-régime.

Then within ⟨verbatim-material to be passed to \scantokens⟩ every # is replaced by a character-token of catcode 6(parameter).

Then ⟨control-word-token⟩ is defined to process arguments according to ⟨xparse-argument-specifiers⟩ and to pass ⟨verbatim-material to be passed to \scantokens⟩ to \scantokens.

\documentclass{article}
%\special{papersize=8.5in,11in}
%\pagestyle{empty}
%\usepackage{bm,comment,luacode,mathtools,scalefnt,tikz}
%\usetikzlibrary{arrows.meta,calc}
\usepackage{luacode}

%=== Code of \DefineVerbatimToScantokens ========================
% With older LaTeX-releases uncomment the following line:
%\usepackage{xparse}   
\NewDocumentCommand\DefineVerbatimToScantokens{mm}{%
  \begingroup
  \catcode`\^^I=12\relax
  \InnerDefineVerbatimToScantokens{#1}{#2}%
}%
\begingroup
\makeatletter
\def\InnerDefineVerbatimToScantokens#1#2{%
  \endgroup
  \NewDocumentCommand\InnerDefineVerbatimToScantokens{mm+v}{%
    \endgroup\ReplaceHashloop{##3}{##1}{##2}%
  }%
  \newcommand\ReplaceHashloop[3]{%
    \ifcat$\detokenize\expandafter{\Hashcheck##1#1}$%
    \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
    {%
      \NewDocumentCommand{##2}{##3}{%
         \begingroup\newlinechar=\endlinechar
         \scantokens{\endgroup##1#2}%
      }%
    }{%
      \expandafter\ReplaceHashloop\expandafter{\Hashreplace##1}{##2}{##3}%
    }%
  }%
  \@ifdefinable\Hashcheck{\long\def\Hashcheck##1#1{}}%
  \@ifdefinable\Hashreplace{\long\def\Hashreplace##1#1{##1####}}%
}%
\catcode`\%=12\relax
\catcode`\#=12\relax
\InnerDefineVerbatimToScantokens{#}{%}%
%=== End of code of \DefineVerbatimToScantokens =================

\DefineVerbatimToScantokens\AAA{+v}{%
  \begin{luacode}
  tex.sprint("Hello World and #1!")
  \end{luacode}
}%

\DefineVerbatimToScantokens\BBB{+v+v+v}{%
  \begin{luacode}
  tex.sprint("Hello #1 and #2 and #3!")
  \end{luacode}
}%

\begin{document}

\AAA{John}

\BBB{Universe}{World}{John}

\end{document}

enter image description here


I don't know if allocating another box-register with each instance of \newABC really is the right way to go:

Storing things in box-registers means "casting font sizes etc in stone":

Maybe your command \ehh produces good-looking results while normalsize is in effect.
But what about situations where \huge or \scriptsize or the like is in use?

I recommend to take into consideration the comment of egreg:

Wouldn't borrowing the k from another font be simpler?

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.