1

There's an active character * (can be anything, asterisk just used for demonstration purposes).

If the character following * is a space, write \marginpar{foo}, otherwise write \marginpar{bar}. This works.

The active character may have an optional argument. The optional argument must immediately follow the active character, otherwise it is not to be considered a macro argument, e.g. *[wut] bla or *[but]yum, but not * [noo] where [noo] is not interpreted as a macro argument as it's separated with a space. This also works.

I considered this to be an easy task but kept getting errors when the token following the active char is a formatting macro, e.g. *[flut]\textbf{...}.

I stole^Wborrowed some LaTeX2e macros to fit my needs, and after hours of trial and error, the best I could come up with was this:

\documentclass{book}
\usepackage{lipsum}

\makeatletter
% Do not skip space in contrast to \@ifnextchar.
\long\def\@ifonlynextchar#1#2#3{%
  \let\reserved@d=#1%
  \def\reserved@a{#2}%
  \def\reserved@b{#3}%
  \futurelet\@let@token\@ifonch}
\def\@ifonch{%
  \ifx\@let@token\reserved@d
    \let\reserved@c\reserved@a
  \else
    \let\reserved@c\reserved@b
  \fi
  \reserved@c}
% Check for trailing space
\long\def\@ifspace#1#2{%
  \def\reserved@a{#1}%
  \def\reserved@b{#2}%
  \futurelet\@let@token\@ifspc}
\def\@ifspc{%
  \if\@let@token\@sptoken
    \let\reserved@c\reserved@a
  \else
    \let\reserved@c\reserved@b
  \fi
  \reserved@c}

\def\foobar{%
  \@ifonlynextchar[{\foobar@opt}%
  {\foobar@{}}%
}
\def\foobar@opt[#1]{\foobar@{#1}}
\def\foobar@#1{%
  {\scriptsize #1}%
  \@ifspace{\foo}{\bar}%
}

\def\foo{\marginpar{foo}}
\def\bar{\marginpar{bar}}

\catcode`\*=13
\let*\foobar

\makeatother

\begin{document}

\lipsum[2][2] There is a trailing space* here should write \emph{foo} in margin.

There is a trailing space*[xy] should write \emph{foo} in margin and \emph{xy} before space. \lipsum[3][4]
There is a space between asterisk and bracket,* [za] should write \emph{foo} in margin and [za] as a literal (shall not be part of the macro argument).

\lipsum[1][3] There is no trailing space *should write \emph{bar}. Then, there is no trailing space but an optional argument so should *[bc]write \emph{bar} in margin and \emph{bc} before ``write.''

Macro after active char *\textbf{shall} detect no space and write \emph{bar} in margin. Then, with an optional argument *[de]\textbf{shall} write \emph{bar} in margin and emit \emph{de} before ``shall.''

\end{document}

Expected results:

  • First occurrence in last paragraph prints bar in margin and does not emit error.
  • Second occurrence in last paragraph prints de before shall, and bar in margin without emitting an error.

Actual results:

  • Error on first occurrence
! Undefined control sequence.
\reserved@c ->\@nil 
                    
l.60 Macro after active char *\textbf
                                     {shall} emit no space and write \emph{b...
  • Error on second occurrence
! Undefined control sequence.
\reserved@c ->\@nil 
                    
l.60 ...en, with an optional argument *[de]\textbf
                                                  {shall} write \emph{bar} i...

I'm stuck here. As a workaround, I can issue a TeX primitive to make the error go away, e.g. bla bla *\relax\textbf{even more bla} but that goes against the intent of using an active character.

The active character should be smart enough to detect any token that is not a space without needing manual intervention. Does anybody spot the error in the minimal working example above? Could somebody suggest a working solution? I'd be very grateful for that.

1
  • *{\textbf{shall}} would also work Commented Mar 28, 2023 at 18:08

1 Answer 1

2

You have a simple typo

\if\@let@token\@sptoken

should be

\ifx\@let@token\@sptoken

You are expanding \@let@token and testing the first two tokens in its expansion.

1
  • Your answer is spot on, David! It works! This was a remnant of one of the many attempts, and I simply didn't see it. Thank you very much!
    – aker
    Commented Mar 28, 2023 at 19:54

You must log in to answer this question.

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