4

Tex (and XeLaTeX) use what I would call prefix syntax for user-defined macros. For example, the macro \newcommand{\identity}[1]{#1} is used with the variables prefixed by the macro: \identity{vars}. Some macros (is ^ a macro?) can also be used sandwiched between the variables: a^b. I would call that infix syntax.

How can I define my own infix macros using Unicode characters, say ♤ U+2660 or ⊗ or •? I want to be able to write {a+b}♤{c+d} and that should be interpreted as \mymacro{a+b}{c+d}.

This post says that new infix macros are impossible. What about defining ♤ as a different type of character. I do not know enough about changing the character classes (or types) but I know it is possible: the character @ works differently within a class file (.sty) than in a normal .tex file.

4
  • 2
    Sorry, no: “infix commands” such as the primitive \over are hardwired in TeX and their behavior cannot be reproduced with macros. Other than with these particular commands, infix syntax is not possible.
    – egreg
    Commented Jan 26, 2022 at 18:05
  • @egreg My hope was that some Unicode characters could be changed catcodes (like the @ character changes classes) and make it work like the ^ or the _ characters.
    – Hector
    Commented Jan 27, 2022 at 1:51
  • @Hector you can change the catcode and allow a unicode symbol as command, but you can always only look forward and never back. So ♤{a+b}{c+d} is ok, even ♤ab, but not a♤b.
    – Marijn
    Commented Jan 27, 2022 at 6:46

3 Answers 3

5

The question is tagged xetex which doesn't allow for infix operators. For future readers however an answer with LuaLaTeX. The idea is to write a line callback with a regular expression that substitutes an infix operator for a prefix macro.

Note that this approach is rather fragile as it does not allow for nested braces for example. This could be made more robust by actually processing tex tokens instead of a (problematic) catch-all regex, but just as a proof of concept.

\documentclass{article}
\usepackage{luacode}
\usepackage{fontspec}
\setmainfont{DejaVu Serif}
\begin{luacode}
    function translate(line)
        return string.gsub(line, "{(.*)}♤{(.*)}", "\\mymacro{%1}{%2}")
    end
    luatexbase.add_to_callback("process_input_buffer", translate, "infix")
\end{luacode}
\let\mymacro\frac
\begin{document}
See the following fraction: ${a+b}♤{c+d}$

Regular club: a ♤ b
\end{document}

Result:

enter image description here

4
  • @Marjin The tag only reflects my ignorance.
    – Hector
    Commented Jan 27, 2022 at 1:49
  • This can be applied for older engines too... except that you have to wrap the block manually (e.g. inside an environment) and the synctex information is lost (only point to the end of the block). If the whole document is wrapped, then the disruption to document source code is minimal, but the synctex is completely lost.
    – user202729
    Commented Jan 29, 2022 at 8:17
  • @user202729 most people that ask for something like this do that out of some idea of 'readability' or 'flow' that doesn't allow for wrapping in a command or environment. Wrapping the whole document in an environment would solve that but this would create various other problems I think, with verbatim environments, other command definitions, compilation performance etc, not just limited to synctex issues.
    – Marijn
    Commented Jan 29, 2022 at 8:37
  • @Marijn • verbatim can be fixed simply by setting correct catcode and rescan later • machines nowadays are fast. A file is maybe a few KB at most • it's also possible to put a single command at the begin that scans forwards to the document end, although I don't see this important.
    – user202729
    Commented Jan 29, 2022 at 8:54
1

What you call “infix macro” actually isn't. The behavior of ^ is hardwired in TeX (not the character itself, just any character with category code 7 would behave in exactly the same way); the same holds for _ (category code 8, actually).

There are “infix commands” such as \over or \overwithdelims, but they're not macros: they're unexpandable primitives.

The working of the above tokens is deeply based on TeX's math mode, where very different things happen with the input. Indeed, a math list is built by classifying the objects found as atoms of various types; an atom has three fields: the nucleus, the subscript and the superscript and each of these can itself be a math list.

How does \over work? First, we note that an “alone” { (one not used for delimiting arguments to macros) or \left start a new math list that will be later inserted in the outer math list.

Now, when TeX finds \over, it stores whatever it has found in the current math list (that might be the main math list), starts a new math list until coming to the matching } or \right (or the end of math mode) and then takes the two so built math lists and forms with them a Frac atom.

This cannot be reproduced via macros: it is a built in behavior (and one of the flaws of TeX, in my opinion).

How might your proposed operator determine what's the “left part” and “right part”? Something like \over can, because its behavior is built in in TeX which has specific processing rules for it.


Footnote. The peculiar syntax of \over is meant to reflect how fractions are read aloud. But what would be the meaning of “x plus one over x plus two”? Does it correspond to

\frac{x+1}{x+2}

or to

x+\frac{1}{x}+2

or to something else?

2
  • The precedence of ♤ would be the highest (just like the precedence of over). Using braces would accomplish the other option {a+b)♤{c+d}. In any case, I thought that category codes were a way to classify the characters for parsing but I see that it also 'associates a hardwired macro' to them. For example, \catcode\♤=4` followed by \def ♤{something with two params} does not work as I expected.
    – Hector
    Commented Jan 27, 2022 at 12:46
  • @Hector Your expectation is unfortunately unfounded. Only category code 13 characters are allowed after \def (besides control sequences, of course). And there no hope to get your infix syntax anyway. Category code 4 characters correspond to no macro; their action is hardwired.
    – egreg
    Commented Jan 27, 2022 at 13:02
1

I write a package for this purpose. Currently the source code is at https://github.com/user202729/TeXlib (not yet published to CTAN); however unfortunately there's little to no documentation and as with most things implemented in TeX, it's slow.

\documentclass{article}
\usepackage{rewriterest}      % ← this package!
\usepackage{newunicodechar}   % pdflatex does not have spade suit

\newunicodechar{♤}{\ensuremath{\spadesuit}}

\begin{document}

\ExplSyntaxOn
\rewriterest:n {
    \regex_replace_all:nnN {
        (    \{ [^\{\}]* \}    )  % group 1: {...}, content does not contain any { or }
        ♤                         % the club
        (    \{ [^\{\}]* \}    )  % group 2: same as group 1
    } {
        \\frac   % we're doing textual substitution here, so no \c{frac}
            \1
            \2
    } \RWRbody
}
\ExplSyntaxOff

See the following fraction: ${a+b}♤{c+d}$

Regular club: a ♤ b
\end{document}

It works by capturing the content of the remaining part of the file, then do some regex substitution on it, then execute the result.

4
  • As a result of the regex substitution, it will also replace the content inside comment/verbatim environment etc.
    – user202729
    Commented Mar 29, 2022 at 10:42
  • Also synctex information is slightly broken (although fixable) on engines other than LuaTeX, see luatex - How can I capture and rescan TeX source code while preserving synctex data? - TeX - LaTeX Stack Exchange .
    – user202729
    Commented Mar 29, 2022 at 10:44
  • So you're proposing to do a gigantic regex substitution just to allow a dubiously useful feature? What about $a ♤ b$? Of course you'll say this is unsupported. A Perl/Python/you-name-it preprocessor would be much faster. And with much I mean it.
    – egreg
    Commented Mar 29, 2022 at 12:14
  • 1
    @egreg I did explain what it does and what the problems are, didn't I?
    – user202729
    Commented Mar 29, 2022 at 13:09

You must log in to answer this question.

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