11

I still don't see (and haven't thought about) pros and cons of making available the arguments as \foo{…} or \foo\bgroup …\egroup (or even unbalanced, \bgroup …} and {…\egroup). But I'm curious about enabling it for certain commands (in a way that, for instance, you could create environments from commands).

How can we create a macro like

\def\foo#1{…}

that does accept

\foo\bgroup …\egroup

Another option I thought about is a command like

\delimitedbybegroup{<pre code>}<argument delimited by braces or \bgroup and \egroup>

So one could say \delimitedbybegroup\emph{Some text} or \delimitedbybegroup\emph\bgroup Some text\egroup both of them giving \emph{Some text}.

A more advanced one, for instance, might be

\delimitedbybegroup{\def\foo#1#2}\bgroup Something with #1 and #2\egroup

In any case any thoughts about why and why not are useful this kind of commands would be welcome.

And, in a similar way… how are (usually) handled commands that do look for a “closing” token, if the token is not visible. Like

\def\foo{\delimiter}
\def\baz#1\delimiter{…#1…}
\baz some code \foo{} and some more here that should be outside the argument of \string\baz.
Manuel
  • 27,118
  • An idea could be using a token register, but a <general text> must end with an explicit brace, not \egroup. For font changing or box making commands it is actually easy, but delimiting an argument of a general macro either by \egroup or }₁ is difficult, if ever possible. And I'm really dubious about its usefulness. – egreg Aug 17 '14 at 20:35
  • 3
    since it's easier to type {] than \bgroup\egroup and much easier and more robust to define commands using that form why would you want to do this? your question gives no indication of any possible reason or potential benefit. – David Carlisle Aug 17 '14 at 21:50
  • @egreg I'm also dubious about its usefulness, but I'm curiuous about why do this option exist for some commands, and if it is possible to create a command that accepts such king of arguments (the same as \hbox). – Manuel Aug 18 '14 at 09:48
  • @DavidCarlisle Curiosity. And also what reason saw Knuth to offer such possibility, and, since I've seen many, many times \hbox\bgroup\egroup, I think it might be useful (although it's true that we have survived all these years without it :D). – Manuel Aug 18 '14 at 09:50
  • That's the point: Knuth didn't offer the possibility in TeX (so any faking of the syntax in the macro layer is going to be very fragile) the syntax for \hbox is completely different and closer to grouping than to delimiting arguments. – David Carlisle Aug 18 '14 at 09:52
  • @DavidCarlisle Well, then having such grouping in an argument is enough. By the way, to the last part, I know the definition of |…verbatim#1\end{verbatim}[…] which is IMO quite fragile, and anything better than that would be great. For instance, something that works like a_\mathrm{min} but in a user created command would be welcome. – Manuel Aug 18 '14 at 09:58
  • a_\mathrm{min} is just such horrible markup (and greatly limits what you can do with the^ and _ syntax:-) So many uses (for language shortcuts or html generation or ...) want to overload _ and ^ with active characters with macro definitions and it's not possible to recreate that syntax sensibly using a macro. If users used the documented a_{\mathrm{min}} form, it would be trivial to overload _. – David Carlisle Aug 18 '14 at 10:01
  • @DavidCarlisle I don't mean I want that (in fact, since two months ago, I use active ^ and _ to adjust them to my needs), it was just an example of what the question asks. – Manuel Aug 18 '14 at 10:07
  • well basically the answer is as below and in egreg's comment to that answer, you can't do this at the macro layer, sorry:-) – David Carlisle Aug 18 '14 at 10:13
  • @DavidCarlisle In that case let's pray for some wipet's sorcery :P – Manuel Aug 18 '14 at 10:14
  • As far as I know, macros defined with \def, or similar defining commands, behave in a very different way from native commands; the latter accept the balanced braces as well as the balanced \bgroup and \egroup pairs, even if these implicit braces are used inside different macros, for example the opening and the closing macros of an environment. Parsing macros for finding their arguments is not the same as processing their arguments by native commands. – Claudio Beccari Aug 18 '14 at 09:08
  • Only primitives that have {<balanced text>} as argument accept \bgroup or \egroup in place of { or } (\hbox, for instance). Primitives that have <general text> as argument, only accept \bgroup as a replacement of {, but require } at the end (\uppercase, for instance). Some primitives (the \def family) have an argument that must be delimited by explicit braces. – egreg Aug 18 '14 at 09:19
  • The task can be solved similar as \Open \Close macro from http://tex.stackexchange.com/questions/196071/macro-to-close-all-open-environments-groups-and-argument-delimiters . But I'am not sure the serviceability of this. These two types of separators will be used only for parameter scanning (without total expandning). The point of these two separators in primitives are that these separators are scanned during total expansion (for example \egroup is in a macro expanded later). For example a_\mathrm{min} does a_\bgroup something\egroup after expansion, no during parameter scanning. – wipet Aug 31 '14 at 09:48
  • @wipet It was more like if there was a way of bringing that behaviour from primitives to user defined macros, rather than hack a preprocessor. By the way, the second subquestion is still vague, although I think the answer is a clear no (expanding the arguments until it finds a the delimiter of the macro —which, in my example, is inside another macro—). – Manuel Sep 01 '14 at 17:39

