5

In the TeX Book (chapter dirty tricks) there is mentioned a way to left align displaymaths.

https://arxiv.org/macros/cp-aa.tex

A macro \displaysetup whose argument is delimited by $$ is used. So it only works for displaymaths ended by $$, not by \Ustopdisplaymath.

So my quenstions:

  1. Is there a possibility to define a macro whose argument is delimited by one of two possible tokens?

  2. I tried to define a macro whose argument is delimited by another token. I thought I could insert this token by \aftergroupbut this fails.

MWE (does not work):

\def\mydisplay#1\xxx{#1}
\everydisplay{\aftergroup\xxx\mydisplay}

$$ 0 + 0 = 0 $$

\bye

% This leads to an error message: Paragraph ended before \mydisplay was complete.

I hoped that \mydisplaycould be defined so that it finds out if the displaymath ends by $$ or \Ustopdisplaymath.

But the following example works… Why?:

\def\mydisplay#1\xxx{#1}
\everydisplay{\mydisplay}

$$ 0 + 0 = 0 $$\xxx

\bye

Any ideas? :)

  • 1
    Something like “macro overloading” does not exist in TeX. What you could do is absorb tokens and check for each whether it is the ending token. – Henri Menke Sep 26 '18 at 2:51
  • And how can I absorb Tokens? I don't know what to do. – user125730 Sep 27 '18 at 17:58
7
+100

First some words of explanation of why your approach didn't work.

What \everydisplay does is to insert the following token list right after it has scanned the beginning of a display math, indicated by $$ (or more precisely, by two math shift characters, catcode 3). The token stream to process now looks like

\aftergroup\xxx\mydisplay 0 + 0 = 0 $$

Next \aftergroup is executed. I think your misunderstanding in this primitive is, that the parameter token is inserted immediately when the command is executed to give the result

\mydisplay 0 + 0 = 0 $$\xxx

But this is not the case. The token is set aside on a special internal token list which is actually inserted at the time the final brace, \endgroup or in this case the end of the math group is found. However, the end of the local group is never found, because the next thing to happen is the expansion of \mydisplay. As a macro with a parameter delimited by \xxx it scans ahead until it finds a control sequence \xxx in the token stream. Here no such token occurs, so it runs out of input eventually.

Not so in your second example. Here you explicitly add \xxx after the group such that \mydisplay is able to find it and reapply the scanned tokens.


As mentioned in the comments, you can achieve the desired result by scanning tokens of the math list until you've arrived at one of the tokens you consider the end of the display math. The basic idea is to use \futurelet to look ahead the next token and continue processing based on what token follows next. The actual token reading is done via TeX's macro parameter processing.

Here's a plain LuaTeX version of such an token-absorbing macro which either stops processing on $$ or the LuaTeX primitve \Ustopdisplaymath:

\catcode`\@=11
\newtoks\absorbed
\begingroup
\def~{\global\let\spacetoken= }~ %
\endgroup

\def\save@tokens#1{\global\absorbed\expandafter{\the\absorbed#1}}
\def\fbox#1{\overline{\underline{#1}}}

\def\absorb{\global\absorbed={}\absorb@}
\def\absorb@{\futurelet\@next\absorb@check}
\def\absorb@check{%
    \ifx\@next\spacetoken
        \let\@next=\absorb@space
    \else\ifx\@next\bgroup
        \let\@next=\absorb@group
    \else\ifx\@next$%
        \let\@next=\absorb@finishI
    \else\ifx\@next\Ustopdisplaymath
        \let\@next=\absorb@finishII
    \else
        \let\@next=\absorb@gobble
    \fi\fi\fi\fi
    \@next%
}
\begingroup
\def~ {\save@tokens{ }\absorb@}
\global\let\absorb@space=~
\endgroup
\def\absorb@group#1{\save@tokens{{#1}}\absorb@}
\def\absorb@gobble#1{\save@tokens{#1}\absorb@}
\def\absorb@finishI$${\absorb@result}
\def\absorb@finishII\Ustopdisplaymath{\absorb@result}
\def\absorb@result{\fbox{\the\absorbed}$$}

\catcode`\@=12

$$\fbox{1 + {i \over n+1} \hbox{ is equal to } x_i^{n+1}}$$

\everydisplay{\absorb}
$$1 + {i \over n+1} \hbox{ is equal to } x_i^{n+1}$$

\Ustartdisplaymath 1 + {i \over n+1} \hbox{ is equal to } x_i^{n+1}\Ustopdisplaymath

\bye

The output is the same for all three equations:

enter image description here

