เขียน @ifnextchar ทันที
ในขณะที่เขียนการทดสอบสำหรับ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
ขอบคุณมาก!
คำตอบ
ฉันสามารถเสนอกลไกที่ขยายได้\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