TransWikia.com

Immediate write of @ifnextchar

TeX - LaTeX Asked by Miquel Ortega on April 15, 2021

While writing a test for l3build I ran across a problem on outputting the result of a command that used @ifnextchar. Concretely, TYPE{command} gave a compiling error. I have reduced it to the following MWE:

documentclass{article}

begin{document}
makeatletter
DeclareRobustCommandlookahead{@ifnextchar{z}{hello}{goodbye}}
immediatewrite128{lookahead z}
makeatother
end{document}

On my machine (pdflatex, TeX Live 2020/Debian) the previous code gives the error: Argument of reserved@a has an extra }. I am somewhat out of my depth here, so after some searching I decided it would be best to ask for help. Do you know why it gives this mistake and could one do anything to solve it? Note that the output method shouldn’t be changed, since it is essentially the one used by l3build.

Many thanks!

One Answer

I can offer an expandable mechanism UD@CheckWhetherLeadingTokens by means of which you can have LaTeX check by means of macros that process delimited arguments whether a macro argument's leading tokens form a specific set of tokens.

UD@CheckWhetherLeadingTokens is different from @ifnextchar/kernel@ifnextchar in several aspects:

  • UD@CheckWhetherLeadingTokens is expandable.
  • UD@CheckWhetherLeadingTokens does not "look ahead" at the next token in the token-stream. Instead it does "look" at the first tokens of a macro-argument.

(Both with @ifnextchar/kernel@ifnextchar and with UD@CheckWhetherLeadingTokens you may need to pay attention when uppercase/lowercase/MakeUppercase/MakeLowercase and the like play a 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>
newcommandUD@CheckWhetherNull[1]{%
  romannumeral0expandafter@secondoftwostring{expandafter
  @secondoftwoexpandafter{expandafter{string#1}expandafter
  @secondoftwostring}expandafter@firstoftwoexpandafter{expandafter
  @secondoftwostring}@firstoftwoexpandafter{} @secondoftwo}%
  {@firstoftwoexpandafter{} @firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Exchange two arguments. (From each argument an outermost level of 
%% surrounding braces will be removed if present.)
%%-----------------------------------------------------------------------------
newcommandUD@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>}%
newcommandUD@CheckWhetherLeadingTokens[4]{%
  romannumeral0UD@CheckWhetherNull{#1}%
  {UD@Exchange{ }expandafter@secondoftwo}%
  {expandafter@secondoftwostring{expandafter
   UD@@CheckWhetherLeadingTokens#4#3#1#2}{}}%
}%
newcommandUD@@CheckWhetherLeadingTokens[1]{%
  expandafterUD@CheckWhetherNullexpandafter{@firstoftwo{}#1}%
  {UD@Exchange{@firstoftwo}}{UD@Exchange{@secondoftwo}}%
  {UD@Exchange{ }{expandafterexpandafterexpandafterexpandafter
   expandafterexpandafterexpandafter}expandafterexpandafter
   expandafter}expandafter@secondoftwoexpandafter{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.
%%-----------------------------------------------------------------------------
newcommandUD@internaltokencheckdefiner[2]{%
  @ifdefinable#1{longdef#1##1#2{{##1}}}%
}%
%=======[end of code for checking leading token-sequences in arguments]=========

UD@internaltokencheckdefiner{zcheck}{z}%
newcommandlookahead[1]{%
  UD@CheckWhetherLeadingTokens{#1}{z}{.}{zcheck}{hello}{goodbye} #1%
}%
makeatother

begin{document}

immediatewrite128{ble ble lookahead{y bla} blu blu}

immediatewrite128{ble ble lookahead{{z} bla} blu blu}

immediatewrite128{ble ble lookahead{z bla} blu blu}

end{document}

With the example above I get this on the terminal:

ble ble goodbye y bla blu blu
ble ble goodbye {z} bla blu blu
ble ble hello z bla blu blu

Another approach could be having lookahead perform a brace-hack for removing the opening-brace before calling another macro lookaheadb for actually performing the lookahead via @ifnextchar and having @ifnextchar perform another brace-hack for adding an opening-brace and calling immediatewrite on the argument:

documentclass{article}

makeatletter
newcommandlookahead{%
  expandafterexpandafterexpandafterlookaheadbexpandafter@gobblestring
}%
newcommandlookaheadb{%
  @ifnextchar{z}%
  {immediatewrite128expandafterexpandafterexpandafter{expandafter@gobblestring}hello }%
  {immediatewrite128expandafterexpandafterexpandafter{expandafter@gobblestring}goodbye }%
}%
makeatother

begin{document}

lookahead{y bla bla bla}

lookahead{{z} bla bla bla}

lookahead{z bla bla bla}

end{document}

With the example above I get this on the terminal:

goodbye y bla bla bla
goodbye {z} bla bla bla
hello z bla bla bla

With this approach lookahead cannot be nested inside the write command but is calling the write-command.

Answered by Ulrich Diez on April 15, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP