เขียน @ifnextchar ทันที

Oct 13 2020

ในขณะที่เขียนการทดสอบสำหรับl3build i @ifnextcharวิ่งข้ามปัญหาในการแสดงผลผลของคำสั่งที่ใช้ ในทางกลับกัน\TYPE{\command}ให้ข้อผิดพลาดในการคอมไพล์ ฉันลดเป็น MWE ต่อไปนี้:

\documentclass{article}

\begin{document}
\makeatletter
\DeclareRobustCommand\lookahead{\@ifnextchar{z}{hello}{goodbye}}
\immediate\write128{\lookahead z}
\makeatother
\end{document}

ในเครื่องของฉัน (pdflatex, TeX Live 2020 / Debian) รหัสก่อนหน้านี้ให้ข้อผิดพลาด: Argument of \reserved@a has an extra }. ฉันค่อนข้างไม่อยู่ในส่วนลึกของฉันที่นี่ดังนั้นหลังจากการค้นหาบางครั้งฉันตัดสินใจว่าจะขอความช่วยเหลือดีที่สุด คุณรู้หรือไม่ว่าเหตุใดจึงทำให้เกิดความผิดพลาดนี้และสามารถทำทุกอย่างเพื่อแก้ไขได้หรือไม่? โปรดทราบว่าวิธีการที่ส่งออกไม่ควรจะมีการเปลี่ยนแปลงเพราะมันเป็นหลักหนึ่งที่ใช้โดยl3build

ขอบคุณมาก!

คำตอบ

1 UlrichDiez Oct 13 2020 at 19:41

ฉันสามารถเสนอกลไกที่ขยายได้\UD@CheckWhetherLeadingTokensโดยคุณสามารถตรวจสอบ LaTeX ได้โดยใช้มาโครที่ประมวลผลอาร์กิวเมนต์ที่คั่นไม่ว่าโทเค็นชั้นนำของมาโครจะสร้างชุดโทเค็นเฉพาะหรือไม่

\UD@CheckWhetherLeadingTokensแตกต่างจาก\@ifnextchar/ \kernel@ifnextcharในหลายแง่มุม:

  • \UD@CheckWhetherLeadingTokens สามารถขยายได้
  • \UD@CheckWhetherLeadingTokensไม่ "มองไปข้างหน้า" ที่โทเค็นถัดไปในโทเค็นสตรีม แต่จะ "ดู" ที่โทเค็นแรกของอาร์กิวเมนต์มาโคร

(ทั้งที่มี\@ifnextchar/ \kernel@ifnextcharและกับ\UD@CheckWhetherLeadingTokensคุณอาจต้องให้ความสนใจเมื่อ\uppercase/ \lowercase/ \MakeUppercase/ \MakeLowercaseและสิ่งที่คล้ายกันเล่นเกม)

\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}

ด้วยตัวอย่างด้านบนฉันได้รับสิ่งนี้จากเทอร์มินัล:

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

อีกวิธีหนึ่งอาจเป็นการ\lookaheadใช้ brace-hack เพื่อลบวงเล็บปีกกาเปิดออกก่อนที่จะเรียกใช้มาโครอื่น\lookaheadbเพื่อทำการ lookahead ผ่าน\@ifnextcharและ\@ifnextcharทำการแฮ็กอีกครั้งเพื่อเพิ่มการเปิดวงเล็บปีกกาและเรียก\immediate\writeใช้อาร์กิวเมนต์:

\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}

ด้วยตัวอย่างด้านบนฉันได้รับสิ่งนี้จากเทอร์มินัล:

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

ด้วยวิธีการนี้\lookaheadไม่สามารถซ้อนกันภายใน\writeคำสั่ง แต่กำลังเรียกใช้\write-command