5

Time and time when I am trying to read or write some macros, I'm always frustrated by lots of \expandafters inside them. Why don't we add some new primitives and grammar into TeX engines to make macros writing and reading more easier?

My idea is to use ( and ) as new group opening and closing characters in some contexts. ( and ) acts like { and }, but when TeX encounter them in some context, it firstly expands tokens inside them completely just like \edef. For example

\expandfirston
\def\cmd(replacement)  % same as \edef\cmd{replacement}
\expandfistoff

Another example:

\expandfirston
\def\cmd#1#2{replacement}
\cmd{\arga}{\argb}  % firstly expands \cmd, then \arga and \argb
\cmd(\arga){\argb}  % firstly expands \arga, then \cmd, lastly \argb
\cmd{\arga}(\argb)  % firstly expands \argb, then \cmd, lastly \arga
\cmd(\arga)(\argb)  % firstly expands \arga, then \argb, lastly \cmd
\expandfirstoff

The third example:

\expandfirston
\def\cmda#1{replacementa}
\def\cmdb#1{replacementb}
\cmdb(\cmda(\arg))% firstly expands \arg, then \cmda, lastly \cmdb
\expandfirstoff

For compatibility, We may restrict the new grammar only takes effect in defining macros and calling macros, and some assignments, etc.

