10

I want to define a macro in a group in LaTeX3 so that it stays defined after the group, but without using global variables. For now I do:

\documentclass{article}
\ExplSyntaxOn
\NewDocumentEnvironment{test}{}{}{
  \str_clear_new:N \mytest
  \str_put_right:Nx \mytest {I ~ like ~}
  \str_gset_eq:NN \dummyvar \mytest
  \group_insert_after:N \str_set_eq:NN
  \group_insert_after:N \mytest
  \group_insert_after:N \dummyvar
}
\ExplSyntaxOff
\begin{document}
\def\mytest{chocolate.}
{
  \begin{test}
    I’d like mytest to be defined at the end of the group without using global variables.
  \end{test}
  \mytest
}
\mytest
\end{document}

It works, but it seems really dirty, like I need to introduce a global dummy var etc… I tried to do:

  \group_insert_after:N \str_set_eq:NN
  \group_insert_after:N \mytest
  \expandafter \group_insert_after:N \mytest

But funnily, the string is written backward now!! I also tried to generate a variant of \group_insert_after:V but it is forbidden.

tobiasBora
  • 8,684
  • I might get away with something like \exp_args:NNNNNV \group_insert_after:N \str_set_eq:NN \group_insert_after:N \mytest \group_insert_after:N \mytest (you'll probably have to generate \exp_args:NNNNNV though). – gusbrs Jul 09 '23 at 23:49
  • @gusbrs, thanks, so I tried, using \exp_args_generate:n {NNNNV}, but can’t make it work, not sure if I’m doing something stupid…Could you provide a full example by any chance? – tobiasBora Jul 09 '23 at 23:59
  • Mhm, sorry, it was just an idea, which I can't seem to make it work either... :-( – gusbrs Jul 10 '23 at 00:13
  • (Off-topic, you probably don't want to call \str_clear_new:N \mytest inside the environment, since it may be used multiple times). – gusbrs Jul 10 '23 at 00:14
  • You might find this useful: https://tex.stackexchange.com/q/56294/86 It's an old question so hopefully there are more modern answers so I'll be interested in what advice you get. – Andrew Stacey Jul 10 '23 at 05:57

3 Answers3

9

I understand that \dummyvar cannot work if you want to keep the meaning of more than single macro after group. But you can set a control sequence derived from the name of given control sequence:

\def\keepaftergroup#1{%
   \global \expandafter\let \csname x:\string#1\endcsname =#1
   \aftergroup\let
   \aftergroup#1%
   \expandafter\aftergroup \csname x:\string#1\endcsname

}

\def\mytest{nazdar}

{ \def\mytest{Hello} \def\mysecond{Hi} \keepaftergroup\mytest \keepaftergroup\mysecond Text in group.% } After group: \mytest, \mysecond

wipet
  • 74,238
5

We do not currently have an 'escape from arbitrary group levels' function, largely as this is an unusual requirement. On the other hand, where you want to set a variable outside a group you control, this is quite straight-forward

\group_begin:
   % Stuff happens
   \str_set:Nn \l__mypkg_str { ... }
\exp_args:NNNV \group_end:
\str_set:Nn \l__mypkg_str \l__mypkg_str

and so on.

Joseph Wright
  • 259,911
  • 34
  • 706
  • 1,036
  • Thanks but this would not work for environments right? I'm surprised, people never try to save a variable from an environment for later use? – tobiasBora Jul 10 '23 at 11:56
  • @tobiasBora Environments are group, so usually either things are supposed to be retained insider them or they are globals – Joseph Wright Jul 10 '23 at 12:02
  • Well they also have the advantage of not tempering the content, which is practical to deal with verbatim content ^^ (+ they are sometimes more visual for big content) – tobiasBora Jul 10 '23 at 17:12
4

The following defines a mechanism to smuggle things up one level with a single auxiliary global token.

It won't work for toks registers. I hope I didn't screw up on other points of the implementation.

\documentclass{article}
\ExplSyntaxOn
\tl_new:N \g__tobiasbora_smuggler_tl
\cs_new:Npn \tobiasbora_smuggle:n #1
  {
    \tl_gset:Nx \g__tobiasbora_smuggler_tl
      {
        \tl_map_function:nN {#1} \__tobiasbora_smuggle:N
        \exp_not:n { \tl_gset:Nn \g__tobiasbora_smuggler_tl }
          { \exp_not:o \g__tobiasbora_smuggler_tl }
      }
    \group_insert_after:N \g__tobiasbora_smuggler_tl
  }
\cs_new:Npn \__tobiasbora_smuggle:N #1
  {
    \token_if_macro:NTF #1
      {
        \exp_not:n { \cs_set:Npx #1 }
          { \exp_not:N \exp_not:n { \exp_not:o #1 } }
      }
      {
        \token_if_chardef:NTF #1
          { \exp_not:n { \chardef #1 } = \exp_not:V #1 \scan_stop: }
          {
            \token_if_mathchardef:NTF #1
              { \exp_not:n { \mathchardef #1 } = \exp_not:V #1 \scan_stop: }
              {
                % assume it's a register type and the following is fine
                #1 = \exp_not:V #1 \scan_stop:
              }
          }
      }
  }
\NewDocumentEnvironment{test}{}{}{
  \str_set:Nn \mytest { like~ }
  \tobiasbora_smuggle:n { \mytest \mycount }
}
\ExplSyntaxOff

\newcount\mycount \mycount=0

\begin{document} \def\mytest{chocolate.} { \begin{test} I’d like mytest to be defined at the end of the group without using global variables. \mycount=1 \end{test}% \uppercase\expandafter{\romannumeral\mycount} % this prints "I" \mytest }% \mytest \romannumeral\mycount % this prints nothing as it's 0 again \end{document}

Skillmon
  • 60,462