A quick overview over the defined macros:

  • \spacetoken is the plain space character saved as an implicit character which is needed in an \ifx comparison later.
  • \save@tokens adds a list of tokens to the token list \absorbed which we use to save the scanned tokens for later use.
  • \fbox is a lukewarm replacement for LaTeX's macro of the same name.
  • \absorb is the main entrance point. It just clears the token list and hands over to \absorb@.
  • \absorb@ uses \futurelet to look ahead the following token (stored in \@next).
  • \absorb@check inspects \@next and decides what to do next. There are five cases to consider:
    • \absorb@gobble: An ordinary token was found which is simply put on the internal save list.
    • \absorb@finishI: The following token is $. We assume the token after that is $ too (otherwise LuaTeX would complain), so we can finish processsing.
    • \absorb@finishII: Similar to \absorb@finishI, but the following token is \Ustopdisplaymath.
    • \absorb@space: Next comes a space token, so it's read by this macro and put back on the save list. The extra handling for spaces is necessary because TeX's standard parameter processing gobbles all space tokens to find the next "real" token.
    • \absorb@group: When the next token is {, because of how TeX's parameter reading works, a call to \absorb@gobble would consume the whole token sequence up to the closing } instead of just the opening brace. So we need a special handling of braced groups that puts back the gobbled braces around the consumed parameter.
  • \absorb@result is the final macro, called when the whole token sequence has been found. As a demo function, the scanned math list is just put into the \fbox replacement.

Note that, while this approach seems to work for most common uses, it most likely breaks in several corner cases.

Note also that it will only work if the $$ or \Ustopdisplaymath macro appears "visually", i.e. not hidden inside other macros. Expanding macros as you scan along will take much more effort.

  • With LuaTeX 1.08 you can do \toksapp\absorbed{#1} instead of \absorbed\expandafter{\the\absorbed#1}. – Henri Menke Oct 2 '18 at 4:17
3

As I proposed in the comments, you could absorb tokens and decide what to do based on the next token.

The other answer presents a nice TeX macro based solution. In this answer I am going to attempt a Lua solution. First I present the code, and then I will discuss what it does.

\def\fbox#1{\overline{\underline{#1}}}

\def\grabdollar#1$${\fbox{#1}$$}
\def\grabUdisplay#1\Ustopdisplaymath{\fbox{#1}\Ustopdisplaymath}

\begingroup
\catcode`\#=12
\gdef\luagrab{\directlua{
local toks = { true }
local check_next = false
while true do
    toks[#toks+1] = token.get_next()
    if check_next and toks[#toks].cmdname == "math_shift" then
        toks[1] = token.create("grabdollar")
        break
    end
    if toks[#toks].csname == "Ustopdisplaymath" then
        toks[1] = token.create("grabUdisplay")
        break
    end
    if toks[#toks].cmdname == "math_shift" then
        check_next = true
    else
        check_next = false
    end
end
token.put_next(toks)
}}
\endgroup

$$\fbox{1 + {i \over n+1} \hbox{ is equal to } x_i^{n+1}}$$

\everydisplay{\luagrab}

$$1 + {i \over n+1} \hbox{ is equal to } x_i^{n+1}$$

\Ustartdisplaymath 1 + {i \over n+1} \hbox{ is equal to } x_i^{n+1}\Ustopdisplaymath

\bye

enter image description here

The macro which goes into \everydisplay to decide which grabber macro to use is \luagrab, which only executes Lua code.

We start by defining a new table to hold all the tokens we read, but reserve the first slot for the grabber macro which has to go in front of all the content. I use true as a sentinel here, but I could have also used a number or a string (basically everything except nil).

local toks = { true }

When we want to detect whether the display is delimiter by a double math shift ($$) we cannot simply terminate after reading the first $ because that could be a nested inline math. That's why we have to remember if the token has to be checked.

local check_next = false

Now we start reading tokens in an infinite loop and append them to the table.

while true do
    toks[#toks+1] = token.get_next()

If the check_next flag was set and the current token is a math shift, that means that we encountered two consecutive math shifts, which means that we are done. In this case we want to use the \grabdollar macro.

    if check_next and toks[#toks].cmdname == "math_shift" then
        toks[1] = token.create("grabdollar")
        break
    end

If \Ustopdisplaymath is encountered we may also stop and we want to use the \grabUdisplay macro.

    if toks[#toks].csname == "Ustopdisplaymath" then
        toks[1] = token.create("grabUdisplay")
        break
    end

If the current token is a math shift, we have to check whether the next token is also a math shift. If the current token is not a math shift we reset the flag.

    if toks[#toks].cmdname == "math_shift" then
        check_next = true
    else
        check_next = false
    end

After we exit the loop, we write all the tokens back to the input stream.

end
token.put_next(toks)

N.B.: I don't want to know what happens if you forget to terminate display math. Probably LuaTeX will crash without a useful error message or eat your whole document and ask for input.

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.