My questions are the followings:

  1. Are there any shortcoming with this new grammar?
  2. If I want to implement this new grammar as macros, which existing macros could I refer to?
  3. If someone implements this new grammar in TeX engine, would pdftex, xetex and luatex accept this patch and new primitives?
  • This is edef expansion x expansion in latex3 terminoligy, which would not really help in most uses of \expandafter much of the latex3 xparse syntax is designed to hide \expandafter so \expandafter\foo\csname x \endcsname becomes \foo:c{x} for example – David Carlisle Oct 9 '14 at 13:16
  • 1
    @DavidCarlisle Some times ago I tried to learn latex3, but its grammer also frustrated me. ... – Z.H. Oct 9 '14 at 13:27
  • Perhaps, but you asked for existing macros. Do you really mean same as \edef or same as \expandafter? the title says \expandafter but you say \edef in the code (edef expansion is a tricky thing and the source of all the issues with \protect – David Carlisle Oct 9 '14 at 13:34
  • @DavidCarlisle Oh, I misunderstood your last comment. Thanks. – Z.H. Oct 9 '14 at 13:42
  • The problem here I think is that TeX works a very different way from the functional-like approach you propose. Moreover, this isn't a new primitive syntax but a new pair(?) of catcodes for the behaviour desired. That's very tricky to imagine as a patch without major work. I'd note that engine changes tend to address things TeX cannot do, not simply ones that are 'oddities': \expandafter does work perfectly well for the semantics DEK defined. – Joseph Wright Oct 9 '14 at 16:18
10

You need to separate out the choice of the : and _ syntax of L3 from the functionality.

The following uses a much smaller expansion library than xparse (just 7 lines of code) but it implements the n (no expansion) x (full \edef expansion) and o (expand once \expandafter) expansion types.

Rather than define variant macro names with :nnx suffixes, just explicitly prefix the use with a \use command that specifies how to expand the arguments (which is also the basis of xparse syntax).

Note how with o, \argb just expands once to bbb {\arga} before \cmd is called but with x, \argb fully expands to bbb {aa}.

enter image description here

\documentclass{article}
\usepackage[T1]{fontenc}

\def\use#1{\xuse#1z}
\def\xuse#1{\csname use#1\endcsname}
\def\usez#1{#1}
\long\def\usen#1z#2#3{\xuse#1z{#2{#3}}}
\long\def\useo#1z#2#3{\expandafter\useohelp\expandafter{#3}{#1}{#2}}
\long\def\useohelp#1#2#3{\xuse#2z{#3{#1}}}
\protected\long\def\usex#1z#2#3{\edef\usetmp{#3}\expandafter\useohelp\expandafter{\usetmp}{#1}{#2}}

\begin{document}



\def\cmd#1#2{{\ttfamily\par\def\a{#1}\#1:\meaning\a \quad\#2:\def\a{#2}\meaning\a\par}}
\def\arga{aa}
\def\argb{bbb {\arga}}



        \cmd{\arga}{\argb}  % normal
\use{nn}\cmd{\arga}{\argb}  % normal
\use{on}\cmd{\arga}{\argb}  % expand #1 once 
\use{no}\cmd{\arga}{\argb}  % expand #2 once
\use{oo}\cmd{\arga}{\argb}  % expand #1 and #2 once
\use{xx}\cmd{\arga}{\argb}  % fully (\edef) expand #1 and #2

\end{document}

As noted in comments it isn't at all clear whether you intended your () syntax to correspond to o or x but either way It is really better handled at the macro layer rather than a change of the engine expansion logic.

| improve this answer | |
  • Perhaps add a group (and \protected status) for \usex? – Joseph Wright Oct 9 '14 at 16:44
  • @JosephWright \protected: you're so modern:-) – David Carlisle Oct 9 '14 at 16:45
  • @JosephWright I added \protected adding a group would require a bit of reaarangement and I think here I'm happy for the definition of \usetmp to leak. – David Carlisle Oct 9 '14 at 16:48
  • Which is the same as expl3 does, because \zh_cmd:oo is defined as \exp_args:Noo\zh_cmd:nn. – egreg Oct 9 '14 at 16:56
  • 1
    @egreg yes, It's a copy of expl3 (from memory:-) – David Carlisle Oct 9 '14 at 16:57
6

It's easy with expl3:

\input expl3-generic

\def\arga{what}
\def\argb{how}

\ExplSyntaxOn
\cs_new:Npx \zh_cmda: { \arga\argb }

\cs_new:Npn \zh_cmdb:nn #1 #2
 {
  \str_if_eq:nnTF { #1 } { what }
   { \msg_term:n{\#1~is~`what'} }
   { \msg_term:n{\#1~isn't~`what'} }
  \str_if_eq:nnTF { #2 } { how }
   { \msg_term:n{\#2~is~`how'} }
   { \msg_term:n{\#2~isn't~`how'} }
 }
\cs_generate_variant:Nn \zh_cmdb:nn { no }
\cs_generate_variant:Nn \zh_cmdb:nn { on }
\cs_generate_variant:Nn \zh_cmdb:nn { oo }

\msg_term:n
 {
  The ~ replacement ~ text ~ of ~ \exp_not:N \zh_cmda: ~ is ~
  `\token_get_replacement_spec:N \zh_cmda:'
 }
\zh_cmdb:nn { \arga } { \argb }
\zh_cmdb:no { \arga } { \argb }
\zh_cmdb:on { \arga } { \argb }
\zh_cmdb:oo { \arga } { \argb }

\bye

The terminal session will show

*************************************************
* The replacement text of \zh_cmda: is `whathow'
*************************************************
*************************************************
* #1 isn't `what'
*************************************************
*************************************************
* #2 isn't `how'
*************************************************
*************************************************
* #1 isn't `what'
*************************************************
*************************************************
* #2 is `how'
*************************************************
*************************************************
* #1 is `what'
*************************************************
*************************************************
* #2 isn't `how'
*************************************************
*************************************************
* #1 is `what'
*************************************************
*************************************************
* #2 is `how'
*************************************************

Note that the result is what's expected: the argument are expanded once when o is used, making the \str_if_eq:nnTF test succeed.

Do you need to use expl3 syntax in your code? Not at all! Just define a suitable wrapper. With \exp_args_generate:n you can define all the variants you need.

\input expl3-generic

\ExplSyntaxOn

\cs_new:Npn \use #1 { \cs_if_exist_use:cF { exp_args:N#1 } { \ERROR } }
\exp_args_generate:n { nn, on, ee }

\ExplSyntaxOff

\def\cmd#1#2{{\tt\par\def\a{#1}\#1:\meaning\a \quad\#2:\def\a{#2}\meaning\a\par}}
\def\arga{aa}
\def\argb{bbb {\arga}}



        \cmd{\arga}{\argb}  % normal
\use{nn}\cmd{\arga}{\argb}  % normal
\use{on}\cmd{\arga}{\argb}  % expand #1 once 
\use{no}\cmd{\arga}{\argb}  % expand #2 once
\use{oo}\cmd{\arga}{\argb}  % expand #1 and #2 once
\use{xx}\cmd{\arga}{\argb}  % fully (\edef) expand #1 and #2
\use{ee}\cmd{\arga}{\argb}  % fully (\edef) expand #1 and #2

\bye

The last is the new e-type expansion, which is similar to x-expansion, but is fully expandable itself. If a variant is not defined, you get an error message. Of course \use{nn} is the same as nothing.

enter image description here

The advantage is that you can define variants for as many arguments as you need. So if \macro has three arguments, you can use

\use{noe}\macro{<first>}{<second>}{<third>}

and accomplish the desired expansions before \macro is actually called, provided you add noe to the list of known variants.

| improve this answer | |
  • The problem is that, in my opinion, expl3 code is also horrible to read or write. – Z.H. Oct 9 '14 at 13:47
  • 2
    @Z.H. Blasphemy! ;-) – user31729 Oct 9 '14 at 13:49
  • @Z.H. Well, the syntax you propose can't be realized with macros without sacrificing full expandability. – egreg Oct 9 '14 at 13:49
  • 5
    @Z.H. Once upon a time I found English horrible to read and write. But then I learned it and now it's easy. With expl3 it was just the same. Actually it was better because I learned a lot about TeX and expansion by learning expl3 :) – cgnieder Oct 9 '14 at 14:09
  • 1
    @Z.H. You still misunderstood me: expl3 is the simpler choice! – cgnieder Oct 10 '14 at 9:52

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.