Écriture immédiate de @ifnextchar
Lors de l'écriture d'un test pour l3build, j'ai rencontré un problème lors de la sortie du résultat d'une commande utilisée @ifnextchar. Concrètement, a \TYPE{\command}donné une erreur de compilation. Je l'ai réduit au MWE suivant:
\documentclass{article}
\begin{document}
\makeatletter
\DeclareRobustCommand\lookahead{\@ifnextchar{z}{hello}{goodbye}}
\immediate\write128{\lookahead z}
\makeatother
\end{document}
Sur ma machine (pdflatex, TeX Live 2020 / Debian) le code précédent donne l'erreur: Argument of \reserved@a has an extra }. Je suis un peu hors de ma profondeur ici, donc après quelques recherches, j'ai décidé qu'il serait préférable de demander de l'aide. Savez-vous pourquoi il donne cette erreur et pourrait-on faire quelque chose pour la résoudre? Notez que la méthode de sortie ne doit pas être modifiée, car c'est essentiellement celle utilisée par l3build .
Merci beaucoup!
Réponses
Je peux proposer un mécanisme extensible \UD@CheckWhetherLeadingTokensau moyen duquel vous pouvez demander à LaTeX de vérifier au moyen de macros qui traitent des arguments délimités si les jetons principaux d'un argument de macro forment un ensemble spécifique de jetons.
\UD@CheckWhetherLeadingTokensest différent de \@ifnextchar/ \kernel@ifnextcharsous plusieurs aspects:
\UD@CheckWhetherLeadingTokensest extensible.\UD@CheckWhetherLeadingTokensne "regarde pas en avant" le jeton suivant dans le flux de jetons. Au lieu de cela, il "regarde" les premiers jetons d'un macro-argument.
(Avec \@ifnextchar/ \kernel@ifnextcharet avec, \UD@CheckWhetherLeadingTokensvous devrez peut-être faire attention lorsque \uppercase/ \lowercase/ \MakeUppercase/ \MakeLowercaseet autres jouent un rôle.)
\documentclass{article}
\makeatletter
%==========[code for checking leading token-sequences in arguments]============
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral0\expandafter\@secondoftwo\string{\expandafter
\@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
\@secondoftwo\string}\@firstoftwo\expandafter{} \@secondoftwo}%
{\@firstoftwo\expandafter{} \@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Exchange two arguments. (From each argument an outermost level of
%% surrounding braces will be removed if present.)
%%-----------------------------------------------------------------------------
\newcommand\UD@Exchange[2]{#2#1}%
%%-----------------------------------------------------------------------------
%% Check whether argument's leading tokens form a specific
%% token-sequence that does not contain explicit character tokens of
%% category code 1 or 2:
%%.............................................................................
%% \UD@CheckWhetherLeadingTokens{<argument which is to be checked>}%
%% {<a <token sequence> without explicit
%% character tokens of category code
%% 1 or 2>}%
%% {a <single non-space token> that does
%% _not_ occur in <token sequence> >}%
%% {<internal token-check-macro>}%
%% {<tokens to be delivered in case
%% <argument which is to be checked> has
%% <token sequence> as leading tokens>}%
%% {<tokens to be delivered in case
%% <argument which is to be checked>
%% does not have <token sequence> as
%% leading tokens>}%
\newcommand\UD@CheckWhetherLeadingTokens[4]{%
\romannumeral0\UD@CheckWhetherNull{#1}%
{\UD@Exchange{ }\expandafter\@secondoftwo}%
{\expandafter\@secondoftwo\string{\expandafter
\UD@@CheckWhetherLeadingTokens#4#3#1#2}{}}%
}%
\newcommand\UD@@CheckWhetherLeadingTokens[1]{%
\expandafter\UD@CheckWhetherNull\expandafter{\@firstoftwo{}#1}%
{\UD@Exchange{\@firstoftwo}}{\UD@Exchange{\@secondoftwo}}%
{\UD@Exchange{ }{\expandafter\expandafter\expandafter\expandafter
\expandafter\expandafter\expandafter}\expandafter\expandafter
\expandafter}\expandafter\@secondoftwo\expandafter{\string}%
}%
%%-----------------------------------------------------------------------------
%% \UD@internaltokencheckdefiner{<internal token-check-macro>}%
%% {<token sequence>}%
%% Defines <internal token-check-macro> to snap everything
%% until reaching <token sequence>-sequence and spit that out
%% nested in braces.
%%-----------------------------------------------------------------------------
\newcommand\UD@internaltokencheckdefiner[2]{%
\@ifdefinable#1{\long\def#1##1#2{{##1}}}%
}%
%=======[end of code for checking leading token-sequences in arguments]=========
\UD@internaltokencheckdefiner{\zcheck}{z}%
\newcommand\lookahead[1]{%
\UD@CheckWhetherLeadingTokens{#1}{z}{.}{\zcheck}{hello}{goodbye} #1%
}%
\makeatother
\begin{document}
\immediate\write128{ble ble \lookahead{y bla} blu blu}
\immediate\write128{ble ble \lookahead{{z} bla} blu blu}
\immediate\write128{ble ble \lookahead{z bla} blu blu}
\end{document}
Avec l'exemple ci-dessus, j'obtiens ceci sur le terminal:
ble ble goodbye y bla blu blu
ble ble goodbye {z} bla blu blu
ble ble hello z bla blu blu
Une autre approche pourrait être d' \lookaheadeffectuer un hack d'accolade pour supprimer l'accolade ouvrante avant d'appeler une autre macro \lookaheadbpour effectuer réellement le lookahead via \@ifnextcharet d' \@ifnextchareffectuer un autre hack d'accolade pour ajouter une accolade d'ouverture et appeler \immediate\writel'argument:
\documentclass{article}
\makeatletter
\newcommand\lookahead{%
\expandafter\expandafter\expandafter\lookaheadb\expandafter\@gobble\string
}%
\newcommand\lookaheadb{%
\@ifnextchar{z}%
{\immediate\write128\expandafter\expandafter\expandafter{\expandafter\@gobble\string}hello }%
{\immediate\write128\expandafter\expandafter\expandafter{\expandafter\@gobble\string}goodbye }%
}%
\makeatother
\begin{document}
\lookahead{y bla bla bla}
\lookahead{{z} bla bla bla}
\lookahead{z bla bla bla}
\end{document}
Avec l'exemple ci-dessus, j'obtiens ceci sur le terminal:
goodbye y bla bla bla
goodbye {z} bla bla bla
hello z bla bla bla
Avec cette approche, \lookaheadne peut pas être imbriqué dans la \writecommande, mais appelle la \writecommande-.