TeX - LaTeX Asked by wen on June 16, 2021
I’d like to define an environment which locally patches the newcommand
and newenvironment
families of commands to prefix each new definition with some string, e.g.
begin{namespace}{hello}
newcommand{world}{Hello, World}
end{namespace}
world % undefined
helloworld % prints "Hello, World"
How can I do this? Can I do this?
This should cover (re)newcommand
, providecommand
, DeclareRobustCommand
, and (re)newenvironment
. It uses two commands, BeginNamespace{<namespace>}
and EndNamespace
(which cannot be nested), rather than the environment syntax, because commands defined in environments are lost after the environment ends.
This works by redefining the commands that grab the command name, and adds <namespace>
before them by using csname<namespace>cs_to_str:N #1endcsname
.
documentclass{article}
makeatletter
ExplSyntaxOn
cs_new_eq:NN CStostr cs_to_str:N
ExplSyntaxOff
newifif@namespace
newcommandBeginNamespace[1]{%
if@namespace ERROR@cannot@nest@namespaces else
@namespacetrue
letNS@new@commandnew@command
letNS@renew@commandrenew@command
letNS@declare@robustcommanddeclare@robustcommand
letNS@new@environmentnew@environment
letNS@renew@environmentrenew@environment
defnew@command##1{%
expandafterNS@new@commandcsname#1CStostr##1endcsname}
defrenew@command##1{%
expandafterNS@renew@commandcsname#1CStostr##1endcsname}
defdeclare@robustcommand##1{%
expandafterNS@declare@robustcommandcsname#1CStostr##1endcsname}
defnew@environment##1{NS@new@environment{#1##1}}
defrenew@environment##1{NS@renew@environment{#1##1}}%
fi}
newcommandEndNamespace{%
if@namespace
letnew@commandNS@new@command
letrenew@commandNS@renew@command
letnew@environmentNS@new@environment
letrenew@environmentNS@renew@environment
@namespacefalse
else ERROR@extra@EndNamespace fi}
makeatother
newcommand{world}{WORLD}
BeginNamespace{hello}
newcommand{world}{Hello, World}
EndNamespace
begin{document}
world % prints "WORLD"
helloworld % prints "Hello, World"
end{document}
Correct answer by Phelype Oleinik on June 16, 2021
I can offer a macro CsNameToCsToken
which might help accomplishing things without patching newcommand
/newenvironment
. Besides this it might be useful when using definition-commands other than newcommand
/newenvironment
:
Syntax:
CsNameToCsToken⟨stuff not in braces⟩{⟨NameOfCs⟩}
→
⟨stuff not in braces⟩NameOfCs
⟨stuff not in braces⟩
can, e.g., be newcommand*
or DeclareRobustCommand
or NewDocumentCommand
or globallongouterdef
or meaning
or string
or globallet
or whatever.
⟨stuff not in braces⟩
may be empty. ⟨stuff not in braces⟩
being empty means just calling the control-sequence.
You can nest CsNameToCsToken
:
CsNameToCsTokenCsNameToCsTokengloballet{foo}={bar}
yields CsNameToCsTokengloballetfoo={bar}
yields globalletfoo=bar
.
begingroup
makeatletter
@firstofone{%
endgroup
@ifdefinableStopromannumeral{chardefStopromannumeral=`^^00}%
@ifdefinableCsNameToCsToken{%
longdefCsNameToCsToken#1#{romannumeralInnerCsNameToCsToken{#1}}%
}%
newcommandInnerCsNameToCsToken[2]{%
expandafterexchangeexpandafter{csname#2endcsname}{Stopromannumeral#1}%
}%
newcommandexchange[2]{#2#1}%
}%
You can, e.g., define newcommandnamespace{hello}
and do
CsNameToCsTokennewcommand{namespace world}{Hello, World}
for defining a control-word-token helloworld
.
documentclass{article}
begingroup
makeatletter
@firstofone{%
endgroup
@ifdefinableStopromannumeral{chardefStopromannumeral=`^^00}%
@ifdefinableCsNameToCsToken{%
longdefCsNameToCsToken#1#{romannumeralInnerCsNameToCsToken{#1}}%
}%
newcommandInnerCsNameToCsToken[2]{%
expandafterexchangeexpandafter{csname#2endcsname}{Stopromannumeral#1}%
}%
newcommandexchange[2]{#2#1}%
}%
begin{document}
newcommandnamespace{hello}
CsNameToCsTokennewcommand{namespace world}{Hello, World}
helloworld
texttt{stringhelloworld: meaninghelloworld}
texttt{stringworld: meaningworld}
end{document}
Additionally you can define a stack for nesting namespaces via BeginNamespace{⟨name of namespace⟩}
and EndNamespace
so that the macro namespace
yields the name of the current namespace:
documentclass{article}
makeatletter
newcommand*namespace{}%
newcommand*EndNamespace{gdefnamespace{}}%
newcommand*BeginNamespace[1]{%
toks@expandafter{expandaftergdefexpandafternamespaceexpandafter{namespace}}%
toks@expandafter{theexpandaftertoks@expandaftergdefexpandafterEndNamespaceexpandafter{EndNamespace}}%
xdefEndNamespace{thetoks@}%
gdefnamespace{#1}%
ignorespaces
}%
makeatother
begin{document}
shownamespace
showEndNamespace
BeginNamespace{A}
shownamespace
showEndNamespace
BeginNamespace{B}
shownamespace
showEndNamespace
BeginNamespace{C}
shownamespace
showEndNamespace
EndNamespace
shownamespace
showEndNamespace
EndNamespace
shownamespace
showEndNamespace
EndNamespace
shownamespace
showEndNamespace
EndNamespace
end{document}
Saving the example above as test.tex
and compiling yields the following messages on the terminal:
This is pdfTeX, Version 3.14159265-2.6-1.40.21 (TeX Live 2020) (preloaded format=pdflatex)
restricted write18 enabled.
entering extended mode
(./test.tex
LaTeX2e <2020-10-01> patch level 4
L3 programming layer <2021-02-18>
(/usr/local/texlive/2020/texmf-dist/tex/latex/base/article.cls
Document Class: article 2020/04/10 v1.4m Standard LaTeX document class
(/usr/local/texlive/2020/texmf-dist/tex/latex/base/size10.clo))
(/usr/local/texlive/2020/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def)
(./test.aux)
> namespace=macro:
->.
l.17 shownamespace
?
> EndNamespace=macro:
->gdef namespace {}.
l.18 showEndNamespace
?
> namespace=macro:
->A.
l.22 shownamespace
?
> EndNamespace=macro:
->gdef namespace {}gdef EndNamespace {gdef namespace {}}.
l.23 showEndNamespace
?
> namespace=macro:
->B.
l.27 shownamespace
?
> EndNamespace=macro:
->gdef namespace {A}gdef EndNamespace {gdef namespace {}gdef EndNamespa
ce {gdef namespace {}}}.
l.28 showEndNamespace
?
> namespace=macro:
->C.
l.32 shownamespace
?
> EndNamespace=macro:
->gdef namespace {B}gdef EndNamespace {gdef namespace {A}gdef EndNamesp
ace {gdef namespace {}gdef EndNamespace {gdef namespace {}}}}.
l.33 showEndNamespace
?
> namespace=macro:
->B.
l.37 shownamespace
?
> EndNamespace=macro:
->gdef namespace {A}gdef EndNamespace {gdef namespace {}gdef EndNamespa
ce {gdef namespace {}}}.
l.38 showEndNamespace
?
> namespace=macro:
->A.
l.42 shownamespace
?
> EndNamespace=macro:
->gdef namespace {}gdef EndNamespace {gdef namespace {}}.
l.43 showEndNamespace
?
> namespace=macro:
->.
l.47 shownamespace
?
> EndNamespace=macro:
->gdef namespace {}.
l.48 showEndNamespace
?
> namespace=macro:
->.
l.52 shownamespace
?
> EndNamespace=macro:
->gdef namespace {}.
l.53 showEndNamespace
?
(./test.aux) )
No pages of output.
Transcript written on test.log.
Combining the two ideas you can use CsNameToCsToken
inside name-spaces to get the expansion of the namespace
-macro as currently defined into names of control sequences—this time instead of toks@
-assignments xdef
is combined with unexpanded
and romannumeral
-expansion for redefining EndNamespace
:
documentclass{article}
makeatletter
@ifdefinableStopromannumeral{chardefStopromannumeral=`^^00}%
@ifdefinableCsNameToCsToken{%
longdefCsNameToCsToken#1#{romannumeralInnerCsNameToCsToken{#1}}%
}%
newcommandInnerCsNameToCsToken[2]{%
expandafterexchangeexpandafter{csname#2endcsname}{Stopromannumeral#1}%
}%
newcommandexchange[2]{#2#1}%
newcommand*namespace{}%
newcommand*EndNamespace{gdefnamespace{}}%
newcommand*BeginNamespace[1]{%
xdefEndNamespace{%
unexpandedexpandafter{romannumeral
expandafterexchangeexpandafter{expandaftergdefexpandafterEndNamespaceexpandafter{EndNamespace}}%
{expandafterStopromannumeralexpandaftergdefexpandafternamespaceexpandafter{namespace}}%
}%
}%
xdefnamespace{unexpanded{#1}}%
ignorespaces
}%
makeatother
begin{document}
BeginNamespace{A}
CsNameToCsTokennewcommand*{Hellonamespace}{Hello, A!}%
newenvironment{namespace Environment}{This is the start of AEnvironment.}{This is the end of AEnvironment.}
BeginNamespace{B}
CsNameToCsTokennewcommand*{Hellonamespace}{Hello, B!}%
newenvironment{namespace Environment}{This is the start of BEnvironment.}{This is the end of BEnvironment.}
BeginNamespace{C}
CsNameToCsTokennewcommand*{Hellonamespace}{Hello, C!}%
newenvironment{namespace Environment}{This is the start of CEnvironment.}{This is the end of CEnvironment.}
texttt{CsNameToCsTokenstring{Hellonamespace}: CsNameToCsTokenmeaning{Hellonamespace}}%
EndNamespace
texttt{CsNameToCsTokenstring{Hellonamespace}: CsNameToCsTokenmeaning{Hellonamespace}}%
EndNamespace
texttt{CsNameToCsTokenstring{Hellonamespace}: CsNameToCsTokenmeaning{Hellonamespace}}%
EndNamespace
hrulefill
texttt{stringHelloA: meaningHelloA}
texttt{stringHelloB: meaningHelloB}
texttt{stringHelloC: meaningHelloC}
begin{AEnvironment} end{AEnvironment}
begin{BEnvironment} end{BEnvironment}
begin{CEnvironment} end{CEnvironment}
end{document}
BeginNamespace
..EndNamespace
is independent from group-nesting and environment-nesting. This might be confusing.
Answered by Ulrich Diez on June 16, 2021
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP