If you wish to use \macrotokey
within the argument of \pgfkeys
, then \macrotokey
needs to be fully expandable.
Expl3 comes along with a fully expandable routine \cs_to_str:N
where the name of a control-sequence-token without leading escape character (usually backslash) is delivered by triggering two expansion-steps to \cs_to_str:N
. With this routine you don't need to care for edge cases like \escapechar
having value 32 or a negative value. (32 means that instead of a backslash a space is delivered as escape-character by things like \string
or \detokenize
. Negative value means that no escape-characteres are delivered at all.)
So let's use expl3 for defining a macro \macrotokey{⟨argument⟩}
which triggers two expansion-steps on \cs_to_str:N ⟨argument⟩
and then initiates a recursive loop \__MyStuff_macrotokeyReplaceAtLoop:n
which checks whether the argument (still) contains @
of category 12(other) and if so replaces the first of these @
by /
before calling itself again and if not so delivers the argument.
I would probably have named the thing \cstokentokey
instead of \macrotokey
because the meaning of the token that is to form the argument of \macrotokey
is not relevant (except that it should not be an \outer
token)—the argument being a control sequence token is sufficient. It does not even need to be defined.
If you uncomment \exp_end:
and \exp:w
, then the result of \macrotokey
is obtained by triggering exactly two expansion-steps on \macrotokey
.
But for \pgfkeys
this is not necessary as \pgfkeys
triggers expansion of the tokens forming the key itself.
\documentclass{article}
\usepackage{pgfkeys}
\ExplSyntaxOn
\cs_new:Npn \MyStuff_GobbleTillAt:w #1@ {}
\cs_new:Npn \MyStuff_ReplaceAt:w #1@ { #1/ }
\cs_new:Nn \__MyStuff_macrotokeyReplaceAtLoop:n
{
\tl_if_empty:oTF {\MyStuff_GobbleTillAt:w #1 @}
{ %\exp_end:
#1
% If the name of the control-sequence-token begins with @, then it denotes an absolute path,
% otherwise the path is relative to what \pgfkeysdefaultpath was defined to via the /.cd-handler.
% If you do /#1 instead of #1, then every path is absolute and the names of
% control-sequence-tokens shall not begin with `@`.
% If you do \pgfkeysdefaultpath #1 instead of #1, then every path is relative and the names of
% control-sequence-tokenss shall not begin with `@`.
}
{
\exp_args:No \__MyStuff_macrotokeyReplaceAtLoop:n
{\MyStuff_ReplaceAt:w #1}
}
}
\cs_new:Npn \macrotokey #1
{ %\exp:w
\exp_args:NNo \exp_args:No
\__MyStuff_macrotokeyReplaceAtLoop:n
{ \cs_to_str:N #1 }
}
\ExplSyntaxOff
\newcommand{\setviamacro}[2]{\pgfkeys{\macrotokey{#1}={#2}}}
\newcommand{\setviakey}[2]{\pgfkeys{#1={#2}}}
\makeatletter
\pgfkeys{%
%---------------------------------------------------------------
% define the code to execute when encountering the key /A/B/C:
%---------------------------------------------------------------
/A/B/C/.code=\message{^^JKey /A/B/C's value in parentheses is: (#1)^^J},
%---------------------------------------------------------------
% Use the key /A/B/C :
%---------------------------------------------------------------
/A/B/C=Test 1: Absolute key provided,
\macrotokey{\@A@B@C} = Test 1: Control sequence token denoting absolute key provided,
/A/.cd,
B/C=Test 2: Relative key provided,
\macrotokey{\B@C} = Test 2: Control sequence token denoting relative key provided,
/A/B/.cd,
C=Test 3: Relative key provided,
\macrotokey{\C} = Test 3: Control sequence token denoting relative key provided,
}
\setviakey{/A/B/C}{Test 4: Absolute key provided}
\setviamacro{\@A@B@C}{Test 4: Control sequence token denoting absolute key provided}
\stop
The following messages are delivered to the console:
Key /A/B/C's value in parentheses is: (Test 1: Absolute key provided)
Key /A/B/C's value in parentheses is: (Test 1: Control sequence token denoting
absolute key provided)
Key /A/B/C's value in parentheses is: (Test 2: Relative key provided)
Key /A/B/C's value in parentheses is: (Test 2: Control sequence token denoting
relative key provided)
Key /A/B/C's value in parentheses is: (Test 3: Relative key provided)
Key /A/B/C's value in parentheses is: (Test 3: Control sequence token denoting
relative key provided)
Key /A/B/C's value in parentheses is: (Test 4: Absolute key provided)
Key /A/B/C's value in parentheses is: (Test 4: Control sequence token denoting
absolute key provided)
\makeatletter
is "on" (to avoid the macro name being used elsewhere). How would you achieve that using\detokenize
?\macrotokey
to transform the name of a control-sequence-token into a key-path. 1) Do you have patterns/rules in mind for distinguishing denotation of an absolute key path via control-sequence-token from denotation of a relative key path via control-sequence-token? 2) Under normal catcode-régime you can have names of keys that contain spaces but you need to do some catcode-trickery for causing TeX to take a space for a component of a name of a control sequence whiile tokenizing .tex-input. 3) How to handle keys whose names contains@
?