How to implement expandbefore, similarly to expandafter?
I am trying to define a new command, but specifying the new control string "on the fly" using csname
and endcsname
. (This is for the purpose of implementing a dependency injection pattern.) **Alternatively, is there a way of doing this with expl3
?
Normally, I have been using expandafternewcommand
... but in this use case, I would like to define the csname
WITHIN newcommand
's first parameter, like so:
newcommand{expandbeforecsname GetCommandName endcsname}[1]{small I did it!!!}
newcommandexpandbeforecsname GetCommandName endcsname{small I did it again!!!}
I would like for everything within the braces to be expanded BEFORE newcommand
- but without relying on expandafter
before newcommand
.
Issues:
I am curious as to whether there is a normal way of doing this in LaTeX, without having to rely on another process input buffer hack, (with Lua).
Adding
expandafter
,nameuse
,edef
,let
,csname
, etc, within thenewcommand
's parameter just results in an error to redefine those commands. (Even if in{}
orbegingroup
closures.Trying to
meaning
expandafter
to figure out how it works fails, (predictably, and funny too).
macros expansion expl3 parameters
add a comment |
I am trying to define a new command, but specifying the new control string "on the fly" using csname
and endcsname
. (This is for the purpose of implementing a dependency injection pattern.) **Alternatively, is there a way of doing this with expl3
?
Normally, I have been using expandafternewcommand
... but in this use case, I would like to define the csname
WITHIN newcommand
's first parameter, like so:
newcommand{expandbeforecsname GetCommandName endcsname}[1]{small I did it!!!}
newcommandexpandbeforecsname GetCommandName endcsname{small I did it again!!!}
I would like for everything within the braces to be expanded BEFORE newcommand
- but without relying on expandafter
before newcommand
.
Issues:
I am curious as to whether there is a normal way of doing this in LaTeX, without having to rely on another process input buffer hack, (with Lua).
Adding
expandafter
,nameuse
,edef
,let
,csname
, etc, within thenewcommand
's parameter just results in an error to redefine those commands. (Even if in{}
orbegingroup
closures.Trying to
meaning
expandafter
to figure out how it works fails, (predictably, and funny too).
macros expansion expl3 parameters
you would need to redefinenewcommand
to doexpandafteroldnewcommand
whereoldnewcommand
is the saved original version, but this would almost certainly break multiple packages, so I'd rather strongly advise that you don't do it.
– David Carlisle
Mar 3 at 8:56
Did you tryexpandafternewcommandexpandafter{GetCommandName}
?
– AndiW
Mar 3 at 8:58
csname
itself triggers expansion of expandable tokens while during its search for the matchingendcsname
gathering the character-tokens that form the name of the control-sequence-token in question. ThereforecsnameGetCommandNameendcsname
might work out as expansion ofGetCommandName
will be triggered bycsname
. All you might need to do is triggeringcsname
-expansion beforenewcommand
gets carried out:expandafternewcommandexpandafter{csname GetCommandName endcsname}[1]{small I did it!!!}
– Ulrich Diez
Mar 3 at 9:46
1
Perhaps you want to look at tex.stackexchange.com/a/317094/4427
– egreg
Mar 3 at 10:05
add a comment |
I am trying to define a new command, but specifying the new control string "on the fly" using csname
and endcsname
. (This is for the purpose of implementing a dependency injection pattern.) **Alternatively, is there a way of doing this with expl3
?
Normally, I have been using expandafternewcommand
... but in this use case, I would like to define the csname
WITHIN newcommand
's first parameter, like so:
newcommand{expandbeforecsname GetCommandName endcsname}[1]{small I did it!!!}
newcommandexpandbeforecsname GetCommandName endcsname{small I did it again!!!}
I would like for everything within the braces to be expanded BEFORE newcommand
- but without relying on expandafter
before newcommand
.
Issues:
I am curious as to whether there is a normal way of doing this in LaTeX, without having to rely on another process input buffer hack, (with Lua).
Adding
expandafter
,nameuse
,edef
,let
,csname
, etc, within thenewcommand
's parameter just results in an error to redefine those commands. (Even if in{}
orbegingroup
closures.Trying to
meaning
expandafter
to figure out how it works fails, (predictably, and funny too).
macros expansion expl3 parameters
I am trying to define a new command, but specifying the new control string "on the fly" using csname
and endcsname
. (This is for the purpose of implementing a dependency injection pattern.) **Alternatively, is there a way of doing this with expl3
?
Normally, I have been using expandafternewcommand
... but in this use case, I would like to define the csname
WITHIN newcommand
's first parameter, like so:
newcommand{expandbeforecsname GetCommandName endcsname}[1]{small I did it!!!}
newcommandexpandbeforecsname GetCommandName endcsname{small I did it again!!!}
I would like for everything within the braces to be expanded BEFORE newcommand
- but without relying on expandafter
before newcommand
.
Issues:
I am curious as to whether there is a normal way of doing this in LaTeX, without having to rely on another process input buffer hack, (with Lua).
Adding
expandafter
,nameuse
,edef
,let
,csname
, etc, within thenewcommand
's parameter just results in an error to redefine those commands. (Even if in{}
orbegingroup
closures.Trying to
meaning
expandafter
to figure out how it works fails, (predictably, and funny too).
macros expansion expl3 parameters
macros expansion expl3 parameters
edited Mar 3 at 8:59
elika kohen
asked Mar 3 at 8:53
elika kohenelika kohen
1376
1376
you would need to redefinenewcommand
to doexpandafteroldnewcommand
whereoldnewcommand
is the saved original version, but this would almost certainly break multiple packages, so I'd rather strongly advise that you don't do it.
– David Carlisle
Mar 3 at 8:56
Did you tryexpandafternewcommandexpandafter{GetCommandName}
?
– AndiW
Mar 3 at 8:58
csname
itself triggers expansion of expandable tokens while during its search for the matchingendcsname
gathering the character-tokens that form the name of the control-sequence-token in question. ThereforecsnameGetCommandNameendcsname
might work out as expansion ofGetCommandName
will be triggered bycsname
. All you might need to do is triggeringcsname
-expansion beforenewcommand
gets carried out:expandafternewcommandexpandafter{csname GetCommandName endcsname}[1]{small I did it!!!}
– Ulrich Diez
Mar 3 at 9:46
1
Perhaps you want to look at tex.stackexchange.com/a/317094/4427
– egreg
Mar 3 at 10:05
add a comment |
you would need to redefinenewcommand
to doexpandafteroldnewcommand
whereoldnewcommand
is the saved original version, but this would almost certainly break multiple packages, so I'd rather strongly advise that you don't do it.
– David Carlisle
Mar 3 at 8:56
Did you tryexpandafternewcommandexpandafter{GetCommandName}
?
– AndiW
Mar 3 at 8:58
csname
itself triggers expansion of expandable tokens while during its search for the matchingendcsname
gathering the character-tokens that form the name of the control-sequence-token in question. ThereforecsnameGetCommandNameendcsname
might work out as expansion ofGetCommandName
will be triggered bycsname
. All you might need to do is triggeringcsname
-expansion beforenewcommand
gets carried out:expandafternewcommandexpandafter{csname GetCommandName endcsname}[1]{small I did it!!!}
– Ulrich Diez
Mar 3 at 9:46
1
Perhaps you want to look at tex.stackexchange.com/a/317094/4427
– egreg
Mar 3 at 10:05
you would need to redefine
newcommand
to do expandafteroldnewcommand
where oldnewcommand
is the saved original version, but this would almost certainly break multiple packages, so I'd rather strongly advise that you don't do it.– David Carlisle
Mar 3 at 8:56
you would need to redefine
newcommand
to do expandafteroldnewcommand
where oldnewcommand
is the saved original version, but this would almost certainly break multiple packages, so I'd rather strongly advise that you don't do it.– David Carlisle
Mar 3 at 8:56
Did you try
expandafternewcommandexpandafter{GetCommandName}
?– AndiW
Mar 3 at 8:58
Did you try
expandafternewcommandexpandafter{GetCommandName}
?– AndiW
Mar 3 at 8:58
csname
itself triggers expansion of expandable tokens while during its search for the matching endcsname
gathering the character-tokens that form the name of the control-sequence-token in question. Therefore csnameGetCommandNameendcsname
might work out as expansion of GetCommandName
will be triggered by csname
. All you might need to do is triggering csname
-expansion before newcommand
gets carried out: expandafternewcommandexpandafter{csname GetCommandName endcsname}[1]{small I did it!!!}
– Ulrich Diez
Mar 3 at 9:46
csname
itself triggers expansion of expandable tokens while during its search for the matching endcsname
gathering the character-tokens that form the name of the control-sequence-token in question. Therefore csnameGetCommandNameendcsname
might work out as expansion of GetCommandName
will be triggered by csname
. All you might need to do is triggering csname
-expansion before newcommand
gets carried out: expandafternewcommandexpandafter{csname GetCommandName endcsname}[1]{small I did it!!!}
– Ulrich Diez
Mar 3 at 9:46
1
1
Perhaps you want to look at tex.stackexchange.com/a/317094/4427
– egreg
Mar 3 at 10:05
Perhaps you want to look at tex.stackexchange.com/a/317094/4427
– egreg
Mar 3 at 10:05
add a comment |
2 Answers
2
active
oldest
votes
I (with slight modifications) quote my answer to the question Define a control sequence after that a space matters as it seems to apply to your question as well:
By applying the #{
-notation, you can define macros whose last argument is delimited by an opening brace. Unlike with other argument delimiters that get removed when gathering arguments, TeX will leave a delimiting opening brace in place.
(Actually the mechanism isn't restricted to opening brace character tokens. You can use any token whose category code is 1 at definition time. Could as well be #WeIrd
after letWeIrd={
.)
Delimited arguments can be empty.
Therefore for obtaining a control sequence token from a set of tokens that expands to a set of character tokens which forms the name of the control sequence token in question both for defining and for calling that control sequence token, you can (by applying the #{
-notation) invent a single control sequence name
which processes a brace delimited argument trailed by an undelimited argument (which is nested in braces). After having TeX fetch the arguments, you can have TeX whirl them around and apply csname..endcsname
to the argument supplied inside braces. The name of the control sequence token in question can contain space tokens as well.
makeatletter
%
newcommandname{}%
longdefname#1#{UD@innername{#1}}%
%
newcommandUD@innername[2]{%
expandafterUD@exchangeexpandafter{csname#2endcsname}{#1}%
}%
%
newcommandUD@exchange[2]{#2#1}%
%
makeatother
name foo{bar}
→ expansion step 1:UD@innername{foo}{bar}
→ expansion step 2:expandafterUD@exchangeexpandafter{csname barendcsname}{foo}
→ expansion step 3:UD@exchange{bar}{foo}
→ expansion step 4:foobar
.
In expansion contexts you would need four expandafter
-chains for obtaining the result.
As romannumeral
does not produce any token when encountering a non-positive number, you can add a bit of romannumeral
-expansion in order to reduce the amount of expandafter
-chains.
Either do romannumeralname0 foo{bar}
. This way only one expandafter
-chain hitting the romannumeral
-token is needed.
Or have the romannumeral
-expansion "hardcoded" within the definition—this way two expandafter
-chains are needed. The first one for obtaining the topl-level-expansion of name
. The second one for inducing romannumeral
-expansion.
makeatletter
%
newcommandname{}%
longdefname#1#{romannumeral0UD@innername{#1}}%
%
newcommandUD@innername[2]{%
expandafterUD@exchangeexpandafter{csname#2endcsname}{ #1}%
}%
%
newcommandUD@exchange[2]{#2#1}%
%
makeatother
With such a macro you are not bound to specific definition commands:
name{foo}
→ foo
. (←This is the way in which you do not define but just call/use control-sequences by means of name
.)
namenewcommand{foo}
→ newcommandfoo
.
nameDeclareRobustCommand{foo}
→ DeclareRobustCommandfoo
.
namegloballongouterdef{foo}
→ globallongouterdeffoo
.
nameexpandafter{foo}bar
→ expandafterfoobar
.
namelet{foo}=bar
→ letfoo=bar
.
namestring{foo}
→ stringfoo
.
namemeaning{foo}
→ meaningfoo
.
You can as well use such a macro for defining/calling macros whose names
contain spaces:
name{foo }
→ foo␣
.
namenewcommand{foo }
→ newcommandfoo␣
.
nameDeclareRobustCommand{foo }
→ DeclareRobustCommandfoo␣
.
namegloballongouterdef{foo }
→ globallongouterdeffoo␣
.
nameexpandafter{foo }bar
→ expandafterfoo␣bar
.
namelet{foo }=bar
→ letfoo␣=bar
.
namestring{foo }
→ stringfoo␣
.
namemeaning{foo }
→ meaningfoo␣
.
While gathering the name of the control sequence token in question, name
will trigger expansion of expandable tokens :
defGetCommandName{FooBar}
namenewcommand{GetCommandName}[1]{small I did it!!!}
→ newcommandFooBar[1]{small I did it!!!}
defGetCommandName{CommandNamePartACommandNamePartB}
defCommandNamePartA{Ba}
defCommandNamePartB{rInnerCommandNamePart o}
defInnerCommandNamePart{Fo}
namenewcommand{GetCommandName}{small I did it again!!!}
→ newcommandBarFoo{small I did it again!!!}
You can also nest the calls to name
:
Example 1:
namenameexpandafter{f o o }{b a r }
Processing the first name
yields:
nameexpandafterf␣o␣o␣{b a r }
.
Processing the second name
yields:
expandafterf␣o␣o␣b␣a␣r␣
.
(Analogously: namenamelet{f o o }={b a r }
→ letf␣o␣o␣=b␣a␣r␣
.)
Example 2:
namenamenameexpandafterexpandafterexpandafter{f o o }expandafter{b a r }{c r a z y }
Processing the first name
yields:
namenameexpandafterexpandafterexpandafterf␣o␣o␣expandafter{b a r }{c r a z y }
.
Processing the second name
yields:
nameexpandafterexpandafterexpandafterf␣o␣o␣expandafterb␣a␣r␣{c r a z y }
.
Processing the third name
yields:
expandafterexpandafterexpandafterf␣o␣o␣expandafterb␣a␣r␣c␣r␣a␣z␣y␣
.
Example 3:
In expansion contexts you can use romannumeral
-expansion in order to keep things going.
romannumeralnamenamename0 expandafterexpandafterexpandafter{f o o }expandafter{b a r }{c r a z y }
romannumeral
keeps expanding until it has found some number. In the end it will find the number0
while with non-positive numbers romannumeral
will not deliver any token:
%romannumneral-expansion in progress
namenamename0 expandafterexpandafterexpandafter{f o o }expandafter{b a r }{c r a z y }
Processing the first name
yields:
%romannumneral-expansion in progress
namename0 expandafterexpandafterexpandafterf␣o␣o␣expandafter{b a r }{c r a z y }
.
Processing the second name
yields:
%romannumneral-expansion in progress
name0 expandafterexpandafterexpandafterf␣o␣o␣expandafterb␣a␣r␣{c r a z y }
.
Processing the third name
yields:
%romannumneral-expansion in progress
0 expandafterexpandafterexpandafterf␣o␣o␣expandafterb␣a␣r␣c␣r␣a␣z␣y␣
.
Now romannumeral
finds the number 0
. Therefore romannumeral
-expansion gets aborted and romannumeral
won't deliver any token:
expandafterexpandafterexpandafterf␣o␣o␣expandafterb␣a␣r␣c␣r␣a␣z␣y␣
.
Be aware that name
internally applies csname
while
expansion of expandable tokens takes place while
csname
during its search for the matchingendcsname
gathers the character tokens that form the name of the control sequence token in question.applying
csname
as a side effect yields assigning the control sequence in question the meaning of therelax
-primitive in case the control sequence in question was undefined before applyingcsname
. That assignment will be restricted to the current scope even if theglobaldefs
-parameter had a positive value at the time of applyingcsname
.
%%errorcontextlines=1000
documentclass[a4paper]{article}
usepackage{textcomp}%
parindent=0cm
parskip=medskipamount
makeatletter
newcommandname{}%
longdefname#1#{romannumeral0UD@innername{#1}}%
newcommandUD@innername[2]{%
expandafterUD@exchangeexpandafter{csname#2endcsname}{ #1}%
}%
newcommandUD@exchange[2]{#2#1}%
makeatother
namenewcommand{foo}[2]{%
Control sequence whose name does not contain any space.\
Argument 1: textit{textlangle#1textrangle}\
Argument 2: textit{textlangle#2textrangle}
}%
namenewcommand{foo }[2]{%
Control sequence whose name has a trailing space.\
Argument 1: textit{textlangle#1textrangle}\
Argument 2: textit{textlangle#2textrangle}
}%
namenewcommand{ f o o }[2]{%
Control sequence whose name is interspersed with spaces.\
Argument 1: textit{textlangle#1textrangle}\
Argument 2: textit{textlangle#2textrangle}
}%
newcommand*GetCommandName{CommandNamePartACommandNamePartB}
newcommand*CommandNamePartA{Ba}
newcommand*CommandNamePartB{rInnerCommandNamePart o}
newcommand*InnerCommandNamePart{Fo}
namenewcommand{GetCommandName}{small I did it again!!!}
begin{document}
name{foo}{Arg 1}{Arg 2}
name{foo }{Arg 1}{Arg 2}
name{ f o o }{Arg 1}{Arg 2}
Nesting texttt{stringname}:
nameexpandafternewcommandexpandafter*expandafter{C o N f u SiO n}expandafter{%
romannumeralnamenamename0 %
expandafterexpandafterexpandafter{F O O}expandafter{B A R}{C R A Z Y}%
}%
texttt{namestring{C o N f u SiO n} is namemeaning{C o N f u SiO n}}%
\
Playing around with expandable tokens:
texttt{namestring{GetCommandName}:}
texttt{namemeaning{GetCommandName}}
name{GetCommandName}%
Playing around with grouping:
%Be aware that texttt itself opens up a new scope for typesetting its argument.
%globaldefs=1relax
texttt{%
begingroupnamestring{w e i r d } is nameendgroupmeaning{w e i r d }%
}%
texttt{%
namestring{w e i r d } is namemeaning{w e i r d }%
}%
end{document}
Why do you havenewcommandname{}%
in front? Just to test for duplicate commands?
– elika kohen
Mar 3 at 16:46
Yes. To test for duplicate commands and to be informed about it via the " command-already-defined"-error-message that comes fromnewcommand
in case of the command in question already being defined.
– Ulrich Diez
Mar 3 at 17:05
add a comment |
LaTeX already has a command form that takes the name of a command rather than the csname token:
@namedef{GetCommandName}{small I did it!!!}
should do what you want this is simply expandafterdefcsnameGetCommandNameendcsname{..}
Funny, I was trying to do it with @nameuse. It would not be possible to go back and modify pre-existingnewcommand
s with this, (let alone deal with the cat code nonsense). But, I think your idea of overriding the pre-existingnewcommand
would be ideal - if I could keep the code very similar to what it already is. The problem, of course, is I have no idea what@star@or@long new@command
actually does, to incorporate what you are talking about. Thank you very much, though. It puts me on the right track.
– elika kohen
Mar 3 at 9:12
@elikakohen If you like@nameuse
, you can do something likeexpandafterexpandafterexpandafternewcommand@nameuse{GetCommandName}{small I did it!!!}
. The 1stexpandafter
"hits" the 3rdexpandafter
which "hits"@nameuse
.@nameuse
deliverscsnameGetCommandNameendcsname
and vanishes. Then the 3rdexpandafter
's work is done and it vanishes. Then the 1stexpandafter
's work is done and it vanishes. Now the 2ndexpandafter
hits thecsname
whose expansion in turn yields the control-sequence-token. Whencsname
is done, the 2ndexpandafter
vanishes. :-) :-)
– Ulrich Diez
Mar 3 at 11:05
@UlrichDiez - Oh no, not at all. I was just trying to figure out@nameuse
and couldn't get it to work, (hence this question). Based on your comment, though, I am certainly glad I didn't pursue that route! Thanks!
– elika kohen
Mar 3 at 11:06
@elikakohen You haven't said what your actual use case is but I can't think of any way that you could redefinenewcommand
to do this that would not break any existing package that is using that command.
– David Carlisle
Mar 3 at 20:35
add a comment |
Your Answer
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "85"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2ftex.stackexchange.com%2fquestions%2f477516%2fhow-to-implement-expandbefore-similarly-to-expandafter%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
I (with slight modifications) quote my answer to the question Define a control sequence after that a space matters as it seems to apply to your question as well:
By applying the #{
-notation, you can define macros whose last argument is delimited by an opening brace. Unlike with other argument delimiters that get removed when gathering arguments, TeX will leave a delimiting opening brace in place.
(Actually the mechanism isn't restricted to opening brace character tokens. You can use any token whose category code is 1 at definition time. Could as well be #WeIrd
after letWeIrd={
.)
Delimited arguments can be empty.
Therefore for obtaining a control sequence token from a set of tokens that expands to a set of character tokens which forms the name of the control sequence token in question both for defining and for calling that control sequence token, you can (by applying the #{
-notation) invent a single control sequence name
which processes a brace delimited argument trailed by an undelimited argument (which is nested in braces). After having TeX fetch the arguments, you can have TeX whirl them around and apply csname..endcsname
to the argument supplied inside braces. The name of the control sequence token in question can contain space tokens as well.
makeatletter
%
newcommandname{}%
longdefname#1#{UD@innername{#1}}%
%
newcommandUD@innername[2]{%
expandafterUD@exchangeexpandafter{csname#2endcsname}{#1}%
}%
%
newcommandUD@exchange[2]{#2#1}%
%
makeatother
name foo{bar}
→ expansion step 1:UD@innername{foo}{bar}
→ expansion step 2:expandafterUD@exchangeexpandafter{csname barendcsname}{foo}
→ expansion step 3:UD@exchange{bar}{foo}
→ expansion step 4:foobar
.
In expansion contexts you would need four expandafter
-chains for obtaining the result.
As romannumeral
does not produce any token when encountering a non-positive number, you can add a bit of romannumeral
-expansion in order to reduce the amount of expandafter
-chains.
Either do romannumeralname0 foo{bar}
. This way only one expandafter
-chain hitting the romannumeral
-token is needed.
Or have the romannumeral
-expansion "hardcoded" within the definition—this way two expandafter
-chains are needed. The first one for obtaining the topl-level-expansion of name
. The second one for inducing romannumeral
-expansion.
makeatletter
%
newcommandname{}%
longdefname#1#{romannumeral0UD@innername{#1}}%
%
newcommandUD@innername[2]{%
expandafterUD@exchangeexpandafter{csname#2endcsname}{ #1}%
}%
%
newcommandUD@exchange[2]{#2#1}%
%
makeatother
With such a macro you are not bound to specific definition commands:
name{foo}
→ foo
. (←This is the way in which you do not define but just call/use control-sequences by means of name
.)
namenewcommand{foo}
→ newcommandfoo
.
nameDeclareRobustCommand{foo}
→ DeclareRobustCommandfoo
.
namegloballongouterdef{foo}
→ globallongouterdeffoo
.
nameexpandafter{foo}bar
→ expandafterfoobar
.
namelet{foo}=bar
→ letfoo=bar
.
namestring{foo}
→ stringfoo
.
namemeaning{foo}
→ meaningfoo
.
You can as well use such a macro for defining/calling macros whose names
contain spaces:
name{foo }
→ foo␣
.
namenewcommand{foo }
→ newcommandfoo␣
.
nameDeclareRobustCommand{foo }
→ DeclareRobustCommandfoo␣
.
namegloballongouterdef{foo }
→ globallongouterdeffoo␣
.
nameexpandafter{foo }bar
→ expandafterfoo␣bar
.
namelet{foo }=bar
→ letfoo␣=bar
.
namestring{foo }
→ stringfoo␣
.
namemeaning{foo }
→ meaningfoo␣
.
While gathering the name of the control sequence token in question, name
will trigger expansion of expandable tokens :
defGetCommandName{FooBar}
namenewcommand{GetCommandName}[1]{small I did it!!!}
→ newcommandFooBar[1]{small I did it!!!}
defGetCommandName{CommandNamePartACommandNamePartB}
defCommandNamePartA{Ba}
defCommandNamePartB{rInnerCommandNamePart o}
defInnerCommandNamePart{Fo}
namenewcommand{GetCommandName}{small I did it again!!!}
→ newcommandBarFoo{small I did it again!!!}
You can also nest the calls to name
:
Example 1:
namenameexpandafter{f o o }{b a r }
Processing the first name
yields:
nameexpandafterf␣o␣o␣{b a r }
.
Processing the second name
yields:
expandafterf␣o␣o␣b␣a␣r␣
.
(Analogously: namenamelet{f o o }={b a r }
→ letf␣o␣o␣=b␣a␣r␣
.)
Example 2:
namenamenameexpandafterexpandafterexpandafter{f o o }expandafter{b a r }{c r a z y }
Processing the first name
yields:
namenameexpandafterexpandafterexpandafterf␣o␣o␣expandafter{b a r }{c r a z y }
.
Processing the second name
yields:
nameexpandafterexpandafterexpandafterf␣o␣o␣expandafterb␣a␣r␣{c r a z y }
.
Processing the third name
yields:
expandafterexpandafterexpandafterf␣o␣o␣expandafterb␣a␣r␣c␣r␣a␣z␣y␣
.
Example 3:
In expansion contexts you can use romannumeral
-expansion in order to keep things going.
romannumeralnamenamename0 expandafterexpandafterexpandafter{f o o }expandafter{b a r }{c r a z y }
romannumeral
keeps expanding until it has found some number. In the end it will find the number0
while with non-positive numbers romannumeral
will not deliver any token:
%romannumneral-expansion in progress
namenamename0 expandafterexpandafterexpandafter{f o o }expandafter{b a r }{c r a z y }
Processing the first name
yields:
%romannumneral-expansion in progress
namename0 expandafterexpandafterexpandafterf␣o␣o␣expandafter{b a r }{c r a z y }
.
Processing the second name
yields:
%romannumneral-expansion in progress
name0 expandafterexpandafterexpandafterf␣o␣o␣expandafterb␣a␣r␣{c r a z y }
.
Processing the third name
yields:
%romannumneral-expansion in progress
0 expandafterexpandafterexpandafterf␣o␣o␣expandafterb␣a␣r␣c␣r␣a␣z␣y␣
.
Now romannumeral
finds the number 0
. Therefore romannumeral
-expansion gets aborted and romannumeral
won't deliver any token:
expandafterexpandafterexpandafterf␣o␣o␣expandafterb␣a␣r␣c␣r␣a␣z␣y␣
.
Be aware that name
internally applies csname
while
expansion of expandable tokens takes place while
csname
during its search for the matchingendcsname
gathers the character tokens that form the name of the control sequence token in question.applying
csname
as a side effect yields assigning the control sequence in question the meaning of therelax
-primitive in case the control sequence in question was undefined before applyingcsname
. That assignment will be restricted to the current scope even if theglobaldefs
-parameter had a positive value at the time of applyingcsname
.
%%errorcontextlines=1000
documentclass[a4paper]{article}
usepackage{textcomp}%
parindent=0cm
parskip=medskipamount
makeatletter
newcommandname{}%
longdefname#1#{romannumeral0UD@innername{#1}}%
newcommandUD@innername[2]{%
expandafterUD@exchangeexpandafter{csname#2endcsname}{ #1}%
}%
newcommandUD@exchange[2]{#2#1}%
makeatother
namenewcommand{foo}[2]{%
Control sequence whose name does not contain any space.\
Argument 1: textit{textlangle#1textrangle}\
Argument 2: textit{textlangle#2textrangle}
}%
namenewcommand{foo }[2]{%
Control sequence whose name has a trailing space.\
Argument 1: textit{textlangle#1textrangle}\
Argument 2: textit{textlangle#2textrangle}
}%
namenewcommand{ f o o }[2]{%
Control sequence whose name is interspersed with spaces.\
Argument 1: textit{textlangle#1textrangle}\
Argument 2: textit{textlangle#2textrangle}
}%
newcommand*GetCommandName{CommandNamePartACommandNamePartB}
newcommand*CommandNamePartA{Ba}
newcommand*CommandNamePartB{rInnerCommandNamePart o}
newcommand*InnerCommandNamePart{Fo}
namenewcommand{GetCommandName}{small I did it again!!!}
begin{document}
name{foo}{Arg 1}{Arg 2}
name{foo }{Arg 1}{Arg 2}
name{ f o o }{Arg 1}{Arg 2}
Nesting texttt{stringname}:
nameexpandafternewcommandexpandafter*expandafter{C o N f u SiO n}expandafter{%
romannumeralnamenamename0 %
expandafterexpandafterexpandafter{F O O}expandafter{B A R}{C R A Z Y}%
}%
texttt{namestring{C o N f u SiO n} is namemeaning{C o N f u SiO n}}%
\
Playing around with expandable tokens:
texttt{namestring{GetCommandName}:}
texttt{namemeaning{GetCommandName}}
name{GetCommandName}%
Playing around with grouping:
%Be aware that texttt itself opens up a new scope for typesetting its argument.
%globaldefs=1relax
texttt{%
begingroupnamestring{w e i r d } is nameendgroupmeaning{w e i r d }%
}%
texttt{%
namestring{w e i r d } is namemeaning{w e i r d }%
}%
end{document}
Why do you havenewcommandname{}%
in front? Just to test for duplicate commands?
– elika kohen
Mar 3 at 16:46
Yes. To test for duplicate commands and to be informed about it via the " command-already-defined"-error-message that comes fromnewcommand
in case of the command in question already being defined.
– Ulrich Diez
Mar 3 at 17:05
add a comment |
I (with slight modifications) quote my answer to the question Define a control sequence after that a space matters as it seems to apply to your question as well:
By applying the #{
-notation, you can define macros whose last argument is delimited by an opening brace. Unlike with other argument delimiters that get removed when gathering arguments, TeX will leave a delimiting opening brace in place.
(Actually the mechanism isn't restricted to opening brace character tokens. You can use any token whose category code is 1 at definition time. Could as well be #WeIrd
after letWeIrd={
.)
Delimited arguments can be empty.
Therefore for obtaining a control sequence token from a set of tokens that expands to a set of character tokens which forms the name of the control sequence token in question both for defining and for calling that control sequence token, you can (by applying the #{
-notation) invent a single control sequence name
which processes a brace delimited argument trailed by an undelimited argument (which is nested in braces). After having TeX fetch the arguments, you can have TeX whirl them around and apply csname..endcsname
to the argument supplied inside braces. The name of the control sequence token in question can contain space tokens as well.
makeatletter
%
newcommandname{}%
longdefname#1#{UD@innername{#1}}%
%
newcommandUD@innername[2]{%
expandafterUD@exchangeexpandafter{csname#2endcsname}{#1}%
}%
%
newcommandUD@exchange[2]{#2#1}%
%
makeatother
name foo{bar}
→ expansion step 1:UD@innername{foo}{bar}
→ expansion step 2:expandafterUD@exchangeexpandafter{csname barendcsname}{foo}
→ expansion step 3:UD@exchange{bar}{foo}
→ expansion step 4:foobar
.
In expansion contexts you would need four expandafter
-chains for obtaining the result.
As romannumeral
does not produce any token when encountering a non-positive number, you can add a bit of romannumeral
-expansion in order to reduce the amount of expandafter
-chains.
Either do romannumeralname0 foo{bar}
. This way only one expandafter
-chain hitting the romannumeral
-token is needed.
Or have the romannumeral
-expansion "hardcoded" within the definition—this way two expandafter
-chains are needed. The first one for obtaining the topl-level-expansion of name
. The second one for inducing romannumeral
-expansion.
makeatletter
%
newcommandname{}%
longdefname#1#{romannumeral0UD@innername{#1}}%
%
newcommandUD@innername[2]{%
expandafterUD@exchangeexpandafter{csname#2endcsname}{ #1}%
}%
%
newcommandUD@exchange[2]{#2#1}%
%
makeatother
With such a macro you are not bound to specific definition commands:
name{foo}
→ foo
. (←This is the way in which you do not define but just call/use control-sequences by means of name
.)
namenewcommand{foo}
→ newcommandfoo
.
nameDeclareRobustCommand{foo}
→ DeclareRobustCommandfoo
.
namegloballongouterdef{foo}
→ globallongouterdeffoo
.
nameexpandafter{foo}bar
→ expandafterfoobar
.
namelet{foo}=bar
→ letfoo=bar
.
namestring{foo}
→ stringfoo
.
namemeaning{foo}
→ meaningfoo
.
You can as well use such a macro for defining/calling macros whose names
contain spaces:
name{foo }
→ foo␣
.
namenewcommand{foo }
→ newcommandfoo␣
.
nameDeclareRobustCommand{foo }
→ DeclareRobustCommandfoo␣
.
namegloballongouterdef{foo }
→ globallongouterdeffoo␣
.
nameexpandafter{foo }bar
→ expandafterfoo␣bar
.
namelet{foo }=bar
→ letfoo␣=bar
.
namestring{foo }
→ stringfoo␣
.
namemeaning{foo }
→ meaningfoo␣
.
While gathering the name of the control sequence token in question, name
will trigger expansion of expandable tokens :
defGetCommandName{FooBar}
namenewcommand{GetCommandName}[1]{small I did it!!!}
→ newcommandFooBar[1]{small I did it!!!}
defGetCommandName{CommandNamePartACommandNamePartB}
defCommandNamePartA{Ba}
defCommandNamePartB{rInnerCommandNamePart o}
defInnerCommandNamePart{Fo}
namenewcommand{GetCommandName}{small I did it again!!!}
→ newcommandBarFoo{small I did it again!!!}
You can also nest the calls to name
:
Example 1:
namenameexpandafter{f o o }{b a r }
Processing the first name
yields:
nameexpandafterf␣o␣o␣{b a r }
.
Processing the second name
yields:
expandafterf␣o␣o␣b␣a␣r␣
.
(Analogously: namenamelet{f o o }={b a r }
→ letf␣o␣o␣=b␣a␣r␣
.)
Example 2:
namenamenameexpandafterexpandafterexpandafter{f o o }expandafter{b a r }{c r a z y }
Processing the first name
yields:
namenameexpandafterexpandafterexpandafterf␣o␣o␣expandafter{b a r }{c r a z y }
.
Processing the second name
yields:
nameexpandafterexpandafterexpandafterf␣o␣o␣expandafterb␣a␣r␣{c r a z y }
.
Processing the third name
yields:
expandafterexpandafterexpandafterf␣o␣o␣expandafterb␣a␣r␣c␣r␣a␣z␣y␣
.
Example 3:
In expansion contexts you can use romannumeral
-expansion in order to keep things going.
romannumeralnamenamename0 expandafterexpandafterexpandafter{f o o }expandafter{b a r }{c r a z y }
romannumeral
keeps expanding until it has found some number. In the end it will find the number0
while with non-positive numbers romannumeral
will not deliver any token:
%romannumneral-expansion in progress
namenamename0 expandafterexpandafterexpandafter{f o o }expandafter{b a r }{c r a z y }
Processing the first name
yields:
%romannumneral-expansion in progress
namename0 expandafterexpandafterexpandafterf␣o␣o␣expandafter{b a r }{c r a z y }
.
Processing the second name
yields:
%romannumneral-expansion in progress
name0 expandafterexpandafterexpandafterf␣o␣o␣expandafterb␣a␣r␣{c r a z y }
.
Processing the third name
yields:
%romannumneral-expansion in progress
0 expandafterexpandafterexpandafterf␣o␣o␣expandafterb␣a␣r␣c␣r␣a␣z␣y␣
.
Now romannumeral
finds the number 0
. Therefore romannumeral
-expansion gets aborted and romannumeral
won't deliver any token:
expandafterexpandafterexpandafterf␣o␣o␣expandafterb␣a␣r␣c␣r␣a␣z␣y␣
.
Be aware that name
internally applies csname
while
expansion of expandable tokens takes place while
csname
during its search for the matchingendcsname
gathers the character tokens that form the name of the control sequence token in question.applying
csname
as a side effect yields assigning the control sequence in question the meaning of therelax
-primitive in case the control sequence in question was undefined before applyingcsname
. That assignment will be restricted to the current scope even if theglobaldefs
-parameter had a positive value at the time of applyingcsname
.
%%errorcontextlines=1000
documentclass[a4paper]{article}
usepackage{textcomp}%
parindent=0cm
parskip=medskipamount
makeatletter
newcommandname{}%
longdefname#1#{romannumeral0UD@innername{#1}}%
newcommandUD@innername[2]{%
expandafterUD@exchangeexpandafter{csname#2endcsname}{ #1}%
}%
newcommandUD@exchange[2]{#2#1}%
makeatother
namenewcommand{foo}[2]{%
Control sequence whose name does not contain any space.\
Argument 1: textit{textlangle#1textrangle}\
Argument 2: textit{textlangle#2textrangle}
}%
namenewcommand{foo }[2]{%
Control sequence whose name has a trailing space.\
Argument 1: textit{textlangle#1textrangle}\
Argument 2: textit{textlangle#2textrangle}
}%
namenewcommand{ f o o }[2]{%
Control sequence whose name is interspersed with spaces.\
Argument 1: textit{textlangle#1textrangle}\
Argument 2: textit{textlangle#2textrangle}
}%
newcommand*GetCommandName{CommandNamePartACommandNamePartB}
newcommand*CommandNamePartA{Ba}
newcommand*CommandNamePartB{rInnerCommandNamePart o}
newcommand*InnerCommandNamePart{Fo}
namenewcommand{GetCommandName}{small I did it again!!!}
begin{document}
name{foo}{Arg 1}{Arg 2}
name{foo }{Arg 1}{Arg 2}
name{ f o o }{Arg 1}{Arg 2}
Nesting texttt{stringname}:
nameexpandafternewcommandexpandafter*expandafter{C o N f u SiO n}expandafter{%
romannumeralnamenamename0 %
expandafterexpandafterexpandafter{F O O}expandafter{B A R}{C R A Z Y}%
}%
texttt{namestring{C o N f u SiO n} is namemeaning{C o N f u SiO n}}%
\
Playing around with expandable tokens:
texttt{namestring{GetCommandName}:}
texttt{namemeaning{GetCommandName}}
name{GetCommandName}%
Playing around with grouping:
%Be aware that texttt itself opens up a new scope for typesetting its argument.
%globaldefs=1relax
texttt{%
begingroupnamestring{w e i r d } is nameendgroupmeaning{w e i r d }%
}%
texttt{%
namestring{w e i r d } is namemeaning{w e i r d }%
}%
end{document}
Why do you havenewcommandname{}%
in front? Just to test for duplicate commands?
– elika kohen
Mar 3 at 16:46
Yes. To test for duplicate commands and to be informed about it via the " command-already-defined"-error-message that comes fromnewcommand
in case of the command in question already being defined.
– Ulrich Diez
Mar 3 at 17:05
add a comment |
I (with slight modifications) quote my answer to the question Define a control sequence after that a space matters as it seems to apply to your question as well:
By applying the #{
-notation, you can define macros whose last argument is delimited by an opening brace. Unlike with other argument delimiters that get removed when gathering arguments, TeX will leave a delimiting opening brace in place.
(Actually the mechanism isn't restricted to opening brace character tokens. You can use any token whose category code is 1 at definition time. Could as well be #WeIrd
after letWeIrd={
.)
Delimited arguments can be empty.
Therefore for obtaining a control sequence token from a set of tokens that expands to a set of character tokens which forms the name of the control sequence token in question both for defining and for calling that control sequence token, you can (by applying the #{
-notation) invent a single control sequence name
which processes a brace delimited argument trailed by an undelimited argument (which is nested in braces). After having TeX fetch the arguments, you can have TeX whirl them around and apply csname..endcsname
to the argument supplied inside braces. The name of the control sequence token in question can contain space tokens as well.
makeatletter
%
newcommandname{}%
longdefname#1#{UD@innername{#1}}%
%
newcommandUD@innername[2]{%
expandafterUD@exchangeexpandafter{csname#2endcsname}{#1}%
}%
%
newcommandUD@exchange[2]{#2#1}%
%
makeatother
name foo{bar}
→ expansion step 1:UD@innername{foo}{bar}
→ expansion step 2:expandafterUD@exchangeexpandafter{csname barendcsname}{foo}
→ expansion step 3:UD@exchange{bar}{foo}
→ expansion step 4:foobar
.
In expansion contexts you would need four expandafter
-chains for obtaining the result.
As romannumeral
does not produce any token when encountering a non-positive number, you can add a bit of romannumeral
-expansion in order to reduce the amount of expandafter
-chains.
Either do romannumeralname0 foo{bar}
. This way only one expandafter
-chain hitting the romannumeral
-token is needed.
Or have the romannumeral
-expansion "hardcoded" within the definition—this way two expandafter
-chains are needed. The first one for obtaining the topl-level-expansion of name
. The second one for inducing romannumeral
-expansion.
makeatletter
%
newcommandname{}%
longdefname#1#{romannumeral0UD@innername{#1}}%
%
newcommandUD@innername[2]{%
expandafterUD@exchangeexpandafter{csname#2endcsname}{ #1}%
}%
%
newcommandUD@exchange[2]{#2#1}%
%
makeatother
With such a macro you are not bound to specific definition commands:
name{foo}
→ foo
. (←This is the way in which you do not define but just call/use control-sequences by means of name
.)
namenewcommand{foo}
→ newcommandfoo
.
nameDeclareRobustCommand{foo}
→ DeclareRobustCommandfoo
.
namegloballongouterdef{foo}
→ globallongouterdeffoo
.
nameexpandafter{foo}bar
→ expandafterfoobar
.
namelet{foo}=bar
→ letfoo=bar
.
namestring{foo}
→ stringfoo
.
namemeaning{foo}
→ meaningfoo
.
You can as well use such a macro for defining/calling macros whose names
contain spaces:
name{foo }
→ foo␣
.
namenewcommand{foo }
→ newcommandfoo␣
.
nameDeclareRobustCommand{foo }
→ DeclareRobustCommandfoo␣
.
namegloballongouterdef{foo }
→ globallongouterdeffoo␣
.
nameexpandafter{foo }bar
→ expandafterfoo␣bar
.
namelet{foo }=bar
→ letfoo␣=bar
.
namestring{foo }
→ stringfoo␣
.
namemeaning{foo }
→ meaningfoo␣
.
While gathering the name of the control sequence token in question, name
will trigger expansion of expandable tokens :
defGetCommandName{FooBar}
namenewcommand{GetCommandName}[1]{small I did it!!!}
→ newcommandFooBar[1]{small I did it!!!}
defGetCommandName{CommandNamePartACommandNamePartB}
defCommandNamePartA{Ba}
defCommandNamePartB{rInnerCommandNamePart o}
defInnerCommandNamePart{Fo}
namenewcommand{GetCommandName}{small I did it again!!!}
→ newcommandBarFoo{small I did it again!!!}
You can also nest the calls to name
:
Example 1:
namenameexpandafter{f o o }{b a r }
Processing the first name
yields:
nameexpandafterf␣o␣o␣{b a r }
.
Processing the second name
yields:
expandafterf␣o␣o␣b␣a␣r␣
.
(Analogously: namenamelet{f o o }={b a r }
→ letf␣o␣o␣=b␣a␣r␣
.)
Example 2:
namenamenameexpandafterexpandafterexpandafter{f o o }expandafter{b a r }{c r a z y }
Processing the first name
yields:
namenameexpandafterexpandafterexpandafterf␣o␣o␣expandafter{b a r }{c r a z y }
.
Processing the second name
yields:
nameexpandafterexpandafterexpandafterf␣o␣o␣expandafterb␣a␣r␣{c r a z y }
.
Processing the third name
yields:
expandafterexpandafterexpandafterf␣o␣o␣expandafterb␣a␣r␣c␣r␣a␣z␣y␣
.
Example 3:
In expansion contexts you can use romannumeral
-expansion in order to keep things going.
romannumeralnamenamename0 expandafterexpandafterexpandafter{f o o }expandafter{b a r }{c r a z y }
romannumeral
keeps expanding until it has found some number. In the end it will find the number0
while with non-positive numbers romannumeral
will not deliver any token:
%romannumneral-expansion in progress
namenamename0 expandafterexpandafterexpandafter{f o o }expandafter{b a r }{c r a z y }
Processing the first name
yields:
%romannumneral-expansion in progress
namename0 expandafterexpandafterexpandafterf␣o␣o␣expandafter{b a r }{c r a z y }
.
Processing the second name
yields:
%romannumneral-expansion in progress
name0 expandafterexpandafterexpandafterf␣o␣o␣expandafterb␣a␣r␣{c r a z y }
.
Processing the third name
yields:
%romannumneral-expansion in progress
0 expandafterexpandafterexpandafterf␣o␣o␣expandafterb␣a␣r␣c␣r␣a␣z␣y␣
.
Now romannumeral
finds the number 0
. Therefore romannumeral
-expansion gets aborted and romannumeral
won't deliver any token:
expandafterexpandafterexpandafterf␣o␣o␣expandafterb␣a␣r␣c␣r␣a␣z␣y␣
.
Be aware that name
internally applies csname
while
expansion of expandable tokens takes place while
csname
during its search for the matchingendcsname
gathers the character tokens that form the name of the control sequence token in question.applying
csname
as a side effect yields assigning the control sequence in question the meaning of therelax
-primitive in case the control sequence in question was undefined before applyingcsname
. That assignment will be restricted to the current scope even if theglobaldefs
-parameter had a positive value at the time of applyingcsname
.
%%errorcontextlines=1000
documentclass[a4paper]{article}
usepackage{textcomp}%
parindent=0cm
parskip=medskipamount
makeatletter
newcommandname{}%
longdefname#1#{romannumeral0UD@innername{#1}}%
newcommandUD@innername[2]{%
expandafterUD@exchangeexpandafter{csname#2endcsname}{ #1}%
}%
newcommandUD@exchange[2]{#2#1}%
makeatother
namenewcommand{foo}[2]{%
Control sequence whose name does not contain any space.\
Argument 1: textit{textlangle#1textrangle}\
Argument 2: textit{textlangle#2textrangle}
}%
namenewcommand{foo }[2]{%
Control sequence whose name has a trailing space.\
Argument 1: textit{textlangle#1textrangle}\
Argument 2: textit{textlangle#2textrangle}
}%
namenewcommand{ f o o }[2]{%
Control sequence whose name is interspersed with spaces.\
Argument 1: textit{textlangle#1textrangle}\
Argument 2: textit{textlangle#2textrangle}
}%
newcommand*GetCommandName{CommandNamePartACommandNamePartB}
newcommand*CommandNamePartA{Ba}
newcommand*CommandNamePartB{rInnerCommandNamePart o}
newcommand*InnerCommandNamePart{Fo}
namenewcommand{GetCommandName}{small I did it again!!!}
begin{document}
name{foo}{Arg 1}{Arg 2}
name{foo }{Arg 1}{Arg 2}
name{ f o o }{Arg 1}{Arg 2}
Nesting texttt{stringname}:
nameexpandafternewcommandexpandafter*expandafter{C o N f u SiO n}expandafter{%
romannumeralnamenamename0 %
expandafterexpandafterexpandafter{F O O}expandafter{B A R}{C R A Z Y}%
}%
texttt{namestring{C o N f u SiO n} is namemeaning{C o N f u SiO n}}%
\
Playing around with expandable tokens:
texttt{namestring{GetCommandName}:}
texttt{namemeaning{GetCommandName}}
name{GetCommandName}%
Playing around with grouping:
%Be aware that texttt itself opens up a new scope for typesetting its argument.
%globaldefs=1relax
texttt{%
begingroupnamestring{w e i r d } is nameendgroupmeaning{w e i r d }%
}%
texttt{%
namestring{w e i r d } is namemeaning{w e i r d }%
}%
end{document}
I (with slight modifications) quote my answer to the question Define a control sequence after that a space matters as it seems to apply to your question as well:
By applying the #{
-notation, you can define macros whose last argument is delimited by an opening brace. Unlike with other argument delimiters that get removed when gathering arguments, TeX will leave a delimiting opening brace in place.
(Actually the mechanism isn't restricted to opening brace character tokens. You can use any token whose category code is 1 at definition time. Could as well be #WeIrd
after letWeIrd={
.)
Delimited arguments can be empty.
Therefore for obtaining a control sequence token from a set of tokens that expands to a set of character tokens which forms the name of the control sequence token in question both for defining and for calling that control sequence token, you can (by applying the #{
-notation) invent a single control sequence name
which processes a brace delimited argument trailed by an undelimited argument (which is nested in braces). After having TeX fetch the arguments, you can have TeX whirl them around and apply csname..endcsname
to the argument supplied inside braces. The name of the control sequence token in question can contain space tokens as well.
makeatletter
%
newcommandname{}%
longdefname#1#{UD@innername{#1}}%
%
newcommandUD@innername[2]{%
expandafterUD@exchangeexpandafter{csname#2endcsname}{#1}%
}%
%
newcommandUD@exchange[2]{#2#1}%
%
makeatother
name foo{bar}
→ expansion step 1:UD@innername{foo}{bar}
→ expansion step 2:expandafterUD@exchangeexpandafter{csname barendcsname}{foo}
→ expansion step 3:UD@exchange{bar}{foo}
→ expansion step 4:foobar
.
In expansion contexts you would need four expandafter
-chains for obtaining the result.
As romannumeral
does not produce any token when encountering a non-positive number, you can add a bit of romannumeral
-expansion in order to reduce the amount of expandafter
-chains.
Either do romannumeralname0 foo{bar}
. This way only one expandafter
-chain hitting the romannumeral
-token is needed.
Or have the romannumeral
-expansion "hardcoded" within the definition—this way two expandafter
-chains are needed. The first one for obtaining the topl-level-expansion of name
. The second one for inducing romannumeral
-expansion.
makeatletter
%
newcommandname{}%
longdefname#1#{romannumeral0UD@innername{#1}}%
%
newcommandUD@innername[2]{%
expandafterUD@exchangeexpandafter{csname#2endcsname}{ #1}%
}%
%
newcommandUD@exchange[2]{#2#1}%
%
makeatother
With such a macro you are not bound to specific definition commands:
name{foo}
→ foo
. (←This is the way in which you do not define but just call/use control-sequences by means of name
.)
namenewcommand{foo}
→ newcommandfoo
.
nameDeclareRobustCommand{foo}
→ DeclareRobustCommandfoo
.
namegloballongouterdef{foo}
→ globallongouterdeffoo
.
nameexpandafter{foo}bar
→ expandafterfoobar
.
namelet{foo}=bar
→ letfoo=bar
.
namestring{foo}
→ stringfoo
.
namemeaning{foo}
→ meaningfoo
.
You can as well use such a macro for defining/calling macros whose names
contain spaces:
name{foo }
→ foo␣
.
namenewcommand{foo }
→ newcommandfoo␣
.
nameDeclareRobustCommand{foo }
→ DeclareRobustCommandfoo␣
.
namegloballongouterdef{foo }
→ globallongouterdeffoo␣
.
nameexpandafter{foo }bar
→ expandafterfoo␣bar
.
namelet{foo }=bar
→ letfoo␣=bar
.
namestring{foo }
→ stringfoo␣
.
namemeaning{foo }
→ meaningfoo␣
.
While gathering the name of the control sequence token in question, name
will trigger expansion of expandable tokens :
defGetCommandName{FooBar}
namenewcommand{GetCommandName}[1]{small I did it!!!}
→ newcommandFooBar[1]{small I did it!!!}
defGetCommandName{CommandNamePartACommandNamePartB}
defCommandNamePartA{Ba}
defCommandNamePartB{rInnerCommandNamePart o}
defInnerCommandNamePart{Fo}
namenewcommand{GetCommandName}{small I did it again!!!}
→ newcommandBarFoo{small I did it again!!!}
You can also nest the calls to name
:
Example 1:
namenameexpandafter{f o o }{b a r }
Processing the first name
yields:
nameexpandafterf␣o␣o␣{b a r }
.
Processing the second name
yields:
expandafterf␣o␣o␣b␣a␣r␣
.
(Analogously: namenamelet{f o o }={b a r }
→ letf␣o␣o␣=b␣a␣r␣
.)
Example 2:
namenamenameexpandafterexpandafterexpandafter{f o o }expandafter{b a r }{c r a z y }
Processing the first name
yields:
namenameexpandafterexpandafterexpandafterf␣o␣o␣expandafter{b a r }{c r a z y }
.
Processing the second name
yields:
nameexpandafterexpandafterexpandafterf␣o␣o␣expandafterb␣a␣r␣{c r a z y }
.
Processing the third name
yields:
expandafterexpandafterexpandafterf␣o␣o␣expandafterb␣a␣r␣c␣r␣a␣z␣y␣
.
Example 3:
In expansion contexts you can use romannumeral
-expansion in order to keep things going.
romannumeralnamenamename0 expandafterexpandafterexpandafter{f o o }expandafter{b a r }{c r a z y }
romannumeral
keeps expanding until it has found some number. In the end it will find the number0
while with non-positive numbers romannumeral
will not deliver any token:
%romannumneral-expansion in progress
namenamename0 expandafterexpandafterexpandafter{f o o }expandafter{b a r }{c r a z y }
Processing the first name
yields:
%romannumneral-expansion in progress
namename0 expandafterexpandafterexpandafterf␣o␣o␣expandafter{b a r }{c r a z y }
.
Processing the second name
yields:
%romannumneral-expansion in progress
name0 expandafterexpandafterexpandafterf␣o␣o␣expandafterb␣a␣r␣{c r a z y }
.
Processing the third name
yields:
%romannumneral-expansion in progress
0 expandafterexpandafterexpandafterf␣o␣o␣expandafterb␣a␣r␣c␣r␣a␣z␣y␣
.
Now romannumeral
finds the number 0
. Therefore romannumeral
-expansion gets aborted and romannumeral
won't deliver any token:
expandafterexpandafterexpandafterf␣o␣o␣expandafterb␣a␣r␣c␣r␣a␣z␣y␣
.
Be aware that name
internally applies csname
while
expansion of expandable tokens takes place while
csname
during its search for the matchingendcsname
gathers the character tokens that form the name of the control sequence token in question.applying
csname
as a side effect yields assigning the control sequence in question the meaning of therelax
-primitive in case the control sequence in question was undefined before applyingcsname
. That assignment will be restricted to the current scope even if theglobaldefs
-parameter had a positive value at the time of applyingcsname
.
%%errorcontextlines=1000
documentclass[a4paper]{article}
usepackage{textcomp}%
parindent=0cm
parskip=medskipamount
makeatletter
newcommandname{}%
longdefname#1#{romannumeral0UD@innername{#1}}%
newcommandUD@innername[2]{%
expandafterUD@exchangeexpandafter{csname#2endcsname}{ #1}%
}%
newcommandUD@exchange[2]{#2#1}%
makeatother
namenewcommand{foo}[2]{%
Control sequence whose name does not contain any space.\
Argument 1: textit{textlangle#1textrangle}\
Argument 2: textit{textlangle#2textrangle}
}%
namenewcommand{foo }[2]{%
Control sequence whose name has a trailing space.\
Argument 1: textit{textlangle#1textrangle}\
Argument 2: textit{textlangle#2textrangle}
}%
namenewcommand{ f o o }[2]{%
Control sequence whose name is interspersed with spaces.\
Argument 1: textit{textlangle#1textrangle}\
Argument 2: textit{textlangle#2textrangle}
}%
newcommand*GetCommandName{CommandNamePartACommandNamePartB}
newcommand*CommandNamePartA{Ba}
newcommand*CommandNamePartB{rInnerCommandNamePart o}
newcommand*InnerCommandNamePart{Fo}
namenewcommand{GetCommandName}{small I did it again!!!}
begin{document}
name{foo}{Arg 1}{Arg 2}
name{foo }{Arg 1}{Arg 2}
name{ f o o }{Arg 1}{Arg 2}
Nesting texttt{stringname}:
nameexpandafternewcommandexpandafter*expandafter{C o N f u SiO n}expandafter{%
romannumeralnamenamename0 %
expandafterexpandafterexpandafter{F O O}expandafter{B A R}{C R A Z Y}%
}%
texttt{namestring{C o N f u SiO n} is namemeaning{C o N f u SiO n}}%
\
Playing around with expandable tokens:
texttt{namestring{GetCommandName}:}
texttt{namemeaning{GetCommandName}}
name{GetCommandName}%
Playing around with grouping:
%Be aware that texttt itself opens up a new scope for typesetting its argument.
%globaldefs=1relax
texttt{%
begingroupnamestring{w e i r d } is nameendgroupmeaning{w e i r d }%
}%
texttt{%
namestring{w e i r d } is namemeaning{w e i r d }%
}%
end{document}
edited Mar 3 at 10:47
answered Mar 3 at 10:21
Ulrich DiezUlrich Diez
5,550620
5,550620
Why do you havenewcommandname{}%
in front? Just to test for duplicate commands?
– elika kohen
Mar 3 at 16:46
Yes. To test for duplicate commands and to be informed about it via the " command-already-defined"-error-message that comes fromnewcommand
in case of the command in question already being defined.
– Ulrich Diez
Mar 3 at 17:05
add a comment |
Why do you havenewcommandname{}%
in front? Just to test for duplicate commands?
– elika kohen
Mar 3 at 16:46
Yes. To test for duplicate commands and to be informed about it via the " command-already-defined"-error-message that comes fromnewcommand
in case of the command in question already being defined.
– Ulrich Diez
Mar 3 at 17:05
Why do you have
newcommandname{}%
in front? Just to test for duplicate commands?– elika kohen
Mar 3 at 16:46
Why do you have
newcommandname{}%
in front? Just to test for duplicate commands?– elika kohen
Mar 3 at 16:46
Yes. To test for duplicate commands and to be informed about it via the " command-already-defined"-error-message that comes from
newcommand
in case of the command in question already being defined.– Ulrich Diez
Mar 3 at 17:05
Yes. To test for duplicate commands and to be informed about it via the " command-already-defined"-error-message that comes from
newcommand
in case of the command in question already being defined.– Ulrich Diez
Mar 3 at 17:05
add a comment |
LaTeX already has a command form that takes the name of a command rather than the csname token:
@namedef{GetCommandName}{small I did it!!!}
should do what you want this is simply expandafterdefcsnameGetCommandNameendcsname{..}
Funny, I was trying to do it with @nameuse. It would not be possible to go back and modify pre-existingnewcommand
s with this, (let alone deal with the cat code nonsense). But, I think your idea of overriding the pre-existingnewcommand
would be ideal - if I could keep the code very similar to what it already is. The problem, of course, is I have no idea what@star@or@long new@command
actually does, to incorporate what you are talking about. Thank you very much, though. It puts me on the right track.
– elika kohen
Mar 3 at 9:12
@elikakohen If you like@nameuse
, you can do something likeexpandafterexpandafterexpandafternewcommand@nameuse{GetCommandName}{small I did it!!!}
. The 1stexpandafter
"hits" the 3rdexpandafter
which "hits"@nameuse
.@nameuse
deliverscsnameGetCommandNameendcsname
and vanishes. Then the 3rdexpandafter
's work is done and it vanishes. Then the 1stexpandafter
's work is done and it vanishes. Now the 2ndexpandafter
hits thecsname
whose expansion in turn yields the control-sequence-token. Whencsname
is done, the 2ndexpandafter
vanishes. :-) :-)
– Ulrich Diez
Mar 3 at 11:05
@UlrichDiez - Oh no, not at all. I was just trying to figure out@nameuse
and couldn't get it to work, (hence this question). Based on your comment, though, I am certainly glad I didn't pursue that route! Thanks!
– elika kohen
Mar 3 at 11:06
@elikakohen You haven't said what your actual use case is but I can't think of any way that you could redefinenewcommand
to do this that would not break any existing package that is using that command.
– David Carlisle
Mar 3 at 20:35
add a comment |
LaTeX already has a command form that takes the name of a command rather than the csname token:
@namedef{GetCommandName}{small I did it!!!}
should do what you want this is simply expandafterdefcsnameGetCommandNameendcsname{..}
Funny, I was trying to do it with @nameuse. It would not be possible to go back and modify pre-existingnewcommand
s with this, (let alone deal with the cat code nonsense). But, I think your idea of overriding the pre-existingnewcommand
would be ideal - if I could keep the code very similar to what it already is. The problem, of course, is I have no idea what@star@or@long new@command
actually does, to incorporate what you are talking about. Thank you very much, though. It puts me on the right track.
– elika kohen
Mar 3 at 9:12
@elikakohen If you like@nameuse
, you can do something likeexpandafterexpandafterexpandafternewcommand@nameuse{GetCommandName}{small I did it!!!}
. The 1stexpandafter
"hits" the 3rdexpandafter
which "hits"@nameuse
.@nameuse
deliverscsnameGetCommandNameendcsname
and vanishes. Then the 3rdexpandafter
's work is done and it vanishes. Then the 1stexpandafter
's work is done and it vanishes. Now the 2ndexpandafter
hits thecsname
whose expansion in turn yields the control-sequence-token. Whencsname
is done, the 2ndexpandafter
vanishes. :-) :-)
– Ulrich Diez
Mar 3 at 11:05
@UlrichDiez - Oh no, not at all. I was just trying to figure out@nameuse
and couldn't get it to work, (hence this question). Based on your comment, though, I am certainly glad I didn't pursue that route! Thanks!
– elika kohen
Mar 3 at 11:06
@elikakohen You haven't said what your actual use case is but I can't think of any way that you could redefinenewcommand
to do this that would not break any existing package that is using that command.
– David Carlisle
Mar 3 at 20:35
add a comment |
LaTeX already has a command form that takes the name of a command rather than the csname token:
@namedef{GetCommandName}{small I did it!!!}
should do what you want this is simply expandafterdefcsnameGetCommandNameendcsname{..}
LaTeX already has a command form that takes the name of a command rather than the csname token:
@namedef{GetCommandName}{small I did it!!!}
should do what you want this is simply expandafterdefcsnameGetCommandNameendcsname{..}
answered Mar 3 at 8:58
David CarlisleDavid Carlisle
497k4111441892
497k4111441892
Funny, I was trying to do it with @nameuse. It would not be possible to go back and modify pre-existingnewcommand
s with this, (let alone deal with the cat code nonsense). But, I think your idea of overriding the pre-existingnewcommand
would be ideal - if I could keep the code very similar to what it already is. The problem, of course, is I have no idea what@star@or@long new@command
actually does, to incorporate what you are talking about. Thank you very much, though. It puts me on the right track.
– elika kohen
Mar 3 at 9:12
@elikakohen If you like@nameuse
, you can do something likeexpandafterexpandafterexpandafternewcommand@nameuse{GetCommandName}{small I did it!!!}
. The 1stexpandafter
"hits" the 3rdexpandafter
which "hits"@nameuse
.@nameuse
deliverscsnameGetCommandNameendcsname
and vanishes. Then the 3rdexpandafter
's work is done and it vanishes. Then the 1stexpandafter
's work is done and it vanishes. Now the 2ndexpandafter
hits thecsname
whose expansion in turn yields the control-sequence-token. Whencsname
is done, the 2ndexpandafter
vanishes. :-) :-)
– Ulrich Diez
Mar 3 at 11:05
@UlrichDiez - Oh no, not at all. I was just trying to figure out@nameuse
and couldn't get it to work, (hence this question). Based on your comment, though, I am certainly glad I didn't pursue that route! Thanks!
– elika kohen
Mar 3 at 11:06
@elikakohen You haven't said what your actual use case is but I can't think of any way that you could redefinenewcommand
to do this that would not break any existing package that is using that command.
– David Carlisle
Mar 3 at 20:35
add a comment |
Funny, I was trying to do it with @nameuse. It would not be possible to go back and modify pre-existingnewcommand
s with this, (let alone deal with the cat code nonsense). But, I think your idea of overriding the pre-existingnewcommand
would be ideal - if I could keep the code very similar to what it already is. The problem, of course, is I have no idea what@star@or@long new@command
actually does, to incorporate what you are talking about. Thank you very much, though. It puts me on the right track.
– elika kohen
Mar 3 at 9:12
@elikakohen If you like@nameuse
, you can do something likeexpandafterexpandafterexpandafternewcommand@nameuse{GetCommandName}{small I did it!!!}
. The 1stexpandafter
"hits" the 3rdexpandafter
which "hits"@nameuse
.@nameuse
deliverscsnameGetCommandNameendcsname
and vanishes. Then the 3rdexpandafter
's work is done and it vanishes. Then the 1stexpandafter
's work is done and it vanishes. Now the 2ndexpandafter
hits thecsname
whose expansion in turn yields the control-sequence-token. Whencsname
is done, the 2ndexpandafter
vanishes. :-) :-)
– Ulrich Diez
Mar 3 at 11:05
@UlrichDiez - Oh no, not at all. I was just trying to figure out@nameuse
and couldn't get it to work, (hence this question). Based on your comment, though, I am certainly glad I didn't pursue that route! Thanks!
– elika kohen
Mar 3 at 11:06
@elikakohen You haven't said what your actual use case is but I can't think of any way that you could redefinenewcommand
to do this that would not break any existing package that is using that command.
– David Carlisle
Mar 3 at 20:35
Funny, I was trying to do it with @nameuse. It would not be possible to go back and modify pre-existing
newcommand
s with this, (let alone deal with the cat code nonsense). But, I think your idea of overriding the pre-existing newcommand
would be ideal - if I could keep the code very similar to what it already is. The problem, of course, is I have no idea what @star@or@long new@command
actually does, to incorporate what you are talking about. Thank you very much, though. It puts me on the right track.– elika kohen
Mar 3 at 9:12
Funny, I was trying to do it with @nameuse. It would not be possible to go back and modify pre-existing
newcommand
s with this, (let alone deal with the cat code nonsense). But, I think your idea of overriding the pre-existing newcommand
would be ideal - if I could keep the code very similar to what it already is. The problem, of course, is I have no idea what @star@or@long new@command
actually does, to incorporate what you are talking about. Thank you very much, though. It puts me on the right track.– elika kohen
Mar 3 at 9:12
@elikakohen If you like
@nameuse
, you can do something like expandafterexpandafterexpandafternewcommand@nameuse{GetCommandName}{small I did it!!!}
. The 1st expandafter
"hits" the 3rd expandafter
which "hits" @nameuse
. @nameuse
delivers csnameGetCommandNameendcsname
and vanishes. Then the 3rd expandafter
's work is done and it vanishes. Then the 1st expandafter
's work is done and it vanishes. Now the 2nd expandafter
hits the csname
whose expansion in turn yields the control-sequence-token. When csname
is done, the 2nd expandafter
vanishes. :-) :-)– Ulrich Diez
Mar 3 at 11:05
@elikakohen If you like
@nameuse
, you can do something like expandafterexpandafterexpandafternewcommand@nameuse{GetCommandName}{small I did it!!!}
. The 1st expandafter
"hits" the 3rd expandafter
which "hits" @nameuse
. @nameuse
delivers csnameGetCommandNameendcsname
and vanishes. Then the 3rd expandafter
's work is done and it vanishes. Then the 1st expandafter
's work is done and it vanishes. Now the 2nd expandafter
hits the csname
whose expansion in turn yields the control-sequence-token. When csname
is done, the 2nd expandafter
vanishes. :-) :-)– Ulrich Diez
Mar 3 at 11:05
@UlrichDiez - Oh no, not at all. I was just trying to figure out
@nameuse
and couldn't get it to work, (hence this question). Based on your comment, though, I am certainly glad I didn't pursue that route! Thanks!– elika kohen
Mar 3 at 11:06
@UlrichDiez - Oh no, not at all. I was just trying to figure out
@nameuse
and couldn't get it to work, (hence this question). Based on your comment, though, I am certainly glad I didn't pursue that route! Thanks!– elika kohen
Mar 3 at 11:06
@elikakohen You haven't said what your actual use case is but I can't think of any way that you could redefine
newcommand
to do this that would not break any existing package that is using that command.– David Carlisle
Mar 3 at 20:35
@elikakohen You haven't said what your actual use case is but I can't think of any way that you could redefine
newcommand
to do this that would not break any existing package that is using that command.– David Carlisle
Mar 3 at 20:35
add a comment |
Thanks for contributing an answer to TeX - LaTeX Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2ftex.stackexchange.com%2fquestions%2f477516%2fhow-to-implement-expandbefore-similarly-to-expandafter%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
you would need to redefine
newcommand
to doexpandafteroldnewcommand
whereoldnewcommand
is the saved original version, but this would almost certainly break multiple packages, so I'd rather strongly advise that you don't do it.– David Carlisle
Mar 3 at 8:56
Did you try
expandafternewcommandexpandafter{GetCommandName}
?– AndiW
Mar 3 at 8:58
csname
itself triggers expansion of expandable tokens while during its search for the matchingendcsname
gathering the character-tokens that form the name of the control-sequence-token in question. ThereforecsnameGetCommandNameendcsname
might work out as expansion ofGetCommandName
will be triggered bycsname
. All you might need to do is triggeringcsname
-expansion beforenewcommand
gets carried out:expandafternewcommandexpandafter{csname GetCommandName endcsname}[1]{small I did it!!!}
– Ulrich Diez
Mar 3 at 9:46
1
Perhaps you want to look at tex.stackexchange.com/a/317094/4427
– egreg
Mar 3 at 10:05