How to properly smuggle (with or even without TikZ)?
Clash Royale CLAN TAG#URR8PPP
I am seeking to find something that allows me to "broadcast" macros outside a group. Concrete examples include paths and scopes in tizpictures
. Here is an M(N)WE.
documentclass[tikz,border=3.14mm]standalone
usetikzlibrarycalc
makeatletter
letsmuggleoutonepgfmath@smuggleone
makeatother
begindocument
begintikzpicture[globalize/.code n args=2xdef#2#1,
localize/.code n args=2pgfmathsetmacro#2#1typeout#2
%smuggleoutone#1
]
beginscope[local bounding box=extra]
path let p1=($(2,1)-(0,0)$),n1=atan2(y1,x1) in
pgfextraxdefmyanglen1;
node at (1,0) myangle;
endscope
node[anchor=south] at (extra.north) using verb;
%
beginscope[local bounding box=globalize,xshift=3cm]
path let p1=($(2,1)-(0,0)$),n1=atan2(y1,x1) in
[globalize=n1myangle];
node at (1,0) myangle;
endscope
node[anchor=south] at (globalize.north) using textttglobalize;
%
xdefmyangle7
beginscope[local bounding box=localize,xshift=6cm]
path let p1=($(2,1)-(0,0)$),n1=atan2(y1,x1) in
[localize=n1myangle];
node at (1,0) myangle;
endscope
node[anchor=south] at (localize.north) attempt to smuggle;
%
endtikzpicture
enddocument
The two options on the left do partly what I am seeking to do, namely broadcast the macro myangle
outside the path. However, they do it at the expense of making myangle
global. TikZ has some internal commands that may allow one to avoid this, and to just smuggle the macro outside the path. Specifically, @DavidCarlisle suggested in the chat to use pgfmath@smuggleone
. However, my above attempts failed, i.e. if I uncomment
%smuggleoutone#1
the code produces errors.
QUESTION: Can one smuggle the macro outside the group without making it global?
"BONUS": Of course it would be great if there was an explanation what all the smuggle commands do.
"BONUUUUS": Conceivably these methods may be useful independently of TikZ, so if there is a way not to make them depend on TikZ being loaded, this would be great, but is certainly not a requirement.
tikz-pgf tex-core
add a comment |
I am seeking to find something that allows me to "broadcast" macros outside a group. Concrete examples include paths and scopes in tizpictures
. Here is an M(N)WE.
documentclass[tikz,border=3.14mm]standalone
usetikzlibrarycalc
makeatletter
letsmuggleoutonepgfmath@smuggleone
makeatother
begindocument
begintikzpicture[globalize/.code n args=2xdef#2#1,
localize/.code n args=2pgfmathsetmacro#2#1typeout#2
%smuggleoutone#1
]
beginscope[local bounding box=extra]
path let p1=($(2,1)-(0,0)$),n1=atan2(y1,x1) in
pgfextraxdefmyanglen1;
node at (1,0) myangle;
endscope
node[anchor=south] at (extra.north) using verb;
%
beginscope[local bounding box=globalize,xshift=3cm]
path let p1=($(2,1)-(0,0)$),n1=atan2(y1,x1) in
[globalize=n1myangle];
node at (1,0) myangle;
endscope
node[anchor=south] at (globalize.north) using textttglobalize;
%
xdefmyangle7
beginscope[local bounding box=localize,xshift=6cm]
path let p1=($(2,1)-(0,0)$),n1=atan2(y1,x1) in
[localize=n1myangle];
node at (1,0) myangle;
endscope
node[anchor=south] at (localize.north) attempt to smuggle;
%
endtikzpicture
enddocument
The two options on the left do partly what I am seeking to do, namely broadcast the macro myangle
outside the path. However, they do it at the expense of making myangle
global. TikZ has some internal commands that may allow one to avoid this, and to just smuggle the macro outside the path. Specifically, @DavidCarlisle suggested in the chat to use pgfmath@smuggleone
. However, my above attempts failed, i.e. if I uncomment
%smuggleoutone#1
the code produces errors.
QUESTION: Can one smuggle the macro outside the group without making it global?
"BONUS": Of course it would be great if there was an explanation what all the smuggle commands do.
"BONUUUUS": Conceivably these methods may be useful independently of TikZ, so if there is a way not to make them depend on TikZ being loaded, this would be great, but is certainly not a requirement.
tikz-pgf tex-core
add a comment |
I am seeking to find something that allows me to "broadcast" macros outside a group. Concrete examples include paths and scopes in tizpictures
. Here is an M(N)WE.
documentclass[tikz,border=3.14mm]standalone
usetikzlibrarycalc
makeatletter
letsmuggleoutonepgfmath@smuggleone
makeatother
begindocument
begintikzpicture[globalize/.code n args=2xdef#2#1,
localize/.code n args=2pgfmathsetmacro#2#1typeout#2
%smuggleoutone#1
]
beginscope[local bounding box=extra]
path let p1=($(2,1)-(0,0)$),n1=atan2(y1,x1) in
pgfextraxdefmyanglen1;
node at (1,0) myangle;
endscope
node[anchor=south] at (extra.north) using verb;
%
beginscope[local bounding box=globalize,xshift=3cm]
path let p1=($(2,1)-(0,0)$),n1=atan2(y1,x1) in
[globalize=n1myangle];
node at (1,0) myangle;
endscope
node[anchor=south] at (globalize.north) using textttglobalize;
%
xdefmyangle7
beginscope[local bounding box=localize,xshift=6cm]
path let p1=($(2,1)-(0,0)$),n1=atan2(y1,x1) in
[localize=n1myangle];
node at (1,0) myangle;
endscope
node[anchor=south] at (localize.north) attempt to smuggle;
%
endtikzpicture
enddocument
The two options on the left do partly what I am seeking to do, namely broadcast the macro myangle
outside the path. However, they do it at the expense of making myangle
global. TikZ has some internal commands that may allow one to avoid this, and to just smuggle the macro outside the path. Specifically, @DavidCarlisle suggested in the chat to use pgfmath@smuggleone
. However, my above attempts failed, i.e. if I uncomment
%smuggleoutone#1
the code produces errors.
QUESTION: Can one smuggle the macro outside the group without making it global?
"BONUS": Of course it would be great if there was an explanation what all the smuggle commands do.
"BONUUUUS": Conceivably these methods may be useful independently of TikZ, so if there is a way not to make them depend on TikZ being loaded, this would be great, but is certainly not a requirement.
tikz-pgf tex-core
I am seeking to find something that allows me to "broadcast" macros outside a group. Concrete examples include paths and scopes in tizpictures
. Here is an M(N)WE.
documentclass[tikz,border=3.14mm]standalone
usetikzlibrarycalc
makeatletter
letsmuggleoutonepgfmath@smuggleone
makeatother
begindocument
begintikzpicture[globalize/.code n args=2xdef#2#1,
localize/.code n args=2pgfmathsetmacro#2#1typeout#2
%smuggleoutone#1
]
beginscope[local bounding box=extra]
path let p1=($(2,1)-(0,0)$),n1=atan2(y1,x1) in
pgfextraxdefmyanglen1;
node at (1,0) myangle;
endscope
node[anchor=south] at (extra.north) using verb;
%
beginscope[local bounding box=globalize,xshift=3cm]
path let p1=($(2,1)-(0,0)$),n1=atan2(y1,x1) in
[globalize=n1myangle];
node at (1,0) myangle;
endscope
node[anchor=south] at (globalize.north) using textttglobalize;
%
xdefmyangle7
beginscope[local bounding box=localize,xshift=6cm]
path let p1=($(2,1)-(0,0)$),n1=atan2(y1,x1) in
[localize=n1myangle];
node at (1,0) myangle;
endscope
node[anchor=south] at (localize.north) attempt to smuggle;
%
endtikzpicture
enddocument
The two options on the left do partly what I am seeking to do, namely broadcast the macro myangle
outside the path. However, they do it at the expense of making myangle
global. TikZ has some internal commands that may allow one to avoid this, and to just smuggle the macro outside the path. Specifically, @DavidCarlisle suggested in the chat to use pgfmath@smuggleone
. However, my above attempts failed, i.e. if I uncomment
%smuggleoutone#1
the code produces errors.
QUESTION: Can one smuggle the macro outside the group without making it global?
"BONUS": Of course it would be great if there was an explanation what all the smuggle commands do.
"BONUUUUS": Conceivably these methods may be useful independently of TikZ, so if there is a way not to make them depend on TikZ being loaded, this would be great, but is certainly not a requirement.
tikz-pgf tex-core
tikz-pgf tex-core
edited Jan 20 at 1:35
marmot
asked Jan 20 at 0:16
marmotmarmot
97.9k4113217
97.9k4113217
add a comment |
add a comment |
3 Answers
3
active
oldest
votes
You can "smuggle" definitions out of their group with the TeX primitive aftergroup
. I'll first explain what aftergroup
does, then give a possible definition of smuggleone
using aftergroup
and finally apply it to your MWE.
The short answer is that you could define smuggleone
(I've removed "out" from the name) as
newcountersmuggle
DeclareRobustCommandsmuggleone[1]%
stepcountersmuggle%
expandafterglobalexpandafterletcsname smuggle@arabicsmuggleendcsname#1%
aftergroupletaftergroup#1expandafteraftergroupcsname smuggle@arabicsmuggleendcsname
If you paste in this definition and replace smuggleoutone#1
by smuggleone#2
in your MWE it should work. (Note that you were passing the wrong argument to smuggleoutone
, it should have been #2
instead of #1
.)
About aftergroup
:
It is possible to insert a single token right after the end of the current group using aftergroup<token>
.
You can only smuggle out one token at a time, so if you want to move out something consisting of multiple tokens (like a definition) you'll need to aftergroup
each of these tokens separately. This includes things like braces (), so for instance
aftergroupdefaftergroupabcaftergroupaftergroup Aaftergroup Baftergroup Caftergroup
is equivalent to defabcABC
.
This is quite a hassle, so the following may be more practical:
gdefsomethinguniquedefabcABCaftergroupsomethingunique
This works by globally assigning defabcABC
to somethingunique
and inserting that after the end of the group.
If ABC is replaced by some macro, say ABC
, that is only defined within the current group and that you want to be fully expanded then you'll want to use xdef
instead:
%
newcommand*ABCABC%
xdefsomethinguniquedefnoexpandabcABC%
aftergroupsomethingunique
I've inserted noexpand
in front of abc
because we don't want abc
to be expanded.
If you only want ABC
to be expanded once you can instead use the slightly more complicated
newcommand*ABCsomethingthatshouldntbeexpanded%
xdefsomethinguniquedefnoexpandabcunexpandedexpandafterABC%
aftergroupsomethingunique
(The primitives noexpand
, unexpanded
and expandafter
are all explained in this this answer.)
To smuggle the definition of abc
out of a group you can do what I just did above with ABC
replaced by abc
itself.
That way abc
will be defined as itself (expanded once) immediately after the end of the group.
There's also AfterGroup
from the etextools
package.
It acts mostly like aftergroup
, but it takes an argument that can consist of any number of tokens.
So, for instance, AftergroupdefabcABC
inserts defabcABC
after the current group without all of the aforementioned hassle.
There's also a starred version, Aftergroup*
, that does the same thing but first expands its arguments fully.
Don't use the
etextools
package though! It is apparently buggy and no longer maintained and it is incompatible with a bunch of other packages. (Thanks to Ulrike Fischer for pointing that out, here are a few examples: 1, 2, 3, 4.)
Even though you shouldn't use the package, AfterGroup
itself can be quite useful. It is defined as follows:
makeatletter %% <- make @ usable in command names
newcountettl@fter
newrobustcmdAfterGroup@ifstarettl@AfterGroup@firstofoneettl@AfterGroupunexpanded
newrobustcmdettl@AfterGroup[2]%
csxdefettl@fterGroupnumbernumexprtheettl@fter+1%
globalcsundefettl@fterGroupnumbernumexprtheettl@fter+1#1#2%
globaladvanceettl@fter@ne
expandafteraftergroupcsname ettl@fterGrouptheettl@fterendcsname
makeatother %% <- revert @
Defining smuggleone
:
To smuggle a macro that was already defined past the end of a group, it may be more effective to use let
instead of def
.
One advantage is that it will also works for macros with arguments:
newcommand*abc[1]``#1''%
globalletsomethinguniqueabc
aftergroupletaftergroupabcaftergroupsomethingunique
abcThis works!
This leads us to a possible definition of smuggleone
.
documentclassarticle
newcountersmuggle
DeclareRobustCommandsmuggleone[1]%
stepcountersmuggle%
expandafterglobalexpandafterletcsname smuggle@arabicsmuggleendcsname#1%
aftergroupletaftergroup#1expandafteraftergroupcsname smuggle@arabicsmuggleendcsname
begindocument
newcommand*abc[1]textbf#1%
%
%
renewcommand*abc[1]``#1''%
smuggleoneabc
abcLocal definition
par
abcLocal definition
par
abcGlobal definition
enddocument
The reason for the use of a counter here is that if you use somethingunique
every time you're smuggling something, it won't really be unique.
Whenever multiple smuggling operations are happening consescutively, because you're using smuggleone
multiple times from within the same group or from a group contained in another one where smuggleone
is used, this will cause trouble.
The above command therefore creates smuggle@<n>
the <n>
-th time it is used.
This can be made more efficient (memory-wise) by reusing these command sequences as much as possible, as in jfbu's answer.
All of this applied to your MWE:
Here is your MWE with two changes: (1) I've added the definition of smuggleone
and (2) I've replaced %smuggleoutone#1
by smuggleone#2
.
documentclass[tikz,border=3.14mm]standalone
usetikzlibrarycalc
newcountersmuggle
DeclareRobustCommandsmuggleone[1]%
stepcountersmuggle%
expandafterglobalexpandafterletcsname smuggle@arabicsmuggleendcsname#1%
aftergroupletaftergroup#1expandafteraftergroupcsname smuggle@arabicsmuggleendcsname
begindocument
begintikzpicture[globalize/.code n args=2xdef#2#1,
localize/.code n args=2pgfmathsetmacro#2#1typeout#2
smuggleone#2
]
beginscope[local bounding box=extra]
path let p1=($(2,1)-(0,0)$),n1=atan2(y1,x1) in
pgfextraxdefmyanglen1;
node at (1,0) myangle;
endscope
node[anchor=south] at (extra.north) using verb;
%
beginscope[local bounding box=globalize,xshift=3cm]
path let p1=($(2,1)-(0,0)$),n1=atan2(y1,x1) in
[globalize=n1myangle];
node at (1,0) myangle;
endscope
node[anchor=south] at (globalize.north) using textttglobalize;
%
xdefmyangle7
beginscope[local bounding box=localize,xshift=6cm]
path let p1=($(2,1)-(0,0)$),n1=atan2(y1,x1) in
[localize=n1myangle];
node at (1,0) myangle;
endscope
node[anchor=south] at (localize.north) attempt to smuggle;
%
endtikzpicture
enddocument
node[anchor=south] at (globalize.north) using textttglobalize;
%
xdefmyangle7
beginscope[local bounding box=localize,xshift=6cm]
path let p1=($(2,1)-(0,0)$),n1=atan2(y1,x1) in
[localize=n1myangle];
node at (1,0) myangle;
endscope
node[anchor=south] at (localize.north) attempt to smuggle;
%
endtikzpicture
enddocument
Addendum
Here's a smuggle
macro that works up to depth 10. It doesn't let you smuggle anything across eleven borders because 10
is two tokens (yeah, that's a stupid reason).
I could make it work for any depth, but I like how short the definition currently is and it seems unlikely that any sane person would need this.
The syntax is smuggle[<depth>]<macro>
, and the default <depth>
is 1
.
It works by calling smuggleone
and then also aftergroup
ing smuggle[<depth-1>]<macro>
.
documentclassarticle
newcountersmuggle
DeclareRobustCommandsmuggleone[1]%
stepcountersmuggle%
expandafterglobalexpandafterletcsname smuggle@arabicsmuggleendcsname#1%
aftergroupletaftergroup#1expandafteraftergroupcsname smuggle@arabicsmuggleendcsname
DeclareRobustCommandsmuggle[2][1]%
smuggleone#2%
ifnum#1>1
aftergroupsmuggleaftergroup[expandafteraftergroupthenumexpr#1-1aftergroup]aftergroup#2%
fi
begindocument
newcommand*abc[1]textbf#1
%
%
%
renewcommand*abc[1]``#1''%
smuggle[2]abc%
Definition at depth 3: abcLocal definition
par
Definition of depth 2: abcLocal definition
par
Definition of depth 1: abcLocal definition
par
Definition at depth 0: abcGlobal definition
enddocument
2
Don't use etextools. Search the site for some of its incompabilies.
– Ulrike Fischer
Jan 20 at 7:36
@UlrikeFischer: Oh, that is good to know. Do you have a suggestion about what to use instead? (I'll just copy the definition ofAftergroup
for now.)
– Circumscribe
Jan 20 at 9:35
come on, we all needSmuggleOutTocomma separated list of upper levelsfoo
, e.g.,SmuggleOutTo+1, +4, +6, +13..inftyFoo
will mean letFoo
be known 1 level up, 4 levels up, 6 levels up and 13 levels up and all the way to infty, whereasSmuggleOutToinftyFoo
will smuggle only to top level. What are you waiting for to do the real job ? :)
– user4686
Jan 20 at 15:17
Hah, indeed! :)
– Circumscribe
Jan 20 at 15:19
@Circumscribe No worries. It will take a while till I smuggle the information into my brain. ;-)
– marmot
Jan 20 at 23:53
add a comment |
The usual approach is to expandafter
around the end-of-group
begingroup
% Various things
defresultsome-tokens-that-need-to-escape%
expandafterendgroup
expandafterdefexpandafterresultexpandafterresult
That can be expressed slightly more concisely if using expl3
group_begin:
% Stuff to set
tl_set:Nn l_result_tl some-tokens-that-need-to-escape
exp_args:NNNV group_end:
tl_set:Nn l_result_tl l_result_tl
In either case, one could define something like
protecteddefsmuggleone#1#2endgroup%
#2%
expandafterendgroup
expandafterdefexpandafter#1expandafter#1%
1
I am wondering if you could add an example for how this is to be used in my above MWE?
– marmot
Jan 20 at 16:02
add a comment |
This covers various things:
smuggling a (no-parameter; see bottom of answer for macros with parameters) macro one level up,
(bizarre, for fun) smuggle it two level up, but it remains undefined one level up,
(more useful) smuggle one level up the contents of some macro, which is way to execute after group closes arbitrarily many tokens (if they are still defined at that level of course).
There is some subtlety in the way some globally defined auxiliary macros are indexed (their index is never globally increased), but I think it is ok. I hesitated about adding extra code to let them globally undefined once used, but did not do that finally. The same index will be used multiple times, but I think no non-yet needed thing ever gets overwritten. (although I may need to think about it more).
documentclassarticle
newcountgoodiescount
makeatletter
defSmuggleMacro%
advancegoodiescount 1 % not done globally !
expandafterSmuggleMacro@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacro@aux #1#2%
expandaftergdefexpandafter#1expandafter
expandafterdefexpandafter#2expandafter#2%
aftergroup#1%
%
% This one will let the macro be known two levels higher,
% but not if only one level higher
defSmuggleMacroUpTwo%
advancegoodiescount 1 % not done globally !
expandafterSmuggleMacroUpTwo@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacroUpTwo@aux#1#2%
expandaftergdefexpandafter#1expandafter
expandafterdefexpandafter#2expandafter#2%
aftergroupSmuggleValue
aftergroup#1%
%
defSmuggleValue%
advancegoodiescount 1 % not done globally !
expandafterSmuggleValue@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleValue@aux #1#2%
globallet#1#2%
aftergroup#1%
%
makeatother
begindocument
tt
begingroup
typeoutDEPTH 1%
deffuzzFUZZ defined at depth 1 and smuggled%
SmuggleMacrofuzz
begingroup
typeoutDEPTH 2%
defbazBAZ defined at depth 2 and smuggled up two%
SmuggleMacroUpTwobaz
begingroup
typeoutDEPTH 3%
DEPTH 3par
deffooFOO defined at depth 3 and smuggled%
SmuggleMacrofoo
defbarBAR defined at depth 3 and smuggled up two%
SmuggleMacroUpTwobar
END OF FIRST DEPTH 3par
endgroup
at depth 2 in-between the two depth 3par
stringfoospace has meaning meaningfoospace and will be smuggled againpar
stringbarspace has meaning meaningbarpar
SmuggleMacrofoo
begingroup
DEPTH 3par
typeoutSECOND TIMES AT DEPTH 3%
deffoofooFOOFOO defined at (second) depth 3 and smuggled%
SmuggleMacrofoofoo
defTrucpar Hello, I am stringTrucspace
I was defined at depth 3, but got executed
at depth 2!par
My own meaning is now: meaningTrucpar
typeoutDEPTH 2 AFTER 3%
showTruc
SmuggleValueTruc
END OF SECOND DEPTH 3par
endgroup
BACK TO DEPTH 2 (after executing aftergroup tokens)par
showTruc
showfuzz
showbaz
showfoo
showfoofoo
showbar
endgroup
BACK TO DEPTH 1 (after executing aftergroup tokens)par
stringfoospace has meaning meaningfoopar
stringbarspace has meaning meaningbarpar
typeoutDEPTH 1 AFTER 2%
showfuzz
showbaz
showfoo
showfoofoo
showbar
endgroup
BACK TO DEPTH 0 (after executing aftergroup tokens)par
stringfoospace has meaning meaningfoopar
typeoutDEPTH 0 AFTER 1
showfuzz
showbaz
showfoo
showfoofoo
showbar
enddocument
DEPTH 1
DEPTH 2
DEPTH 3
SECOND TIMES AT DEPTH 3
> Truc=macro:
->par Hello, I am string Truc space I was defined at depth 3, but got executed at depth 2!par My own meaning is now: meaning Truc par typeout DEPTH 2 AFTER 3.
l.77 showTruc
DEPTH 2 AFTER 3
> Truc=undefined.
l.82 showTruc
> fuzz=macro:
->FUZZ defined at depth 1 and smuggled.
l.83 showfuzz
> baz=macro:
->BAZ defined at depth 2 and smuggled up two.
l.84 showbaz
> foo=macro:
->FOO defined at depth 3 and smuggled.
l.85 showfoo
> foofoo=macro:
->FOOFOO defined at (second) depth 3 and smuggled.
l.86 showfoofoo
> bar=macro:
->mathaccent "7016relax .
l.87 showbar
DEPTH 1 AFTER 2
> fuzz=macro:
->FUZZ defined at depth 1 and smuggled.
l.92 showfuzz
> baz=undefined.
l.93 showbaz
> foo=macro:
->FOO defined at depth 3 and smuggled.
l.94 showfoo
> foofoo=undefined.
l.95 showfoofoo
> bar=macro:
->BAR defined at depth 3 and smuggled up two.
l.96 showbar
DEPTH 0 AFTER 1
> fuzz=macro:
->FUZZ defined at depth 1 and smuggled.
l.101 showfuzz
> baz=macro:
->BAZ defined at depth 2 and smuggled up two.
l.102 showbaz
> foo=undefined.
l.103 showfoo
> foofoo=undefined.
l.104 showfoofoo
> bar=macro:
->mathaccent "7016relax .
l.105 showbar
Addendum
I am adding SmuggleMacroNtimesUp <number>.macro
which will let the macro
be known <number>
levels up (of course, to the extent that its meaning uses tokens known at these levels...). Currently only parameter less macros, because this is how I started this...
Not much tested. In fact only tested on the single example below...
documentclassarticle
newcountgoodiescount
makeatletter
defSmuggleMacro%
advancegoodiescount 1 % not done globally !
expandafterSmuggleMacro@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacro@aux #1#2%
expandaftergdefexpandafter#1expandafter
expandafterdefexpandafter#2expandafter#2%
aftergroup#1%
%
% This one will let the macro be known two levels higher,
% but not if only one level higher
defSmuggleMacroUpTwo%
advancegoodiescount 1 % not done globally !
expandafterSmuggleMacroUpTwo@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacroUpTwo@aux#1#2%
expandaftergdefexpandafter#1expandafter
expandafterdefexpandafter#2expandafter#2%
aftergroupSmuggleValue
aftergroup#1%
%
defSmuggleValue%
advancegoodiescount 1 % not done globally !
expandafterSmuggleValue@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleValue@aux #1#2%
globallet#1#2%
aftergroup#1%
%
%
% This one makes known the macros 1, 2, ..., N levels up.
% Syntax SmuggleMacroNtimesUp<number>.macro
defSmuggleMacroNtimesUp%
advancegoodiescount 1 % not done globally!
expandafterSmuggleMacroNtimesUp@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacroNtimesUp@aux#1#2.#3%
expandaftergdefexpandafter#1expandafter
expandafterdefexpandafter#3expandafter#3%
aftergroup#1%
expandafterSmuggleMacroNtimesUp@athenumexpr#2-1.#1%
%
defSmuggleMacroNtimesUp@a#1%
if0#1expandafter@gobbletwo
else
aftergroupSmuggleValueNtimesUp
aftergroup #1%
expandafterSmuggleNtimesUp@loop
fi
%
defSmuggleNtimesUp@loop#1%
aftergroup#1%
if.#1expandafteraftergroup
else
expandafterSmuggleNtimesUp@loop
fi
%
% This one makes **executes the macro**
% at all levels 1, 2, ..., N up.
% Syntax SmuggleValueNtimesUp<number>.macro
defSmuggleValueNtimesUp%
advancegoodiescount 1 % not done globally!
expandafterSmuggleValueNtimesUp@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleValueNtimesUp@aux#1#2.#3%
globallet#1#3%
aftergroup#1%
expandafterSmuggleValueNtimesUp@athenumexpr#2-1.#1%
%
defSmuggleValueNtimesUp@a#1%
if0#1expandafter@gobbletwo
else
aftergroupSmuggleValueNtimesUp
aftergroup #1%
expandafterSmuggleNtimesUp@loop
fi
%
makeatother
begindocument
ttfamily
deffoo
% 8 deep
% 16 deep
deffooFOO defined at 16 will be made known all the way to 3%
SmuggleMacroNtimesUp13.foo
16.foopar%
15.foopar%+1
14.foopar%+2
13.foopar%+3
12.foopar%+4
11.foopar%
10.foopar%
9.foopar%
8.foopar%+8
7.foopar%
6.foopar%
5.foopar%
4.foopar%+12
3.foopar%+13
2.foopar%
1.foopar%
0.foopar
enddocument
Final version
Under pressure of @Circumscribe example I have refactored to handle macros with parameters. Not much tested... Added @marmot query about moving meaning to top level (aka bottom level...)
Thus the defined things are
SmuggleMacro foo
: makesfoo
keep its meaning one level up,SmuggleMacroUpTwo foo
: makesfoo
recover its meaning two levels up (but not one level up...)SmuggleMacroNtimesUp <number>.foo
: makesfoo
keep its meaning for the<number>
less nested levels. Must be used with<number>
at least1
.SmuggleValueNtimesUp <number>.foo
: executes the meaning offoo
for the<number>
less nested levels, viaaftergroup
so as soon as a more nested level is left.foo
itselfs (if not globally defined) is not smuggled.SmuggleMacroToTopfoo
: makesfoo
known at bottom level (sic), but not at any intermediate level (of course while moving from inner to outer, once we reach bottom level, next time we enter a groupfoo
will be known).
(if the code looks crazy, it is also because it tries to define the less auxiliary storage macros, by keeping this idea of never globally stepping the index of these storage things)
documentclassarticle
usepackagegeometry
newcountgoodiescount
makeatletter
defSmuggleMacro%
advancegoodiescount 1 % not done globally !
expandafterSmuggleMacro@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacro@aux#1#2%
globallet#1#2%
aftergrouplet
aftergroup#2%
aftergroup#1%
%
% This one will let the macro be known two levels higher,
% but not if only one level higher
defSmuggleMacroUpTwo%
advancegoodiescount 1 % not done globally !
expandafterSmuggleMacroUpTwo@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacroUpTwo@aux#1#2%
globallet#1#2%
aftergroupSmuggleLet
aftergroup#2%
aftergroup#1%
%
defSmuggleLet%
advancegoodiescount 1 % not done globally !
expandafterSmuggleLet@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleLet@aux#1#2#3%
globallet#1#3%
aftergrouplet
aftergroup#2%
aftergroup#1%
%
%
% This one makes known the macros 1, 2, ..., N levels up.
% Syntax SmuggleMacroNtimesUp<number>.macro
defSmuggleMacroNtimesUp%
advancegoodiescount 1 % not done globally!
expandafterSmuggleMacroNtimesUp@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacroNtimesUp@aux#1#2.#3%
globallet#1#3%
aftergrouplet
aftergroup#3%
aftergroup#1%
expandafterSmuggleMacroNtimesUp@athenumexpr#2-1.#3%
%
%longdef@gobblethree#1#2#3%
defSmuggleMacroNtimesUp@a#1%
if0#1expandafter@gobbletwo
else
aftergroupSmuggleMacroNtimesUp
aftergroup #1%
expandafterSmuggleNtimesUp@loop
fi
%
defSmuggleNtimesUp@loop#1%
aftergroup#1%
if.#1expandafteraftergroup
else
expandafterSmuggleNtimesUp@loop
fi
%
defSmuggleValueNtimesUp%
advancegoodiescount 1 % not done globally!
expandafterSmuggleValueNtimesUp@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleValueNtimesUp@aux#1#2.#3%
globallet#1#3%
aftergroup#1%
expandafterSmuggleValueNtimesUp@athenumexpr#2-1.#1%
%
defSmuggleValueNtimesUp@a#1%
if0#1expandafter@gobbletwo
else
aftergroupSmuggleValueNtimesUp
aftergroup #1%
expandafterSmuggleNtimesUp@loop
fi
%
% SmuggleMacroToTop
defSmuggleMacroToTop%
ifnumcurrentgrouplevel=z@
expandafter@gobble
else
expandafterSmuggleMacro@ToTop
fi
%
defSmuggleMacro@ToTop%
advancegoodiescount 1 % not done globally!
expandafterSmuggleMacroToTop@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacroToTop@aux#1#2%
globallet#1#2%
aftergroupSmuggleLetToTop
aftergroup#2%
aftergroup#1%
%
defSmuggleLetToTop%
ifnumcurrentgrouplevel=z@
expandafterlet
else
expandafterSmuggleLet@ToTop
fi
%
defSmuggleLet@ToTop%
advancegoodiescount 1 % not done globally !
expandafterSmuggleLetToTop@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleLetToTop@aux#1#2#3%
globallet#1#3%
aftergroupSmuggleLetToTop
aftergroup#2%
aftergroup#1%
%
makeatother
begindocument
ttfamily
deffoo
% 8 deep
% 16 deep
defBAR#1#2#3Hello, I am BAR%
SmuggleMacroBAR
SmuggleMacroToTopBAR
defBAZ#1#2#3#4Hello, I am BAZ%
SmuggleMacroUpTwoBAZ
deffoo#1#2FOO defined at 16 will be made known all the way to 3%
SmuggleMacroNtimesUp13.foo
16.FOO meaningfoopar
16.BAZ meaningBAZpar
16.BAR meaningBARpar
defxleavevmodellapaaa %
SmuggleValueNtimesUp7.x
medskip%
15.FOO meaningfoopar
15.BAZ meaningBAZpar
15.BAR meaningBARparmedskip%
14.FOO meaningfoopar
14.BAZ meaningBAZpar
14.BAR meaningBARparmedskip%
13.FOO meaningfoopar%+3
12.FOO meaningfoopar%+4
11.FOO meaningfoopar%
10.FOO meaningfoopar%
9.FOO meaningfoopar
9.BAR meaningBARpar
%
8.FOO meaningfoopar%+8
7.FOO meaningfoopar%
6.FOO meaningfoopar%
5.FOO meaningfoopar
5.BAR meaningBARpar%
4.FOO meaningfoopar%+12
3.FOO meaningfoopar%+13
2.FOO meaningfoopar%
1.FOO meaningfoopar%
0.FOO meaningfoopar
0.BAR meaningBARpar
enddocument
although I may need to think about it more: I think it is fine, although I have not really thought at depth. (ah ah).
– user4686
Jan 20 at 13:33
Trying to mentally parseSmuggleMacroUpTwo
makes my head hurt, but it appears to be airtight. You're really minimising the number of different macros that need to be assigned by keeping track of how many smuggling operations are currently ongoing and assigning them appropriate numbers :).
– Circumscribe
Jan 20 at 14:20
@Circumscribe yes, after a group closes, new definition of a given index can only happen after or at same time its old meaning is actually needed, and when you open an inner group you can only then define macros with a higher index than what you had stored earlier as to be used after the outer group. Thus, ..., it should be airtight indeed, but indeed a bit head-spinning :)
– user4686
Jan 20 at 15:04
Did you read " Of course, infty means that this macro has the value in all LaTeX documents of the world. ;-) " ? ;-)
– marmot
Jan 20 at 17:56
@marmot I understood that I would become rich, did I get that right?
– user4686
Jan 20 at 17:58
|
show 1 more 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%2f470961%2fhow-to-properly-smuggle-with-or-even-without-tikz%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
You can "smuggle" definitions out of their group with the TeX primitive aftergroup
. I'll first explain what aftergroup
does, then give a possible definition of smuggleone
using aftergroup
and finally apply it to your MWE.
The short answer is that you could define smuggleone
(I've removed "out" from the name) as
newcountersmuggle
DeclareRobustCommandsmuggleone[1]%
stepcountersmuggle%
expandafterglobalexpandafterletcsname smuggle@arabicsmuggleendcsname#1%
aftergroupletaftergroup#1expandafteraftergroupcsname smuggle@arabicsmuggleendcsname
If you paste in this definition and replace smuggleoutone#1
by smuggleone#2
in your MWE it should work. (Note that you were passing the wrong argument to smuggleoutone
, it should have been #2
instead of #1
.)
About aftergroup
:
It is possible to insert a single token right after the end of the current group using aftergroup<token>
.
You can only smuggle out one token at a time, so if you want to move out something consisting of multiple tokens (like a definition) you'll need to aftergroup
each of these tokens separately. This includes things like braces (), so for instance
aftergroupdefaftergroupabcaftergroupaftergroup Aaftergroup Baftergroup Caftergroup
is equivalent to defabcABC
.
This is quite a hassle, so the following may be more practical:
gdefsomethinguniquedefabcABCaftergroupsomethingunique
This works by globally assigning defabcABC
to somethingunique
and inserting that after the end of the group.
If ABC is replaced by some macro, say ABC
, that is only defined within the current group and that you want to be fully expanded then you'll want to use xdef
instead:
%
newcommand*ABCABC%
xdefsomethinguniquedefnoexpandabcABC%
aftergroupsomethingunique
I've inserted noexpand
in front of abc
because we don't want abc
to be expanded.
If you only want ABC
to be expanded once you can instead use the slightly more complicated
newcommand*ABCsomethingthatshouldntbeexpanded%
xdefsomethinguniquedefnoexpandabcunexpandedexpandafterABC%
aftergroupsomethingunique
(The primitives noexpand
, unexpanded
and expandafter
are all explained in this this answer.)
To smuggle the definition of abc
out of a group you can do what I just did above with ABC
replaced by abc
itself.
That way abc
will be defined as itself (expanded once) immediately after the end of the group.
There's also AfterGroup
from the etextools
package.
It acts mostly like aftergroup
, but it takes an argument that can consist of any number of tokens.
So, for instance, AftergroupdefabcABC
inserts defabcABC
after the current group without all of the aforementioned hassle.
There's also a starred version, Aftergroup*
, that does the same thing but first expands its arguments fully.
Don't use the
etextools
package though! It is apparently buggy and no longer maintained and it is incompatible with a bunch of other packages. (Thanks to Ulrike Fischer for pointing that out, here are a few examples: 1, 2, 3, 4.)
Even though you shouldn't use the package, AfterGroup
itself can be quite useful. It is defined as follows:
makeatletter %% <- make @ usable in command names
newcountettl@fter
newrobustcmdAfterGroup@ifstarettl@AfterGroup@firstofoneettl@AfterGroupunexpanded
newrobustcmdettl@AfterGroup[2]%
csxdefettl@fterGroupnumbernumexprtheettl@fter+1%
globalcsundefettl@fterGroupnumbernumexprtheettl@fter+1#1#2%
globaladvanceettl@fter@ne
expandafteraftergroupcsname ettl@fterGrouptheettl@fterendcsname
makeatother %% <- revert @
Defining smuggleone
:
To smuggle a macro that was already defined past the end of a group, it may be more effective to use let
instead of def
.
One advantage is that it will also works for macros with arguments:
newcommand*abc[1]``#1''%
globalletsomethinguniqueabc
aftergroupletaftergroupabcaftergroupsomethingunique
abcThis works!
This leads us to a possible definition of smuggleone
.
documentclassarticle
newcountersmuggle
DeclareRobustCommandsmuggleone[1]%
stepcountersmuggle%
expandafterglobalexpandafterletcsname smuggle@arabicsmuggleendcsname#1%
aftergroupletaftergroup#1expandafteraftergroupcsname smuggle@arabicsmuggleendcsname
begindocument
newcommand*abc[1]textbf#1%
%
%
renewcommand*abc[1]``#1''%
smuggleoneabc
abcLocal definition
par
abcLocal definition
par
abcGlobal definition
enddocument
The reason for the use of a counter here is that if you use somethingunique
every time you're smuggling something, it won't really be unique.
Whenever multiple smuggling operations are happening consescutively, because you're using smuggleone
multiple times from within the same group or from a group contained in another one where smuggleone
is used, this will cause trouble.
The above command therefore creates smuggle@<n>
the <n>
-th time it is used.
This can be made more efficient (memory-wise) by reusing these command sequences as much as possible, as in jfbu's answer.
All of this applied to your MWE:
Here is your MWE with two changes: (1) I've added the definition of smuggleone
and (2) I've replaced %smuggleoutone#1
by smuggleone#2
.
documentclass[tikz,border=3.14mm]standalone
usetikzlibrarycalc
newcountersmuggle
DeclareRobustCommandsmuggleone[1]%
stepcountersmuggle%
expandafterglobalexpandafterletcsname smuggle@arabicsmuggleendcsname#1%
aftergroupletaftergroup#1expandafteraftergroupcsname smuggle@arabicsmuggleendcsname
begindocument
begintikzpicture[globalize/.code n args=2xdef#2#1,
localize/.code n args=2pgfmathsetmacro#2#1typeout#2
smuggleone#2
]
beginscope[local bounding box=extra]
path let p1=($(2,1)-(0,0)$),n1=atan2(y1,x1) in
pgfextraxdefmyanglen1;
node at (1,0) myangle;
endscope
node[anchor=south] at (extra.north) using verb;
%
beginscope[local bounding box=globalize,xshift=3cm]
path let p1=($(2,1)-(0,0)$),n1=atan2(y1,x1) in
[globalize=n1myangle];
node at (1,0) myangle;
endscope
node[anchor=south] at (globalize.north) using textttglobalize;
%
xdefmyangle7
beginscope[local bounding box=localize,xshift=6cm]
path let p1=($(2,1)-(0,0)$),n1=atan2(y1,x1) in
[localize=n1myangle];
node at (1,0) myangle;
endscope
node[anchor=south] at (localize.north) attempt to smuggle;
%
endtikzpicture
enddocument
node[anchor=south] at (globalize.north) using textttglobalize;
%
xdefmyangle7
beginscope[local bounding box=localize,xshift=6cm]
path let p1=($(2,1)-(0,0)$),n1=atan2(y1,x1) in
[localize=n1myangle];
node at (1,0) myangle;
endscope
node[anchor=south] at (localize.north) attempt to smuggle;
%
endtikzpicture
enddocument
Addendum
Here's a smuggle
macro that works up to depth 10. It doesn't let you smuggle anything across eleven borders because 10
is two tokens (yeah, that's a stupid reason).
I could make it work for any depth, but I like how short the definition currently is and it seems unlikely that any sane person would need this.
The syntax is smuggle[<depth>]<macro>
, and the default <depth>
is 1
.
It works by calling smuggleone
and then also aftergroup
ing smuggle[<depth-1>]<macro>
.
documentclassarticle
newcountersmuggle
DeclareRobustCommandsmuggleone[1]%
stepcountersmuggle%
expandafterglobalexpandafterletcsname smuggle@arabicsmuggleendcsname#1%
aftergroupletaftergroup#1expandafteraftergroupcsname smuggle@arabicsmuggleendcsname
DeclareRobustCommandsmuggle[2][1]%
smuggleone#2%
ifnum#1>1
aftergroupsmuggleaftergroup[expandafteraftergroupthenumexpr#1-1aftergroup]aftergroup#2%
fi
begindocument
newcommand*abc[1]textbf#1
%
%
%
renewcommand*abc[1]``#1''%
smuggle[2]abc%
Definition at depth 3: abcLocal definition
par
Definition of depth 2: abcLocal definition
par
Definition of depth 1: abcLocal definition
par
Definition at depth 0: abcGlobal definition
enddocument
2
Don't use etextools. Search the site for some of its incompabilies.
– Ulrike Fischer
Jan 20 at 7:36
@UlrikeFischer: Oh, that is good to know. Do you have a suggestion about what to use instead? (I'll just copy the definition ofAftergroup
for now.)
– Circumscribe
Jan 20 at 9:35
come on, we all needSmuggleOutTocomma separated list of upper levelsfoo
, e.g.,SmuggleOutTo+1, +4, +6, +13..inftyFoo
will mean letFoo
be known 1 level up, 4 levels up, 6 levels up and 13 levels up and all the way to infty, whereasSmuggleOutToinftyFoo
will smuggle only to top level. What are you waiting for to do the real job ? :)
– user4686
Jan 20 at 15:17
Hah, indeed! :)
– Circumscribe
Jan 20 at 15:19
@Circumscribe No worries. It will take a while till I smuggle the information into my brain. ;-)
– marmot
Jan 20 at 23:53
add a comment |
You can "smuggle" definitions out of their group with the TeX primitive aftergroup
. I'll first explain what aftergroup
does, then give a possible definition of smuggleone
using aftergroup
and finally apply it to your MWE.
The short answer is that you could define smuggleone
(I've removed "out" from the name) as
newcountersmuggle
DeclareRobustCommandsmuggleone[1]%
stepcountersmuggle%
expandafterglobalexpandafterletcsname smuggle@arabicsmuggleendcsname#1%
aftergroupletaftergroup#1expandafteraftergroupcsname smuggle@arabicsmuggleendcsname
If you paste in this definition and replace smuggleoutone#1
by smuggleone#2
in your MWE it should work. (Note that you were passing the wrong argument to smuggleoutone
, it should have been #2
instead of #1
.)
About aftergroup
:
It is possible to insert a single token right after the end of the current group using aftergroup<token>
.
You can only smuggle out one token at a time, so if you want to move out something consisting of multiple tokens (like a definition) you'll need to aftergroup
each of these tokens separately. This includes things like braces (), so for instance
aftergroupdefaftergroupabcaftergroupaftergroup Aaftergroup Baftergroup Caftergroup
is equivalent to defabcABC
.
This is quite a hassle, so the following may be more practical:
gdefsomethinguniquedefabcABCaftergroupsomethingunique
This works by globally assigning defabcABC
to somethingunique
and inserting that after the end of the group.
If ABC is replaced by some macro, say ABC
, that is only defined within the current group and that you want to be fully expanded then you'll want to use xdef
instead:
%
newcommand*ABCABC%
xdefsomethinguniquedefnoexpandabcABC%
aftergroupsomethingunique
I've inserted noexpand
in front of abc
because we don't want abc
to be expanded.
If you only want ABC
to be expanded once you can instead use the slightly more complicated
newcommand*ABCsomethingthatshouldntbeexpanded%
xdefsomethinguniquedefnoexpandabcunexpandedexpandafterABC%
aftergroupsomethingunique
(The primitives noexpand
, unexpanded
and expandafter
are all explained in this this answer.)
To smuggle the definition of abc
out of a group you can do what I just did above with ABC
replaced by abc
itself.
That way abc
will be defined as itself (expanded once) immediately after the end of the group.
There's also AfterGroup
from the etextools
package.
It acts mostly like aftergroup
, but it takes an argument that can consist of any number of tokens.
So, for instance, AftergroupdefabcABC
inserts defabcABC
after the current group without all of the aforementioned hassle.
There's also a starred version, Aftergroup*
, that does the same thing but first expands its arguments fully.
Don't use the
etextools
package though! It is apparently buggy and no longer maintained and it is incompatible with a bunch of other packages. (Thanks to Ulrike Fischer for pointing that out, here are a few examples: 1, 2, 3, 4.)
Even though you shouldn't use the package, AfterGroup
itself can be quite useful. It is defined as follows:
makeatletter %% <- make @ usable in command names
newcountettl@fter
newrobustcmdAfterGroup@ifstarettl@AfterGroup@firstofoneettl@AfterGroupunexpanded
newrobustcmdettl@AfterGroup[2]%
csxdefettl@fterGroupnumbernumexprtheettl@fter+1%
globalcsundefettl@fterGroupnumbernumexprtheettl@fter+1#1#2%
globaladvanceettl@fter@ne
expandafteraftergroupcsname ettl@fterGrouptheettl@fterendcsname
makeatother %% <- revert @
Defining smuggleone
:
To smuggle a macro that was already defined past the end of a group, it may be more effective to use let
instead of def
.
One advantage is that it will also works for macros with arguments:
newcommand*abc[1]``#1''%
globalletsomethinguniqueabc
aftergroupletaftergroupabcaftergroupsomethingunique
abcThis works!
This leads us to a possible definition of smuggleone
.
documentclassarticle
newcountersmuggle
DeclareRobustCommandsmuggleone[1]%
stepcountersmuggle%
expandafterglobalexpandafterletcsname smuggle@arabicsmuggleendcsname#1%
aftergroupletaftergroup#1expandafteraftergroupcsname smuggle@arabicsmuggleendcsname
begindocument
newcommand*abc[1]textbf#1%
%
%
renewcommand*abc[1]``#1''%
smuggleoneabc
abcLocal definition
par
abcLocal definition
par
abcGlobal definition
enddocument
The reason for the use of a counter here is that if you use somethingunique
every time you're smuggling something, it won't really be unique.
Whenever multiple smuggling operations are happening consescutively, because you're using smuggleone
multiple times from within the same group or from a group contained in another one where smuggleone
is used, this will cause trouble.
The above command therefore creates smuggle@<n>
the <n>
-th time it is used.
This can be made more efficient (memory-wise) by reusing these command sequences as much as possible, as in jfbu's answer.
All of this applied to your MWE:
Here is your MWE with two changes: (1) I've added the definition of smuggleone
and (2) I've replaced %smuggleoutone#1
by smuggleone#2
.
documentclass[tikz,border=3.14mm]standalone
usetikzlibrarycalc
newcountersmuggle
DeclareRobustCommandsmuggleone[1]%
stepcountersmuggle%
expandafterglobalexpandafterletcsname smuggle@arabicsmuggleendcsname#1%
aftergroupletaftergroup#1expandafteraftergroupcsname smuggle@arabicsmuggleendcsname
begindocument
begintikzpicture[globalize/.code n args=2xdef#2#1,
localize/.code n args=2pgfmathsetmacro#2#1typeout#2
smuggleone#2
]
beginscope[local bounding box=extra]
path let p1=($(2,1)-(0,0)$),n1=atan2(y1,x1) in
pgfextraxdefmyanglen1;
node at (1,0) myangle;
endscope
node[anchor=south] at (extra.north) using verb;
%
beginscope[local bounding box=globalize,xshift=3cm]
path let p1=($(2,1)-(0,0)$),n1=atan2(y1,x1) in
[globalize=n1myangle];
node at (1,0) myangle;
endscope
node[anchor=south] at (globalize.north) using textttglobalize;
%
xdefmyangle7
beginscope[local bounding box=localize,xshift=6cm]
path let p1=($(2,1)-(0,0)$),n1=atan2(y1,x1) in
[localize=n1myangle];
node at (1,0) myangle;
endscope
node[anchor=south] at (localize.north) attempt to smuggle;
%
endtikzpicture
enddocument
node[anchor=south] at (globalize.north) using textttglobalize;
%
xdefmyangle7
beginscope[local bounding box=localize,xshift=6cm]
path let p1=($(2,1)-(0,0)$),n1=atan2(y1,x1) in
[localize=n1myangle];
node at (1,0) myangle;
endscope
node[anchor=south] at (localize.north) attempt to smuggle;
%
endtikzpicture
enddocument
Addendum
Here's a smuggle
macro that works up to depth 10. It doesn't let you smuggle anything across eleven borders because 10
is two tokens (yeah, that's a stupid reason).
I could make it work for any depth, but I like how short the definition currently is and it seems unlikely that any sane person would need this.
The syntax is smuggle[<depth>]<macro>
, and the default <depth>
is 1
.
It works by calling smuggleone
and then also aftergroup
ing smuggle[<depth-1>]<macro>
.
documentclassarticle
newcountersmuggle
DeclareRobustCommandsmuggleone[1]%
stepcountersmuggle%
expandafterglobalexpandafterletcsname smuggle@arabicsmuggleendcsname#1%
aftergroupletaftergroup#1expandafteraftergroupcsname smuggle@arabicsmuggleendcsname
DeclareRobustCommandsmuggle[2][1]%
smuggleone#2%
ifnum#1>1
aftergroupsmuggleaftergroup[expandafteraftergroupthenumexpr#1-1aftergroup]aftergroup#2%
fi
begindocument
newcommand*abc[1]textbf#1
%
%
%
renewcommand*abc[1]``#1''%
smuggle[2]abc%
Definition at depth 3: abcLocal definition
par
Definition of depth 2: abcLocal definition
par
Definition of depth 1: abcLocal definition
par
Definition at depth 0: abcGlobal definition
enddocument
2
Don't use etextools. Search the site for some of its incompabilies.
– Ulrike Fischer
Jan 20 at 7:36
@UlrikeFischer: Oh, that is good to know. Do you have a suggestion about what to use instead? (I'll just copy the definition ofAftergroup
for now.)
– Circumscribe
Jan 20 at 9:35
come on, we all needSmuggleOutTocomma separated list of upper levelsfoo
, e.g.,SmuggleOutTo+1, +4, +6, +13..inftyFoo
will mean letFoo
be known 1 level up, 4 levels up, 6 levels up and 13 levels up and all the way to infty, whereasSmuggleOutToinftyFoo
will smuggle only to top level. What are you waiting for to do the real job ? :)
– user4686
Jan 20 at 15:17
Hah, indeed! :)
– Circumscribe
Jan 20 at 15:19
@Circumscribe No worries. It will take a while till I smuggle the information into my brain. ;-)
– marmot
Jan 20 at 23:53
add a comment |
You can "smuggle" definitions out of their group with the TeX primitive aftergroup
. I'll first explain what aftergroup
does, then give a possible definition of smuggleone
using aftergroup
and finally apply it to your MWE.
The short answer is that you could define smuggleone
(I've removed "out" from the name) as
newcountersmuggle
DeclareRobustCommandsmuggleone[1]%
stepcountersmuggle%
expandafterglobalexpandafterletcsname smuggle@arabicsmuggleendcsname#1%
aftergroupletaftergroup#1expandafteraftergroupcsname smuggle@arabicsmuggleendcsname
If you paste in this definition and replace smuggleoutone#1
by smuggleone#2
in your MWE it should work. (Note that you were passing the wrong argument to smuggleoutone
, it should have been #2
instead of #1
.)
About aftergroup
:
It is possible to insert a single token right after the end of the current group using aftergroup<token>
.
You can only smuggle out one token at a time, so if you want to move out something consisting of multiple tokens (like a definition) you'll need to aftergroup
each of these tokens separately. This includes things like braces (), so for instance
aftergroupdefaftergroupabcaftergroupaftergroup Aaftergroup Baftergroup Caftergroup
is equivalent to defabcABC
.
This is quite a hassle, so the following may be more practical:
gdefsomethinguniquedefabcABCaftergroupsomethingunique
This works by globally assigning defabcABC
to somethingunique
and inserting that after the end of the group.
If ABC is replaced by some macro, say ABC
, that is only defined within the current group and that you want to be fully expanded then you'll want to use xdef
instead:
%
newcommand*ABCABC%
xdefsomethinguniquedefnoexpandabcABC%
aftergroupsomethingunique
I've inserted noexpand
in front of abc
because we don't want abc
to be expanded.
If you only want ABC
to be expanded once you can instead use the slightly more complicated
newcommand*ABCsomethingthatshouldntbeexpanded%
xdefsomethinguniquedefnoexpandabcunexpandedexpandafterABC%
aftergroupsomethingunique
(The primitives noexpand
, unexpanded
and expandafter
are all explained in this this answer.)
To smuggle the definition of abc
out of a group you can do what I just did above with ABC
replaced by abc
itself.
That way abc
will be defined as itself (expanded once) immediately after the end of the group.
There's also AfterGroup
from the etextools
package.
It acts mostly like aftergroup
, but it takes an argument that can consist of any number of tokens.
So, for instance, AftergroupdefabcABC
inserts defabcABC
after the current group without all of the aforementioned hassle.
There's also a starred version, Aftergroup*
, that does the same thing but first expands its arguments fully.
Don't use the
etextools
package though! It is apparently buggy and no longer maintained and it is incompatible with a bunch of other packages. (Thanks to Ulrike Fischer for pointing that out, here are a few examples: 1, 2, 3, 4.)
Even though you shouldn't use the package, AfterGroup
itself can be quite useful. It is defined as follows:
makeatletter %% <- make @ usable in command names
newcountettl@fter
newrobustcmdAfterGroup@ifstarettl@AfterGroup@firstofoneettl@AfterGroupunexpanded
newrobustcmdettl@AfterGroup[2]%
csxdefettl@fterGroupnumbernumexprtheettl@fter+1%
globalcsundefettl@fterGroupnumbernumexprtheettl@fter+1#1#2%
globaladvanceettl@fter@ne
expandafteraftergroupcsname ettl@fterGrouptheettl@fterendcsname
makeatother %% <- revert @
Defining smuggleone
:
To smuggle a macro that was already defined past the end of a group, it may be more effective to use let
instead of def
.
One advantage is that it will also works for macros with arguments:
newcommand*abc[1]``#1''%
globalletsomethinguniqueabc
aftergroupletaftergroupabcaftergroupsomethingunique
abcThis works!
This leads us to a possible definition of smuggleone
.
documentclassarticle
newcountersmuggle
DeclareRobustCommandsmuggleone[1]%
stepcountersmuggle%
expandafterglobalexpandafterletcsname smuggle@arabicsmuggleendcsname#1%
aftergroupletaftergroup#1expandafteraftergroupcsname smuggle@arabicsmuggleendcsname
begindocument
newcommand*abc[1]textbf#1%
%
%
renewcommand*abc[1]``#1''%
smuggleoneabc
abcLocal definition
par
abcLocal definition
par
abcGlobal definition
enddocument
The reason for the use of a counter here is that if you use somethingunique
every time you're smuggling something, it won't really be unique.
Whenever multiple smuggling operations are happening consescutively, because you're using smuggleone
multiple times from within the same group or from a group contained in another one where smuggleone
is used, this will cause trouble.
The above command therefore creates smuggle@<n>
the <n>
-th time it is used.
This can be made more efficient (memory-wise) by reusing these command sequences as much as possible, as in jfbu's answer.
All of this applied to your MWE:
Here is your MWE with two changes: (1) I've added the definition of smuggleone
and (2) I've replaced %smuggleoutone#1
by smuggleone#2
.
documentclass[tikz,border=3.14mm]standalone
usetikzlibrarycalc
newcountersmuggle
DeclareRobustCommandsmuggleone[1]%
stepcountersmuggle%
expandafterglobalexpandafterletcsname smuggle@arabicsmuggleendcsname#1%
aftergroupletaftergroup#1expandafteraftergroupcsname smuggle@arabicsmuggleendcsname
begindocument
begintikzpicture[globalize/.code n args=2xdef#2#1,
localize/.code n args=2pgfmathsetmacro#2#1typeout#2
smuggleone#2
]
beginscope[local bounding box=extra]
path let p1=($(2,1)-(0,0)$),n1=atan2(y1,x1) in
pgfextraxdefmyanglen1;
node at (1,0) myangle;
endscope
node[anchor=south] at (extra.north) using verb;
%
beginscope[local bounding box=globalize,xshift=3cm]
path let p1=($(2,1)-(0,0)$),n1=atan2(y1,x1) in
[globalize=n1myangle];
node at (1,0) myangle;
endscope
node[anchor=south] at (globalize.north) using textttglobalize;
%
xdefmyangle7
beginscope[local bounding box=localize,xshift=6cm]
path let p1=($(2,1)-(0,0)$),n1=atan2(y1,x1) in
[localize=n1myangle];
node at (1,0) myangle;
endscope
node[anchor=south] at (localize.north) attempt to smuggle;
%
endtikzpicture
enddocument
node[anchor=south] at (globalize.north) using textttglobalize;
%
xdefmyangle7
beginscope[local bounding box=localize,xshift=6cm]
path let p1=($(2,1)-(0,0)$),n1=atan2(y1,x1) in
[localize=n1myangle];
node at (1,0) myangle;
endscope
node[anchor=south] at (localize.north) attempt to smuggle;
%
endtikzpicture
enddocument
Addendum
Here's a smuggle
macro that works up to depth 10. It doesn't let you smuggle anything across eleven borders because 10
is two tokens (yeah, that's a stupid reason).
I could make it work for any depth, but I like how short the definition currently is and it seems unlikely that any sane person would need this.
The syntax is smuggle[<depth>]<macro>
, and the default <depth>
is 1
.
It works by calling smuggleone
and then also aftergroup
ing smuggle[<depth-1>]<macro>
.
documentclassarticle
newcountersmuggle
DeclareRobustCommandsmuggleone[1]%
stepcountersmuggle%
expandafterglobalexpandafterletcsname smuggle@arabicsmuggleendcsname#1%
aftergroupletaftergroup#1expandafteraftergroupcsname smuggle@arabicsmuggleendcsname
DeclareRobustCommandsmuggle[2][1]%
smuggleone#2%
ifnum#1>1
aftergroupsmuggleaftergroup[expandafteraftergroupthenumexpr#1-1aftergroup]aftergroup#2%
fi
begindocument
newcommand*abc[1]textbf#1
%
%
%
renewcommand*abc[1]``#1''%
smuggle[2]abc%
Definition at depth 3: abcLocal definition
par
Definition of depth 2: abcLocal definition
par
Definition of depth 1: abcLocal definition
par
Definition at depth 0: abcGlobal definition
enddocument
You can "smuggle" definitions out of their group with the TeX primitive aftergroup
. I'll first explain what aftergroup
does, then give a possible definition of smuggleone
using aftergroup
and finally apply it to your MWE.
The short answer is that you could define smuggleone
(I've removed "out" from the name) as
newcountersmuggle
DeclareRobustCommandsmuggleone[1]%
stepcountersmuggle%
expandafterglobalexpandafterletcsname smuggle@arabicsmuggleendcsname#1%
aftergroupletaftergroup#1expandafteraftergroupcsname smuggle@arabicsmuggleendcsname
If you paste in this definition and replace smuggleoutone#1
by smuggleone#2
in your MWE it should work. (Note that you were passing the wrong argument to smuggleoutone
, it should have been #2
instead of #1
.)
About aftergroup
:
It is possible to insert a single token right after the end of the current group using aftergroup<token>
.
You can only smuggle out one token at a time, so if you want to move out something consisting of multiple tokens (like a definition) you'll need to aftergroup
each of these tokens separately. This includes things like braces (), so for instance
aftergroupdefaftergroupabcaftergroupaftergroup Aaftergroup Baftergroup Caftergroup
is equivalent to defabcABC
.
This is quite a hassle, so the following may be more practical:
gdefsomethinguniquedefabcABCaftergroupsomethingunique
This works by globally assigning defabcABC
to somethingunique
and inserting that after the end of the group.
If ABC is replaced by some macro, say ABC
, that is only defined within the current group and that you want to be fully expanded then you'll want to use xdef
instead:
%
newcommand*ABCABC%
xdefsomethinguniquedefnoexpandabcABC%
aftergroupsomethingunique
I've inserted noexpand
in front of abc
because we don't want abc
to be expanded.
If you only want ABC
to be expanded once you can instead use the slightly more complicated
newcommand*ABCsomethingthatshouldntbeexpanded%
xdefsomethinguniquedefnoexpandabcunexpandedexpandafterABC%
aftergroupsomethingunique
(The primitives noexpand
, unexpanded
and expandafter
are all explained in this this answer.)
To smuggle the definition of abc
out of a group you can do what I just did above with ABC
replaced by abc
itself.
That way abc
will be defined as itself (expanded once) immediately after the end of the group.
There's also AfterGroup
from the etextools
package.
It acts mostly like aftergroup
, but it takes an argument that can consist of any number of tokens.
So, for instance, AftergroupdefabcABC
inserts defabcABC
after the current group without all of the aforementioned hassle.
There's also a starred version, Aftergroup*
, that does the same thing but first expands its arguments fully.
Don't use the
etextools
package though! It is apparently buggy and no longer maintained and it is incompatible with a bunch of other packages. (Thanks to Ulrike Fischer for pointing that out, here are a few examples: 1, 2, 3, 4.)
Even though you shouldn't use the package, AfterGroup
itself can be quite useful. It is defined as follows:
makeatletter %% <- make @ usable in command names
newcountettl@fter
newrobustcmdAfterGroup@ifstarettl@AfterGroup@firstofoneettl@AfterGroupunexpanded
newrobustcmdettl@AfterGroup[2]%
csxdefettl@fterGroupnumbernumexprtheettl@fter+1%
globalcsundefettl@fterGroupnumbernumexprtheettl@fter+1#1#2%
globaladvanceettl@fter@ne
expandafteraftergroupcsname ettl@fterGrouptheettl@fterendcsname
makeatother %% <- revert @
Defining smuggleone
:
To smuggle a macro that was already defined past the end of a group, it may be more effective to use let
instead of def
.
One advantage is that it will also works for macros with arguments:
newcommand*abc[1]``#1''%
globalletsomethinguniqueabc
aftergroupletaftergroupabcaftergroupsomethingunique
abcThis works!
This leads us to a possible definition of smuggleone
.
documentclassarticle
newcountersmuggle
DeclareRobustCommandsmuggleone[1]%
stepcountersmuggle%
expandafterglobalexpandafterletcsname smuggle@arabicsmuggleendcsname#1%
aftergroupletaftergroup#1expandafteraftergroupcsname smuggle@arabicsmuggleendcsname
begindocument
newcommand*abc[1]textbf#1%
%
%
renewcommand*abc[1]``#1''%
smuggleoneabc
abcLocal definition
par
abcLocal definition
par
abcGlobal definition
enddocument
The reason for the use of a counter here is that if you use somethingunique
every time you're smuggling something, it won't really be unique.
Whenever multiple smuggling operations are happening consescutively, because you're using smuggleone
multiple times from within the same group or from a group contained in another one where smuggleone
is used, this will cause trouble.
The above command therefore creates smuggle@<n>
the <n>
-th time it is used.
This can be made more efficient (memory-wise) by reusing these command sequences as much as possible, as in jfbu's answer.
All of this applied to your MWE:
Here is your MWE with two changes: (1) I've added the definition of smuggleone
and (2) I've replaced %smuggleoutone#1
by smuggleone#2
.
documentclass[tikz,border=3.14mm]standalone
usetikzlibrarycalc
newcountersmuggle
DeclareRobustCommandsmuggleone[1]%
stepcountersmuggle%
expandafterglobalexpandafterletcsname smuggle@arabicsmuggleendcsname#1%
aftergroupletaftergroup#1expandafteraftergroupcsname smuggle@arabicsmuggleendcsname
begindocument
begintikzpicture[globalize/.code n args=2xdef#2#1,
localize/.code n args=2pgfmathsetmacro#2#1typeout#2
smuggleone#2
]
beginscope[local bounding box=extra]
path let p1=($(2,1)-(0,0)$),n1=atan2(y1,x1) in
pgfextraxdefmyanglen1;
node at (1,0) myangle;
endscope
node[anchor=south] at (extra.north) using verb;
%
beginscope[local bounding box=globalize,xshift=3cm]
path let p1=($(2,1)-(0,0)$),n1=atan2(y1,x1) in
[globalize=n1myangle];
node at (1,0) myangle;
endscope
node[anchor=south] at (globalize.north) using textttglobalize;
%
xdefmyangle7
beginscope[local bounding box=localize,xshift=6cm]
path let p1=($(2,1)-(0,0)$),n1=atan2(y1,x1) in
[localize=n1myangle];
node at (1,0) myangle;
endscope
node[anchor=south] at (localize.north) attempt to smuggle;
%
endtikzpicture
enddocument
node[anchor=south] at (globalize.north) using textttglobalize;
%
xdefmyangle7
beginscope[local bounding box=localize,xshift=6cm]
path let p1=($(2,1)-(0,0)$),n1=atan2(y1,x1) in
[localize=n1myangle];
node at (1,0) myangle;
endscope
node[anchor=south] at (localize.north) attempt to smuggle;
%
endtikzpicture
enddocument
Addendum
Here's a smuggle
macro that works up to depth 10. It doesn't let you smuggle anything across eleven borders because 10
is two tokens (yeah, that's a stupid reason).
I could make it work for any depth, but I like how short the definition currently is and it seems unlikely that any sane person would need this.
The syntax is smuggle[<depth>]<macro>
, and the default <depth>
is 1
.
It works by calling smuggleone
and then also aftergroup
ing smuggle[<depth-1>]<macro>
.
documentclassarticle
newcountersmuggle
DeclareRobustCommandsmuggleone[1]%
stepcountersmuggle%
expandafterglobalexpandafterletcsname smuggle@arabicsmuggleendcsname#1%
aftergroupletaftergroup#1expandafteraftergroupcsname smuggle@arabicsmuggleendcsname
DeclareRobustCommandsmuggle[2][1]%
smuggleone#2%
ifnum#1>1
aftergroupsmuggleaftergroup[expandafteraftergroupthenumexpr#1-1aftergroup]aftergroup#2%
fi
begindocument
newcommand*abc[1]textbf#1
%
%
%
renewcommand*abc[1]``#1''%
smuggle[2]abc%
Definition at depth 3: abcLocal definition
par
Definition of depth 2: abcLocal definition
par
Definition of depth 1: abcLocal definition
par
Definition at depth 0: abcGlobal definition
enddocument
edited Jan 21 at 0:19
answered Jan 20 at 3:54
CircumscribeCircumscribe
6,4312836
6,4312836
2
Don't use etextools. Search the site for some of its incompabilies.
– Ulrike Fischer
Jan 20 at 7:36
@UlrikeFischer: Oh, that is good to know. Do you have a suggestion about what to use instead? (I'll just copy the definition ofAftergroup
for now.)
– Circumscribe
Jan 20 at 9:35
come on, we all needSmuggleOutTocomma separated list of upper levelsfoo
, e.g.,SmuggleOutTo+1, +4, +6, +13..inftyFoo
will mean letFoo
be known 1 level up, 4 levels up, 6 levels up and 13 levels up and all the way to infty, whereasSmuggleOutToinftyFoo
will smuggle only to top level. What are you waiting for to do the real job ? :)
– user4686
Jan 20 at 15:17
Hah, indeed! :)
– Circumscribe
Jan 20 at 15:19
@Circumscribe No worries. It will take a while till I smuggle the information into my brain. ;-)
– marmot
Jan 20 at 23:53
add a comment |
2
Don't use etextools. Search the site for some of its incompabilies.
– Ulrike Fischer
Jan 20 at 7:36
@UlrikeFischer: Oh, that is good to know. Do you have a suggestion about what to use instead? (I'll just copy the definition ofAftergroup
for now.)
– Circumscribe
Jan 20 at 9:35
come on, we all needSmuggleOutTocomma separated list of upper levelsfoo
, e.g.,SmuggleOutTo+1, +4, +6, +13..inftyFoo
will mean letFoo
be known 1 level up, 4 levels up, 6 levels up and 13 levels up and all the way to infty, whereasSmuggleOutToinftyFoo
will smuggle only to top level. What are you waiting for to do the real job ? :)
– user4686
Jan 20 at 15:17
Hah, indeed! :)
– Circumscribe
Jan 20 at 15:19
@Circumscribe No worries. It will take a while till I smuggle the information into my brain. ;-)
– marmot
Jan 20 at 23:53
2
2
Don't use etextools. Search the site for some of its incompabilies.
– Ulrike Fischer
Jan 20 at 7:36
Don't use etextools. Search the site for some of its incompabilies.
– Ulrike Fischer
Jan 20 at 7:36
@UlrikeFischer: Oh, that is good to know. Do you have a suggestion about what to use instead? (I'll just copy the definition of
Aftergroup
for now.)– Circumscribe
Jan 20 at 9:35
@UlrikeFischer: Oh, that is good to know. Do you have a suggestion about what to use instead? (I'll just copy the definition of
Aftergroup
for now.)– Circumscribe
Jan 20 at 9:35
come on, we all need
SmuggleOutTocomma separated list of upper levelsfoo
, e.g., SmuggleOutTo+1, +4, +6, +13..inftyFoo
will mean let Foo
be known 1 level up, 4 levels up, 6 levels up and 13 levels up and all the way to infty, whereas SmuggleOutToinftyFoo
will smuggle only to top level. What are you waiting for to do the real job ? :)– user4686
Jan 20 at 15:17
come on, we all need
SmuggleOutTocomma separated list of upper levelsfoo
, e.g., SmuggleOutTo+1, +4, +6, +13..inftyFoo
will mean let Foo
be known 1 level up, 4 levels up, 6 levels up and 13 levels up and all the way to infty, whereas SmuggleOutToinftyFoo
will smuggle only to top level. What are you waiting for to do the real job ? :)– user4686
Jan 20 at 15:17
Hah, indeed! :)
– Circumscribe
Jan 20 at 15:19
Hah, indeed! :)
– Circumscribe
Jan 20 at 15:19
@Circumscribe No worries. It will take a while till I smuggle the information into my brain. ;-)
– marmot
Jan 20 at 23:53
@Circumscribe No worries. It will take a while till I smuggle the information into my brain. ;-)
– marmot
Jan 20 at 23:53
add a comment |
The usual approach is to expandafter
around the end-of-group
begingroup
% Various things
defresultsome-tokens-that-need-to-escape%
expandafterendgroup
expandafterdefexpandafterresultexpandafterresult
That can be expressed slightly more concisely if using expl3
group_begin:
% Stuff to set
tl_set:Nn l_result_tl some-tokens-that-need-to-escape
exp_args:NNNV group_end:
tl_set:Nn l_result_tl l_result_tl
In either case, one could define something like
protecteddefsmuggleone#1#2endgroup%
#2%
expandafterendgroup
expandafterdefexpandafter#1expandafter#1%
1
I am wondering if you could add an example for how this is to be used in my above MWE?
– marmot
Jan 20 at 16:02
add a comment |
The usual approach is to expandafter
around the end-of-group
begingroup
% Various things
defresultsome-tokens-that-need-to-escape%
expandafterendgroup
expandafterdefexpandafterresultexpandafterresult
That can be expressed slightly more concisely if using expl3
group_begin:
% Stuff to set
tl_set:Nn l_result_tl some-tokens-that-need-to-escape
exp_args:NNNV group_end:
tl_set:Nn l_result_tl l_result_tl
In either case, one could define something like
protecteddefsmuggleone#1#2endgroup%
#2%
expandafterendgroup
expandafterdefexpandafter#1expandafter#1%
1
I am wondering if you could add an example for how this is to be used in my above MWE?
– marmot
Jan 20 at 16:02
add a comment |
The usual approach is to expandafter
around the end-of-group
begingroup
% Various things
defresultsome-tokens-that-need-to-escape%
expandafterendgroup
expandafterdefexpandafterresultexpandafterresult
That can be expressed slightly more concisely if using expl3
group_begin:
% Stuff to set
tl_set:Nn l_result_tl some-tokens-that-need-to-escape
exp_args:NNNV group_end:
tl_set:Nn l_result_tl l_result_tl
In either case, one could define something like
protecteddefsmuggleone#1#2endgroup%
#2%
expandafterendgroup
expandafterdefexpandafter#1expandafter#1%
The usual approach is to expandafter
around the end-of-group
begingroup
% Various things
defresultsome-tokens-that-need-to-escape%
expandafterendgroup
expandafterdefexpandafterresultexpandafterresult
That can be expressed slightly more concisely if using expl3
group_begin:
% Stuff to set
tl_set:Nn l_result_tl some-tokens-that-need-to-escape
exp_args:NNNV group_end:
tl_set:Nn l_result_tl l_result_tl
In either case, one could define something like
protecteddefsmuggleone#1#2endgroup%
#2%
expandafterendgroup
expandafterdefexpandafter#1expandafter#1%
answered Jan 20 at 9:00
Joseph Wright♦Joseph Wright
203k22559885
203k22559885
1
I am wondering if you could add an example for how this is to be used in my above MWE?
– marmot
Jan 20 at 16:02
add a comment |
1
I am wondering if you could add an example for how this is to be used in my above MWE?
– marmot
Jan 20 at 16:02
1
1
I am wondering if you could add an example for how this is to be used in my above MWE?
– marmot
Jan 20 at 16:02
I am wondering if you could add an example for how this is to be used in my above MWE?
– marmot
Jan 20 at 16:02
add a comment |
This covers various things:
smuggling a (no-parameter; see bottom of answer for macros with parameters) macro one level up,
(bizarre, for fun) smuggle it two level up, but it remains undefined one level up,
(more useful) smuggle one level up the contents of some macro, which is way to execute after group closes arbitrarily many tokens (if they are still defined at that level of course).
There is some subtlety in the way some globally defined auxiliary macros are indexed (their index is never globally increased), but I think it is ok. I hesitated about adding extra code to let them globally undefined once used, but did not do that finally. The same index will be used multiple times, but I think no non-yet needed thing ever gets overwritten. (although I may need to think about it more).
documentclassarticle
newcountgoodiescount
makeatletter
defSmuggleMacro%
advancegoodiescount 1 % not done globally !
expandafterSmuggleMacro@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacro@aux #1#2%
expandaftergdefexpandafter#1expandafter
expandafterdefexpandafter#2expandafter#2%
aftergroup#1%
%
% This one will let the macro be known two levels higher,
% but not if only one level higher
defSmuggleMacroUpTwo%
advancegoodiescount 1 % not done globally !
expandafterSmuggleMacroUpTwo@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacroUpTwo@aux#1#2%
expandaftergdefexpandafter#1expandafter
expandafterdefexpandafter#2expandafter#2%
aftergroupSmuggleValue
aftergroup#1%
%
defSmuggleValue%
advancegoodiescount 1 % not done globally !
expandafterSmuggleValue@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleValue@aux #1#2%
globallet#1#2%
aftergroup#1%
%
makeatother
begindocument
tt
begingroup
typeoutDEPTH 1%
deffuzzFUZZ defined at depth 1 and smuggled%
SmuggleMacrofuzz
begingroup
typeoutDEPTH 2%
defbazBAZ defined at depth 2 and smuggled up two%
SmuggleMacroUpTwobaz
begingroup
typeoutDEPTH 3%
DEPTH 3par
deffooFOO defined at depth 3 and smuggled%
SmuggleMacrofoo
defbarBAR defined at depth 3 and smuggled up two%
SmuggleMacroUpTwobar
END OF FIRST DEPTH 3par
endgroup
at depth 2 in-between the two depth 3par
stringfoospace has meaning meaningfoospace and will be smuggled againpar
stringbarspace has meaning meaningbarpar
SmuggleMacrofoo
begingroup
DEPTH 3par
typeoutSECOND TIMES AT DEPTH 3%
deffoofooFOOFOO defined at (second) depth 3 and smuggled%
SmuggleMacrofoofoo
defTrucpar Hello, I am stringTrucspace
I was defined at depth 3, but got executed
at depth 2!par
My own meaning is now: meaningTrucpar
typeoutDEPTH 2 AFTER 3%
showTruc
SmuggleValueTruc
END OF SECOND DEPTH 3par
endgroup
BACK TO DEPTH 2 (after executing aftergroup tokens)par
showTruc
showfuzz
showbaz
showfoo
showfoofoo
showbar
endgroup
BACK TO DEPTH 1 (after executing aftergroup tokens)par
stringfoospace has meaning meaningfoopar
stringbarspace has meaning meaningbarpar
typeoutDEPTH 1 AFTER 2%
showfuzz
showbaz
showfoo
showfoofoo
showbar
endgroup
BACK TO DEPTH 0 (after executing aftergroup tokens)par
stringfoospace has meaning meaningfoopar
typeoutDEPTH 0 AFTER 1
showfuzz
showbaz
showfoo
showfoofoo
showbar
enddocument
DEPTH 1
DEPTH 2
DEPTH 3
SECOND TIMES AT DEPTH 3
> Truc=macro:
->par Hello, I am string Truc space I was defined at depth 3, but got executed at depth 2!par My own meaning is now: meaning Truc par typeout DEPTH 2 AFTER 3.
l.77 showTruc
DEPTH 2 AFTER 3
> Truc=undefined.
l.82 showTruc
> fuzz=macro:
->FUZZ defined at depth 1 and smuggled.
l.83 showfuzz
> baz=macro:
->BAZ defined at depth 2 and smuggled up two.
l.84 showbaz
> foo=macro:
->FOO defined at depth 3 and smuggled.
l.85 showfoo
> foofoo=macro:
->FOOFOO defined at (second) depth 3 and smuggled.
l.86 showfoofoo
> bar=macro:
->mathaccent "7016relax .
l.87 showbar
DEPTH 1 AFTER 2
> fuzz=macro:
->FUZZ defined at depth 1 and smuggled.
l.92 showfuzz
> baz=undefined.
l.93 showbaz
> foo=macro:
->FOO defined at depth 3 and smuggled.
l.94 showfoo
> foofoo=undefined.
l.95 showfoofoo
> bar=macro:
->BAR defined at depth 3 and smuggled up two.
l.96 showbar
DEPTH 0 AFTER 1
> fuzz=macro:
->FUZZ defined at depth 1 and smuggled.
l.101 showfuzz
> baz=macro:
->BAZ defined at depth 2 and smuggled up two.
l.102 showbaz
> foo=undefined.
l.103 showfoo
> foofoo=undefined.
l.104 showfoofoo
> bar=macro:
->mathaccent "7016relax .
l.105 showbar
Addendum
I am adding SmuggleMacroNtimesUp <number>.macro
which will let the macro
be known <number>
levels up (of course, to the extent that its meaning uses tokens known at these levels...). Currently only parameter less macros, because this is how I started this...
Not much tested. In fact only tested on the single example below...
documentclassarticle
newcountgoodiescount
makeatletter
defSmuggleMacro%
advancegoodiescount 1 % not done globally !
expandafterSmuggleMacro@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacro@aux #1#2%
expandaftergdefexpandafter#1expandafter
expandafterdefexpandafter#2expandafter#2%
aftergroup#1%
%
% This one will let the macro be known two levels higher,
% but not if only one level higher
defSmuggleMacroUpTwo%
advancegoodiescount 1 % not done globally !
expandafterSmuggleMacroUpTwo@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacroUpTwo@aux#1#2%
expandaftergdefexpandafter#1expandafter
expandafterdefexpandafter#2expandafter#2%
aftergroupSmuggleValue
aftergroup#1%
%
defSmuggleValue%
advancegoodiescount 1 % not done globally !
expandafterSmuggleValue@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleValue@aux #1#2%
globallet#1#2%
aftergroup#1%
%
%
% This one makes known the macros 1, 2, ..., N levels up.
% Syntax SmuggleMacroNtimesUp<number>.macro
defSmuggleMacroNtimesUp%
advancegoodiescount 1 % not done globally!
expandafterSmuggleMacroNtimesUp@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacroNtimesUp@aux#1#2.#3%
expandaftergdefexpandafter#1expandafter
expandafterdefexpandafter#3expandafter#3%
aftergroup#1%
expandafterSmuggleMacroNtimesUp@athenumexpr#2-1.#1%
%
defSmuggleMacroNtimesUp@a#1%
if0#1expandafter@gobbletwo
else
aftergroupSmuggleValueNtimesUp
aftergroup #1%
expandafterSmuggleNtimesUp@loop
fi
%
defSmuggleNtimesUp@loop#1%
aftergroup#1%
if.#1expandafteraftergroup
else
expandafterSmuggleNtimesUp@loop
fi
%
% This one makes **executes the macro**
% at all levels 1, 2, ..., N up.
% Syntax SmuggleValueNtimesUp<number>.macro
defSmuggleValueNtimesUp%
advancegoodiescount 1 % not done globally!
expandafterSmuggleValueNtimesUp@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleValueNtimesUp@aux#1#2.#3%
globallet#1#3%
aftergroup#1%
expandafterSmuggleValueNtimesUp@athenumexpr#2-1.#1%
%
defSmuggleValueNtimesUp@a#1%
if0#1expandafter@gobbletwo
else
aftergroupSmuggleValueNtimesUp
aftergroup #1%
expandafterSmuggleNtimesUp@loop
fi
%
makeatother
begindocument
ttfamily
deffoo
% 8 deep
% 16 deep
deffooFOO defined at 16 will be made known all the way to 3%
SmuggleMacroNtimesUp13.foo
16.foopar%
15.foopar%+1
14.foopar%+2
13.foopar%+3
12.foopar%+4
11.foopar%
10.foopar%
9.foopar%
8.foopar%+8
7.foopar%
6.foopar%
5.foopar%
4.foopar%+12
3.foopar%+13
2.foopar%
1.foopar%
0.foopar
enddocument
Final version
Under pressure of @Circumscribe example I have refactored to handle macros with parameters. Not much tested... Added @marmot query about moving meaning to top level (aka bottom level...)
Thus the defined things are
SmuggleMacro foo
: makesfoo
keep its meaning one level up,SmuggleMacroUpTwo foo
: makesfoo
recover its meaning two levels up (but not one level up...)SmuggleMacroNtimesUp <number>.foo
: makesfoo
keep its meaning for the<number>
less nested levels. Must be used with<number>
at least1
.SmuggleValueNtimesUp <number>.foo
: executes the meaning offoo
for the<number>
less nested levels, viaaftergroup
so as soon as a more nested level is left.foo
itselfs (if not globally defined) is not smuggled.SmuggleMacroToTopfoo
: makesfoo
known at bottom level (sic), but not at any intermediate level (of course while moving from inner to outer, once we reach bottom level, next time we enter a groupfoo
will be known).
(if the code looks crazy, it is also because it tries to define the less auxiliary storage macros, by keeping this idea of never globally stepping the index of these storage things)
documentclassarticle
usepackagegeometry
newcountgoodiescount
makeatletter
defSmuggleMacro%
advancegoodiescount 1 % not done globally !
expandafterSmuggleMacro@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacro@aux#1#2%
globallet#1#2%
aftergrouplet
aftergroup#2%
aftergroup#1%
%
% This one will let the macro be known two levels higher,
% but not if only one level higher
defSmuggleMacroUpTwo%
advancegoodiescount 1 % not done globally !
expandafterSmuggleMacroUpTwo@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacroUpTwo@aux#1#2%
globallet#1#2%
aftergroupSmuggleLet
aftergroup#2%
aftergroup#1%
%
defSmuggleLet%
advancegoodiescount 1 % not done globally !
expandafterSmuggleLet@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleLet@aux#1#2#3%
globallet#1#3%
aftergrouplet
aftergroup#2%
aftergroup#1%
%
%
% This one makes known the macros 1, 2, ..., N levels up.
% Syntax SmuggleMacroNtimesUp<number>.macro
defSmuggleMacroNtimesUp%
advancegoodiescount 1 % not done globally!
expandafterSmuggleMacroNtimesUp@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacroNtimesUp@aux#1#2.#3%
globallet#1#3%
aftergrouplet
aftergroup#3%
aftergroup#1%
expandafterSmuggleMacroNtimesUp@athenumexpr#2-1.#3%
%
%longdef@gobblethree#1#2#3%
defSmuggleMacroNtimesUp@a#1%
if0#1expandafter@gobbletwo
else
aftergroupSmuggleMacroNtimesUp
aftergroup #1%
expandafterSmuggleNtimesUp@loop
fi
%
defSmuggleNtimesUp@loop#1%
aftergroup#1%
if.#1expandafteraftergroup
else
expandafterSmuggleNtimesUp@loop
fi
%
defSmuggleValueNtimesUp%
advancegoodiescount 1 % not done globally!
expandafterSmuggleValueNtimesUp@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleValueNtimesUp@aux#1#2.#3%
globallet#1#3%
aftergroup#1%
expandafterSmuggleValueNtimesUp@athenumexpr#2-1.#1%
%
defSmuggleValueNtimesUp@a#1%
if0#1expandafter@gobbletwo
else
aftergroupSmuggleValueNtimesUp
aftergroup #1%
expandafterSmuggleNtimesUp@loop
fi
%
% SmuggleMacroToTop
defSmuggleMacroToTop%
ifnumcurrentgrouplevel=z@
expandafter@gobble
else
expandafterSmuggleMacro@ToTop
fi
%
defSmuggleMacro@ToTop%
advancegoodiescount 1 % not done globally!
expandafterSmuggleMacroToTop@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacroToTop@aux#1#2%
globallet#1#2%
aftergroupSmuggleLetToTop
aftergroup#2%
aftergroup#1%
%
defSmuggleLetToTop%
ifnumcurrentgrouplevel=z@
expandafterlet
else
expandafterSmuggleLet@ToTop
fi
%
defSmuggleLet@ToTop%
advancegoodiescount 1 % not done globally !
expandafterSmuggleLetToTop@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleLetToTop@aux#1#2#3%
globallet#1#3%
aftergroupSmuggleLetToTop
aftergroup#2%
aftergroup#1%
%
makeatother
begindocument
ttfamily
deffoo
% 8 deep
% 16 deep
defBAR#1#2#3Hello, I am BAR%
SmuggleMacroBAR
SmuggleMacroToTopBAR
defBAZ#1#2#3#4Hello, I am BAZ%
SmuggleMacroUpTwoBAZ
deffoo#1#2FOO defined at 16 will be made known all the way to 3%
SmuggleMacroNtimesUp13.foo
16.FOO meaningfoopar
16.BAZ meaningBAZpar
16.BAR meaningBARpar
defxleavevmodellapaaa %
SmuggleValueNtimesUp7.x
medskip%
15.FOO meaningfoopar
15.BAZ meaningBAZpar
15.BAR meaningBARparmedskip%
14.FOO meaningfoopar
14.BAZ meaningBAZpar
14.BAR meaningBARparmedskip%
13.FOO meaningfoopar%+3
12.FOO meaningfoopar%+4
11.FOO meaningfoopar%
10.FOO meaningfoopar%
9.FOO meaningfoopar
9.BAR meaningBARpar
%
8.FOO meaningfoopar%+8
7.FOO meaningfoopar%
6.FOO meaningfoopar%
5.FOO meaningfoopar
5.BAR meaningBARpar%
4.FOO meaningfoopar%+12
3.FOO meaningfoopar%+13
2.FOO meaningfoopar%
1.FOO meaningfoopar%
0.FOO meaningfoopar
0.BAR meaningBARpar
enddocument
although I may need to think about it more: I think it is fine, although I have not really thought at depth. (ah ah).
– user4686
Jan 20 at 13:33
Trying to mentally parseSmuggleMacroUpTwo
makes my head hurt, but it appears to be airtight. You're really minimising the number of different macros that need to be assigned by keeping track of how many smuggling operations are currently ongoing and assigning them appropriate numbers :).
– Circumscribe
Jan 20 at 14:20
@Circumscribe yes, after a group closes, new definition of a given index can only happen after or at same time its old meaning is actually needed, and when you open an inner group you can only then define macros with a higher index than what you had stored earlier as to be used after the outer group. Thus, ..., it should be airtight indeed, but indeed a bit head-spinning :)
– user4686
Jan 20 at 15:04
Did you read " Of course, infty means that this macro has the value in all LaTeX documents of the world. ;-) " ? ;-)
– marmot
Jan 20 at 17:56
@marmot I understood that I would become rich, did I get that right?
– user4686
Jan 20 at 17:58
|
show 1 more comment
This covers various things:
smuggling a (no-parameter; see bottom of answer for macros with parameters) macro one level up,
(bizarre, for fun) smuggle it two level up, but it remains undefined one level up,
(more useful) smuggle one level up the contents of some macro, which is way to execute after group closes arbitrarily many tokens (if they are still defined at that level of course).
There is some subtlety in the way some globally defined auxiliary macros are indexed (their index is never globally increased), but I think it is ok. I hesitated about adding extra code to let them globally undefined once used, but did not do that finally. The same index will be used multiple times, but I think no non-yet needed thing ever gets overwritten. (although I may need to think about it more).
documentclassarticle
newcountgoodiescount
makeatletter
defSmuggleMacro%
advancegoodiescount 1 % not done globally !
expandafterSmuggleMacro@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacro@aux #1#2%
expandaftergdefexpandafter#1expandafter
expandafterdefexpandafter#2expandafter#2%
aftergroup#1%
%
% This one will let the macro be known two levels higher,
% but not if only one level higher
defSmuggleMacroUpTwo%
advancegoodiescount 1 % not done globally !
expandafterSmuggleMacroUpTwo@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacroUpTwo@aux#1#2%
expandaftergdefexpandafter#1expandafter
expandafterdefexpandafter#2expandafter#2%
aftergroupSmuggleValue
aftergroup#1%
%
defSmuggleValue%
advancegoodiescount 1 % not done globally !
expandafterSmuggleValue@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleValue@aux #1#2%
globallet#1#2%
aftergroup#1%
%
makeatother
begindocument
tt
begingroup
typeoutDEPTH 1%
deffuzzFUZZ defined at depth 1 and smuggled%
SmuggleMacrofuzz
begingroup
typeoutDEPTH 2%
defbazBAZ defined at depth 2 and smuggled up two%
SmuggleMacroUpTwobaz
begingroup
typeoutDEPTH 3%
DEPTH 3par
deffooFOO defined at depth 3 and smuggled%
SmuggleMacrofoo
defbarBAR defined at depth 3 and smuggled up two%
SmuggleMacroUpTwobar
END OF FIRST DEPTH 3par
endgroup
at depth 2 in-between the two depth 3par
stringfoospace has meaning meaningfoospace and will be smuggled againpar
stringbarspace has meaning meaningbarpar
SmuggleMacrofoo
begingroup
DEPTH 3par
typeoutSECOND TIMES AT DEPTH 3%
deffoofooFOOFOO defined at (second) depth 3 and smuggled%
SmuggleMacrofoofoo
defTrucpar Hello, I am stringTrucspace
I was defined at depth 3, but got executed
at depth 2!par
My own meaning is now: meaningTrucpar
typeoutDEPTH 2 AFTER 3%
showTruc
SmuggleValueTruc
END OF SECOND DEPTH 3par
endgroup
BACK TO DEPTH 2 (after executing aftergroup tokens)par
showTruc
showfuzz
showbaz
showfoo
showfoofoo
showbar
endgroup
BACK TO DEPTH 1 (after executing aftergroup tokens)par
stringfoospace has meaning meaningfoopar
stringbarspace has meaning meaningbarpar
typeoutDEPTH 1 AFTER 2%
showfuzz
showbaz
showfoo
showfoofoo
showbar
endgroup
BACK TO DEPTH 0 (after executing aftergroup tokens)par
stringfoospace has meaning meaningfoopar
typeoutDEPTH 0 AFTER 1
showfuzz
showbaz
showfoo
showfoofoo
showbar
enddocument
DEPTH 1
DEPTH 2
DEPTH 3
SECOND TIMES AT DEPTH 3
> Truc=macro:
->par Hello, I am string Truc space I was defined at depth 3, but got executed at depth 2!par My own meaning is now: meaning Truc par typeout DEPTH 2 AFTER 3.
l.77 showTruc
DEPTH 2 AFTER 3
> Truc=undefined.
l.82 showTruc
> fuzz=macro:
->FUZZ defined at depth 1 and smuggled.
l.83 showfuzz
> baz=macro:
->BAZ defined at depth 2 and smuggled up two.
l.84 showbaz
> foo=macro:
->FOO defined at depth 3 and smuggled.
l.85 showfoo
> foofoo=macro:
->FOOFOO defined at (second) depth 3 and smuggled.
l.86 showfoofoo
> bar=macro:
->mathaccent "7016relax .
l.87 showbar
DEPTH 1 AFTER 2
> fuzz=macro:
->FUZZ defined at depth 1 and smuggled.
l.92 showfuzz
> baz=undefined.
l.93 showbaz
> foo=macro:
->FOO defined at depth 3 and smuggled.
l.94 showfoo
> foofoo=undefined.
l.95 showfoofoo
> bar=macro:
->BAR defined at depth 3 and smuggled up two.
l.96 showbar
DEPTH 0 AFTER 1
> fuzz=macro:
->FUZZ defined at depth 1 and smuggled.
l.101 showfuzz
> baz=macro:
->BAZ defined at depth 2 and smuggled up two.
l.102 showbaz
> foo=undefined.
l.103 showfoo
> foofoo=undefined.
l.104 showfoofoo
> bar=macro:
->mathaccent "7016relax .
l.105 showbar
Addendum
I am adding SmuggleMacroNtimesUp <number>.macro
which will let the macro
be known <number>
levels up (of course, to the extent that its meaning uses tokens known at these levels...). Currently only parameter less macros, because this is how I started this...
Not much tested. In fact only tested on the single example below...
documentclassarticle
newcountgoodiescount
makeatletter
defSmuggleMacro%
advancegoodiescount 1 % not done globally !
expandafterSmuggleMacro@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacro@aux #1#2%
expandaftergdefexpandafter#1expandafter
expandafterdefexpandafter#2expandafter#2%
aftergroup#1%
%
% This one will let the macro be known two levels higher,
% but not if only one level higher
defSmuggleMacroUpTwo%
advancegoodiescount 1 % not done globally !
expandafterSmuggleMacroUpTwo@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacroUpTwo@aux#1#2%
expandaftergdefexpandafter#1expandafter
expandafterdefexpandafter#2expandafter#2%
aftergroupSmuggleValue
aftergroup#1%
%
defSmuggleValue%
advancegoodiescount 1 % not done globally !
expandafterSmuggleValue@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleValue@aux #1#2%
globallet#1#2%
aftergroup#1%
%
%
% This one makes known the macros 1, 2, ..., N levels up.
% Syntax SmuggleMacroNtimesUp<number>.macro
defSmuggleMacroNtimesUp%
advancegoodiescount 1 % not done globally!
expandafterSmuggleMacroNtimesUp@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacroNtimesUp@aux#1#2.#3%
expandaftergdefexpandafter#1expandafter
expandafterdefexpandafter#3expandafter#3%
aftergroup#1%
expandafterSmuggleMacroNtimesUp@athenumexpr#2-1.#1%
%
defSmuggleMacroNtimesUp@a#1%
if0#1expandafter@gobbletwo
else
aftergroupSmuggleValueNtimesUp
aftergroup #1%
expandafterSmuggleNtimesUp@loop
fi
%
defSmuggleNtimesUp@loop#1%
aftergroup#1%
if.#1expandafteraftergroup
else
expandafterSmuggleNtimesUp@loop
fi
%
% This one makes **executes the macro**
% at all levels 1, 2, ..., N up.
% Syntax SmuggleValueNtimesUp<number>.macro
defSmuggleValueNtimesUp%
advancegoodiescount 1 % not done globally!
expandafterSmuggleValueNtimesUp@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleValueNtimesUp@aux#1#2.#3%
globallet#1#3%
aftergroup#1%
expandafterSmuggleValueNtimesUp@athenumexpr#2-1.#1%
%
defSmuggleValueNtimesUp@a#1%
if0#1expandafter@gobbletwo
else
aftergroupSmuggleValueNtimesUp
aftergroup #1%
expandafterSmuggleNtimesUp@loop
fi
%
makeatother
begindocument
ttfamily
deffoo
% 8 deep
% 16 deep
deffooFOO defined at 16 will be made known all the way to 3%
SmuggleMacroNtimesUp13.foo
16.foopar%
15.foopar%+1
14.foopar%+2
13.foopar%+3
12.foopar%+4
11.foopar%
10.foopar%
9.foopar%
8.foopar%+8
7.foopar%
6.foopar%
5.foopar%
4.foopar%+12
3.foopar%+13
2.foopar%
1.foopar%
0.foopar
enddocument
Final version
Under pressure of @Circumscribe example I have refactored to handle macros with parameters. Not much tested... Added @marmot query about moving meaning to top level (aka bottom level...)
Thus the defined things are
SmuggleMacro foo
: makesfoo
keep its meaning one level up,SmuggleMacroUpTwo foo
: makesfoo
recover its meaning two levels up (but not one level up...)SmuggleMacroNtimesUp <number>.foo
: makesfoo
keep its meaning for the<number>
less nested levels. Must be used with<number>
at least1
.SmuggleValueNtimesUp <number>.foo
: executes the meaning offoo
for the<number>
less nested levels, viaaftergroup
so as soon as a more nested level is left.foo
itselfs (if not globally defined) is not smuggled.SmuggleMacroToTopfoo
: makesfoo
known at bottom level (sic), but not at any intermediate level (of course while moving from inner to outer, once we reach bottom level, next time we enter a groupfoo
will be known).
(if the code looks crazy, it is also because it tries to define the less auxiliary storage macros, by keeping this idea of never globally stepping the index of these storage things)
documentclassarticle
usepackagegeometry
newcountgoodiescount
makeatletter
defSmuggleMacro%
advancegoodiescount 1 % not done globally !
expandafterSmuggleMacro@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacro@aux#1#2%
globallet#1#2%
aftergrouplet
aftergroup#2%
aftergroup#1%
%
% This one will let the macro be known two levels higher,
% but not if only one level higher
defSmuggleMacroUpTwo%
advancegoodiescount 1 % not done globally !
expandafterSmuggleMacroUpTwo@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacroUpTwo@aux#1#2%
globallet#1#2%
aftergroupSmuggleLet
aftergroup#2%
aftergroup#1%
%
defSmuggleLet%
advancegoodiescount 1 % not done globally !
expandafterSmuggleLet@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleLet@aux#1#2#3%
globallet#1#3%
aftergrouplet
aftergroup#2%
aftergroup#1%
%
%
% This one makes known the macros 1, 2, ..., N levels up.
% Syntax SmuggleMacroNtimesUp<number>.macro
defSmuggleMacroNtimesUp%
advancegoodiescount 1 % not done globally!
expandafterSmuggleMacroNtimesUp@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacroNtimesUp@aux#1#2.#3%
globallet#1#3%
aftergrouplet
aftergroup#3%
aftergroup#1%
expandafterSmuggleMacroNtimesUp@athenumexpr#2-1.#3%
%
%longdef@gobblethree#1#2#3%
defSmuggleMacroNtimesUp@a#1%
if0#1expandafter@gobbletwo
else
aftergroupSmuggleMacroNtimesUp
aftergroup #1%
expandafterSmuggleNtimesUp@loop
fi
%
defSmuggleNtimesUp@loop#1%
aftergroup#1%
if.#1expandafteraftergroup
else
expandafterSmuggleNtimesUp@loop
fi
%
defSmuggleValueNtimesUp%
advancegoodiescount 1 % not done globally!
expandafterSmuggleValueNtimesUp@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleValueNtimesUp@aux#1#2.#3%
globallet#1#3%
aftergroup#1%
expandafterSmuggleValueNtimesUp@athenumexpr#2-1.#1%
%
defSmuggleValueNtimesUp@a#1%
if0#1expandafter@gobbletwo
else
aftergroupSmuggleValueNtimesUp
aftergroup #1%
expandafterSmuggleNtimesUp@loop
fi
%
% SmuggleMacroToTop
defSmuggleMacroToTop%
ifnumcurrentgrouplevel=z@
expandafter@gobble
else
expandafterSmuggleMacro@ToTop
fi
%
defSmuggleMacro@ToTop%
advancegoodiescount 1 % not done globally!
expandafterSmuggleMacroToTop@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacroToTop@aux#1#2%
globallet#1#2%
aftergroupSmuggleLetToTop
aftergroup#2%
aftergroup#1%
%
defSmuggleLetToTop%
ifnumcurrentgrouplevel=z@
expandafterlet
else
expandafterSmuggleLet@ToTop
fi
%
defSmuggleLet@ToTop%
advancegoodiescount 1 % not done globally !
expandafterSmuggleLetToTop@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleLetToTop@aux#1#2#3%
globallet#1#3%
aftergroupSmuggleLetToTop
aftergroup#2%
aftergroup#1%
%
makeatother
begindocument
ttfamily
deffoo
% 8 deep
% 16 deep
defBAR#1#2#3Hello, I am BAR%
SmuggleMacroBAR
SmuggleMacroToTopBAR
defBAZ#1#2#3#4Hello, I am BAZ%
SmuggleMacroUpTwoBAZ
deffoo#1#2FOO defined at 16 will be made known all the way to 3%
SmuggleMacroNtimesUp13.foo
16.FOO meaningfoopar
16.BAZ meaningBAZpar
16.BAR meaningBARpar
defxleavevmodellapaaa %
SmuggleValueNtimesUp7.x
medskip%
15.FOO meaningfoopar
15.BAZ meaningBAZpar
15.BAR meaningBARparmedskip%
14.FOO meaningfoopar
14.BAZ meaningBAZpar
14.BAR meaningBARparmedskip%
13.FOO meaningfoopar%+3
12.FOO meaningfoopar%+4
11.FOO meaningfoopar%
10.FOO meaningfoopar%
9.FOO meaningfoopar
9.BAR meaningBARpar
%
8.FOO meaningfoopar%+8
7.FOO meaningfoopar%
6.FOO meaningfoopar%
5.FOO meaningfoopar
5.BAR meaningBARpar%
4.FOO meaningfoopar%+12
3.FOO meaningfoopar%+13
2.FOO meaningfoopar%
1.FOO meaningfoopar%
0.FOO meaningfoopar
0.BAR meaningBARpar
enddocument
although I may need to think about it more: I think it is fine, although I have not really thought at depth. (ah ah).
– user4686
Jan 20 at 13:33
Trying to mentally parseSmuggleMacroUpTwo
makes my head hurt, but it appears to be airtight. You're really minimising the number of different macros that need to be assigned by keeping track of how many smuggling operations are currently ongoing and assigning them appropriate numbers :).
– Circumscribe
Jan 20 at 14:20
@Circumscribe yes, after a group closes, new definition of a given index can only happen after or at same time its old meaning is actually needed, and when you open an inner group you can only then define macros with a higher index than what you had stored earlier as to be used after the outer group. Thus, ..., it should be airtight indeed, but indeed a bit head-spinning :)
– user4686
Jan 20 at 15:04
Did you read " Of course, infty means that this macro has the value in all LaTeX documents of the world. ;-) " ? ;-)
– marmot
Jan 20 at 17:56
@marmot I understood that I would become rich, did I get that right?
– user4686
Jan 20 at 17:58
|
show 1 more comment
This covers various things:
smuggling a (no-parameter; see bottom of answer for macros with parameters) macro one level up,
(bizarre, for fun) smuggle it two level up, but it remains undefined one level up,
(more useful) smuggle one level up the contents of some macro, which is way to execute after group closes arbitrarily many tokens (if they are still defined at that level of course).
There is some subtlety in the way some globally defined auxiliary macros are indexed (their index is never globally increased), but I think it is ok. I hesitated about adding extra code to let them globally undefined once used, but did not do that finally. The same index will be used multiple times, but I think no non-yet needed thing ever gets overwritten. (although I may need to think about it more).
documentclassarticle
newcountgoodiescount
makeatletter
defSmuggleMacro%
advancegoodiescount 1 % not done globally !
expandafterSmuggleMacro@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacro@aux #1#2%
expandaftergdefexpandafter#1expandafter
expandafterdefexpandafter#2expandafter#2%
aftergroup#1%
%
% This one will let the macro be known two levels higher,
% but not if only one level higher
defSmuggleMacroUpTwo%
advancegoodiescount 1 % not done globally !
expandafterSmuggleMacroUpTwo@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacroUpTwo@aux#1#2%
expandaftergdefexpandafter#1expandafter
expandafterdefexpandafter#2expandafter#2%
aftergroupSmuggleValue
aftergroup#1%
%
defSmuggleValue%
advancegoodiescount 1 % not done globally !
expandafterSmuggleValue@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleValue@aux #1#2%
globallet#1#2%
aftergroup#1%
%
makeatother
begindocument
tt
begingroup
typeoutDEPTH 1%
deffuzzFUZZ defined at depth 1 and smuggled%
SmuggleMacrofuzz
begingroup
typeoutDEPTH 2%
defbazBAZ defined at depth 2 and smuggled up two%
SmuggleMacroUpTwobaz
begingroup
typeoutDEPTH 3%
DEPTH 3par
deffooFOO defined at depth 3 and smuggled%
SmuggleMacrofoo
defbarBAR defined at depth 3 and smuggled up two%
SmuggleMacroUpTwobar
END OF FIRST DEPTH 3par
endgroup
at depth 2 in-between the two depth 3par
stringfoospace has meaning meaningfoospace and will be smuggled againpar
stringbarspace has meaning meaningbarpar
SmuggleMacrofoo
begingroup
DEPTH 3par
typeoutSECOND TIMES AT DEPTH 3%
deffoofooFOOFOO defined at (second) depth 3 and smuggled%
SmuggleMacrofoofoo
defTrucpar Hello, I am stringTrucspace
I was defined at depth 3, but got executed
at depth 2!par
My own meaning is now: meaningTrucpar
typeoutDEPTH 2 AFTER 3%
showTruc
SmuggleValueTruc
END OF SECOND DEPTH 3par
endgroup
BACK TO DEPTH 2 (after executing aftergroup tokens)par
showTruc
showfuzz
showbaz
showfoo
showfoofoo
showbar
endgroup
BACK TO DEPTH 1 (after executing aftergroup tokens)par
stringfoospace has meaning meaningfoopar
stringbarspace has meaning meaningbarpar
typeoutDEPTH 1 AFTER 2%
showfuzz
showbaz
showfoo
showfoofoo
showbar
endgroup
BACK TO DEPTH 0 (after executing aftergroup tokens)par
stringfoospace has meaning meaningfoopar
typeoutDEPTH 0 AFTER 1
showfuzz
showbaz
showfoo
showfoofoo
showbar
enddocument
DEPTH 1
DEPTH 2
DEPTH 3
SECOND TIMES AT DEPTH 3
> Truc=macro:
->par Hello, I am string Truc space I was defined at depth 3, but got executed at depth 2!par My own meaning is now: meaning Truc par typeout DEPTH 2 AFTER 3.
l.77 showTruc
DEPTH 2 AFTER 3
> Truc=undefined.
l.82 showTruc
> fuzz=macro:
->FUZZ defined at depth 1 and smuggled.
l.83 showfuzz
> baz=macro:
->BAZ defined at depth 2 and smuggled up two.
l.84 showbaz
> foo=macro:
->FOO defined at depth 3 and smuggled.
l.85 showfoo
> foofoo=macro:
->FOOFOO defined at (second) depth 3 and smuggled.
l.86 showfoofoo
> bar=macro:
->mathaccent "7016relax .
l.87 showbar
DEPTH 1 AFTER 2
> fuzz=macro:
->FUZZ defined at depth 1 and smuggled.
l.92 showfuzz
> baz=undefined.
l.93 showbaz
> foo=macro:
->FOO defined at depth 3 and smuggled.
l.94 showfoo
> foofoo=undefined.
l.95 showfoofoo
> bar=macro:
->BAR defined at depth 3 and smuggled up two.
l.96 showbar
DEPTH 0 AFTER 1
> fuzz=macro:
->FUZZ defined at depth 1 and smuggled.
l.101 showfuzz
> baz=macro:
->BAZ defined at depth 2 and smuggled up two.
l.102 showbaz
> foo=undefined.
l.103 showfoo
> foofoo=undefined.
l.104 showfoofoo
> bar=macro:
->mathaccent "7016relax .
l.105 showbar
Addendum
I am adding SmuggleMacroNtimesUp <number>.macro
which will let the macro
be known <number>
levels up (of course, to the extent that its meaning uses tokens known at these levels...). Currently only parameter less macros, because this is how I started this...
Not much tested. In fact only tested on the single example below...
documentclassarticle
newcountgoodiescount
makeatletter
defSmuggleMacro%
advancegoodiescount 1 % not done globally !
expandafterSmuggleMacro@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacro@aux #1#2%
expandaftergdefexpandafter#1expandafter
expandafterdefexpandafter#2expandafter#2%
aftergroup#1%
%
% This one will let the macro be known two levels higher,
% but not if only one level higher
defSmuggleMacroUpTwo%
advancegoodiescount 1 % not done globally !
expandafterSmuggleMacroUpTwo@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacroUpTwo@aux#1#2%
expandaftergdefexpandafter#1expandafter
expandafterdefexpandafter#2expandafter#2%
aftergroupSmuggleValue
aftergroup#1%
%
defSmuggleValue%
advancegoodiescount 1 % not done globally !
expandafterSmuggleValue@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleValue@aux #1#2%
globallet#1#2%
aftergroup#1%
%
%
% This one makes known the macros 1, 2, ..., N levels up.
% Syntax SmuggleMacroNtimesUp<number>.macro
defSmuggleMacroNtimesUp%
advancegoodiescount 1 % not done globally!
expandafterSmuggleMacroNtimesUp@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacroNtimesUp@aux#1#2.#3%
expandaftergdefexpandafter#1expandafter
expandafterdefexpandafter#3expandafter#3%
aftergroup#1%
expandafterSmuggleMacroNtimesUp@athenumexpr#2-1.#1%
%
defSmuggleMacroNtimesUp@a#1%
if0#1expandafter@gobbletwo
else
aftergroupSmuggleValueNtimesUp
aftergroup #1%
expandafterSmuggleNtimesUp@loop
fi
%
defSmuggleNtimesUp@loop#1%
aftergroup#1%
if.#1expandafteraftergroup
else
expandafterSmuggleNtimesUp@loop
fi
%
% This one makes **executes the macro**
% at all levels 1, 2, ..., N up.
% Syntax SmuggleValueNtimesUp<number>.macro
defSmuggleValueNtimesUp%
advancegoodiescount 1 % not done globally!
expandafterSmuggleValueNtimesUp@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleValueNtimesUp@aux#1#2.#3%
globallet#1#3%
aftergroup#1%
expandafterSmuggleValueNtimesUp@athenumexpr#2-1.#1%
%
defSmuggleValueNtimesUp@a#1%
if0#1expandafter@gobbletwo
else
aftergroupSmuggleValueNtimesUp
aftergroup #1%
expandafterSmuggleNtimesUp@loop
fi
%
makeatother
begindocument
ttfamily
deffoo
% 8 deep
% 16 deep
deffooFOO defined at 16 will be made known all the way to 3%
SmuggleMacroNtimesUp13.foo
16.foopar%
15.foopar%+1
14.foopar%+2
13.foopar%+3
12.foopar%+4
11.foopar%
10.foopar%
9.foopar%
8.foopar%+8
7.foopar%
6.foopar%
5.foopar%
4.foopar%+12
3.foopar%+13
2.foopar%
1.foopar%
0.foopar
enddocument
Final version
Under pressure of @Circumscribe example I have refactored to handle macros with parameters. Not much tested... Added @marmot query about moving meaning to top level (aka bottom level...)
Thus the defined things are
SmuggleMacro foo
: makesfoo
keep its meaning one level up,SmuggleMacroUpTwo foo
: makesfoo
recover its meaning two levels up (but not one level up...)SmuggleMacroNtimesUp <number>.foo
: makesfoo
keep its meaning for the<number>
less nested levels. Must be used with<number>
at least1
.SmuggleValueNtimesUp <number>.foo
: executes the meaning offoo
for the<number>
less nested levels, viaaftergroup
so as soon as a more nested level is left.foo
itselfs (if not globally defined) is not smuggled.SmuggleMacroToTopfoo
: makesfoo
known at bottom level (sic), but not at any intermediate level (of course while moving from inner to outer, once we reach bottom level, next time we enter a groupfoo
will be known).
(if the code looks crazy, it is also because it tries to define the less auxiliary storage macros, by keeping this idea of never globally stepping the index of these storage things)
documentclassarticle
usepackagegeometry
newcountgoodiescount
makeatletter
defSmuggleMacro%
advancegoodiescount 1 % not done globally !
expandafterSmuggleMacro@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacro@aux#1#2%
globallet#1#2%
aftergrouplet
aftergroup#2%
aftergroup#1%
%
% This one will let the macro be known two levels higher,
% but not if only one level higher
defSmuggleMacroUpTwo%
advancegoodiescount 1 % not done globally !
expandafterSmuggleMacroUpTwo@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacroUpTwo@aux#1#2%
globallet#1#2%
aftergroupSmuggleLet
aftergroup#2%
aftergroup#1%
%
defSmuggleLet%
advancegoodiescount 1 % not done globally !
expandafterSmuggleLet@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleLet@aux#1#2#3%
globallet#1#3%
aftergrouplet
aftergroup#2%
aftergroup#1%
%
%
% This one makes known the macros 1, 2, ..., N levels up.
% Syntax SmuggleMacroNtimesUp<number>.macro
defSmuggleMacroNtimesUp%
advancegoodiescount 1 % not done globally!
expandafterSmuggleMacroNtimesUp@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacroNtimesUp@aux#1#2.#3%
globallet#1#3%
aftergrouplet
aftergroup#3%
aftergroup#1%
expandafterSmuggleMacroNtimesUp@athenumexpr#2-1.#3%
%
%longdef@gobblethree#1#2#3%
defSmuggleMacroNtimesUp@a#1%
if0#1expandafter@gobbletwo
else
aftergroupSmuggleMacroNtimesUp
aftergroup #1%
expandafterSmuggleNtimesUp@loop
fi
%
defSmuggleNtimesUp@loop#1%
aftergroup#1%
if.#1expandafteraftergroup
else
expandafterSmuggleNtimesUp@loop
fi
%
defSmuggleValueNtimesUp%
advancegoodiescount 1 % not done globally!
expandafterSmuggleValueNtimesUp@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleValueNtimesUp@aux#1#2.#3%
globallet#1#3%
aftergroup#1%
expandafterSmuggleValueNtimesUp@athenumexpr#2-1.#1%
%
defSmuggleValueNtimesUp@a#1%
if0#1expandafter@gobbletwo
else
aftergroupSmuggleValueNtimesUp
aftergroup #1%
expandafterSmuggleNtimesUp@loop
fi
%
% SmuggleMacroToTop
defSmuggleMacroToTop%
ifnumcurrentgrouplevel=z@
expandafter@gobble
else
expandafterSmuggleMacro@ToTop
fi
%
defSmuggleMacro@ToTop%
advancegoodiescount 1 % not done globally!
expandafterSmuggleMacroToTop@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacroToTop@aux#1#2%
globallet#1#2%
aftergroupSmuggleLetToTop
aftergroup#2%
aftergroup#1%
%
defSmuggleLetToTop%
ifnumcurrentgrouplevel=z@
expandafterlet
else
expandafterSmuggleLet@ToTop
fi
%
defSmuggleLet@ToTop%
advancegoodiescount 1 % not done globally !
expandafterSmuggleLetToTop@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleLetToTop@aux#1#2#3%
globallet#1#3%
aftergroupSmuggleLetToTop
aftergroup#2%
aftergroup#1%
%
makeatother
begindocument
ttfamily
deffoo
% 8 deep
% 16 deep
defBAR#1#2#3Hello, I am BAR%
SmuggleMacroBAR
SmuggleMacroToTopBAR
defBAZ#1#2#3#4Hello, I am BAZ%
SmuggleMacroUpTwoBAZ
deffoo#1#2FOO defined at 16 will be made known all the way to 3%
SmuggleMacroNtimesUp13.foo
16.FOO meaningfoopar
16.BAZ meaningBAZpar
16.BAR meaningBARpar
defxleavevmodellapaaa %
SmuggleValueNtimesUp7.x
medskip%
15.FOO meaningfoopar
15.BAZ meaningBAZpar
15.BAR meaningBARparmedskip%
14.FOO meaningfoopar
14.BAZ meaningBAZpar
14.BAR meaningBARparmedskip%
13.FOO meaningfoopar%+3
12.FOO meaningfoopar%+4
11.FOO meaningfoopar%
10.FOO meaningfoopar%
9.FOO meaningfoopar
9.BAR meaningBARpar
%
8.FOO meaningfoopar%+8
7.FOO meaningfoopar%
6.FOO meaningfoopar%
5.FOO meaningfoopar
5.BAR meaningBARpar%
4.FOO meaningfoopar%+12
3.FOO meaningfoopar%+13
2.FOO meaningfoopar%
1.FOO meaningfoopar%
0.FOO meaningfoopar
0.BAR meaningBARpar
enddocument
This covers various things:
smuggling a (no-parameter; see bottom of answer for macros with parameters) macro one level up,
(bizarre, for fun) smuggle it two level up, but it remains undefined one level up,
(more useful) smuggle one level up the contents of some macro, which is way to execute after group closes arbitrarily many tokens (if they are still defined at that level of course).
There is some subtlety in the way some globally defined auxiliary macros are indexed (their index is never globally increased), but I think it is ok. I hesitated about adding extra code to let them globally undefined once used, but did not do that finally. The same index will be used multiple times, but I think no non-yet needed thing ever gets overwritten. (although I may need to think about it more).
documentclassarticle
newcountgoodiescount
makeatletter
defSmuggleMacro%
advancegoodiescount 1 % not done globally !
expandafterSmuggleMacro@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacro@aux #1#2%
expandaftergdefexpandafter#1expandafter
expandafterdefexpandafter#2expandafter#2%
aftergroup#1%
%
% This one will let the macro be known two levels higher,
% but not if only one level higher
defSmuggleMacroUpTwo%
advancegoodiescount 1 % not done globally !
expandafterSmuggleMacroUpTwo@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacroUpTwo@aux#1#2%
expandaftergdefexpandafter#1expandafter
expandafterdefexpandafter#2expandafter#2%
aftergroupSmuggleValue
aftergroup#1%
%
defSmuggleValue%
advancegoodiescount 1 % not done globally !
expandafterSmuggleValue@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleValue@aux #1#2%
globallet#1#2%
aftergroup#1%
%
makeatother
begindocument
tt
begingroup
typeoutDEPTH 1%
deffuzzFUZZ defined at depth 1 and smuggled%
SmuggleMacrofuzz
begingroup
typeoutDEPTH 2%
defbazBAZ defined at depth 2 and smuggled up two%
SmuggleMacroUpTwobaz
begingroup
typeoutDEPTH 3%
DEPTH 3par
deffooFOO defined at depth 3 and smuggled%
SmuggleMacrofoo
defbarBAR defined at depth 3 and smuggled up two%
SmuggleMacroUpTwobar
END OF FIRST DEPTH 3par
endgroup
at depth 2 in-between the two depth 3par
stringfoospace has meaning meaningfoospace and will be smuggled againpar
stringbarspace has meaning meaningbarpar
SmuggleMacrofoo
begingroup
DEPTH 3par
typeoutSECOND TIMES AT DEPTH 3%
deffoofooFOOFOO defined at (second) depth 3 and smuggled%
SmuggleMacrofoofoo
defTrucpar Hello, I am stringTrucspace
I was defined at depth 3, but got executed
at depth 2!par
My own meaning is now: meaningTrucpar
typeoutDEPTH 2 AFTER 3%
showTruc
SmuggleValueTruc
END OF SECOND DEPTH 3par
endgroup
BACK TO DEPTH 2 (after executing aftergroup tokens)par
showTruc
showfuzz
showbaz
showfoo
showfoofoo
showbar
endgroup
BACK TO DEPTH 1 (after executing aftergroup tokens)par
stringfoospace has meaning meaningfoopar
stringbarspace has meaning meaningbarpar
typeoutDEPTH 1 AFTER 2%
showfuzz
showbaz
showfoo
showfoofoo
showbar
endgroup
BACK TO DEPTH 0 (after executing aftergroup tokens)par
stringfoospace has meaning meaningfoopar
typeoutDEPTH 0 AFTER 1
showfuzz
showbaz
showfoo
showfoofoo
showbar
enddocument
DEPTH 1
DEPTH 2
DEPTH 3
SECOND TIMES AT DEPTH 3
> Truc=macro:
->par Hello, I am string Truc space I was defined at depth 3, but got executed at depth 2!par My own meaning is now: meaning Truc par typeout DEPTH 2 AFTER 3.
l.77 showTruc
DEPTH 2 AFTER 3
> Truc=undefined.
l.82 showTruc
> fuzz=macro:
->FUZZ defined at depth 1 and smuggled.
l.83 showfuzz
> baz=macro:
->BAZ defined at depth 2 and smuggled up two.
l.84 showbaz
> foo=macro:
->FOO defined at depth 3 and smuggled.
l.85 showfoo
> foofoo=macro:
->FOOFOO defined at (second) depth 3 and smuggled.
l.86 showfoofoo
> bar=macro:
->mathaccent "7016relax .
l.87 showbar
DEPTH 1 AFTER 2
> fuzz=macro:
->FUZZ defined at depth 1 and smuggled.
l.92 showfuzz
> baz=undefined.
l.93 showbaz
> foo=macro:
->FOO defined at depth 3 and smuggled.
l.94 showfoo
> foofoo=undefined.
l.95 showfoofoo
> bar=macro:
->BAR defined at depth 3 and smuggled up two.
l.96 showbar
DEPTH 0 AFTER 1
> fuzz=macro:
->FUZZ defined at depth 1 and smuggled.
l.101 showfuzz
> baz=macro:
->BAZ defined at depth 2 and smuggled up two.
l.102 showbaz
> foo=undefined.
l.103 showfoo
> foofoo=undefined.
l.104 showfoofoo
> bar=macro:
->mathaccent "7016relax .
l.105 showbar
Addendum
I am adding SmuggleMacroNtimesUp <number>.macro
which will let the macro
be known <number>
levels up (of course, to the extent that its meaning uses tokens known at these levels...). Currently only parameter less macros, because this is how I started this...
Not much tested. In fact only tested on the single example below...
documentclassarticle
newcountgoodiescount
makeatletter
defSmuggleMacro%
advancegoodiescount 1 % not done globally !
expandafterSmuggleMacro@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacro@aux #1#2%
expandaftergdefexpandafter#1expandafter
expandafterdefexpandafter#2expandafter#2%
aftergroup#1%
%
% This one will let the macro be known two levels higher,
% but not if only one level higher
defSmuggleMacroUpTwo%
advancegoodiescount 1 % not done globally !
expandafterSmuggleMacroUpTwo@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacroUpTwo@aux#1#2%
expandaftergdefexpandafter#1expandafter
expandafterdefexpandafter#2expandafter#2%
aftergroupSmuggleValue
aftergroup#1%
%
defSmuggleValue%
advancegoodiescount 1 % not done globally !
expandafterSmuggleValue@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleValue@aux #1#2%
globallet#1#2%
aftergroup#1%
%
%
% This one makes known the macros 1, 2, ..., N levels up.
% Syntax SmuggleMacroNtimesUp<number>.macro
defSmuggleMacroNtimesUp%
advancegoodiescount 1 % not done globally!
expandafterSmuggleMacroNtimesUp@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacroNtimesUp@aux#1#2.#3%
expandaftergdefexpandafter#1expandafter
expandafterdefexpandafter#3expandafter#3%
aftergroup#1%
expandafterSmuggleMacroNtimesUp@athenumexpr#2-1.#1%
%
defSmuggleMacroNtimesUp@a#1%
if0#1expandafter@gobbletwo
else
aftergroupSmuggleValueNtimesUp
aftergroup #1%
expandafterSmuggleNtimesUp@loop
fi
%
defSmuggleNtimesUp@loop#1%
aftergroup#1%
if.#1expandafteraftergroup
else
expandafterSmuggleNtimesUp@loop
fi
%
% This one makes **executes the macro**
% at all levels 1, 2, ..., N up.
% Syntax SmuggleValueNtimesUp<number>.macro
defSmuggleValueNtimesUp%
advancegoodiescount 1 % not done globally!
expandafterSmuggleValueNtimesUp@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleValueNtimesUp@aux#1#2.#3%
globallet#1#3%
aftergroup#1%
expandafterSmuggleValueNtimesUp@athenumexpr#2-1.#1%
%
defSmuggleValueNtimesUp@a#1%
if0#1expandafter@gobbletwo
else
aftergroupSmuggleValueNtimesUp
aftergroup #1%
expandafterSmuggleNtimesUp@loop
fi
%
makeatother
begindocument
ttfamily
deffoo
% 8 deep
% 16 deep
deffooFOO defined at 16 will be made known all the way to 3%
SmuggleMacroNtimesUp13.foo
16.foopar%
15.foopar%+1
14.foopar%+2
13.foopar%+3
12.foopar%+4
11.foopar%
10.foopar%
9.foopar%
8.foopar%+8
7.foopar%
6.foopar%
5.foopar%
4.foopar%+12
3.foopar%+13
2.foopar%
1.foopar%
0.foopar
enddocument
Final version
Under pressure of @Circumscribe example I have refactored to handle macros with parameters. Not much tested... Added @marmot query about moving meaning to top level (aka bottom level...)
Thus the defined things are
SmuggleMacro foo
: makesfoo
keep its meaning one level up,SmuggleMacroUpTwo foo
: makesfoo
recover its meaning two levels up (but not one level up...)SmuggleMacroNtimesUp <number>.foo
: makesfoo
keep its meaning for the<number>
less nested levels. Must be used with<number>
at least1
.SmuggleValueNtimesUp <number>.foo
: executes the meaning offoo
for the<number>
less nested levels, viaaftergroup
so as soon as a more nested level is left.foo
itselfs (if not globally defined) is not smuggled.SmuggleMacroToTopfoo
: makesfoo
known at bottom level (sic), but not at any intermediate level (of course while moving from inner to outer, once we reach bottom level, next time we enter a groupfoo
will be known).
(if the code looks crazy, it is also because it tries to define the less auxiliary storage macros, by keeping this idea of never globally stepping the index of these storage things)
documentclassarticle
usepackagegeometry
newcountgoodiescount
makeatletter
defSmuggleMacro%
advancegoodiescount 1 % not done globally !
expandafterSmuggleMacro@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacro@aux#1#2%
globallet#1#2%
aftergrouplet
aftergroup#2%
aftergroup#1%
%
% This one will let the macro be known two levels higher,
% but not if only one level higher
defSmuggleMacroUpTwo%
advancegoodiescount 1 % not done globally !
expandafterSmuggleMacroUpTwo@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacroUpTwo@aux#1#2%
globallet#1#2%
aftergroupSmuggleLet
aftergroup#2%
aftergroup#1%
%
defSmuggleLet%
advancegoodiescount 1 % not done globally !
expandafterSmuggleLet@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleLet@aux#1#2#3%
globallet#1#3%
aftergrouplet
aftergroup#2%
aftergroup#1%
%
%
% This one makes known the macros 1, 2, ..., N levels up.
% Syntax SmuggleMacroNtimesUp<number>.macro
defSmuggleMacroNtimesUp%
advancegoodiescount 1 % not done globally!
expandafterSmuggleMacroNtimesUp@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacroNtimesUp@aux#1#2.#3%
globallet#1#3%
aftergrouplet
aftergroup#3%
aftergroup#1%
expandafterSmuggleMacroNtimesUp@athenumexpr#2-1.#3%
%
%longdef@gobblethree#1#2#3%
defSmuggleMacroNtimesUp@a#1%
if0#1expandafter@gobbletwo
else
aftergroupSmuggleMacroNtimesUp
aftergroup #1%
expandafterSmuggleNtimesUp@loop
fi
%
defSmuggleNtimesUp@loop#1%
aftergroup#1%
if.#1expandafteraftergroup
else
expandafterSmuggleNtimesUp@loop
fi
%
defSmuggleValueNtimesUp%
advancegoodiescount 1 % not done globally!
expandafterSmuggleValueNtimesUp@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleValueNtimesUp@aux#1#2.#3%
globallet#1#3%
aftergroup#1%
expandafterSmuggleValueNtimesUp@athenumexpr#2-1.#1%
%
defSmuggleValueNtimesUp@a#1%
if0#1expandafter@gobbletwo
else
aftergroupSmuggleValueNtimesUp
aftergroup #1%
expandafterSmuggleNtimesUp@loop
fi
%
% SmuggleMacroToTop
defSmuggleMacroToTop%
ifnumcurrentgrouplevel=z@
expandafter@gobble
else
expandafterSmuggleMacro@ToTop
fi
%
defSmuggleMacro@ToTop%
advancegoodiescount 1 % not done globally!
expandafterSmuggleMacroToTop@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleMacroToTop@aux#1#2%
globallet#1#2%
aftergroupSmuggleLetToTop
aftergroup#2%
aftergroup#1%
%
defSmuggleLetToTop%
ifnumcurrentgrouplevel=z@
expandafterlet
else
expandafterSmuggleLet@ToTop
fi
%
defSmuggleLet@ToTop%
advancegoodiescount 1 % not done globally !
expandafterSmuggleLetToTop@aux
csname Goodiesthegoodiescountendcsname
%
defSmuggleLetToTop@aux#1#2#3%
globallet#1#3%
aftergroupSmuggleLetToTop
aftergroup#2%
aftergroup#1%
%
makeatother
begindocument
ttfamily
deffoo
% 8 deep
% 16 deep
defBAR#1#2#3Hello, I am BAR%
SmuggleMacroBAR
SmuggleMacroToTopBAR
defBAZ#1#2#3#4Hello, I am BAZ%
SmuggleMacroUpTwoBAZ
deffoo#1#2FOO defined at 16 will be made known all the way to 3%
SmuggleMacroNtimesUp13.foo
16.FOO meaningfoopar
16.BAZ meaningBAZpar
16.BAR meaningBARpar
defxleavevmodellapaaa %
SmuggleValueNtimesUp7.x
medskip%
15.FOO meaningfoopar
15.BAZ meaningBAZpar
15.BAR meaningBARparmedskip%
14.FOO meaningfoopar
14.BAZ meaningBAZpar
14.BAR meaningBARparmedskip%
13.FOO meaningfoopar%+3
12.FOO meaningfoopar%+4
11.FOO meaningfoopar%
10.FOO meaningfoopar%
9.FOO meaningfoopar
9.BAR meaningBARpar
%
8.FOO meaningfoopar%+8
7.FOO meaningfoopar%
6.FOO meaningfoopar%
5.FOO meaningfoopar
5.BAR meaningBARpar%
4.FOO meaningfoopar%+12
3.FOO meaningfoopar%+13
2.FOO meaningfoopar%
1.FOO meaningfoopar%
0.FOO meaningfoopar
0.BAR meaningBARpar
enddocument
edited Jan 20 at 17:54
answered Jan 20 at 11:33
user4686
although I may need to think about it more: I think it is fine, although I have not really thought at depth. (ah ah).
– user4686
Jan 20 at 13:33
Trying to mentally parseSmuggleMacroUpTwo
makes my head hurt, but it appears to be airtight. You're really minimising the number of different macros that need to be assigned by keeping track of how many smuggling operations are currently ongoing and assigning them appropriate numbers :).
– Circumscribe
Jan 20 at 14:20
@Circumscribe yes, after a group closes, new definition of a given index can only happen after or at same time its old meaning is actually needed, and when you open an inner group you can only then define macros with a higher index than what you had stored earlier as to be used after the outer group. Thus, ..., it should be airtight indeed, but indeed a bit head-spinning :)
– user4686
Jan 20 at 15:04
Did you read " Of course, infty means that this macro has the value in all LaTeX documents of the world. ;-) " ? ;-)
– marmot
Jan 20 at 17:56
@marmot I understood that I would become rich, did I get that right?
– user4686
Jan 20 at 17:58
|
show 1 more comment
although I may need to think about it more: I think it is fine, although I have not really thought at depth. (ah ah).
– user4686
Jan 20 at 13:33
Trying to mentally parseSmuggleMacroUpTwo
makes my head hurt, but it appears to be airtight. You're really minimising the number of different macros that need to be assigned by keeping track of how many smuggling operations are currently ongoing and assigning them appropriate numbers :).
– Circumscribe
Jan 20 at 14:20
@Circumscribe yes, after a group closes, new definition of a given index can only happen after or at same time its old meaning is actually needed, and when you open an inner group you can only then define macros with a higher index than what you had stored earlier as to be used after the outer group. Thus, ..., it should be airtight indeed, but indeed a bit head-spinning :)
– user4686
Jan 20 at 15:04
Did you read " Of course, infty means that this macro has the value in all LaTeX documents of the world. ;-) " ? ;-)
– marmot
Jan 20 at 17:56
@marmot I understood that I would become rich, did I get that right?
– user4686
Jan 20 at 17:58
although I may need to think about it more: I think it is fine, although I have not really thought at depth. (ah ah).
– user4686
Jan 20 at 13:33
although I may need to think about it more: I think it is fine, although I have not really thought at depth. (ah ah).
– user4686
Jan 20 at 13:33
Trying to mentally parse
SmuggleMacroUpTwo
makes my head hurt, but it appears to be airtight. You're really minimising the number of different macros that need to be assigned by keeping track of how many smuggling operations are currently ongoing and assigning them appropriate numbers :).– Circumscribe
Jan 20 at 14:20
Trying to mentally parse
SmuggleMacroUpTwo
makes my head hurt, but it appears to be airtight. You're really minimising the number of different macros that need to be assigned by keeping track of how many smuggling operations are currently ongoing and assigning them appropriate numbers :).– Circumscribe
Jan 20 at 14:20
@Circumscribe yes, after a group closes, new definition of a given index can only happen after or at same time its old meaning is actually needed, and when you open an inner group you can only then define macros with a higher index than what you had stored earlier as to be used after the outer group. Thus, ..., it should be airtight indeed, but indeed a bit head-spinning :)
– user4686
Jan 20 at 15:04
@Circumscribe yes, after a group closes, new definition of a given index can only happen after or at same time its old meaning is actually needed, and when you open an inner group you can only then define macros with a higher index than what you had stored earlier as to be used after the outer group. Thus, ..., it should be airtight indeed, but indeed a bit head-spinning :)
– user4686
Jan 20 at 15:04
Did you read " Of course, infty means that this macro has the value in all LaTeX documents of the world. ;-) " ? ;-)
– marmot
Jan 20 at 17:56
Did you read " Of course, infty means that this macro has the value in all LaTeX documents of the world. ;-) " ? ;-)
– marmot
Jan 20 at 17:56
@marmot I understood that I would become rich, did I get that right?
– user4686
Jan 20 at 17:58
@marmot I understood that I would become rich, did I get that right?
– user4686
Jan 20 at 17:58
|
show 1 more 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%2f470961%2fhow-to-properly-smuggle-with-or-even-without-tikz%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