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.
  • 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
  • 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 9: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 9:19
  • @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 9:48
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.

  • 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 2 '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 2 '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 2 '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 2 '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 2 '14 at 17:46
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).

  • I didn't accept this answer, it was accepted itself when upvoted (may be some of the remaining problems from yesterday?). – Manuel Aug 18 '14 at 11:13
  • Ok, now it has disappeared. – Manuel Aug 18 '14 at 11:18
  • It seems so :-) – Manuel Aug 19 '14 at 0:50
  • Small question: how does environ package works? Is it somehow related? How does it do to grab the “argument” ending in \end{<environ>}? Or may be it has nothing to do? – Manuel Aug 27 '14 at 12:22
  • @Manuel Yes, an environment defined with \NewEnviron grabs the argument delimited by \end{<name>}. – egreg Aug 27 '14 at 19:05
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

  • +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 7 '14 at 4: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 7 '14 at 4: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 7 '14 at 4: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 7 '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 7 '14 at 10:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.