TransWikia.com

Locally patch newcommand and newenvironment

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?

2 Answers

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}

enter image description here

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}

enter image description here

BeginNamespace..EndNamespace is independent from group-nesting and environment-nesting. This might be confusing.

Answered by Ulrich Diez on June 16, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP