#Scary plain TeX question: nonlocal defs?

1 messages · Page 1 of 1 (latest)

late prawn
#

We all know the difference between \global\def and \def inside a group. I'd like to know if there is a middle ground (like the nonlocal keyword in Python) where the \def is scoped to the parent group.

For example:

\begingroup
  \begingroup
    \def\a{a}
    \global\def\b{b}
    % wishlist modifier
    \nonlocal\def\c{c} 
  \endgroup

  \a % breaks
  \b % should be fine
  \c % should be fine
\endgroup

\a % breaks
\b % should be fine
\c % breaks

The simple solution is to define \c before the second group, but this isn't always possible. What if \c depends on \a and \b?

agile fable
#

No

agile fable
#

But you could try doing some things with \aftergroup.
Here's something I came up with, I'm not sure how good it is though

\def\glet{\global\let}

\newcount\nonlocalassigns
\def\nonlocalA{%
    \expandafter\def\expandafter\regB\expandafter{\csname macro:\the\nonlocalassigns\endcsname}%
    \expandafter\expandafter\expandafter\glet\expandafter\regB\regA%
}

\def\nonlocalB{\global\advance\nonlocalassigns by 1\relax}

\def\nonlocalC{\nonlocalA\nonlocalB}

\def\nonlocal#1#2{%
    \def\regA{#2}%
    \afterassignment\nonlocalC%
    \aftergroup\let \aftergroup#2%
    \expandafter\aftergroup\csname macro:\the\nonlocalassigns\endcsname%
    #1#2%
}

{
    {\nonlocal\def\test{abc}\test}
    \test
}

\tt\meaning\test
upper echoBOT
agile fable
#

Though what's the purpose of this?

late prawn
#

Thanks for \aftergroup, it's almost what I need.
I think a better way to explain this is "I need to define a macro after a group".

I've gotten it almost working with the following:

\let\ea\expandafter
\let\ag\aftergroup

\begingroup
  \def\makefoo##1{%
    \ag\def%
      \ag\foo%
      \ag{\ag##1\ag}%
  }
  \makefoo{test}
\endgroup

\foo

The problem here is that only the first token of the argument to \makefoo is \aftergrouped, so \foo is t.

agile fable
#

That doesn't work

#

Because \aftergroup test would just place t after the group

late prawn
#

exactly
(sorry, I sent that message too early and had to edit)

#

Is there a way to aftergroup the whole #1?

agile fable
#

You could do something like \def\reg{#1} and then \aftergroup \let\foo\reg

late prawn
#

I just tried this, it doesn't work
\reg isn't defined after the group!

agile fable
#

You'd need to \gdef it

agile fable
late prawn
#

hmmm
yes, it is...

I'm trying to avoid making a mess of macros, though.
In fact, the whole goal is to keep utility macros confined to a group while allowing the useful macros they make to escape.

agile fable
#

That's not really a good idea

#

A lot of useful macros have utility macros within their definition (replacement text) and if you confine the utility macros to a group, you can't use the useful ones (because they're not defined outside the group)

#

If you want to hide utility macros, a common solution is to use other characters in their names (like @ and _ etc)

late prawn
#

Yes, of course

But in this case, the "final" macros are guaranteed to not use the "utility" ones.
All they do is create them.

#

This isn't a mission-critical feature, just me seeing what's possible.

agile fable
#

I see

#

Why not just globally define the final macros?

#

Well if you want to just see what's possible then I'd understand why not

#

You could try doing some IO shenanigans where you write the definitions to a file and then \input the file after the group

late prawn
#

The goal is to make a system for naming parameters
I have many macros with 5+ args, and remembering what goes where is a pain (even with fancy \NewDocumentCommand delimiters.

\newenvironment{demo}[1]{
  % Named parameters
  \def\vala##1{\def\ARGvala{##1}}
  \def\valb##1{\def\ARGvalb{##1}}
  \def\valc##1{\def\ARGvalc{##1}}
  #1 % Include argument block

  % Using \ARG* after this point
  % ... snip ...
} {}

\begin{demo}{
  \vala{value}
  \valb{value}
}

  ... snip ...

\end{demo}

This all works perfectly, but I'd like to put the "named-arg-parsing" block in a group to guarantee that those \defs don't overwrite anything important (\year, for example).

agile fable
#

I see

#

I implemented a solution to this some time ago with a different approach

#

Basically I just saved the original definitions of the macros to a stack, and then after the function call I popped them off the stack

#

That should have the necessary code

#

Ah wait

#

I'm not sure if this solves your problem

#

This might actually

#

You may just want the macros for localizing definitions (something like lines 95-126)