3 Answers3

10

The syntax rules of TeX allow for certain primitives to have an “argument” delimited by \bgroup or \egroup. The main examples are

\hbox, \vbox, \vtop, \vcenter, \halign and \noalign.

This is the basis where, for instance, \halign LaTeX builds tabular on: with \begin{tabular}{<arg>} the argument is transformed into a suitable preamble for \halign and then, besides other things irrelevant for this discussion, TeX executes

\leavevmode$\vcenter\bgroup\halign\bgroup<built preamble>\cr

and, when \end{tabular} is found,

\crcr\egroup\egroup$

is performed. The \vcenter can be \vbox or \vtop depending on the option given to tabular. Of course, this possibility is exploited also in lrbox.

Other primitive commands accept an argument in the form of a <general text> and the TeXbook describes this as

<filler>{<balanced text><right brace>

where <filler> is an arbitrary sequence of \relax and space tokens, { means an implicit or explicit token with category code 1 (so either { or \bgroup) and <right brace> means an explicit character token of category code 1.

Examples of primitives with this are \uppercase, \message, \write and \mark, besides assignments to token registers:

<token variable><equals><general text>

So you can't define a macro based on \uppercase whose argument is delimited by \egroup, but you can do it with \hbox.

Worse, for your plan, is the fact that the replacement text in a macro definition must be delimited by explicit character tokens of categories 1 and 2 respectively.

Note that there is a rule of thumb for knowing which primitives accept \egroup as final delimiter: those where the argument is enclosed in an implicit group (with a quirk about \halign because \tabskip assignments will be performed outside this implicit group). This is the case also for the primitive actions of _ and ^, by the way.

It's impossible to define a general macro whose argument can be delimited by } or \egroup. An apparent argument can, provided the replacement text ends with a primitive accepting \egroup in the first place; \aftergroup trickery can then be used for completing the task. Just to make a silly example, here's an implementation of \vfbox that takes a named color as optional argument:

\documentclass{article}
\usepackage{color}

\makeatletter
\newcommand{\vfbox}[1][black]{%
  \def\vfbox@color{#1}%
  \setbox\vfbox@box=\hbox\bgroup
  \aftergroup\vfbox@do
  \let\next=
}

\newcommand{\vfbox@do}{%
  \begingroup
  \color{\vfbox@color}%
  \fbox{\box\vfbox@box}%
  \endgroup
}

\newbox\vfbox@box
\makeatother

\begin{document}
\vfbox\bgroup abc\egroup

\vfbox{abc\egroup

\vfbox[blue]\bgroup abc}
\end{document}

enter image description here

But you can't do it with \uppercase (besides its limitation).

egreg
  • 1,121,712
10

The question gives me a sense if it is read from its end: give the possibility of creating a macro which expands its parameter during parameter scanning. Then the variants } or \egroup as a delimiter of the parameter is serviceable.

I've created the \eparam macro with this syntax:

\def\mymacro #1{the #1 parameter is declared as undelimited}
...
\eparam\mymacro parameter-text

The parameter-text is equal to real-parameter-text enclosed by braces or by another control sequences declared by \eparamopen and \eparamclose.
Example:

\eparamopen\start  \eparamclose\stop
\eparam\mymacro {real-parameter-text}
\eparam\mymacro \start real-parameter-text\stop
\eparam\mymacro \start real-parameter-text}
\eparam\mymacro {real-parameter-text\stop

The main point of the \eparam is that it prepares an Expanded Parameter. The real-parameter-text is expanded during parameter scanning like by \edef. This means that all expandable primitives and macros are expanded during the parameter is read. Unexpandable primitives do nothing in this time (like \edef) so you can do reassigmnent of registers/macros inside this parameter but without any effect for parameter scaninng. This is main difference between this case and the \hbox {...} primitive syntax.

There is one little difference between \edef and parameter scanning: undefined control sequences do nothing (like unexpandable primitives) during parameter scanning. The error can be occur only when the parameter is used (no during parameter scanning).

The separator declared by \eparamclose can be hidden in a macro. Example:

\def\x{-text\stop}
\eparam\mymacro {real-parameter\x

The first open brace or delimiter given by \eparamopen is optional. I.e. you can omit it:

\eparam\mymacro real-parameter-text\stop

The parameter is always balanced by braces. This means that the delimiter declared by \eparamclose does no effect inside inner braces pair (like normal parameter scanning):

\eparam \start text{inside \stop braces}text\stop
% the parameter is: "text{inside \stop braces}text"

The implementation (or wipet's sorcery :) and little tests follow.

\def\tmp{% all expandable primitives (only from classical TeX, you can add others):
  \botmark \csname \else \endcsname \endinput \expandafter \fi \firstmark \fontname
  \if \ifcase \ifcat \ifdim \ifeof \iffalse \ifhbox \ifhmode \ifinner       
  \ifmmode \ifnum \ifodd \iftrue \ifvbox \ifvmode \ifvoid \ifx
  \input \jobname \meaning \noexpand \number \or \romannumeral
  \splitbotmark \splitfirstmark \string \the \topmark
}
\def\skipmm#1->{}  \def\showmm#1->{#1}
\edef\textmm{\expandafter\showmm\meaning\empty}
\edef\expandprimitives{\expandafter\skipmm\meaning\tmp}

\def\isinlist#1#2#3{% from opmac.tex
   \def\tmp##1#2##2\end{\def\tmp{##2}%
   \ifx\tmp\empty \csname iffalse\expandafter\endcsname \else
                  \csname iftrue\expandafter\endcsname \fi}% end of \def\tmp
   \expandafter\tmp#1\endlistsep#2\end
}
\def\isexpanded#1#2{% \isexpanded X\iftrue the X is expandable primitive or macro\fi
   \edef\tmpb{\meaning#1\space}%
   \expandafter\isinlist\expandafter\tmpb\expandafter{\textmm}%
   \iftrue \csname iftrue\expandafter\endcsname\else
      \def\nexxt{\expandafter\isinlist\expandafter\expandprimitives\expandafter{\tmpb}.}%
      \expandafter\nexxt\fi
}
\def\eparamopen#1{\def\eparamopenA{\let#1=\eparamopenA}}
\def\eparamclose#1{\def\eparamcloseA{\let#1=\eparamcloseA}}

\newtoks\eparamT
\def\eparam#1{\begingroup
  \toks0={#1}\let\bgroup=\relax \let\egroup=\relax
  \let\ifIamInGroup=\iffalse
  \ifx\eparamopenA\undefined \def\eparamopenA{^\eparam^}\else \eparamopenA\fi
  \ifx\eparamcloseA\undefined \def\eparamcloseA{^\eparam^}\else \eparamcloseA\fi
  \eparamT={}\eparamA
}
\def\eparamA{\futurelet\tmpc\eparamB}
\def\eparamB{\let\next=\eparamD
   \isexpanded\tmpc\iftrue \def\next{\expandafter\eparamA}\fi
   \ifx\tmpc\bgroupOri \let\next=\eparamC \let\nexxt=\eparamD \fi  
   \ifx\tmpc\eparamopenA  \let\next=\eparamC \let\nexxt=\eparamD \fi
   \next
}
\def\eparamC{\afterassignment\nexxt \let\next= }
\def\eparamD{\futurelet\tmpc\eparamE}
\def\eparamE{\let\next=\eparamN
   \isexpanded\tmpc\iftrue \def\next{\expandafter\eparamD}\fi
   \ifx\tmpc\spacetoken \let\next=\eparamC \let\nexxt=\eparamD \eparamX{ }\fi
   \ifx\tmpc\eparamcloseA \ifIamInGroup \let\next=\eparamN
                          \else \let\next=\eparamC \let\nexxt=\eparamF \fi\fi
   \ifx\tmpc\egroupOri \let\next=\eparamC \let\nexxt=\eparamF \fi
   \ifx\tmpc\bgroupOri \let\next=\eparamC \let\nexxt=\eparamG \fi
   \next
}
\def\eparamN#1{\eparamX#1\eparamD}
\def\eparamG{\begingroup \let\ifIamInGroup=\iftrue \eparamT={}\eparamD}
\def\eparamF{\ifIamInGroup \let\next=\eparamY \else \let\next=\eparamZ \fi \next}
\long\def\eparamX#1{\eparamT\expandafter{\the\eparamT#1}}
\def\eparamY{\expandafter\endgroup
   \expandafter\eparamT\expandafter\expandafter\expandafter
      {\expandafter\the\expandafter\eparamT\expandafter{\the\eparamT}}%
   \eparamD
}
\def\eparamZ{\expandafter\endgroup\the\toks0\expandafter{\the\eparamT}}

\let\bgroupOri=\bgroup
\let\egroupOri=\egroup
\def\tmp/{\let\spacetoken= }\tmp/ %

\def\macro#1{\toks0={#1}\message{the parameter is "\the\toks0"}}

\eparam\macro {abc}  % the parameter is "abc"

\def\x{ww}
\eparam\macro {ab\x c}          % the parameter is "abwwc"
\eparam\macro {ab\the\pageno c} % the parameter is "ab1c"
\eparam\macro {ab\ifx\x\x true\else false\fi c}        % the parameter is "abtruec"
\eparam\macro {ab\ifnum\folio=1 true\else false\fi c}  % the parameter is "abtruec"
\eparam\macro {ab\ifcase\pageno oo\or one\or two\fi c} % the parameter is "abonec"

\eparamopen\start \eparamclose\stop

\eparam\macro {abc\stop        % the parameter is "abc"
\eparam\macro \start abc\stop  % the parameter is "abc"
\eparam\macro \start abc}      % the parameter is "abc"

\eparam\macro abc}     % the parameter is "abc"
\eparam\macro abc\stop % the parameter is "abc"

\eparam\macro \start abc{uf\stop fu}ee\stop  % the parameter is "abc{uf\stop fu}ee"

\def\y{end\stop}

\eparam\macro {a\x\y   % the parameter is "awwend"

\end

Edit: I did do a small modification of the code in order to solve the last demand from the question:

\eparamopen\bgroup \eparamclose\egroup
\eparam {\def\foo#1#2}\bgroup Something with #1 and #2\egroup

acts like \edef\foo#1#2{Something with #1 and #2}.

If you need to deactivate the expansion process (i.e. you need to do \def, no \edef) then you can declare

\def\isexpanded#1#2{\iffalse}

Of course, the delimiter declared by \eparamclose cannot be found in nested macro in such case.

wipet
  • 74,238
  • Well, this is clearly great. Just one thing that might come in handy, although I don't know if it is easy: is there an easy way to control expansion of macros (I mean, like a list of those which should not expand, where you could add macros at wish). – Manuel Sep 02 '14 at 14:32
  • @Manuel IMHO, it is simple. User can \def\macrolist{\a \b \othermacro} as a list of noexpanded macros. Then you can insert \expandafter\settorelax\macrolist\end to the \eparam macro (after \begingroup) and finally you need to define \settorelax as following: \def\settorelax#1{\ifx#1\end \else \let#1=\relax \expandafter\settorelax\fi}. – wipet Sep 02 '14 at 15:01
  • Well, this is not what the question asks. You don't define \macro with an argument delimited by either {} or \bgroup\egroup, but \eparam. – egreg Sep 02 '14 at 17:38
  • @egreg This is more like the answer to my \delimitedbybegroup macro. The answer to that seems to be it's not possible to bring that behaviour from primitives to a user defined command (unless you use hacks like these), isn't it? – Manuel Sep 02 '14 at 17:44
  • @Manuel That's what I wrote in my answer, isn't it? If a primitive accepts \bgroup and \egroup you can do by having the primitive last in the definition of the macro you want. Otherwise you simply can't. This is clever code, but I guess you wouldn't try having \textbf in the “argument”; of course you can add it to the list of unexpanded macros: how long would this list be? – egreg Sep 02 '14 at 17:46
  • @egreg You are not right (as usually:-). User can declare \eparamopen\bgroup \eparamclose\egroup and define \def\macro#1{...} \def\Macro{\eparam\macro} and use \Macro{text} or \Macro\bgroup text} or \Macro{text\egroup or \Macro\bgroup text\egroup. – wipet Sep 02 '14 at 17:50
  • @egreg Well, here you can do that without needing a primitive (like \hbox) that acts like that. – Manuel Sep 02 '14 at 17:54
  • There is another iteresting extension of this code. We can use controlled expansion + assignment. User can declare which type of assigment (\let, \count...= etc.) would be done during parameter scanning and the expansion plus choosen assignments are done at this time. I will not modify the code in this way because this was not the subject of the question. – wipet Sep 02 '14 at 17:57
1

I know this question has created an argument, and some users here think it's not straightforward nor useful. However, as I said in the question, it was curiosity, so wipet's solution does work.

However, I tend to understand much more easier expl3 code rather than the TeX primitives alone (for instance, many macros used here were found just by logic, searching in the documentation for what I thought would be a correct name for the command I was looking for, and I found them).

This is more or less wipet's answer, but trying to bring a little clarity through expl3. I did change some things, so may be it doesn't work; it just seems to work with the examples given.

I did not try at all to find better names for the functions (see my lazyness even in using @@ in the name of the functions to avoid “inventing” something, or using :w everywhere to avoid thinking). I did not find a way of using only token lists rather than the deprecated toks (although I tried, I'm quite unexperienced here). I might even misused expl3 due to my abscence of knowledge. So, anything is welcome.

Here it is.

\documentclass[parskip=full,dvipsnames,x11names]{scrartcl}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
%\usepackage[mathlf]{MinionPro}
\usepackage{xparse,xcolor,etoolbox,multicol}

\makeatletter
\ExplSyntaxOn

% From l3toks (begin)

\cs_new_protected:Npn \toks_new:N #1
  { \__chk_if_free_cs:N #1 \newtoks #1 }
\toks_new:N \c_empty_toks
\cs_new_eq:NN \toks_use:N \tex_the:D
\cs_new_protected_nopar:Npn \toks_set_eq:NN #1 #2 { #1 = #2 }
\cs_new_protected:Npn \toks_set:Nn #1 #2 { #1 =               { #2 } }
\cs_new_protected:Npn \toks_set:No #1 #2 { #1 = \exp_after:wN { #2 } }
\cs_new_protected:Npn \toks_gset:Nn { \tex_global:D \toks_set:Nn }
\cs_new_protected:Npn \toks_gset:No { \tex_global:D \toks_set:No }
\cs_new_protected_nopar:Npn \toks_clear:N #1
  { \toks_set_eq:NN #1 \c_empty_toks }
\cs_new_protected:Npn \toks_put_right:Nn #1 #2
  { #1 = \exp_after:wN { \toks_use:N #1 #2 } }
\cs_generate_variant:Nn \toks_set:Nn  { Nx }
\cs_generate_variant:Nn \toks_gset:Nn { Nx }
\toks_new:N \g_tmpa_toks

% (end) % check if correct and drop this and use _tl if possible

\NewDocumentCommand \egrabparameter { }
  {
    \tl_set_eq:NN \l_@@_macro_noexpand_tl \g_@@_macros_noexpand_tl
    \bool_set_true:N \l_@@_expand_bool
    \@@_grabparameter:nw
  }
\NewDocumentCommand \grabparameter { }
  {
    \tl_set_eq:NN \l_@@_macro_noexpand_tl \g_@@_macros_noexpand_tl
    \bool_set_false:N \l_@@_expand_bool
    \@@_grabparameter:nw
  }

\cs_new_protected_nopar:Npn \@@_set_begindelim:N #1
  { \cs_set:Npn \@@_begin_delim: { \cs_set_eq:NN #1 \@@_begin_delim: } }
\cs_new_protected_nopar:Npn \@@_set_enddelim:N #1
  { \cs_set:Npn \@@_end_delim: {\cs_set_eq:NN #1 \@@_end_delim: } }
\cs_new_protected_nopar:Npn \@@_set_to_relax:N #1
  { \cs_set_eq:NN #1 \scan_stop: }

\toks_new:N \l_@@_parameter_toks
\toks_new:N \l_@@_preamble_toks
\bool_new:N \l_@@_ingroup_bool
\bool_new:N \l_@@_expand_bool
\tl_new:N \g_@@_macros_noexpand_tl
\tl_new:N \l_@@_macros_noexpand_tl
\tl_new:N \l_@@_parameter_tl
\tl_new:N \l_@@_preamble_tl

\cs_new_protected:Npn \@@_grabparameter:nw #1
  {
    \group_begin:
      \tl_map_function:NN \g_@@_macros_noexpand_tl \@@_set_to_relax:N
      \toks_set:Nn \l_@@_preamble_toks { #1 }
      \cs_set_eq:NN \bgroup \scan_stop:
      \cs_set_eq:NN \egroup \scan_stop:
      \bool_set_false:N \l_@@_ingroup_bool
      \bool_if:NF \l_@@_expand_bool
        { \cs_set_eq:NN \token_if_expandable:NT \use_none:nn }
      \cs_if_exist:NTF \@@_begin_delim: 
        { \@@_begin_delim: }
        { \cs_set:Npn \@@_begin_delim: { ^\eparam^ } }
      \cs_if_exist:NTF \@@_end_delim: 
        { \@@_end_delim: }
        { \cs_set:Npn \@@_end_delim: { ^\eparam^ } }
      \toks_clear:N \l_@@_parameter_toks
      \@@_aux_A:w
  }
\cs_new_protected_nopar:Npn \@@_aux_A:w { \peek_after:Nw \@@_aux_B:w }
\cs_new_protected_nopar:Npn \@@_aux_B:w 
  {
    \cs_set_eq:NN \__tmpa_cs:w \@@_aux_D:w
    \token_if_expandable:NT \l_peek_token
      { \cs_set:Npn \__tmpa_cs:w { \exp_after:wN \@@_aux_A:w } }
    \token_if_group_begin:NT \l_peek_token
      {
        \cs_set_eq:NN \__tmpa_cs:w \@@_aux_C:w 
        \cs_set_eq:NN \__tmpb_cs:w \@@_aux_D:w
      }
    \token_if_eq_meaning:NNT \l_peek_token \@@_begin_delim:
      {
        \cs_set_eq:NN \__tmpa_cs:w \@@_aux_C:w
        \cs_set_eq:NN \__tmpb_cs:w \@@_aux_D:w
      }
    \__tmpa_cs:w
  }
\cs_new_protected_nopar:Npn \@@_aux_C:w 
  {
    \tex_afterassignment:D \__tmpb_cs:w
    \cs_set_eq:NN \__tmpa_cs:w
  }
\cs_new_protected_nopar:Npn \@@_aux_D:w { \peek_after:Nw \@@_aux_E:w }
\cs_new_protected_nopar:Npn \@@_aux_E:w
  {
    \cs_set_eq:NN \__tmpa_cs:w \@@_aux_N:w 
    \token_if_expandable:NT \l_peek_token 
      { \cs_set:Npn \__tmpa_cs:w { \exp_after:wN \@@_aux_D:w } }
    \token_if_space:NT \l_peek_token
      {
        \cs_set_eq:NN \__tmpa_cs:w \@@_aux_C:w
        \cs_set_eq:NN \__tmpb_cs:w \@@_aux_D:w 
        \@@_aux_X:w { ~ }
      }
    \token_if_eq_meaning:NNT \l_peek_token \@@_end_delim:
      {
        \bool_if:NTF \l_@@_ingroup_bool
          { \cs_set_eq:NN \__tmpa_cs:w \@@_aux_N:w }
          {
            \cs_set_eq:NN \__tmpa_cs:w \@@_aux_C:w
            \cs_set_eq:NN \__tmpb_cs:w \@@_aux_F:w
          }
      }
    \token_if_group_end:NT \l_peek_token
      {
        \cs_set_eq:NN \__tmpa_cs:w \@@_aux_C:w
        \cs_set_eq:NN \__tmpb_cs:w \@@_aux_F:w
      }
    \token_if_group_begin:NT \l_peek_token
      {
        \cs_set_eq:NN \__tmpa_cs:w \@@_aux_C:w
        \cs_set_eq:NN \__tmpb_cs:w \@@_aux_G:w
      }
    \__tmpa_cs:w 
  }
\cs_new_protected:Npn \@@_aux_N:w #1 { \@@_aux_X:w #1 \@@_aux_D:w }
\cs_new_protected_nopar:Npn \@@_aux_G:w
  {
    \group_begin:
      \bool_set_true:N \l_@@_ingroup_bool
      \toks_clear:N \l_@@_parameter_toks
      \@@_aux_D:w
  }
\cs_new_protected_nopar:Npn \@@_aux_F:w
  {
    \bool_if:NTF \l_@@_ingroup_bool
      { \cs_set_eq:NN \__tmpa_cs:w \@@_aux_Y:w }
      { \cs_set_eq:NN \__tmpa_cs:w \@@_aux_Z:w }
    \__tmpa_cs:w
  }
\cs_new_protected:Npn \@@_aux_X:w #1
  {
    \toks_put_right:Nn \l_@@_parameter_toks { #1 }
  }
\cs_new_protected_nopar:Npn \@@_aux_Y:w
  {
%   \exp_after:wN \group_end: % -
%   \exp_after:wN \l_@@_parameter_toks % -
%   \exp_after:wN \exp_after:wN % -
%   \exp_after:wN { % -
%   \exp_after:wN \toks_use:N % -
%   \exp_after:wN \l_@@_parameter_toks % -
%   \exp_after:wN { % -
%     \toks_use:N \l_@@_parameter_toks } } % -
      \toks_gset:Nx \g_tmpa_toks { \toks_use:N \l_@@_parameter_toks } % +
    \group_end: % +
    \toks_set:Nx \l_@@_parameter_toks % +
      { \toks_use:N \l_@@_parameter_toks { \toks_use:N \g_tmpa_toks } } % +
    \@@_aux_D:w
  }
\cs_new_nopar:Npn \@@_aux_Z:w
  {
%   \exp_after:wN \group_end: % -
%     \toks_use:N \toks0 % -
%%  \exp_after:wN \exp_after:wN % +
%%  \exp_after:wN \group_end: % +
%%  \exp_after:wN \toks_use:N % +
%%  \exp_after:wN \l_@@_preamble_toks % +
%%  \exp_after:wN {
%%    \toks_use:N \l_@@_parameter_toks }
      \tl_gset:Nx \g_tmpa_tl
        { \toks_use:N \l_@@_preamble_toks { \toks_use:N \l_@@_parameter_toks } }
    \group_end:
    \g_tmpa_tl
  }

\@@_set_begindelim:N \bgroup
\@@_set_enddelim:N   \egroup
\tl_gput_right:Nn \g_@@_macros_noexpand_tl { }

\ExplSyntaxOff
\makeatother

\begin{document}

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
culpa qui officia deserunt mollit anim id est laborum.

\begin{multicols}{3}\KOMAoptions{parskip=never}\parindent=0pt

\def\tmp#1{[{#1}]}
\def\macro{\grabparameter\tmp}
\def\emacro{\egrabparameter\tmp}%

\macro\bgroup abc \textit{\color{Tomato3} foo} no\egroup

\macro{real-parameter-text}\par
\macro\bgroup real-parameter-text\egroup\par
\macro\bgroup real-parameter-text}\par
\macro{real-parameter-text\egroup\par
\macro{abc}
\def\x{ww}\par
\emacro{ab\x c}\par
\emacro{ab\thepage c}\par
\emacro{ab\ifx\x\x true\else false\fi c}\par
\emacro{ab\ifnum\value{page}=1 true\else false\fi c}\par
\emacro{ab\ifcase\value{page} oo\or one\or two\fi c}\par
\emacro{abc\egroup\par
\emacro\bgroup abc\egroup\par
\emacro\bgroup abc}\par
\emacro abc}\par
\emacro abc\egroup\par
\emacro\bgroup abc{\noexpand\color{red}uf\string^ fu}ee\egroup

\end{multicols}\unskip

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
culpa qui officia deserunt mollit anim id est laborum.

\newenvironment{texttolowercase}{\egrabparameter\lowercase\bgroup}{\egroup}

\begin{texttolowercase}
  Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
  incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
  nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
  Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
  fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
  culpa qui officia deserunt mollit anim id est laborum.
\end{texttolowercase}

\end{document}

enter image description here

Manuel
  • 27,118
  • +1 I am happy that somebody did understand my code at this level of complexity without comments in the code. And it was extensive work. – wipet Sep 07 '14 at 04:14
  • There is a little bug: spaces are converted to \c_space_token, no real space. It doesn't matter for printing but it woul be problem when scanning parameter separated by space. – wipet Sep 07 '14 at 04:33
  • I still don't understand the usefulness of expl3. We can do comparison with the approach with primitives. Manuel said that expl3 spares the coding of \isexpanded because it is already done. The \isexpanded is implemented on 17 lines in plain TeX code and whole stuff (including \isexpanded) is implemented on 63 lines (2.9kB) without loading any external file. The expl3 code is on 160 lines (6kB). All these lines have to read the human if he/she needs to understand. This is less comfort. And five external lines are loaded with next 19482 lines of code. Where is added value in use? – wipet Sep 07 '14 at 04:48
  • That in your code I need more than two or three seconds to understand where I am, and in expl3 just one. Apart from that I do like verbose names. I guess it's just the same as everywhere: “young people” tend to adapt better to new things basically because they were never used to the “old things”. And yes, it's longer (and loads tons of lines) but does it matter? I mean… the hard drives nowadays have tons of gigabytes, and loading those lines are negligible with current processors. – Manuel Sep 07 '14 at 10:10
  • If you are doing this as an exercise in expl3 coding, I'd suggest replacing all of the toks stuff with the appropriate tl functions :-) – Joseph Wright Sep 07 '14 at 10:14
  • @JosephWright It doesn't work, and I don't know why. – Manuel Sep 07 '14 at 10:16
  • @Manuel You are doing something wrong :-) As always, try to par it down and ask a separate question. – Joseph Wright Sep 07 '14 at 10:18
  • @JosephWright I know. An I probably post it… but in some time, it's a lot to “par down” :) – Manuel Sep 07 '14 at 10:20