12

Is it possible to do some TeX magic to get a command \mycmd which takes one argument before the command and one afterwards. So something like <arg1>\mycmd<arg2>. I tried

\documentclass{memoir}
\def#1\mycmd#2{Hello World of #1 and #2}
\begon{document}

{foo}\mycmd{bar}

\end{document}

but obviously failed. Can't say I expected that it would work, anyway.

9
  • 2
    Btw: I'd say it is impossible because TeX by definition isn't capable of reading backwards.
    – Ruben
    Commented Dec 20, 2014 at 17:14
  • 1
    @ruben Not only for fun, actually. I'm converting an XML document into LaTeX. It uses calligraphic letters, which I'd like to do LaTeX-style with lettrine. Only problem: Sometimes there are quotation marks before the calligraphic letters. lettrine can handle this, provided that I can tell it to. But that requires such a command.
    – Gaussler
    Commented Dec 20, 2014 at 17:15
  • 1
    Something like this should be possible. After all, $a \over b$ and $a \choose b$ are examples of (something pretty close to) what you want. However, I don't know how those are defined. (Yes, I really need to read the TeX-book, but I still haven't had the time for it.)
    – jmc
    Commented Dec 20, 2014 at 18:02
  • 1
    The situation with \over is very different: first of all it's not a macro, but a primitive. When TeX finds it, it stores whatever is in the current math list, determines the denominator and then builds the Frac atom. With macros it's not possible: TeX strictly expands in the order tokens are created and after expansion or execution it discards the items.
    – egreg
    Commented Dec 20, 2014 at 18:17
  • 1
    @Gaussler No, it's not possible. TeX is only capable of using the last object from a constructed list, provided it's a skip, a penalty, a kern or a box.
    – egreg
    Commented Dec 20, 2014 at 18:19

3 Answers 3

17

The problem has been examined under a different point of view in Is a command with an argument before and after the command possible? but the situation is different: the object that was to be placed before or after the main command is itself a macro.

It's not possible to have a macro that “looks backwards”. TeX is strictly “first in, first out”, after macro expansion. Moreover it tokenizes the input only when needed for supplying arguments to macros or primitives.

After macro expansion, only unexpandable tokens remain and they are executed in the order they are found and removes them. So in a situation such as

{abc}\macro{def}

the {abc} part is already gone when \macro is examined. Trace of it can remain in some of the internal lists, so, for example, TeX is able to execute \unskip which really is a command that, upon execution, removes the last node from the current list, provided it is glue (there are hairy details, but this is basically the truth).

A command that seems to do what you want is \over. However, this is not a macro, but a primitive.

Its work is performed at the level of the internal lists: when TeX finds \over, it executes it, because it's unexpandable. The execution consists in storing whatever is in the current math list in a special place and go on. When the end of the current math list is found, TeX uses it and the stored part to build a Frac atom with the head sublist as numerator and the tail sublist as denominator. But nothing of this happens at the macro expansion level.

You can have a macro \first that looks ahead after its argument to see if a macro \second comes along and then take appropriate decisions about what to do.

Even LuaTeX can't do it, unless you modify the macro processor, because it can examine and manage all types of nodes in the current list, while TeX abilities are more limited (only penalties, glues, kerns and boxes and not in every situation).

14

Your requirement is possible for example with encTeX. Try the following code compiled by csplain format:

\mubyte \phantomcmd #1 {\endmubyte

\mubytein=0
\def\phantomcmd#1\mycmd#2{1=#1, 2=#2}
\def\mycmd{my}
\def\normalcmd#1{normal=#1}

\mubytein=1

\normalcmd{hello}    % gives: normal=hello 
...{foo}\mycmd{bar}  % gives: ...1=foo, 2=bar

\bye

When \mubytein=1 then special input is activated: The \phantomcmd is inserted before each occurrence of the { character. This is done before token and expand processor. But there is an exception: if the { immediately follows after control sequence tokenized by token procesor then the \phantomcmd isn't inserted. So both examples work: \normalcmd{hello} and {foo}\cmd{bar}. The second one internally works as:

\phantomcmd{foo}\mycmd{bar}

and this is expanded as desired. But this technique is very fragile, because each occurrence of { which is not immediately after control sequence, is automaticaly preceded by \phantomcmd (when \mybytein=1). For example this crashes:

\normalcmd {hello}   % this is transformed to \normalcmd\phantomcmd{hello}
bb {foo}\mycmd{bar}  % 

Note the difference between this and previous example: the space immediately after \normalcmd.

If you don't be afraid from this fragility, you can define \pfantomcmd which processes the various \mycmds:

\mubyte \phantomcmd #1 {\endmubyte
\mubytein=0
\def\phantomcmd#1{\def\tmp{#1}}
\def\mycmdA#1{I am A. before: \tmp, after: #1}
\def\mycmdB#1{I am B. before: \tmp, after: #1}
\mubytein=1
... {a}\mycmdA{b} ... {x}\mycmdB{y}
\bye
2
  • Interesting. Maybe you should change “is immediately followed“ by “immediately follows”, as this seems to reflect the described behavior.
    – egreg
    Commented Dec 21, 2014 at 11:16
  • Do I get it right that this only allows you to define one command with an argument before it? Commented Dec 22, 2014 at 7:57
6

In a comment made by the OP, mention was made that the application was one of predetermining quotes prior to a calligraphic letter. As I said in my comment reply, perhaps there is some hope if the problem can be more constrained. For example, if <arg1> is always (and only) a single glyph from a limited set of catcode 12 possibilities, then you could achieve your desired result by making those glyphs \active, and make it look ahead to see if \mycmd follows them or not.

This approach could be used unless the <arg1> possibilities were needed inside other macros, like the period . for example, would not work well for <arg1>, since it is frequently used to specify dimensions via a decimal point.

In the MWE, <arg1>\mycmd<arg2> will print out a Huge \fbox{<arg1><arg2>}. I have it only set up to intercept a double quote or a question mark for <arg1>, though other catcode 12 <arg1> glyphs could be added.

EDITED to ignore calls with "illegal" values of <arg1>.

\documentclass{article}
\let\precommand\relax
\let\mycmd\relax
\def\testnext#1#2{\ifx#1\mycmd\myactualcmd\precommand{#2}\else\precommand#1#2\fi}
\def\myactualcmd#1#2{\if#1\precommand\fbox{\Huge#1#2}\else#1#2\fi}
\let\svquote"
\catcode`"=\active
\def"{\let\precommand\svquote\testnext}
\let\svqmark?
\catcode`?=\active
\def?{\let\precommand\svqmark\testnext}

\begin{document}
Is this is a "test"\textbf{?}  This is a "\mycmd{BIG} test?\mycmd{!}

Here mycmd is used with illegal precommand =\mycmd{which} is ignored.
\end{document}

enter image description here

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .