1

Assume there is defined a macro \foo:
\def\foo{\bar\space A hash {in {braces: \string##}\string##}}

Expanding a control word token/macro token \foo yields:
\bar\spaceA1110h11a11s11h1110{1i11n1110{1b11r11a11c11e11s11:1210\string#6}2\string#6}2

Assume none of the control sequence tokens ocurring within this is \outer.

How to post-process to have each hash/each explicit character token of category 6/parameter doubled in order to get
\bar\spaceA1110h11a11s11h1110{1i11n1110{1b11r11a11c11e11s11:1210\string#6#6}2\string#6#6}2 ?

That in turn can be used as (a component of) the ⟨replacement text⟩ of another macro that is to be defined via \def and where I don't like to change to defining via \edef\AnotherMacro{\unexpanded{..}}/where I don't like to change to \edef\AnotherMacro{\the\tokenregister} as the code that carries out the \def and that would need to be changed comes from a macro package which is not mine and where I don't know if users rely on things not being changed/not being patched.

(Unfortunately, within replacement texts of many macros of pgfkeys, where a thing like \edef\macro{\unexpanded{#1}} could be used to allow hashes as components of the argument denoted by the parameter #1, \def\macro{#1} is used. This seems to be the case, e.g., with routines underlying handlers like .store in and .search also. In case this is already rectified in most recent pgfkeys: One of my colleagues still uses TeX Live 2018 and won't update until retiring.)

I know that \meaning, \write and \scantokens and \detokenize double hashes, but with these you loose information about categories of character tokens. And in case of flipping category codes of characters between 11 and s.th. else, you also don't know what characters belong to the names of what control sequences. And in case \escapechar is changed or not positive, you don't even know what characters belong to the names of control sequences at all.

2 Answers2

1

There are functions in etl that can search&replace macro parameter tokens. It has a few other shortcomings though (see its documentation, mostly it's about a few internal tokens not being allowed, the fact that it normalises category 1 and 2 tokens to {}, and the fact that some tokens can't be distinguished expandably from each other).

Be that as it may, the following implements a hash doubling with a straight forward single call to a function, and then uses it in some pseudo-setup macro as an example for your pgfkeys-internal. I removed the \bar so that I can typeset the contents without further changes.

\documentclass{article}

\usepackage{etl}

\def\foo{\space A hash {in {braces: \string##}\string##}}

\ExplSyntaxOn \cs_new:Npn \cattleya_double_hashes:n #1 { \etl_token_replace_all_deep:nNn {#1} ## { #### } } \cs_new_eq:NN \doublehashes \cattleya_double_hashes:n \ExplSyntaxOff

% the other definition you don't have control over \NewDocumentCommand\mysetupmacro{m} {\def\macro{Stuff and #1 and stuff}} \newcommand*\macro{} % initial value

\expanded{\noexpand\mysetupmacro{\expandafter\doublehashes\expandafter{\foo}}}

\begin{document} \texttt{\meaning\macro}

\macro \end{document}

enter image description here

Skillmon
  • 60,462
0

I am not sure if this token cycle meets your requirements:

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{tokcycle,lmodern}
\tokcycleenvironment\showcats
 {\ifcatSIX\addcytoks{\thistok{##1}{6}}\else\addcytoks{\catcomp{##1}}\fi}
 {\addcytoks{\thistok{\{}{1}}\processtoks{##1}\addcytoks{\thistok{\}}{2}}}
 {\addcytoks{\catcomp{##1}}\tctestifx{\par##1}{\addcytoks{\par}}{}}
 {\ifimplicittok\addcytoks{\catcomp{##1}}\else
  \addcytoks{\thistok{\textvisiblespace}{\number\catcode`##1}}\fi}
\newcommand\thistok[2]{#1$_{#2}$\,\allowbreak}
\makeatletter
\newcommand\catcomp[1]{%
  \tctestifx{\implicitsixtok#1}{\expandafter\string#1$_{6}}{%
  \string#1$_{%
  \tctestifcatnx #1\relax{0}{%
  \tctestifcatnx #1${3}{%
  \tctestifcatnx #1&{4}{%
  \tctestifcatnx #1^{7}{%
  \tctestifcatnx #1_{8}{%
  \tctestifcatnx #1\@sptoken{10}{%
  \tctestifcatnx #1a{11}{%
  \tctestifcatnx #11{12}{%
  \tctestifcatnx #1~{13}{%
  *%
  }}}}}}}}}}%
  }$\,\allowbreak%
}
\makeatother
\begin{document}

\let\foo\relax

\showcats\def\foo{\bar\space A hash {in {braces: \string##}\string##}}\endshowcats

\hrulefill

\def\foo{\bar\space A hash {in {braces: \string##}\string##}}

\detokenize\expandafter{\foo}

\expandafter\showcats\foo\endshowcats

\hrulefill

\newcommand\addhash{\cytoks\expandafter{\the\cytoks####}}

\resettokcycle \Characterdirective{\ifcatSIX\addhash\addhash\else\addcytoks{#1}\fi} \expandafter\tokcyclexpress\expandafter{\foo} \expandafter\def\expandafter\food\expandafter{\the\cytoks}

\detokenize\expandafter{\food}

\expandafter\showcats\food\endshowcats \end{document}

enter image description here

In the first block of output, the double hashes can be recognized, if processed before the \def takes place, but this is not what you need.

In the 2nd block, if the \def occurs first, the double hash is lost, per the rules of \def.

In the 3rd block, the double hash is restored, by operating the given token cycle upon the predefined \foo to create \food with a \def of the cycle tokens. I show the detokenization \food, as well as its token/catcode breakdown.

This might be what you requested, I hope.