% \iffalse %% File: unravel.dtx Copyright (C) 2013, 2015, 2018-2019, 2021, 2024 Bruno Le Floch %% %% It may be distributed and/or modified under the conditions of the %% LaTeX Project Public License (LPPL), either version 1.3c of this %% license or (at your option) any later version. The latest version %% of this license is in the file %% %% http://www.latex-project.org/lppl.txt %% %% ----------------------------------------------------------------------- % %<*driver> %\fi %\iffalse \documentclass[full]{l3doc} \usepackage{unravel} \begin{document} \DocInput{unravel.dtx} \end{document} % % \fi % % \title{The \textsf{unravel} package: \\ % watching TeX digest tokens\thanks{This % file has version number 0.3c, last revised 2024/01/05.}} % \author{Bruno Le Floch} % \date{2024/01/05} % % \maketitle % \tableofcontents % % \begin{documentation} % % \section{\pkg{unravel} documentation} % % The aim of this \LaTeX{} package is to help debug complicated macros. % This is done by letting the user step through the execution of some % \TeX{} code, going through the details of nested expansions, % performing assignments, as well as some simple typesetting commands. % To use this package, one should normally run \TeX{} in a terminal. % % \subsection{Commands} % % \begin{function}{\unravel} % \begin{syntax} % \cs{unravel} \oarg{key-value list} \marg{code} % \end{syntax} % This command shows in the terminal the steps performed by \TeX{} % when running the \meta{code}. By default, it pauses to let the % user read the description of every step: simply press || to % proceed. Typing |s|\meta{integer} instead will go forward % \meta{integer} steps somewhat silently. In the future it will be % possible to use a negative \meta{integer} to go back a few steps. % Typing |h| gives a list of various other possibilities. % The available \meta{key-value} options are described in % Section~\ref{sec:options}. % \end{function} % % \begin{function}{\unravelsetup} % \begin{syntax} % \cs{unravelsetup} \marg{options} % \end{syntax} % Sets \meta{options} that apply to all subsequent \cs{unravel}. See % options in Section~\ref{sec:options}. % \end{function} % % \begin{function}{\unravel:nn} % \begin{syntax} % \cs{unravel:nn} \Arg{options} \Arg{code} % \end{syntax} % See \cs{unravel}. % \end{function} % % \begin{function}{\unravel_get:nnN} % \begin{syntax} % \cs{unravel_get:nnN} \Arg{options} \Arg{code} \meta{tl var} % \end{syntax} % Performs \cs{unravel:nn} with the \meta{options} and \meta{code} % then saves the output into the \meta{tl var}. The option % |mute| is useful in this case. % \end{function} % % \begin{function}{\unravel_setup:n} % \begin{syntax} % \cs{unravel_setup:n} \Arg{options} % \end{syntax} % See \cs{unravelsetup}. % \end{function} % % \subsection{Examples} % % The \pkg{unravel} package is currently based on the behaviour of % \pdfTeX{}, but it should work in all engines supported by \pkg{expl3} % (\pdfTeX{}, \XeTeX{}, \LuaTeX{}, ep\TeX{}, eup\TeX{}) as long as none % of the primitives specific to those engines is used. Any % difference between how \pkg{unravel} and (pdf)\TeX{} process a given % piece of code, unless described in the section~\ref{sec:differences}, % should be reported on the issue tracker % (\url{https://github.com/blefloch/latex-unravel/issues}). % % As a simple example, one can run \LaTeX{} on the following file. % \begin{verbatim} % \documentclass{article} % \usepackage{unravel} % \unravel % { % \title{My title} % \author{Me} % \date{\today} % } % \begin{document} % \maketitle % \end{document} % \end{verbatim} % % A more elaborate example is to understand how \tn{newcommand} works. % \begin{verbatim} % \documentclass{article} % \usepackage{unravel} % \begin{document} % \unravel % { % \newcommand*{\foo}[1]{bar(#1)} % \foo{3} % } % \end{document} % \end{verbatim} % % The \pkg{unravel} package understands deeply nested expansions as can % be seen for instance by unravelling functions from \pkg{l3fp}, such as % with the following code (given the current default settings, this code % runs for roughly 2000 steps: you can type |s1980| as a response to the % prompt, then press ``enter'' a few times to see the last few steps of % expansion). % \begin{verbatim} % \documentclass{article} % \usepackage{unravel} % \begin{document} % \ExplSyntaxOn % \unravel { \fp_eval:n { 3.45 * 2 pi } } % \ExplSyntaxOff % \end{document} % \end{verbatim} % % Given all the work that \pkg{unravel} has to do to emulate \TeX{}, it % is not fast on very large pieces of code. For instance, running it on % |\documentclass{article}| takes about thirty seconds on my machine, and % finishes after somewhat less than $21000$ steps. % \begin{verbatim} % \RequirePackage{unravel} % \unravel{\documentclass{article}\relax} % \usepackage{lipsum} % \begin{document} % \lipsum % \end{document} % \end{verbatim} % The \tn{relax} command is needed after |\documentclass{article}| % because this command tries to look for an optional argument: % \cs{unravel} would not find any token, and would give up, as \TeX{} % would if your file ended just after |\documentclass{article}|. After % running the above through \pdfTeX{}, one can check that the result is % identical to that without \pkg{unravel}. Note that % \cs{unravel}|{\usepackage{lipsum}\relax}|, despite taking roughly as many steps % to complete, is ten times slower, because \tn{newcommand} uses % delimited arguments, which prevent some optimizations that % \pkg{unravel} can otherwise obtain. For comparison, % |\unravel{\lipsum[1-30]}| also takes $20000$ step and is ten times % faster than loading the package. % % \subsection{Options} % \label{sec:options} % % \begin{function}{explicit-prompt} % Boolean option (default \texttt{false}) determining whether to give % an explicit prompt. If \texttt{true}, the text ``|Your input=|'' % will appear at the beginning of lines where user input is expected. % \end{function} % % \begin{function}{internal-debug} % Boolean option (default \texttt{false}) used to debug \pkg{unravel} % itself. % \end{function} % % \begin{function}{machine} % Option which takes no value and makes \pkg{unravel} produce an % output that is somewhat more suitable for automatic processing. In % particular, it sets |max-action|, |max-output|, |max-input| to very % large values, and |number-steps| to \texttt{false}. % \end{function} % % \begin{function}{max-action, max-output, max-input} % Integer options (defaults $50$, $300$, $300$) determining the % maximum number of characters displayed for the action, the output % part, and the input part. % \end{function} % % \begin{function}{mute} % Make none of the steps produce any output, by setting % |trace-assigns|, |trace-expansion|, |trace-other|, |welcome-message| % to \texttt{false}. This is only useful with \cs{unravel_get:nnN} or % when other options change some of these settings. % \end{function} % % \begin{function}{number-steps} % Boolean option (default \texttt{true}) determining whether to % number steps. % \end{function} % % \begin{function}{online} % Integer option (default~$1$) determining where to write the output: % terminal and log if the option is positive, log only if the option % is zero, neither if the option is negative. % \end{function} % % \begin{function}{output-file} % Name of a file where the output is written instead of the log file. % Setting this option also sets \texttt{online} to zero. If % \texttt{online} is further modified to be positive then the output % is written to the terminal as well (hence inevitably to the log % file), while if it is made negative then the output is written % nowhere. % \end{function} % % \begin{function}{prompt-input} % Comma-delimited list option (empty by default) whose items are used % one by one as if the user typed them at the prompt. Since the % key-value list is itself comma-delimited, the value here must be % wrapped in braces. For instance, |prompt-input = {s10, m, u\def}| % skips $10$ steps, shows the first token's meaning, then continues % silently until the first token is~|\def|, and any subsequent prompt % is treated normally with user interaction. This can be useful when % repeatedly debugging complicated code when the issue is known to lie % quite late in the code. % % As for any |clist|, spaces are discarded around each comma and empty % entries are removed, then for each item one pair of braces is % removed (if any is present); to get an empty item use an empty brace % group, such as in |prompt-input = {s10, {}, x}|. Category codes are % those in effect when the |prompt-input| option is read. % \end{function} % % \begin{function}{trace-assigns, trace-expansion, trace-other} % Boolean options (default \texttt{true}) controlling what steps % produce any output at all. The keys |trace-assigns|, % |trace-expansion|, |trace-other| control tracing of different types % of steps. % \end{function} % % \begin{function}{welcome-message} % Boolean option (default \texttt{true}) determining whether to % display the welcome message. % \end{function} % % \subsection{Differences between \pkg{unravel} and \TeX{}'s processing} % \label{sec:differences} % % Bugs are listed at \url{https://github.com/blefloch/latex-unravel/issues}. % % Differences. % \begin{itemize} % \item Kerning between letters of a word is omitted, which can lead to % incorrect widths. % \item Some primitives are not implemented yet: alignments % (\tn{halign}, \tn{valign}, \tn{noalign}, \tn{omit}, \tn{span}, % \tn{cr}, \tn{crcr},~|&|), some math mode primitives, and % \tn{pdfprimitive}, as well as % many primitives specific to engines other than \pdfTeX{}. % This list may sadly be incomplete! % \item \tn{aftergroup} is only partially implemented. % \item \tn{everyhbox}, \tn{everyvbox}, \tn{everymath}, % \tn{everydisplay}, \tn{lastkern}, \tn{lastnodetype}, % \tn{lastpenalty}, \tn{lastskip}, \tn{currentifbranch} % may have wrong values. Perhaps % \tn{currentgrouplevel} and \tn{currentgrouptype} too. % \item Setting \tn{globaldefs} to a non-zero value may cause problems. % \item Tokens passed to \tn{aftergroup} are lost when \pkg{unravel} is % done. % \item For \pkg{unravel}, category codes are fixed when a file is read % using \tn{input}, % while \TeX{} only fixes category codes when the corresponding % characters are converted to tokens. Similarly, the argument of % \tn{scantokens} is converted to the new category code regime in % one go, and the result must be balanced. % \item Explicit begin-group and end-group characters other than the % usual left and right braces may make \pkg{unravel} choke, or may be % silently replaced by the usual left and right braces. % \item \tn{endinput} is ignored with a warning, as it is very difficult % to implement it in a way similar to \TeX{}'s, and as it is most % often used at the very end of files, in a redundant way. % \item \tn{outer} is not supported. % \item \cs{unravel} cannot be nested. % \item Control sequences of the form \cs{notexpanded:\ldots{}} are % reserved for use by \pkg{unravel}. % \end{itemize} % % \subsection{Future perhaps} % % \begin{itemize} % \item Use the |file-error| fatal error message: first implement % \cs{@@_file_if_exist:nTF} and use it to determine whether % \tn{input} will throw a fatal error in \tn{batchmode} and % \tn{nonstopmode}. % \item Use the |interwoven-preambles| fatal error message once % alignments are implemented. % \item Find out why so many input levels are used (see the log of the % |unravel003| testfile for instance) % \end{itemize} % % \end{documentation} % % \begin{implementation} % % \section{\pkg{unravel} implementation} % % Some support packages are loaded first, then we declare the package's % name, date, version, and purpose. % % \begin{macrocode} %<*package> % \end{macrocode} % % \begin{macrocode} %<@@=unravel> % \end{macrocode} % % Catcode settings. In a group, set |\c| to be a synonym of |\catcode| % for short, set the catcode of space to be 10 (using |\fam| to avoid % needing a space or an equal sign to separate the two integer % arguments of |\catcode|) and that of |%| to be 14 (using |\fam| again % to avoid needing the digit 7 to have catcode other: we need the digit % 5 anyway in two steps). Then make |-|, |6|, |7|, |8|, |9| other (we % must assume that |0| through |5| are already other), and make |:|, % |_|, |h|, |j|, |k|, |q|, |s|, |w|, |x|, |y|, |z| letters (other % lowercase letters already need to be letters in the rest of the % code). Make sure there is no |\endlinechar|. We are finally ready % to safely test whether the package has already been loaded and bail % out in case it has. Expanding |\fi| before ending the group ensures % that the whole line has been read by \TeX{} before restoring earlier % catcodes. % \begin{macrocode} \begingroup\let\c\catcode\fam32\c\fam10\advance\fam5\c\fam14\c45 12 % \c54 12\c55 12\c56 12\c57 12\c58 11\c95 11\c104 11\c106 11\c107 11 % \c113 11\c115 11\c119 11\c120 11\c121 11\c122 11\endlinechar-1 % \expandafter\ifx\csname unravel\endcsname\relax \else\endinput\expandafter\endgroup\fi % \end{macrocode} % % Set |T| and |X| to be letters for an error message. Set up braces % and |#| for definitions, |=| for nicer character code assignments, % |>| for integer comparison, |+| for integer expressions. % \begin{macrocode} \c84 11\c88 11\c35 6\c123 1\c125 2\c62 12\c61 12\c43 12 % % \end{macrocode} % % If \eTeX{}'s \tn{numexpr} or \tn{protected} are not available, bail % out with an error. % \begin{macrocode} \expandafter\ifx\csname numexpr\endcsname\relax \errmessage{unravel requires \numexpr from eTeX} \endinput\expandafter\endgroup\fi \expandafter\ifx\csname protected\endcsname\relax \errmessage{unravel requires \protected from eTeX} \endinput\expandafter\endgroup\fi % \end{macrocode} % % If \pkg{unravel} is loaded within a group, bail out because % \pkg{expl3} would not be loaded properly. % \begin{macrocode} \expandafter\ifx\csname currentgrouplevel\endcsname\relax\else \ifnum\currentgrouplevel>1 \errmessage{unravel loaded in a group} \endinput\expandafter\expandafter\expandafter\endgroup\fi\fi % \end{macrocode} % % Make spaces ignored and make |~| a space, to prettify code. % \begin{macrocode} \catcode 32 = 9 \relax \catcode 126 = 10 \relax % \end{macrocode} % % \begin{variable}{\l_@@_setup_restore_tl} % This token list variable will contain code to restore category % codes to their value when the package was loaded. % \begin{macrocode} \gdef \l_@@_setup_restore_tl { } % \end{macrocode} % \end{variable} % % \begin{macro}{\@@_setup_restore:} % Use the token list to restore catcodes to their former values, then % empty the list since there is no catcode to restore anymore. This % mechanism cannot be nested. % \begin{macrocode} \protected \gdef \@@_setup_restore: { \l_@@_setup_restore_tl \def \l_@@_setup_restore_tl { } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_setup_save:} % \begin{macro}[EXP]{\@@_setup_save_aux:n} % This saves into \cs{l_@@_setup_restore_tl} the current catcodes % (from $0$ to $255$ only), \tn{endlinechar}, \tn{escapechar}, % \tn{newlinechar}. % \begin{macrocode} \protected \gdef \@@_setup_save: { \edef \l_@@_setup_restore_tl { \@@_setup_save_aux:w 0 = \endlinechar = \the \endlinechar \escapechar = \the \escapechar \newlinechar = \the \newlinechar \relax } } \long \gdef \@@_setup_save_aux:w #1 = { \catcode #1 = \the \catcode #1 ~ \ifnum 255 > #1 ~ \expandafter \@@_setup_save_aux:w \the \numexpr #1 + 1 \expandafter = \fi } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\@@_setup_catcodes:nnn} % This sets all characters from |#1| to |#2| (inclusive) to have % catcode |#3|. % \begin{macrocode} \protected \long \gdef \@@_setup_catcodes:nnn #1 #2 #3 { \ifnum #1 > #2 ~ \else \catcode #1 = #3 ~ \expandafter \@@_setup_catcodes:nnn \expandafter { \the \numexpr #1 + 1 } {#2} {#3} \fi } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_setup_latexe:} % This saves the catcodes and related parameters, then sets them to % the value they normally have in a \LaTeXe{} package (in particular, % |@| is a letter). % \begin{macrocode} \protected \gdef \@@_setup_latexe: { \@@_setup_save: \@@_setup_catcodes:nnn {0} {8} {15} \catcode 9 = 10 ~ \catcode 10 = 12 ~ \catcode 11 = 15 ~ \catcode 12 = 13 ~ \catcode 13 = 5 ~ \@@_setup_catcodes:nnn {14} {31} {15} \catcode 32 = 10 ~ \catcode 33 = 12 ~ \catcode 34 = 12 ~ \catcode 35 = 6 ~ \catcode 36 = 3 ~ \catcode 37 = 14 ~ \catcode 38 = 4 ~ \@@_setup_catcodes:nnn {39} {63} {12} \@@_setup_catcodes:nnn {64} {90} {11} \catcode 91 = 12 ~ \catcode 92 = 0 ~ \catcode 93 = 12 ~ \catcode 94 = 7 ~ \catcode 95 = 8 ~ \catcode 96 = 12 ~ \@@_setup_catcodes:nnn {97} {122} {11} \catcode 123 = 1 ~ \catcode 124 = 12 ~ \catcode 125 = 2 ~ \catcode 126 = 13 ~ \catcode 127 = 15 ~ \@@_setup_catcodes:nnn {128} {255} {12} \endlinechar = 13 ~ \escapechar = 92 ~ \newlinechar = 10 ~ } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_setup_unravel:} % Catcodes for \pkg{unravel} (in particular, |@| is other, |:| and % |_| are letters, spaces are ignored, |~| is a space). % \begin{macrocode} \protected \gdef \@@_setup_unravel: { \@@_setup_save: \@@_setup_catcodes:nnn {0} {8} {15} \catcode 9 = 9 ~ \catcode 10 = 12 ~ \catcode 11 = 15 ~ \catcode 12 = 13 ~ \catcode 13 = 5 ~ \@@_setup_catcodes:nnn {14} {31} {15} \catcode 32 = 9 ~ \catcode 33 = 12 ~ \catcode 34 = 12 ~ \catcode 35 = 6 ~ \catcode 36 = 3 ~ \catcode 37 = 14 ~ \catcode 38 = 4 ~ \@@_setup_catcodes:nnn {39} {57} {12} \catcode 58 = 11 ~ \@@_setup_catcodes:nnn {59} {64} {12} \@@_setup_catcodes:nnn {65} {90} {11} \catcode 91 = 12 ~ \catcode 92 = 0 ~ \catcode 93 = 12 ~ \catcode 94 = 7 ~ \catcode 95 = 11 ~ \catcode 96 = 12 ~ \@@_setup_catcodes:nnn {97} {122} {11} \catcode 123 = 1 ~ \catcode 124 = 12 ~ \catcode 125 = 2 ~ \catcode 126 = 10 ~ \catcode 127 = 15 ~ \@@_setup_catcodes:nnn {128} {255} {12} \escapechar = 92 ~ \endlinechar = 32 ~ \newlinechar = 10 ~ } % \end{macrocode} % \end{macro} % % End the group where all catcodes where changed, but expand % \cs{@@_setup_latexe:} to sanitize catcodes again outside the % group. The catcodes are saved. % \begin{macrocode} \expandafter \endgroup \@@_setup_latexe: % \end{macrocode} % % Load the \pkg{gtl} dependency. % \begin{macrocode} \RequirePackage{gtl}[2024/01/04] % \end{macrocode} % % Before loading \pkg{unravel}, restore catcodes, so that the implicit % \cs{ExplSyntaxOn} in \cs{ProvidesExplPackage} picks up the correct % catcodes to restore when \cs{ExplSyntaxOff} is run at the end of the % package. The place where catcodes are restored are beyond % \pkg{unravel}'s reach, which is why we cannot bypass \pkg{expl3} and % simply restore the catcodes once everything is done. To avoid issues % with crazy catcodes, make \TeX{} read the arguments of % \cs{ProvidesExplPackage} before restoring catcodes. Then immediately % go to the catcodes we want. % \begin{macrocode} \csname use:n\endcsname {% \csname @@_setup_restore:\endcsname \ProvidesExplPackage {unravel} {2024/01/05} {0.3c} {Watching TeX digest tokens}% \csname @@_setup_unravel:\endcsname }% % \end{macrocode} % % \subsection{Primitives, variants, and helpers} % % \subsubsection{Adjustments to \pkg{expl3}} % % In upcoming versions of \pkg{expl3}, the \cs{group_align_safe_begin:} % and \cs{group_align_safe_end:} commands may involve an explicit % end-group character token with non-standard character code, which % would wrongly be normalized by \pkg{gtl} (used by \pkg{unravel}), % hence break. To avoid this we change here the definitions slightly. % \begin{macrocode} \cs_gset:Npn \group_align_safe_begin: { \exp:w \if_false: { \fi: -`} \exp_stop_f: } \cs_gset:Npn \group_align_safe_end: { \if_int_compare:w `{ = \c_zero_int } \fi: } % \end{macrocode} % % \subsubsection{Renamed primitives} % % \begin{macro} % { % \@@_currentgrouptype:, \@@_everyeof:w, \@@_everypar:w, % \@@_set_escapechar:n, \@@_nullfont:, % \@@_hbox:w, \@@_the:w, % } % Copy primitives which are used multiple times, to avoid littering % the code with |:D| commands. Primitives are left as |:D| in the % code when that is clearer (typically when testing the meaning of % a token against that of a primitive). % \begin{macrocode} \cs_new_eq:NN \@@_currentgrouptype: \tex_currentgrouptype:D \cs_new_protected:Npn \@@_set_escapechar:n { \int_set:Nn \tex_escapechar:D } \cs_new_eq:NN \@@_everyeof:w \tex_everyeof:D \cs_new_eq:NN \@@_everypar:w \tex_everypar:D \cs_new_eq:NN \@@_hbox:w \tex_hbox:D \cs_new_eq:NN \@@_mag: \tex_mag:D \cs_new_eq:NN \@@_nullfont: \tex_nullfont:D \cs_new_eq:NN \@@_the:w \tex_the:D \cs_new_eq:NN \@@_number:w \tex_number:D % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_special_relax:} % A special marker slightly different from \tn{relax} (its % \tn{meaning} is \tn{relax} but it differs from \tn{relax} according % to \tn{ifx}). In the right-hand side of our assignment, % \cs{@@_special_relax:} could be replaced by any other expandable % command. % \begin{macrocode} \exp_after:wN \cs_new_eq:NN \exp_after:wN \@@_special_relax: \exp_not:N \@@_special_relax: % \end{macrocode} % \end{macro} % % \begin{macro}{\c_@@_prompt_ior, \c_@@_noprompt_ior} % These are not quite primitives, but are very low-level % |ior| streams to prompt the user explicitly or not. % \begin{macrocode} \int_const:Nn \c_@@_prompt_ior { 16 } \int_const:Nn \c_@@_noprompt_ior { -1 } % \end{macrocode} % \end{macro} % % \subsubsection{Variants} % % Variants that we need. % \begin{macrocode} \cs_generate_variant:Nn \seq_push:Nn { Nf } \cs_generate_variant:Nn \str_head:n { f } \cs_generate_variant:Nn \tl_to_str:n { o } \cs_generate_variant:Nn \tl_if_eq:nnTF { o } \cs_generate_variant:Nn \tl_if_head_eq_meaning:nNT { V } \cs_generate_variant:Nn \tl_if_head_eq_meaning:nNTF { V } \cs_generate_variant:Nn \tl_if_single_token:nT { V } \cs_generate_variant:Nn \gtl_gput_right:Nn { NV } \cs_generate_variant:Nn \gtl_if_empty:NTF { c } \cs_generate_variant:Nn \gtl_if_tl:NT { c } \cs_generate_variant:Nn \gtl_to_str:N { c } \cs_generate_variant:Nn \gtl_gpop_left:NN { c } \cs_generate_variant:Nn \gtl_get_left:NN { c } \cs_generate_variant:Nn \gtl_gset:Nn { c } \cs_generate_variant:Nn \gtl_gconcat:NNN { ccc , cNc } \cs_generate_variant:Nn \gtl_gclear:N { c } \cs_generate_variant:Nn \gtl_gclear_new:N { c } \cs_generate_variant:Nn \gtl_left_tl:N { c } % \end{macrocode} % % \begin{macro}{\@@_tl_if_in:ooTF} % Analogue of \cs{tl_if_in:ooTF} but with an extra group because that % function redefines an auxiliary that may appear in the code being % debugged (see Github issue |#27|). % \begin{macrocode} \cs_new_protected:Npn \@@_tl_if_in:ooTF #1#2#3#4 { \group_begin: \exp_args:Noo \tl_if_in:nnTF {#1} {#2} { \group_end: #3 } { \group_end: #4 } } % \end{macrocode} % \end{macro} % % \subsubsection{Miscellanous helpers} % % \begin{macro}{\@@_tmp:w} % Temporary function used to define other functions. % \begin{macrocode} \cs_new_protected:Npn \@@_tmp:w { } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_file_get:nN} % \begin{macro}{\@@_file_get_aux:wN} % \begin{macrocode} \cs_set_protected:Npn \@@_tmp:w #1 { \cs_new_protected:Npn \@@_file_get:nN ##1##2 { \group_begin: \@@_everyeof:w { #1 ##2 } \exp_after:wN \@@_file_get_aux:wN \exp_after:wN \prg_do_nothing: \tex_input:D ##1 \scan_stop: } \cs_new_protected:Npn \@@_file_get_aux:wN ##1 #1 ##2 { \group_end: \tl_set:Ne ##2 { \exp_not:o {##1} \exp_not:V \@@_everyeof:w } } } \exp_args:No \@@_tmp:w { \token_to_str:N : : } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[EXP]{\@@_tl_first_int:N} % \begin{macro}[EXP]{\@@_tl_first_int_aux:Nn} % Function that finds an explicit number in a token list. This is % used for instance when implementing \tn{read}, to find the stream % \meta{number} within the whole \tn{read} \meta{number} |to| % \meta{cs} construction. The auxiliary initially has itself as a % first argument, and once a first digit is found it has % \cs{use_none_delimit_by_q_stop:w}. That first argument is used % whenever what follows is not a digit, hence initially we loop, % while after the first digit is found any non-digit stops the % recursion. If no integer is found, $0$ is left in the token list. % The surrounding \cs{int_eval:n} lets us dump digits in the input % stream while keeping the function fully expandable. % \begin{macrocode} \cs_new:Npn \@@_tl_first_int:N #1 { \int_eval:n { \exp_after:wN \@@_tl_first_int_aux:Nn \exp_after:wN \@@_tl_first_int_aux:Nn #1 ? 0 ? \q_stop } } \cs_new:Npn \@@_tl_first_int_aux:Nn #1#2 { \tl_if_single:nT {#2} { \token_if_eq_catcode:NNT + #2 { \if_int_compare:w 1 < 1 #2 \exp_stop_f: #2 \exp_after:wN \use_i_ii:nnn \exp_after:wN \@@_tl_first_int_aux:Nn \exp_after:wN \use_none_delimit_by_q_stop:w \fi: } } #1 } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[EXP]{\@@_use_ii_i:nn} % \begin{macrocode} \cs_new:Npn \@@_use_ii_i:nn #1#2 { #2 #1 } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_prompt_input:Nn} % \begin{macro}[rEXP]{\@@_prompt_input:w} % \begin{macro}[EXP]{\@@_prompt_input_aux:w, \@@_use_none_delimit_by_q_recursion_tail:w} % \begin{variable}{\q_@@_recursion_tail} % \begin{macrocode} \cs_new_protected:Npn \@@_prompt_input:Nn #1#2 { \clist_gset:Ne #1 { \@@_prompt_input:w \prg_do_nothing: #2 , \q_@@_recursion_tail , } } \cs_new:Npn \@@_prompt_input:w #1 , { \tl_trim_spaces_apply:oN {#1} \@@_use_ii_i:nn \@@_prompt_input_aux:w , } \cs_new:Npn \@@_prompt_input_aux:w #1 , { \@@_use_none_delimit_by_q_recursion_tail:w #1 \use_none:nnnnn \q_@@_recursion_tail { \tl_to_str:n {#1} } , \@@_prompt_input:w \prg_do_nothing: } \cs_new:Npn \@@_use_none_delimit_by_q_recursion_tail:w #1 \q_@@_recursion_tail { } \quark_new:N \q_@@_recursion_tail % \end{macrocode} % \end{variable} % \end{macro} % \end{macro} % \end{macro} % % \subsubsection{String helpers} % % \begin{macro}{\@@_strip_escape:w} % \begin{macro}{\@@_strip_escape_aux:N, \@@_strip_escape_aux:w} % This is based on the 2013-07-19 (and earlier) version of % \cs{cs_to_str:N}. There are three cases. If the escape character % is printable, the charcode test is false, and % \cs{@@_strip_escape_aux:N} removes one character. If the escape % character is a space, the charcode test is true, and if there is no % escape charcter, the test is unfinished after |\token_to_str:N \ |. % In both of those cases, \cs{@@_strip_escape_aux:w} inserts % |-\@@_number:w \fi: \c_zero_int|. If the escape character was a % space, the test was true, and \cs{int_value:w} converts % \cs{c_zero_int} to~|0|, hence the leading roman numeral expansion % removes a space from what follows (it is important that what follows % cannot start with a digit). Otherwise, the test takes~|-| as its % second operand, is false, and the roman numeral expansion only sees % \cs{c_zero_int}, thus does not remove anything from what follows. % \begin{macrocode} \cs_new:Npn \@@_strip_escape:w { \tex_romannumeral:D \if_charcode:w \token_to_str:N \ \@@_strip_escape_aux:w \fi: \@@_strip_escape_aux:N } \cs_new:Npn \@@_strip_escape_aux:N #1 { \c_zero_int } \cs_new:Npn \@@_strip_escape_aux:w #1#2 { - \@@_number:w #1 \c_zero_int } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[EXP]{\@@_to_str:Nn} % Use the type-appropriate conversion to string. % \begin{macrocode} \cs_new:Npn \@@_to_str:Nn #1 { \if_meaning:w T #1 \exp_after:wN \tl_to_str:n \else: \exp_after:wN \gtl_to_str:n \fi: } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_str_truncate_left:nn} % \begin{macro}{\@@_str_truncate_left_aux:nnn} % Truncate the string |#1| to a maximum of |#2| characters. If it is % longer, replace some characters on the left of the string by % |(123~more~chars)~| with the appropriate number instead of~|123|. % In any reasonable case, $25$ is big enough to fit this extra text. % \begin{macrocode} \cs_new:Npn \@@_str_truncate_left:nn #1#2 { \exp_args:Nf \@@_str_truncate_left_aux:nnn { \str_count:n {#1} } {#1} {#2} } \cs_new:Npn \@@_str_truncate_left_aux:nnn #1#2#3 { \int_compare:nNnTF {#1} > {#3} { ( \int_eval:n { #1 - #3 + 25 } ~ more~chars ) ~ \str_range:nnn {#2} { #1 - #3 + 26 } {#1} } { \tl_to_str:n {#2} } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\@@_str_truncate_right:nn} % \begin{macro}{\@@_str_truncate_right_aux:nnn} % Truncate the string |#1| to a maximum of |#2| characters. If it is % longer, replace some characters on the right of the string by % |~(123~more~chars)| with the appropriate number instead of~|123|. % In any reasonable case, $25$ is big enough to fit this extra text. % \begin{macrocode} \cs_new:Npn \@@_str_truncate_right:nn #1#2 { \exp_args:Nf \@@_str_truncate_right_aux:nnn { \str_count:n {#1} } {#1} {#2} } \cs_new:Npn \@@_str_truncate_right_aux:nnn #1#2#3 { \int_compare:nNnTF {#1} > {#3} { \str_range:nnn {#2} { 1 } { #3 - 25 } ~ ( \int_eval:n { #1 - #3 + 25 } ~ more~chars ) } { \tl_to_str:n {#2} } } % \end{macrocode} % \end{macro} % \end{macro} % % \subsubsection{Helpers for control flow} % % \begin{macro}[EXP]{\@@_exit:w, \@@_exit_hard:w, \@@_exit_point:} % Jump to the very end of this instance of \cs{unravel}. % \begin{macrocode} \cs_new_eq:NN \@@_exit_point: \prg_do_nothing: \cs_new:Npn \@@_exit:w #1 \@@_exit_point: { } \cs_new:Npn \@@_exit_hard:w #1 \@@_exit_point: #2 \@@_exit_point: { } % \end{macrocode} % \end{macro} % % \begin{macro}[EXP]{\@@_break:w, \@@_break_point:} % Useful to jump out of complicated conditionals. % \begin{macrocode} \cs_new_eq:NN \@@_break_point: \prg_do_nothing: \cs_new:Npn \@@_break:w #1 \@@_break_point: { } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_cmd_if_internal:TF} % Test whether the \cs{l_@@_head_cmd_int} denotes an ``internal'' % command, between |min_internal| and |max_internal| (see % Section~\ref{sec:numeric-codes}). % \begin{macrocode} \prg_new_conditional:Npnn \@@_cmd_if_internal: { TF } { \int_compare:nNnTF \l_@@_head_cmd_int < { \@@_tex_use:n { min_internal } } { \prg_return_false: } { \int_compare:nNnTF \l_@@_head_cmd_int > { \@@_tex_use:n { max_internal } } { \prg_return_false: } { \prg_return_true: } } } % \end{macrocode} % \end{macro} % % \subsubsection{Helpers concerning tokens} % % \begin{macro}{\@@_active_do:nn} % Apply some code to an active character token constructed from its % character code. % \begin{macrocode} \cs_new_protected:Npn \@@_active_do:nn #1#2 { \group_begin: \char_set_active_eq:nN {#1} \scan_stop: \use:e { \group_end: \exp_not:n {#2} { \char_generate:nn {#1} { 13 } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_token_to_char:N} % \begin{macro}{\@@_meaning_to_char:n, \@@_meaning_to_char:o} % \begin{macro} % {\@@_meaning_to_char_auxi:w, \@@_meaning_to_char_auxii:w} % From the meaning of a character token (with arbitrary character % code, except active), extract the character itself (with string % category codes). This is somewhat robust against wrong input. % \begin{macrocode} \cs_new:Npn \@@_meaning_to_char:n #1 { \@@_meaning_to_char_auxi:w #1 \q_mark ~ {} ~ \q_mark \q_stop } \cs_new:Npn \@@_meaning_to_char_auxi:w #1 ~ #2 ~ #3 \q_mark #4 \q_stop { \@@_meaning_to_char_auxii:w #3 ~ #3 ~ \q_stop } \cs_new:Npn \@@_meaning_to_char_auxii:w #1 ~ #2 ~ #3 \q_stop { \tl_if_empty:nTF {#2} { ~ } {#2} } \cs_generate_variant:Nn \@@_meaning_to_char:n { o } \cs_new:Npn \@@_token_to_char:N #1 { \@@_meaning_to_char:o { \token_to_meaning:N #1 } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}[EXP,pTF]{\@@_token_if_expandable:N} % We need to cook up our own version of \cs{token_if_expandable:NTF} % because the \pkg{expl3} one does not think that |undefined| is % expandable. % \begin{macrocode} \prg_new_conditional:Npnn \@@_token_if_expandable:N #1 { p , T , F , TF } { \exp_after:wN \if_meaning:w \exp_not:N #1 #1 \prg_return_false: \else: \prg_return_true: \fi: } % \end{macrocode} % \end{macro} % % \begin{macro}[EXP,pTF]{\@@_token_if_protected:N} % Returns \texttt{true} if the token is either not expandable or is a % protected macro. % \begin{macrocode} \prg_new_conditional:Npnn \@@_token_if_protected:N #1 { p , T , F , TF } { \@@_token_if_expandable:NTF #1 { \token_if_protected_macro:NTF #1 { \prg_return_true: } { \token_if_protected_long_macro:NTF #1 { \prg_return_true: } { \prg_return_false: } } } { \prg_return_true: } } % \end{macrocode} % \end{macro} % % \begin{macro}[TF]{\@@_token_if_active_char:N} % Lowercase the token after setting its \tn{lccode} (more precisely % the \tn{lccode} of the first character in its string representation) % to a known value, then compare the result with that active % character. % \begin{macrocode} \group_begin: \char_set_catcode_active:n { `Z } \prg_new_protected_conditional:Npnn \@@_token_if_active_char:N #1 { TF } { \group_begin: \exp_args:Ne \char_set_lccode:nn { ` \exp_args:No \str_head:n { \token_to_str:N #1 } } { ` Z } \tex_lowercase:D { \tl_if_eq:nnTF {#1} } { Z } { \group_end: \prg_return_true: } { \group_end: \prg_return_false: } } \group_end: % \end{macrocode} % \end{macro} % % \begin{macro}[TF]{\@@_token_if_definable:N} % Within a group, set the escape character to a non-space value % (backslash). Convert the token to a string with % \cs{token_to_str:N}. The result is multiple characters if the token % is a control sequence, and a single character otherwise (even for % explicit catcode~$6$ character tokens which would be doubled if we % used \cs{tl_to_str:n} instead of \cs{token_to_str:N}). Thus % \cs{str_tail:n} gives a non-empty result exactly for control % sequences. Those are definable (technically, not always: % |\expandafter\font\csname\endcsname=cmr10| % |\expandafter\def\the\csname\endcsname{}|). For characters just % check for active characters. In both cases remember to end the % group. % \begin{macrocode} \group_begin: \char_set_catcode_active:n { `Z } \prg_new_protected_conditional:Npnn \@@_token_if_definable:N #1 { TF } { \group_begin: \@@_set_escapechar:n { 92 } \tl_set:Ne \l_@@_tmpa_tl { \exp_args:No \str_tail:n { \token_to_str:N #1 } } \tl_if_empty:NTF \l_@@_tmpa_tl { \@@_token_if_active_char:NTF #1 { \group_end: \prg_return_true: } { \group_end: \prg_return_false: } } { \group_end: \prg_return_true: } } \group_end: % \end{macrocode} % \end{macro} % % \begin{macro}[TF]{\@@_gtl_if_head_is_definable:N} % Tests if a generalized token list is a single control sequence or a % single active character. First test that it is single, then filter % out the case of (explicit) begin-group, end-group, and blank space % characters: those are neither control sequences nor active. Then % feed the single normal token to a first auxiliary. % \begin{macrocode} \prg_new_protected_conditional:Npnn \@@_gtl_if_head_is_definable:N #1 { TF , F } { \gtl_if_single_token:NTF #1 { \gtl_if_head_is_N_type:NTF #1 { \gtl_head_do:NN #1 \@@_token_if_definable:NTF { \prg_return_true: } { \prg_return_false: } } { \prg_return_false: } } { \prg_return_false: } } % \end{macrocode} % \end{macro} % % \subsubsection{Helpers for previous input} % % \begin{macro}[EXP]{\@@_prev_input_count:, \@@_prev_input_count_aux:n, \@@_prev_input_count_aux:Nn} % Count prev input levels, skipping empty ones (of either tl or gtl % type). % \begin{macrocode} \cs_new:Npn \@@_prev_input_count: { \int_eval:n { 0 \seq_map_function:NN \g_@@_prev_input_seq \@@_prev_input_count_aux:n } } \cs_new:Npn \@@_prev_input_count_aux:n #1 { \@@_prev_input_count_aux:Nn #1 } \cs_new:Npn \@@_prev_input_count_aux:Nn #1#2 { \if_meaning:w T #1 \exp_after:wN \tl_if_empty:nF \else: \exp_after:wN \str_if_eq:onF \exp_after:wN \c_empty_gtl \fi: {#2} { + 1 } } % \end{macrocode} % \end{macro} % % \begin{macro} % { % \@@_prev_input_gpush:, % \@@_prev_input_gpush:N, % \@@_prev_input_gpush_gtl:, % \@@_prev_input_gpush_gtl:N, % \@@_prev_input_gpush_aux:NN % } % \begin{macrocode} \cs_new_protected:Npn \@@_prev_input_gpush: { \seq_gput_right:Nn \g_@@_prev_input_seq { T { } } } \cs_new_protected:Npn \@@_prev_input_gpush:N { \@@_prev_input_gpush_aux:NN T } \cs_new_protected:Npn \@@_prev_input_gpush_gtl: { \@@_prev_input_gpush_gtl:N \c_empty_gtl } \cs_new_protected:Npn \@@_prev_input_gpush_gtl:N { \@@_prev_input_gpush_aux:NN G } \cs_new_protected:Npn \@@_prev_input_gpush_aux:NN #1#2 { \seq_gput_right:Ne \g_@@_prev_input_seq { #1 { \exp_not:o {#2} } } } % \end{macrocode} % \end{macro} % % \begin{variable}{\l_@@_prev_aux_tl} % \begin{macro} % { % \@@_prev_input_get:N, % \@@_prev_input_gpop:N, % \@@_prev_input_gpop_gtl:N, % \@@_prev_input_aux:NNN, % \@@_prev_input_aux:NNNn, % } % \begin{macrocode} \tl_new:N \l_@@_prev_aux_tl \cs_new_protected:Npn \@@_prev_input_get:N { \@@_prev_input_aux:NNN \seq_get_right:NN T } \cs_new_protected:Npn \@@_prev_input_gpop:N { \@@_prev_input_aux:NNN \seq_gpop_right:NN T } \cs_new_protected:Npn \@@_prev_input_gpop_gtl:N { \@@_prev_input_aux:NNN \seq_gpop_right:NN G } \cs_new_protected:Npn \@@_prev_input_aux:NNN #1#2#3 { #1 \g_@@_prev_input_seq \l_@@_prev_aux_tl \exp_after:wN \@@_prev_input_aux:NNNn \exp_after:wN #2 \exp_after:wN #3 \l_@@_prev_aux_tl } \cs_new_protected:Npn \@@_prev_input_aux:NNNn #1#2#3 { \token_if_eq_meaning:NNTF #1#3 { \tl_set:Nn } { \@@_error:nnnnn { prev-input } {#1} {#3} } #2 } % \end{macrocode} % \end{macro} % \end{variable} % % \begin{macro} % { % \@@_prev_input_silent:n, % \@@_prev_input_silent:V, % \@@_prev_input_silent:e % } % \begin{macro} % { % \@@_prev_input:n, % \@@_prev_input:V, % \@@_prev_input:e % } % \begin{macrocode} \cs_new_protected:Npn \@@_prev_input_silent:n #1 { \@@_prev_input_gpop:N \l_@@_prev_input_tl \tl_put_right:Nn \l_@@_prev_input_tl {#1} \@@_prev_input_gpush:N \l_@@_prev_input_tl } \cs_generate_variant:Nn \@@_prev_input_silent:n { V , e } \cs_new_protected:Npn \@@_prev_input:n #1 { \@@_prev_input_silent:n {#1} \@@_print_action:e { \tl_to_str:n {#1} } } \cs_generate_variant:Nn \@@_prev_input:n { V , e } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\@@_prev_input_gtl:N} % \begin{macrocode} \cs_new_protected:Npn \@@_prev_input_gtl:N #1 { \@@_prev_input_gpop_gtl:N \l_@@_prev_input_gtl \gtl_concat:NNN \l_@@_prev_input_gtl \l_@@_prev_input_gtl #1 \@@_prev_input_gpush_gtl:N \l_@@_prev_input_gtl } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_prev_input_join_get:nnN} % \begin{macro}{\@@_join_get_aux:NNnN} % Pops the previous-input sequence twice to get some value in % \cs{l_@@_head_tl} and some sign or decimal number in % \cs{l_@@_tmpa_tl}. Combines them into a value, using % the appropriate evaluation function, determined based % on~|#1|. % \begin{macrocode} \cs_new_protected:Npn \@@_prev_input_join_get:nnN #1 { \int_case:nnF {#1} { { 2 } { \@@_join_get_aux:NNnN \skip_eval:n \tex_glueexpr:D } { 3 } { \@@_join_get_aux:NNnN \muskip_eval:n \tex_muexpr:D } } { \@@_error:nnnnn { internal } { join-factor } { } { } { } \@@_join_get_aux:NNnN \use:n \prg_do_nothing: } } \cs_new_protected:Npn \@@_join_get_aux:NNnN #1#2#3#4 { \@@_prev_input_gpop:N \l_@@_head_tl \@@_prev_input_gpop:N \l_@@_tmpa_tl \tl_set:Ne #4 { #1 { \l_@@_tmpa_tl #2 \l_@@_head_tl #3 } } } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Variables} % % \subsubsection{User interaction} % % \begin{variable}{\g_@@_iow, \g_@@_current_output_file_tl} % The stream is used to implement the \texttt{output-file} option. At % any given time it points to the file named % \cs{g_@@_current_output_file_tl}, unless that is empty, in which % case the stream is closed. The idea is that we do not want to close % the file in between different \cs{unravel} calls since file writing % is not additive in \TeX{}. % \begin{macrocode} \iow_new:N \g_@@_iow \tl_new:N \g_@@_current_output_file_tl % \end{macrocode} % \end{variable} % % \begin{variable}{\g_@@_before_print_state_tl, \g_@@_before_prompt_tl} % Code to run before printing the state or before the prompt. % \begin{macrocode} \tl_new:N \g_@@_before_print_state_tl \tl_new:N \g_@@_before_prompt_tl % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_prompt_tmpa_int} % \begin{macrocode} \int_new:N \l_@@_prompt_tmpa_int % \end{macrocode} % \end{variable} % % \begin{variable}{\g_@@_nonstop_int} % The number of prompts to skip. % \begin{macrocode} \int_new:N \g_@@_nonstop_int % \end{macrocode} % \end{variable} % % \begin{variable}{\g_@@_current_online_int} % Temporary value replacing \cs{g_@@_online_int} and that can be set % through the prompt. % \begin{macrocode} \int_new:N \g_@@_current_online_int % \end{macrocode} % \end{variable} % % \begin{variable} % { % \g_@@_default_explicit_prompt_bool , \g_@@_explicit_prompt_bool , % \g_@@_default_internal_debug_bool , \g_@@_internal_debug_bool , % \g_@@_default_number_steps_bool , \g_@@_number_steps_bool , % \g_@@_default_online_int , \g_@@_online_int , % \g_@@_default_output_file_tl , \g_@@_output_file_tl , % \g_@@_default_prompt_input_clist , \g_@@_prompt_input_clist , % \g_@@_default_trace_assigns_bool , \g_@@_trace_assigns_bool , % \g_@@_default_trace_expansion_bool , \g_@@_trace_expansion_bool , % \g_@@_default_trace_other_bool , \g_@@_trace_other_bool , % \g_@@_default_welcome_message_bool , \g_@@_welcome_message_bool , % } % Variables for the options |explicit-prompt|, % |internal-debug|, |number-steps|, and so on. The % \texttt{default_} booleans/integers store the default value of these % options, and are affected by \cs{unravelsetup} or % \cs{unravel_setup:n}. % \begin{macrocode} \bool_new:N \g_@@_default_explicit_prompt_bool \bool_new:N \g_@@_default_internal_debug_bool \bool_new:N \g_@@_default_number_steps_bool \int_new:N \g_@@_default_online_int \tl_new:N \g_@@_default_output_file_tl \clist_new:N \g_@@_default_prompt_input_clist \bool_new:N \g_@@_default_trace_assigns_bool \bool_new:N \g_@@_default_trace_expansion_bool \bool_new:N \g_@@_default_trace_other_bool \bool_new:N \g_@@_default_welcome_message_bool \bool_gset_true:N \g_@@_default_number_steps_bool \int_gset:Nn \g_@@_default_online_int { 1 } \bool_gset_true:N \g_@@_default_trace_assigns_bool \bool_gset_true:N \g_@@_default_trace_expansion_bool \bool_gset_true:N \g_@@_default_trace_other_bool \bool_gset_true:N \g_@@_default_welcome_message_bool \bool_new:N \g_@@_explicit_prompt_bool \bool_new:N \g_@@_internal_debug_bool \bool_new:N \g_@@_number_steps_bool \int_new:N \g_@@_online_int \tl_new:N \g_@@_output_file_tl \clist_new:N \g_@@_prompt_input_clist \bool_new:N \g_@@_trace_assigns_bool \bool_new:N \g_@@_trace_expansion_bool \bool_new:N \g_@@_trace_other_bool \bool_new:N \g_@@_welcome_message_bool % \end{macrocode} % \end{variable} % % \begin{variable}{\g_@@_step_int} % Current expansion step. % \begin{macrocode} \int_new:N \g_@@_step_int % \end{macrocode} % \end{variable} % % \begin{variable}{\g_@@_action_text_str} % Text describing the action, displayed at each step. This should % only be altered through \cs{@@_set_action_text:e}, which sets the % escape character as appropriate before converting the argument to a % string. % \begin{macrocode} \str_new:N \g_@@_action_text_str % \end{macrocode} % \end{variable} % % \begin{variable} % { % \g_@@_default_max_action_int, % \g_@@_default_max_output_int, % \g_@@_default_max_input_int, % \g_@@_max_action_int, % \g_@@_max_output_int, % \g_@@_max_input_int % } % Maximum length of various pieces of what is shown on the terminal. % \begin{macrocode} \int_new:N \g_@@_default_max_action_int \int_new:N \g_@@_default_max_output_int \int_new:N \g_@@_default_max_input_int \int_gset:Nn \g_@@_default_max_action_int { 50 } \int_gset:Nn \g_@@_default_max_output_int { 300 } \int_gset:Nn \g_@@_default_max_input_int { 300 } \int_new:N \g_@@_max_action_int \int_new:N \g_@@_max_output_int \int_new:N \g_@@_max_input_int % \end{macrocode} % \end{variable} % % \begin{variable}{\g_@@_speedup_macros_bool} % If this boolean is true, speed up macros which have a simple % parameter text. This may not be safe if very weird macros appear. % \begin{macrocode} \bool_new:N \g_@@_speedup_macros_bool \bool_gset_true:N \g_@@_speedup_macros_bool % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_print_int} % The length of one piece of the terminal output. % \begin{macrocode} \int_new:N \l_@@_print_int % \end{macrocode} % \end{variable} % % \subsubsection{Working with tokens} % % \begin{variable}{\g_@@_input_int} % The user input, at each stage of expansion, is stored in multiple % \texttt{gtl} variables, from |\g_@@_input_|\meta{n}|_gtl| to % \cs{g_@@_input_1_gtl}. The split between variables is akin to % \TeX{}'s input stack, and allows us to manipulate smaller token % lists, speeding up processing. The total number \meta{n} of lists % is \cs{g_@@_input_int}. The highest numbered \texttt{gtl} % represents input that comes to the left of lower numbered ones. % \begin{macrocode} \int_new:N \g_@@_input_int % \end{macrocode} % \end{variable} % % \begin{variable}{\g_@@_input_tmpa_int} % \begin{variable}{\l_@@_input_tmpa_tl} % \begin{macrocode} \int_new:N \g_@@_input_tmpa_int \tl_new:N \l_@@_input_tmpa_tl % \end{macrocode} % \end{variable} % \end{variable} % % \begin{variable} % {\g_@@_prev_input_seq, \l_@@_prev_input_tl, \l_@@_prev_input_gtl} % The different levels of expansion are stored in % \cs{g_@@_prev_input_seq}, with the innermost at the end of the % sequence (otherwise the sequence would have to be reversed for % display). When adding material to the last level of expansion, % \cs{l_@@_prev_input_tl} or \cs{l_@@_prev_input_gtl} are used to % temporarily store the last level of expansion. % \begin{macrocode} \seq_new:N \g_@@_prev_input_seq \tl_new:N \l_@@_prev_input_tl \gtl_new:N \l_@@_prev_input_gtl % \end{macrocode} % \end{variable} % % \begin{variable}{\g_@@_output_gtl} % Material that is ``typeset'' or otherwise sent further down \TeX{}'s % digestion. % \begin{macrocode} \gtl_new:N \g_@@_output_gtl % \end{macrocode} % \end{variable} % % \begin{variable} % { % \l_@@_head_gtl, % \l_@@_head_tl, % \l_@@_head_token, % \l_@@_head_cmd_int, % \l_@@_head_char_int % } % First token in the input, as a generalized token list (general case) % or as a token list whenever this is possible. Also, a token set % equal to it, and its command code and character code, following % \TeX{}. % \begin{macrocode} \gtl_new:N \l_@@_head_gtl \tl_new:N \l_@@_head_tl \cs_new_eq:NN \l_@@_head_token ? \int_new:N \l_@@_head_cmd_int \int_new:N \l_@@_head_char_int % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_head_meaning_tl} % \begin{macrocode} \tl_new:N \l_@@_head_meaning_tl % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_argi_tl, \l_@@_argii_tl} % Token list variables used to store first/second arguments. % \begin{macrocode} \tl_new:N \l_@@_argi_tl \tl_new:N \l_@@_argii_tl % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_tmpa_tl, \l_@@_tmpb_gtl, \g_@@_tmpc_tl, \l_@@_tmpa_seq, \l_@@_unused_gtl, \l_@@_tmpb_token} % Temporary storage. The \cs{l_@@_unused_gtl} is only used once, % to ignore some unwanted tokens. % \begin{macrocode} \tl_new:N \l_@@_tmpa_tl \gtl_new:N \l_@@_unused_gtl \gtl_new:N \l_@@_tmpb_gtl \tl_new:N \g_@@_tmpc_tl \seq_new:N \l_@@_tmpa_seq \cs_new_eq:NN \l_@@_tmpb_token ? % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_defined_tl, \l_@@_defining_tl} % The token that is defined by the prefixed command (such as % \tn{chardef} or \tn{futurelet}), and the code to define it. We do % not use the the previous-input sequence to store that code because % this sequence contains a string representation of the code, which is % not suitable for the definition. Using a single variable here is % safe, as definitions cannot be nested. This is needed for expanding % assignments, as expansion should be shown to the user, but then % later should not be performed again when defining. It is also % helpful in tracking some register assignments. % \begin{macrocode} \tl_new:N \l_@@_defined_tl \tl_new:N \l_@@_defining_tl % \end{macrocode} % \end{variable} % % \begin{macro}{\@@_inaccessible:w} % \begin{macrocode} \cs_new_eq:NN \@@_inaccessible:w ? % \end{macrocode} % \end{macro} % % \begin{variable}{\g_@@_lastnamedcs_tl} % Used for \LuaTeX{}'s \tn{lastnamedcs} primitive. % \begin{macrocode} \tl_new:N \g_@@_lastnamedcs_tl % \end{macrocode} % \end{variable} % % \begin{variable} % { % \g_@@_after_assignment_gtl, % \g_@@_set_box_allowed_bool, % \g_@@_name_in_progress_bool % } % Global variables keeping track of the state of \TeX{}. Token to % insert after the next assignment. Is \tn{setbox} currently allowed? % Should \tn{input} expand? % \begin{macrocode} \gtl_new:N \g_@@_after_assignment_gtl \bool_new:N \g_@@_set_box_allowed_bool \bool_new:N \g_@@_name_in_progress_bool % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_after_group_gtl} % Tokens to insert after the current group ends. This variable must % be emptied at the beginning of every group. % \begin{macrocode} \gtl_new:N \l_@@_after_group_gtl % \end{macrocode} % \end{variable} % % \begin{variable}{\c_@@_parameters_tl} % Used to determine if a macro has simple parameters or not. % \begin{macrocode} \group_begin: \cs_set_nopar:Npe \@@_tmp:w #1 { \c_hash_str #1 } \tl_const:Ne \c_@@_parameters_tl { ^ \tl_map_function:nN { 123456789 } \@@_tmp:w } \group_end: % \end{macrocode} % \end{variable} % % \subsubsection{Numbers and conditionals} % % \begin{variable}{\g_@@_val_level_int} % See \TeX{}'s |cur_val_level| variable. This is set by % \cs{@@_rescan_something_internal:n} to % \begin{itemize} % \item $0$ for integer values, % \item $1$ for dimension values, % \item $2$ for glue values, % \item $3$ for mu glue values, % \item $4$ for font identifiers, % \item $5$ for token lists. % \end{itemize} % \begin{macrocode} \int_new:N \g_@@_val_level_int % \end{macrocode} % \end{variable} % % \begin{variable}{\g_@@_if_limit_tl} % \begin{variable}{\g_@@_if_limit_int} % \begin{variable}{\g_@@_if_depth_int} % Stack for what \TeX{} calls |if_limit|, and its depth. % \begin{macrocode} \tl_new:N \g_@@_if_limit_tl \int_new:N \g_@@_if_limit_int \int_new:N \g_@@_if_depth_int % \end{macrocode} % \end{variable} % \end{variable} % \end{variable} % % \begin{variable}{\l_@@_if_nesting_int} % \begin{macrocode} \int_new:N \l_@@_if_nesting_int % \end{macrocode} % \end{variable} % % \subsubsection{Boxes and groups} % % \begin{variable}{\l_@@_leaders_box_seq} % A stack of letters: the first token in the token list is |h|~if the % innermost explicit box (created with \tn{vtop}, \tn{vbox}, or % \tn{hbox}) appears in a horizontal (or math) mode leaders % construction; it is |v|~if the innermost explicit box appears in a % vertical mode leaders construction; it is |Z|~otherwise. % \begin{macrocode} \seq_new:N \l_@@_leaders_box_seq % \end{macrocode} % \end{variable} % % \begin{variable}{\g_@@_ends_int} % Number of times \tn{end} will be put back into the input in case % there remains to ship some pages. % \begin{macrocode} \int_new:N \g_@@_ends_int \int_gset:Nn \g_@@_ends_int { 3 } % \end{macrocode} % \end{variable} % % \subsubsection{Constants} % % \begin{variable} % { % \c_@@_plus_tl, \c_@@_minus_tl, \c_@@_times_tl, \c_@@_over_tl, % \c_@@_lq_tl, \c_@@_rq_tl, \c_@@_dq_tl, \c_@@_lp_tl, \c_@@_rp_tl, % \c_@@_eq_tl, \c_@@_comma_tl, \c_@@_point_tl, % } % \begin{macrocode} \tl_const:Nn \c_@@_plus_tl { + } \tl_const:Nn \c_@@_minus_tl { - } \tl_const:Nn \c_@@_times_tl { * } \tl_const:Nn \c_@@_over_tl { / } \tl_const:Nn \c_@@_lq_tl { ` } \tl_const:Nn \c_@@_rq_tl { ' } \tl_const:Nn \c_@@_dq_tl { " } %" \tl_const:Nn \c_@@_lp_tl { ( } \tl_const:Nn \c_@@_rp_tl { ) } \tl_const:Nn \c_@@_eq_tl { = } \tl_const:Nn \c_@@_comma_tl { , } \tl_const:Nn \c_@@_point_tl { . } % \end{macrocode} % \end{variable} % % \begin{variable}{\c_@@_frozen_relax_gtl} % \TeX{}'s |frozen_relax|, inserted by \cs{@@_insert_relax:}. % \begin{macrocode} \gtl_const:Ne \c_@@_frozen_relax_gtl { \if_int_compare:w 0 = 0 \fi: } % \end{macrocode} % \end{variable} % % \subsubsection{\TeX{} parameters} % % \begin{variable}{\g_@@_mag_set_int} % The first time \TeX{} uses the value of \tn{mag}, it stores it in a % global parameter |mag_set| (initially $0$ to denote not being set). % Any time \TeX{} needs the value of \tn{mag}, it checks that the % value matches |mag_set|. This is done in \pkg{unravel} by % \cs{@@_prepare_mag:}, storing |mag_set| in \cs{g_@@_mag_set_int}. % \begin{macrocode} \int_new:N \g_@@_mag_set_int % \end{macrocode} % \end{variable} % % \begin{macro}{\@@_prepare_mag:} % Used whenever \TeX{} needs the value of \tn{mag}. % \begin{macrocode} \cs_new_protected:Npn \@@_prepare_mag: { \int_compare:nNnT { \g_@@_mag_set_int } > { 0 } { \int_compare:nNnF { \@@_mag: } = { \g_@@_mag_set_int } { \@@_tex_error:nn { incompatible-mag } { } \int_gset_eq:NN \@@_mag: \g_@@_mag_set_int } } \int_compare:nF { 1 <= \@@_mag: <= 32768 } { \@@_tex_error:nV { illegal-mag } \l_@@_head_tl \int_gset:Nn \@@_mag: { 1000 } } \int_gset_eq:NN \g_@@_mag_set_int \@@_mag: } % \end{macrocode} % \end{macro} % % \subsection{Numeric codes} % \label{sec:numeric-codes} % % First we define some numeric codes, following Section~15 of the \TeX{} % web code, then we associate a command code to each \TeX{} primitive, % and a character code, to decide what action to perform upon seeing % them. % % \begin{macro}{\@@_tex_const:nn} % \begin{macro}[EXP]{\@@_tex_use:n} % \begin{macrocode} \cs_new_protected:Npn \@@_tex_const:nn #1#2 { \int_const:cn { c_@@_tex_#1_int } {#2} } \cs_new:Npn \@@_tex_use:n #1 { \int_use:c { c_@@_tex_#1_int } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\@@_tex_primitive:nnn, \@@_tex_primitive_pdf:nnn} % \begin{macrocode} \cs_new_protected:Npn \@@_tex_primitive:nnn #1#2#3 { \tl_const:ce { c_@@_tex_#1_tl } { { \@@_tex_use:n {#2} } {#3} } } \cs_new_protected:Npn \@@_tex_primitive_pdf:nnn #1#2#3 { \sys_if_engine_pdftex:F { \@@_tex_primitive:nnn {#1} {#2} {#3} } \@@_tex_primitive:nnn { pdf #1 } {#2} {#3} } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_new_tex_cmd:nn, \@@_new_eq_tex_cmd:nn} % \begin{macrocode} \cs_new_protected:Npn \@@_new_tex_cmd:nn #1#2 { \cs_new_protected:cpn { @@_cmd_ \@@_tex_use:n {#1} : } {#2} } \cs_new_protected:Npn \@@_new_eq_tex_cmd:nn #1#2 { \cs_new_eq:cc { @@_cmd_ \@@_tex_use:n {#1} : } { @@_cmd_ \@@_tex_use:n {#2} : } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_new_tex_expandable:nn} % \begin{macrocode} \cs_new_protected:Npn \@@_new_tex_expandable:nn #1#2 { \cs_new_protected:cpn { @@_expandable_ \@@_tex_use:n {#1} : } {#2} } % \end{macrocode} % \end{macro} % % Contrarily to \TeX{}, all macros are |call|, no |long_call| and the % like. % \begin{macrocode} \@@_tex_const:nn { relax } { 0 } \@@_tex_const:nn { begin-group_char } { 1 } \@@_tex_const:nn { end-group_char } { 2 } \@@_tex_const:nn { math_char } { 3 } \@@_tex_const:nn { tab_mark } { 4 } \@@_tex_const:nn { alignment_char } { 4 } \@@_tex_const:nn { car_ret } { 5 } \@@_tex_const:nn { macro_char } { 6 } \@@_tex_const:nn { superscript_char } { 7 } \@@_tex_const:nn { subscript_char } { 8 } \@@_tex_const:nn { endv } { 9 } \@@_tex_const:nn { blank_char } { 10 } \@@_tex_const:nn { the_char } { 11 } \@@_tex_const:nn { other_char } { 12 } \@@_tex_const:nn { par_end } { 13 } \@@_tex_const:nn { stop } { 14 } \@@_tex_const:nn { delim_num } { 15 } \@@_tex_const:nn { max_char_code } { 15 } \@@_tex_const:nn { char_num } { 16 } \@@_tex_const:nn { math_char_num } { 17 } \@@_tex_const:nn { mark } { 18 } \@@_tex_const:nn { xray } { 19 } \@@_tex_const:nn { make_box } { 20 } \@@_tex_const:nn { hmove } { 21 } \@@_tex_const:nn { vmove } { 22 } \@@_tex_const:nn { un_hbox } { 23 } \@@_tex_const:nn { un_vbox } { 24 } \@@_tex_const:nn { remove_item } { 25 } \@@_tex_const:nn { hskip } { 26 } \@@_tex_const:nn { vskip } { 27 } \@@_tex_const:nn { mskip } { 28 } \@@_tex_const:nn { kern } { 29 } \@@_tex_const:nn { mkern } { 30 } \@@_tex_const:nn { leader_ship } { 31 } \@@_tex_const:nn { halign } { 32 } \@@_tex_const:nn { valign } { 33 } \@@_tex_const:nn { no_align } { 34 } \@@_tex_const:nn { vrule } { 35 } \@@_tex_const:nn { hrule } { 36 } \@@_tex_const:nn { insert } { 37 } \@@_tex_const:nn { vadjust } { 38 } \@@_tex_const:nn { ignore_spaces } { 39 } \@@_tex_const:nn { after_assignment } { 40 } \@@_tex_const:nn { after_group } { 41 } \@@_tex_const:nn { break_penalty } { 42 } \@@_tex_const:nn { start_par } { 43 } \@@_tex_const:nn { ital_corr } { 44 } \@@_tex_const:nn { accent } { 45 } \@@_tex_const:nn { math_accent } { 46 } \@@_tex_const:nn { discretionary } { 47 } \@@_tex_const:nn { eq_no } { 48 } \@@_tex_const:nn { left_right } { 49 } \@@_tex_const:nn { math_comp } { 50 } \@@_tex_const:nn { limit_switch } { 51 } \@@_tex_const:nn { above } { 52 } \@@_tex_const:nn { math_style } { 53 } \@@_tex_const:nn { math_choice } { 54 } \@@_tex_const:nn { non_script } { 55 } \@@_tex_const:nn { vcenter } { 56 } \@@_tex_const:nn { case_shift } { 57 } \@@_tex_const:nn { message } { 58 } \@@_tex_const:nn { extension } { 59 } \@@_tex_const:nn { in_stream } { 60 } \@@_tex_const:nn { begin_group } { 61 } \@@_tex_const:nn { end_group } { 62 } \@@_tex_const:nn { omit } { 63 } \@@_tex_const:nn { ex_space } { 64 } \@@_tex_const:nn { no_boundary } { 65 } \@@_tex_const:nn { radical } { 66 } \@@_tex_const:nn { end_cs_name } { 67 } \@@_tex_const:nn { min_internal } { 68 } \@@_tex_const:nn { char_given } { 68 } \@@_tex_const:nn { math_given } { 69 } \@@_tex_const:nn { last_item } { 70 } \@@_tex_const:nn { max_non_prefixed_command } { 70 } \@@_tex_const:nn { toks_register } { 71 } \@@_tex_const:nn { assign_toks } { 72 } \@@_tex_const:nn { assign_int } { 73 } \@@_tex_const:nn { assign_dimen } { 74 } \@@_tex_const:nn { assign_glue } { 75 } \@@_tex_const:nn { assign_mu_glue } { 76 } \@@_tex_const:nn { assign_font_dimen } { 77 } \@@_tex_const:nn { assign_font_int } { 78 } \@@_tex_const:nn { set_aux } { 79 } \@@_tex_const:nn { set_prev_graf } { 80 } \@@_tex_const:nn { set_page_dimen } { 81 } \@@_tex_const:nn { set_page_int } { 82 } \@@_tex_const:nn { set_box_dimen } { 83 } \@@_tex_const:nn { set_shape } { 84 } \@@_tex_const:nn { def_code } { 85 } \@@_tex_const:nn { def_family } { 86 } \@@_tex_const:nn { set_font } { 87 } \@@_tex_const:nn { def_font } { 88 } \@@_tex_const:nn { register } { 89 } \@@_tex_const:nn { max_internal } { 89 } \@@_tex_const:nn { advance } { 90 } \@@_tex_const:nn { multiply } { 91 } \@@_tex_const:nn { divide } { 92 } \@@_tex_const:nn { prefix } { 93 } \@@_tex_const:nn { let } { 94 } \@@_tex_const:nn { shorthand_def } { 95 } \@@_tex_const:nn { read_to_cs } { 96 } \@@_tex_const:nn { def } { 97 } \@@_tex_const:nn { set_box } { 98 } \@@_tex_const:nn { hyph_data } { 99 } \@@_tex_const:nn { set_interaction } { 100 } \@@_tex_const:nn { letterspace_font } { 101 } \@@_tex_const:nn { pdf_copy_font } { 102 } \@@_tex_const:nn { max_command } { 102 } \@@_tex_const:nn { undefined_cs } { 103 } \@@_tex_const:nn { expand_after } { 104 } \@@_tex_const:nn { no_expand } { 105 } \@@_tex_const:nn { input } { 106 } \@@_tex_const:nn { if_test } { 107 } \@@_tex_const:nn { fi_or_else } { 108 } \@@_tex_const:nn { cs_name } { 109 } \@@_tex_const:nn { convert } { 110 } \@@_tex_const:nn { the } { 111 } \@@_tex_const:nn { top_bot_mark } { 112 } \@@_tex_const:nn { call } { 113 } \@@_tex_const:nn { end_template } { 117 } % \end{macrocode} % So far we've implemented properly [71,104]; [107,113]. % % A few minor differences with pdf\TeX{}'s internal numbers are as % follows. % \begin{itemize} % \item |case_shift| is shifted by |3983|. % \item |assign_toks| is shifted by |local_base=3412|. % \item |assign_int| is shifted by |int_base=5263|. % \item |assign_dimen| is shifted by |dimen_base=5830|. % \item |assign_glue| and |assign_mu_glue| are shifted by % |glue_base=2882|. % \item |set_shape| is shifted (in \eTeX{}) by |local_base|. % \item |def_code| and |def_family| is shifted by |cat_code_base=3983|. % \item In \TeX{}, |inputlineno.char=3| and |badness.char=4|. % \end{itemize} % A special case for \LuaTeX{} deals with the fact that the % \cs{@@_special_relax:} has a strange meaning \enquote{[unknown command % code! (0, 1)]}. For instance \tn{expandafter} \tn{show} \tn{noexpand} % \tn{undefined} shows this. % \begin{macrocode} \sys_if_engine_luatex:T { \@@_tex_primitive:nnn { \exp_after:wN \use_none:n \token_to_meaning:N \@@_special_relax: } { relax } { 1 } } \@@_tex_primitive:nnn { relax } { relax } { 256 } \@@_tex_primitive:nnn { span } { tab_mark } { 256 } \@@_tex_primitive:nnn { cr } { car_ret } { 257 } \@@_tex_primitive:nnn { crcr } { car_ret } { 258 } \@@_tex_primitive:nnn { par } { par_end } { 256 } \@@_tex_primitive:nnn { end } { stop } { 0 } \@@_tex_primitive:nnn { dump } { stop } { 1 } \@@_tex_primitive:nnn { delimiter } { delim_num } { 0 } \@@_tex_primitive:nnn { char } { char_num } { 0 } \@@_tex_primitive:nnn { mathchar } { math_char_num } { 0 } \@@_tex_primitive:nnn { mark } { mark } { 0 } \@@_tex_primitive:nnn { marks } { mark } { 5 } \@@_tex_primitive:nnn { show } { xray } { 0 } \@@_tex_primitive:nnn { showbox } { xray } { 1 } \@@_tex_primitive:nnn { showthe } { xray } { 2 } \@@_tex_primitive:nnn { showlists } { xray } { 3 } \@@_tex_primitive:nnn { showgroups } { xray } { 4 } \@@_tex_primitive:nnn { showtokens } { xray } { 5 } \@@_tex_primitive:nnn { showifs } { xray } { 6 } \@@_tex_primitive:nnn { box } { make_box } { 0 } \@@_tex_primitive:nnn { copy } { make_box } { 1 } \@@_tex_primitive:nnn { lastbox } { make_box } { 2 } \@@_tex_primitive:nnn { vsplit } { make_box } { 3 } \@@_tex_primitive:nnn { vtop } { make_box } { 4 } \@@_tex_primitive:nnn { vbox } { make_box } { 5 } \@@_tex_primitive:nnn { hbox } { make_box } { 106 } \@@_tex_primitive:nnn { moveright } { hmove } { 0 } \@@_tex_primitive:nnn { moveleft } { hmove } { 1 } \@@_tex_primitive:nnn { lower } { vmove } { 0 } \@@_tex_primitive:nnn { raise } { vmove } { 1 } \@@_tex_primitive:nnn { unhbox } { un_hbox } { 0 } \@@_tex_primitive:nnn { unhcopy } { un_hbox } { 1 } \@@_tex_primitive:nnn { unvbox } { un_vbox } { 0 } \@@_tex_primitive:nnn { unvcopy } { un_vbox } { 1 } \@@_tex_primitive:nnn { pagediscards } { un_vbox } { 2 } \@@_tex_primitive:nnn { splitdiscards } { un_vbox } { 3 } \@@_tex_primitive:nnn { unpenalty } { remove_item } { 12 } \@@_tex_primitive:nnn { unkern } { remove_item } { 11 } \@@_tex_primitive:nnn { unskip } { remove_item } { 10 } \@@_tex_primitive:nnn { hfil } { hskip } { 0 } \@@_tex_primitive:nnn { hfill } { hskip } { 1 } \@@_tex_primitive:nnn { hss } { hskip } { 2 } \@@_tex_primitive:nnn { hfilneg } { hskip } { 3 } \@@_tex_primitive:nnn { hskip } { hskip } { 4 } \@@_tex_primitive:nnn { vfil } { vskip } { 0 } \@@_tex_primitive:nnn { vfill } { vskip } { 1 } \@@_tex_primitive:nnn { vss } { vskip } { 2 } \@@_tex_primitive:nnn { vfilneg } { vskip } { 3 } \@@_tex_primitive:nnn { vskip } { vskip } { 4 } \@@_tex_primitive:nnn { mskip } { mskip } { 5 } \@@_tex_primitive:nnn { kern } { kern } { 1 } \@@_tex_primitive:nnn { mkern } { mkern } { 99 } \@@_tex_primitive:nnn { shipout } { leader_ship } { 99 } \@@_tex_primitive:nnn { leaders } { leader_ship } { 100 } \@@_tex_primitive:nnn { cleaders } { leader_ship } { 101 } \@@_tex_primitive:nnn { xleaders } { leader_ship } { 102 } \@@_tex_primitive:nnn { halign } { halign } { 0 } \@@_tex_primitive:nnn { valign } { valign } { 0 } \@@_tex_primitive:nnn { beginL } { valign } { 4 } \@@_tex_primitive:nnn { endL } { valign } { 5 } \@@_tex_primitive:nnn { beginR } { valign } { 8 } \@@_tex_primitive:nnn { endR } { valign } { 9 } \@@_tex_primitive:nnn { noalign } { no_align } { 0 } \@@_tex_primitive:nnn { vrule } { vrule } { 0 } \@@_tex_primitive:nnn { hrule } { hrule } { 0 } \@@_tex_primitive:nnn { insert } { insert } { 0 } \@@_tex_primitive:nnn { vadjust } { vadjust } { 0 } \@@_tex_primitive:nnn { ignorespaces } { ignore_spaces } { 0 } \@@_tex_primitive:nnn { afterassignment } { after_assignment } { 0 } \@@_tex_primitive:nnn { aftergroup } { after_group } { 0 } \@@_tex_primitive:nnn { penalty } { break_penalty } { 0 } \@@_tex_primitive:nnn { indent } { start_par } { 1 } \@@_tex_primitive:nnn { noindent } { start_par } { 0 } \@@_tex_primitive:nnn { quitvmode } { start_par } { 2 } \@@_tex_primitive:nnn { / } { ital_corr } { 0 } \@@_tex_primitive:nnn { accent } { accent } { 0 } \@@_tex_primitive:nnn { mathaccent } { math_accent } { 0 } \sys_if_engine_luatex:T { \@@_tex_primitive:nnn { explicitdiscretionary } { discretionary } { 1 } } \@@_tex_primitive:nnn { - } { discretionary } { 1 } \@@_tex_primitive:nnn { discretionary } { discretionary } { 0 } \@@_tex_primitive:nnn { eqno } { eq_no } { 0 } \@@_tex_primitive:nnn { leqno } { eq_no } { 1 } \@@_tex_primitive:nnn { left } { left_right } { 30 } \@@_tex_primitive:nnn { right } { left_right } { 31 } \@@_tex_primitive:nnn { middle } { left_right } { 17 } \@@_tex_primitive:nnn { mathord } { math_comp } { 16 } \@@_tex_primitive:nnn { mathop } { math_comp } { 17 } \@@_tex_primitive:nnn { mathbin } { math_comp } { 18 } \@@_tex_primitive:nnn { mathrel } { math_comp } { 19 } \@@_tex_primitive:nnn { mathopen } { math_comp } { 20 } \@@_tex_primitive:nnn { mathclose } { math_comp } { 21 } \@@_tex_primitive:nnn { mathpunct } { math_comp } { 22 } \@@_tex_primitive:nnn { mathinner } { math_comp } { 23 } \@@_tex_primitive:nnn { underline } { math_comp } { 26 } \@@_tex_primitive:nnn { overline } { math_comp } { 27 } \@@_tex_primitive:nnn { displaylimits } { limit_switch } { 0 } \@@_tex_primitive:nnn { limits } { limit_switch } { 1 } \@@_tex_primitive:nnn { nolimits } { limit_switch } { 2 } \@@_tex_primitive:nnn { above } { above } { 0 } \@@_tex_primitive:nnn { over } { above } { 1 } \@@_tex_primitive:nnn { atop } { above } { 2 } \@@_tex_primitive:nnn { abovewithdelims } { above } { 3 } \@@_tex_primitive:nnn { overwithdelims } { above } { 4 } \@@_tex_primitive:nnn { atopwithdelims } { above } { 5 } \@@_tex_primitive:nnn { displaystyle } { math_style } { 0 } \@@_tex_primitive:nnn { textstyle } { math_style } { 2 } \@@_tex_primitive:nnn { scriptstyle } { math_style } { 4 } \@@_tex_primitive:nnn { scriptscriptstyle } { math_style } { 6 } \@@_tex_primitive:nnn { mathchoice } { math_choice } { 0 } \@@_tex_primitive:nnn { nonscript } { non_script } { 0 } \@@_tex_primitive:nnn { vcenter } { vcenter } { 0 } \@@_tex_primitive:nnn { lowercase } { case_shift } { 256 } \@@_tex_primitive:nnn { uppercase } { case_shift } { 512 } \@@_tex_primitive:nnn { message } { message } { 0 } \@@_tex_primitive:nnn { errmessage } { message } { 1 } \@@_tex_primitive:nnn { openout } { extension } { 0 } \@@_tex_primitive:nnn { write } { extension } { 1 } \@@_tex_primitive:nnn { closeout } { extension } { 2 } \@@_tex_primitive:nnn { special } { extension } { 3 } \@@_tex_primitive:nnn { immediate } { extension } { 4 } \@@_tex_primitive:nnn { setlanguage } { extension } { 5 } \@@_tex_primitive_pdf:nnn { literal } { extension } { 6 } \@@_tex_primitive_pdf:nnn { obj } { extension } { 7 } \@@_tex_primitive_pdf:nnn { refobj } { extension } { 8 } \@@_tex_primitive_pdf:nnn { xform } { extension } { 9 } \@@_tex_primitive_pdf:nnn { refxform } { extension } { 10 } \@@_tex_primitive_pdf:nnn { ximage } { extension } { 11 } \@@_tex_primitive_pdf:nnn { refximage } { extension } { 12 } \@@_tex_primitive_pdf:nnn { annot } { extension } { 13 } \@@_tex_primitive_pdf:nnn { startlink } { extension } { 14 } \@@_tex_primitive_pdf:nnn { endlink } { extension } { 15 } \@@_tex_primitive_pdf:nnn { outline } { extension } { 16 } \@@_tex_primitive_pdf:nnn { dest } { extension } { 17 } \@@_tex_primitive_pdf:nnn { thread } { extension } { 18 } \@@_tex_primitive_pdf:nnn { startthread } { extension } { 19 } \@@_tex_primitive_pdf:nnn { endthread } { extension } { 20 } \@@_tex_primitive_pdf:nnn { savepos } { extension } { 21 } \@@_tex_primitive_pdf:nnn { info } { extension } { 22 } \@@_tex_primitive_pdf:nnn { catalog } { extension } { 23 } \@@_tex_primitive_pdf:nnn { names } { extension } { 24 } \@@_tex_primitive_pdf:nnn { fontattr } { extension } { 25 } \@@_tex_primitive_pdf:nnn { includechars } { extension } { 26 } \@@_tex_primitive_pdf:nnn { mapfile } { extension } { 27 } \@@_tex_primitive_pdf:nnn { mapline } { extension } { 28 } \@@_tex_primitive_pdf:nnn { trailer } { extension } { 29 } \@@_tex_primitive_pdf:nnn { resettimer } { extension } { 30 } \@@_tex_primitive_pdf:nnn { fontexpand } { extension } { 31 } \@@_tex_primitive_pdf:nnn { setrandomseed } { extension } { 32 } \@@_tex_primitive_pdf:nnn { snaprefpoint } { extension } { 33 } \@@_tex_primitive_pdf:nnn { snapy } { extension } { 34 } \@@_tex_primitive_pdf:nnn { snapycomp } { extension } { 35 } \@@_tex_primitive_pdf:nnn { glyphtounicode} { extension } { 36 } \@@_tex_primitive_pdf:nnn { colorstack } { extension } { 37 } \@@_tex_primitive_pdf:nnn { setmatrix } { extension } { 38 } \@@_tex_primitive_pdf:nnn { save } { extension } { 39 } \@@_tex_primitive_pdf:nnn { restore } { extension } { 40 } \@@_tex_primitive_pdf:nnn { nobuiltintounicode } { extension } { 41 } \@@_tex_primitive:nnn { openin } { in_stream } { 1 } \@@_tex_primitive:nnn { closein } { in_stream } { 0 } \@@_tex_primitive:nnn { begingroup } { begin_group } { 0 } \@@_tex_primitive:nnn { endgroup } { end_group } { 0 } \@@_tex_primitive:nnn { omit } { omit } { 0 } \@@_tex_primitive:nnn { ~ } { ex_space } { 0 } \@@_tex_primitive:nnn { noboundary } { no_boundary } { 0 } \@@_tex_primitive:nnn { radical } { radical } { 0 } \@@_tex_primitive:nnn { endcsname } { end_cs_name } { 0 } \@@_tex_primitive:nnn { lastpenalty } { last_item } { 0 } \@@_tex_primitive:nnn { lastkern } { last_item } { 1 } \@@_tex_primitive:nnn { lastskip } { last_item } { 2 } \@@_tex_primitive:nnn { lastnodetype } { last_item } { 3 } \@@_tex_primitive:nnn { inputlineno } { last_item } { 4 } \@@_tex_primitive:nnn { badness } { last_item } { 5 } \@@_tex_primitive_pdf:nnn { texversion } { last_item } { 6 } \@@_tex_primitive_pdf:nnn { lastobj } { last_item } { 7 } \@@_tex_primitive_pdf:nnn { lastxform } { last_item } { 8 } \@@_tex_primitive_pdf:nnn { lastximage } { last_item } { 9 } \@@_tex_primitive_pdf:nnn { lastximagepages } { last_item } { 10 } \@@_tex_primitive_pdf:nnn { lastannot } { last_item } { 11 } \@@_tex_primitive_pdf:nnn { lastxpos } { last_item } { 12 } \@@_tex_primitive_pdf:nnn { lastypos } { last_item } { 13 } \@@_tex_primitive_pdf:nnn { retval } { last_item } { 14 } \@@_tex_primitive_pdf:nnn { lastximagecolordepth } { last_item } { 15 } \@@_tex_primitive_pdf:nnn { elapsedtime } { last_item } { 16 } \@@_tex_primitive_pdf:nnn { shellescape } { last_item } { 17 } \@@_tex_primitive_pdf:nnn { randomseed } { last_item } { 18 } \@@_tex_primitive_pdf:nnn { lastlink } { last_item } { 19 } \@@_tex_primitive:nnn { eTeXversion } { last_item } { 20 } \@@_tex_primitive:nnn { currentgrouplevel } { last_item } { 21 } \@@_tex_primitive:nnn { currentgrouptype } { last_item } { 22 } \@@_tex_primitive:nnn { currentiflevel } { last_item } { 23 } \@@_tex_primitive:nnn { currentiftype } { last_item } { 24 } \@@_tex_primitive:nnn { currentifbranch } { last_item } { 25 } \@@_tex_primitive:nnn { gluestretchorder } { last_item } { 26 } \@@_tex_primitive:nnn { glueshrinkorder } { last_item } { 27 } \@@_tex_primitive:nnn { fontcharwd } { last_item } { 28 } \@@_tex_primitive:nnn { fontcharht } { last_item } { 29 } \@@_tex_primitive:nnn { fontchardp } { last_item } { 30 } \@@_tex_primitive:nnn { fontcharic } { last_item } { 31 } \@@_tex_primitive:nnn { parshapelength } { last_item } { 32 } \@@_tex_primitive:nnn { parshapeindent } { last_item } { 33 } \@@_tex_primitive:nnn { parshapedimen } { last_item } { 34 } \@@_tex_primitive:nnn { gluestretch } { last_item } { 35 } \@@_tex_primitive:nnn { glueshrink } { last_item } { 36 } \@@_tex_primitive:nnn { mutoglue } { last_item } { 37 } \@@_tex_primitive:nnn { gluetomu } { last_item } { 38 } \@@_tex_primitive:nnn { numexpr } { last_item } { 39 } \@@_tex_primitive:nnn { dimexpr } { last_item } { 40 } \@@_tex_primitive:nnn { glueexpr } { last_item } { 41 } \@@_tex_primitive:nnn { muexpr } { last_item } { 42 } \@@_tex_primitive:nnn { toks } { toks_register } { 0 } \@@_tex_primitive:nnn { output } { assign_toks } { 1 } \@@_tex_primitive:nnn { everypar } { assign_toks } { 2 } \@@_tex_primitive:nnn { everymath } { assign_toks } { 3 } \@@_tex_primitive:nnn { everydisplay } { assign_toks } { 4 } \@@_tex_primitive:nnn { everyhbox } { assign_toks } { 5 } \@@_tex_primitive:nnn { everyvbox } { assign_toks } { 6 } \@@_tex_primitive:nnn { everyjob } { assign_toks } { 7 } \@@_tex_primitive:nnn { everycr } { assign_toks } { 8 } \@@_tex_primitive:nnn { errhelp } { assign_toks } { 9 } \@@_tex_primitive_pdf:nnn { pagesattr } { assign_toks } { 10 } \@@_tex_primitive_pdf:nnn { pageattr } { assign_toks } { 11 } \@@_tex_primitive_pdf:nnn { pageresources } { assign_toks } { 12 } \@@_tex_primitive_pdf:nnn { pkmode } { assign_toks } { 13 } \@@_tex_primitive:nnn { everyeof } { assign_toks } { 14 } \@@_tex_primitive:nnn { pretolerance } { assign_int } { 0 } \@@_tex_primitive:nnn { tolerance } { assign_int } { 1 } \@@_tex_primitive:nnn { linepenalty } { assign_int } { 2 } \@@_tex_primitive:nnn { hyphenpenalty } { assign_int } { 3 } \@@_tex_primitive:nnn { exhyphenpenalty } { assign_int } { 4 } \@@_tex_primitive:nnn { clubpenalty } { assign_int } { 5 } \@@_tex_primitive:nnn { widowpenalty } { assign_int } { 6 } \@@_tex_primitive:nnn { displaywidowpenalty } { assign_int } { 7 } \@@_tex_primitive:nnn { brokenpenalty } { assign_int } { 8 } \@@_tex_primitive:nnn { binoppenalty } { assign_int } { 9 } \@@_tex_primitive:nnn { relpenalty } { assign_int } { 10 } \@@_tex_primitive:nnn { predisplaypenalty } { assign_int } { 11 } \@@_tex_primitive:nnn { postdisplaypenalty } { assign_int } { 12 } \@@_tex_primitive:nnn { interlinepenalty } { assign_int } { 13 } \@@_tex_primitive:nnn { doublehyphendemerits } { assign_int } { 14 } \@@_tex_primitive:nnn { finalhyphendemerits } { assign_int } { 15 } \@@_tex_primitive:nnn { adjdemerits } { assign_int } { 16 } \@@_tex_primitive:nnn { mag } { assign_int } { 17 } \@@_tex_primitive:nnn { delimiterfactor } { assign_int } { 18 } \@@_tex_primitive:nnn { looseness } { assign_int } { 19 } \@@_tex_primitive:nnn { time } { assign_int } { 20 } \@@_tex_primitive:nnn { day } { assign_int } { 21 } \@@_tex_primitive:nnn { month } { assign_int } { 22 } \@@_tex_primitive:nnn { year } { assign_int } { 23 } \@@_tex_primitive:nnn { showboxbreadth } { assign_int } { 24 } \@@_tex_primitive:nnn { showboxdepth } { assign_int } { 25 } \@@_tex_primitive:nnn { hbadness } { assign_int } { 26 } \@@_tex_primitive:nnn { vbadness } { assign_int } { 27 } \@@_tex_primitive:nnn { pausing } { assign_int } { 28 } \@@_tex_primitive:nnn { tracingonline } { assign_int } { 29 } \@@_tex_primitive:nnn { tracingmacros } { assign_int } { 30 } \@@_tex_primitive:nnn { tracingstats } { assign_int } { 31 } \@@_tex_primitive:nnn { tracingparagraphs } { assign_int } { 32 } \@@_tex_primitive:nnn { tracingpages } { assign_int } { 33 } \@@_tex_primitive:nnn { tracingoutput } { assign_int } { 34 } \@@_tex_primitive:nnn { tracinglostchars } { assign_int } { 35 } \@@_tex_primitive:nnn { tracingcommands } { assign_int } { 36 } \@@_tex_primitive:nnn { tracingrestores } { assign_int } { 37 } \@@_tex_primitive:nnn { uchyph } { assign_int } { 38 } \@@_tex_primitive:nnn { outputpenalty } { assign_int } { 39 } \@@_tex_primitive:nnn { maxdeadcycles } { assign_int } { 40 } \@@_tex_primitive:nnn { hangafter } { assign_int } { 41 } \@@_tex_primitive:nnn { floatingpenalty } { assign_int } { 42 } \@@_tex_primitive:nnn { globaldefs } { assign_int } { 43 } \@@_tex_primitive:nnn { fam } { assign_int } { 44 } \@@_tex_primitive:nnn { escapechar } { assign_int } { 45 } \@@_tex_primitive:nnn { defaulthyphenchar } { assign_int } { 46 } \@@_tex_primitive:nnn { defaultskewchar } { assign_int } { 47 } \@@_tex_primitive:nnn { endlinechar } { assign_int } { 48 } \@@_tex_primitive:nnn { newlinechar } { assign_int } { 49 } \@@_tex_primitive:nnn { language } { assign_int } { 50 } \@@_tex_primitive:nnn { lefthyphenmin } { assign_int } { 51 } \@@_tex_primitive:nnn { righthyphenmin } { assign_int } { 52 } \@@_tex_primitive:nnn { holdinginserts } { assign_int } { 53 } \@@_tex_primitive:nnn { errorcontextlines } { assign_int } { 54 } \@@_tex_primitive:nnn { pdfoutput } { assign_int } { 55 } \@@_tex_primitive_pdf:nnn { compresslevel } { assign_int } { 56 } \@@_tex_primitive_pdf:nnn { decimaldigits } { assign_int } { 57 } \@@_tex_primitive_pdf:nnn { movechars } { assign_int } { 58 } \@@_tex_primitive_pdf:nnn { imageresolution } { assign_int } { 59 } \@@_tex_primitive_pdf:nnn { pkresolution } { assign_int } { 60 } \@@_tex_primitive_pdf:nnn { uniqueresname } { assign_int } { 61 } \@@_tex_primitive_pdf:nnn { optionalwaysusepdfpagebox } { assign_int } { 62 } \@@_tex_primitive_pdf:nnn { optionpdfinclusionerrorlevel } { assign_int } { 63 } \@@_tex_primitive_pdf:nnn { optionpdfminorversion } { assign_int } { 64 } \@@_tex_primitive_pdf:nnn { minorversion } { assign_int } { 64 } \@@_tex_primitive_pdf:nnn { forcepagebox } { assign_int } { 65 } \@@_tex_primitive_pdf:nnn { pagebox } { assign_int } { 66 } \@@_tex_primitive_pdf:nnn { inclusionerrorlevel } { assign_int } { 67 } \@@_tex_primitive_pdf:nnn { gamma } { assign_int } { 68 } \@@_tex_primitive_pdf:nnn { imagegamma } { assign_int } { 69 } \@@_tex_primitive_pdf:nnn { imagehicolor } { assign_int } { 70 } \@@_tex_primitive_pdf:nnn { imageapplygamma } { assign_int } { 71 } \@@_tex_primitive_pdf:nnn { adjustspacing } { assign_int } { 72 } \@@_tex_primitive_pdf:nnn { protrudechars } { assign_int } { 73 } \@@_tex_primitive_pdf:nnn { tracingfonts } { assign_int } { 74 } \@@_tex_primitive_pdf:nnn { objcompresslevel } { assign_int } { 75 } \@@_tex_primitive_pdf:nnn { adjustinterwordglue } { assign_int } { 76 } \@@_tex_primitive_pdf:nnn { prependkern } { assign_int } { 77 } \@@_tex_primitive_pdf:nnn { appendkern } { assign_int } { 78 } \@@_tex_primitive_pdf:nnn { gentounicode } { assign_int } { 79 } \@@_tex_primitive_pdf:nnn { draftmode } { assign_int } { 80 } \@@_tex_primitive_pdf:nnn { inclusioncopyfonts } { assign_int } { 81 } \@@_tex_primitive:nnn { tracingassigns } { assign_int } { 82 } \@@_tex_primitive:nnn { tracinggroups } { assign_int } { 83 } \@@_tex_primitive:nnn { tracingifs } { assign_int } { 84 } \@@_tex_primitive:nnn { tracingscantokens } { assign_int } { 85 } \@@_tex_primitive:nnn { tracingnesting } { assign_int } { 86 } \@@_tex_primitive:nnn { predisplaydirection } { assign_int } { 87 } \@@_tex_primitive:nnn { lastlinefit } { assign_int } { 88 } \@@_tex_primitive:nnn { savingvdiscards } { assign_int } { 89 } \@@_tex_primitive:nnn { savinghyphcodes } { assign_int } { 90 } \@@_tex_primitive:nnn { TeXXeTstate } { assign_int } { 91 } \@@_tex_primitive:nnn { parindent } { assign_dimen } { 0 } \@@_tex_primitive:nnn { mathsurround } { assign_dimen } { 1 } \@@_tex_primitive:nnn { lineskiplimit } { assign_dimen } { 2 } \@@_tex_primitive:nnn { hsize } { assign_dimen } { 3 } \@@_tex_primitive:nnn { vsize } { assign_dimen } { 4 } \@@_tex_primitive:nnn { maxdepth } { assign_dimen } { 5 } \@@_tex_primitive:nnn { splitmaxdepth } { assign_dimen } { 6 } \@@_tex_primitive:nnn { boxmaxdepth } { assign_dimen } { 7 } \@@_tex_primitive:nnn { hfuzz } { assign_dimen } { 8 } \@@_tex_primitive:nnn { vfuzz } { assign_dimen } { 9 } \@@_tex_primitive:nnn { delimitershortfall } { assign_dimen } { 10 } \@@_tex_primitive:nnn { nulldelimiterspace } { assign_dimen } { 11 } \@@_tex_primitive:nnn { scriptspace } { assign_dimen } { 12 } \@@_tex_primitive:nnn { predisplaysize } { assign_dimen } { 13 } \@@_tex_primitive:nnn { displaywidth } { assign_dimen } { 14 } \@@_tex_primitive:nnn { displayindent } { assign_dimen } { 15 } \@@_tex_primitive:nnn { overfullrule } { assign_dimen } { 16 } \@@_tex_primitive:nnn { hangindent } { assign_dimen } { 17 } \@@_tex_primitive:nnn { hoffset } { assign_dimen } { 18 } \@@_tex_primitive:nnn { voffset } { assign_dimen } { 19 } \@@_tex_primitive:nnn { emergencystretch } { assign_dimen } { 20 } \@@_tex_primitive_pdf:nnn { horigin } { assign_dimen } { 21 } \@@_tex_primitive_pdf:nnn { vorigin } { assign_dimen } { 22 } \@@_tex_primitive_pdf:nnn { pagewidth } { assign_dimen } { 23 } \@@_tex_primitive_pdf:nnn { pageheight } { assign_dimen } { 24 } \@@_tex_primitive_pdf:nnn { linkmargin } { assign_dimen } { 25 } \@@_tex_primitive_pdf:nnn { destmargin } { assign_dimen } { 26 } \@@_tex_primitive_pdf:nnn { threadmargin } { assign_dimen } { 27 } \@@_tex_primitive_pdf:nnn { firstlineheight } { assign_dimen } { 28 } \@@_tex_primitive_pdf:nnn { lastlinedepth } { assign_dimen } { 29 } \@@_tex_primitive_pdf:nnn { eachlineheight } { assign_dimen } { 30 } \@@_tex_primitive_pdf:nnn { eachlinedepth } { assign_dimen } { 31 } \@@_tex_primitive_pdf:nnn { ignoreddimen } { assign_dimen } { 32 } \@@_tex_primitive_pdf:nnn { pxdimen } { assign_dimen } { 33 } \@@_tex_primitive:nnn { lineskip } { assign_glue } { 0 } \@@_tex_primitive:nnn { baselineskip } { assign_glue } { 1 } \@@_tex_primitive:nnn { parskip } { assign_glue } { 2 } \@@_tex_primitive:nnn { abovedisplayskip } { assign_glue } { 3 } \@@_tex_primitive:nnn { belowdisplayskip } { assign_glue } { 4 } \@@_tex_primitive:nnn { abovedisplayshortskip } { assign_glue } { 5 } \@@_tex_primitive:nnn { belowdisplayshortskip } { assign_glue } { 6 } \@@_tex_primitive:nnn { leftskip } { assign_glue } { 7 } \@@_tex_primitive:nnn { rightskip } { assign_glue } { 8 } \@@_tex_primitive:nnn { topskip } { assign_glue } { 9 } \@@_tex_primitive:nnn { splittopskip } { assign_glue } { 10 } \@@_tex_primitive:nnn { tabskip } { assign_glue } { 11 } \@@_tex_primitive:nnn { spaceskip } { assign_glue } { 12 } \@@_tex_primitive:nnn { xspaceskip } { assign_glue } { 13 } \@@_tex_primitive:nnn { parfillskip } { assign_glue } { 14 } \@@_tex_primitive:nnn { thinmuskip } { assign_mu_glue } { 15 } \@@_tex_primitive:nnn { medmuskip } { assign_mu_glue } { 16 } \@@_tex_primitive:nnn { thickmuskip } { assign_mu_glue } { 17 } \@@_tex_primitive:nnn { fontdimen } { assign_font_dimen } { 0 } \@@_tex_primitive:nnn { hyphenchar } { assign_font_int } { 0 } \@@_tex_primitive:nnn { skewchar } { assign_font_int } { 1 } \@@_tex_primitive:nnn { lpcode } { assign_font_int } { 2 } \@@_tex_primitive:nnn { rpcode } { assign_font_int } { 3 } \@@_tex_primitive:nnn { efcode } { assign_font_int } { 4 } \@@_tex_primitive:nnn { tagcode } { assign_font_int } { 5 } \@@_tex_primitive_pdf:nnn { noligatures } { assign_font_int } { 6 } \@@_tex_primitive:nnn { knbscode } { assign_font_int } { 7 } \@@_tex_primitive:nnn { stbscode } { assign_font_int } { 8 } \@@_tex_primitive:nnn { shbscode } { assign_font_int } { 9 } \@@_tex_primitive:nnn { knbccode } { assign_font_int } { 10 } \@@_tex_primitive:nnn { knaccode } { assign_font_int } { 11 } \@@_tex_primitive:nnn { spacefactor } { set_aux } { 102 } \@@_tex_primitive:nnn { prevdepth } { set_aux } { 1 } \@@_tex_primitive:nnn { prevgraf } { set_prev_graf } { 0 } \@@_tex_primitive:nnn { pagegoal } { set_page_dimen } { 0 } \@@_tex_primitive:nnn { pagetotal } { set_page_dimen } { 1 } \@@_tex_primitive:nnn { pagestretch } { set_page_dimen } { 2 } \@@_tex_primitive:nnn { pagefilstretch } { set_page_dimen } { 3 } \@@_tex_primitive:nnn { pagefillstretch } { set_page_dimen } { 4 } \@@_tex_primitive:nnn { pagefilllstretch } { set_page_dimen } { 5 } \@@_tex_primitive:nnn { pageshrink } { set_page_dimen } { 6 } \@@_tex_primitive:nnn { pagedepth } { set_page_dimen } { 7 } \@@_tex_primitive:nnn { deadcycles } { set_page_int } { 0 } \@@_tex_primitive:nnn { insertpenalties } { set_page_int } { 1 } \@@_tex_primitive:nnn { interactionmode } { set_page_int } { 2 } \@@_tex_primitive:nnn { wd } { set_box_dimen } { 1 } \@@_tex_primitive:nnn { dp } { set_box_dimen } { 2 } \@@_tex_primitive:nnn { ht } { set_box_dimen } { 3 } \@@_tex_primitive:nnn { parshape } { set_shape } { 0 } \@@_tex_primitive:nnn { interlinepenalties } { set_shape } { 1 } \@@_tex_primitive:nnn { clubpenalties } { set_shape } { 2 } \@@_tex_primitive:nnn { widowpenalties } { set_shape } { 3 } \@@_tex_primitive:nnn { displaywidowpenalties } { set_shape } { 4 } \@@_tex_primitive:nnn { catcode } { def_code } { 0 } \@@_tex_primitive:nnn { lccode } { def_code } { 256 } \@@_tex_primitive:nnn { uccode } { def_code } { 512 } \@@_tex_primitive:nnn { sfcode } { def_code } { 768 } \@@_tex_primitive:nnn { mathcode } { def_code } { 1024 } \@@_tex_primitive:nnn { delcode } { def_code } { 1591 } \@@_tex_primitive:nnn { textfont } { def_family } { -48 } \@@_tex_primitive:nnn { scriptfont } { def_family } { -32 } \@@_tex_primitive:nnn { scriptscriptfont } { def_family } { -16 } \@@_tex_primitive:nnn { nullfont } { set_font } { 0 } \@@_tex_primitive:nnn { font } { def_font } { 0 } \@@_tex_primitive:nnn { count } { register } { 1 000 000 } \@@_tex_primitive:nnn { dimen } { register } { 2 000 000 } \@@_tex_primitive:nnn { skip } { register } { 3 000 000 } \@@_tex_primitive:nnn { muskip } { register } { 4 000 000 } \@@_tex_primitive:nnn { advance } { advance } { 0 } \@@_tex_primitive:nnn { multiply } { multiply } { 0 } \@@_tex_primitive:nnn { divide } { divide } { 0 } \@@_tex_primitive:nnn { long } { prefix } { 1 } \@@_tex_primitive:nnn { outer } { prefix } { 2 } \@@_tex_primitive:nnn { global } { prefix } { 4 } \@@_tex_primitive:nnn { protected } { prefix } { 8 } \@@_tex_primitive:nnn { let } { let } { 0 } \@@_tex_primitive:nnn { futurelet } { let } { 1 } \@@_tex_primitive:nnn { chardef } { shorthand_def } { 0 } \@@_tex_primitive:nnn { mathchardef } { shorthand_def } { 1 } \@@_tex_primitive:nnn { countdef } { shorthand_def } { 2 } \@@_tex_primitive:nnn { dimendef } { shorthand_def } { 3 } \@@_tex_primitive:nnn { skipdef } { shorthand_def } { 4 } \@@_tex_primitive:nnn { muskipdef } { shorthand_def } { 5 } \@@_tex_primitive:nnn { toksdef } { shorthand_def } { 6 } \@@_tex_primitive:nnn { read } { read_to_cs } { 0 } \@@_tex_primitive:nnn { readline } { read_to_cs } { 1 } \@@_tex_primitive:nnn { def } { def } { 0 } \@@_tex_primitive:nnn { gdef } { def } { 1 } \@@_tex_primitive:nnn { edef } { def } { 2 } \@@_tex_primitive:nnn { xdef } { def } { 3 } \@@_tex_primitive:nnn { setbox } { set_box } { 0 } \@@_tex_primitive:nnn { hyphenation } { hyph_data } { 0 } \@@_tex_primitive:nnn { patterns } { hyph_data } { 1 } \@@_tex_primitive:nnn { batchmode } { set_interaction } { 0 } \@@_tex_primitive:nnn { nonstopmode } { set_interaction } { 1 } \@@_tex_primitive:nnn { scrollmode } { set_interaction } { 2 } \@@_tex_primitive:nnn { errorstopmode } { set_interaction } { 3 } \@@_tex_primitive:nnn { letterspacefont } { letterspace_font } { 0 } \@@_tex_primitive_pdf:nnn { copyfont } { pdf_copy_font } { 0 } \@@_tex_primitive:nnn { undefined } { undefined_cs } { 0 } \@@_tex_primitive:nnn { ndefined } { undefined_cs } { 0 } \@@_tex_primitive:nnn { expandafter } { expand_after } { 0 } \@@_tex_primitive:nnn { unless } { expand_after } { 1 } \@@_tex_primitive_pdf:nnn { primitive } { no_expand } { 1 } \@@_tex_primitive:nnn { noexpand } { no_expand } { 0 } \@@_tex_primitive:nnn { input } { input } { 0 } \@@_tex_primitive:nnn { endinput } { input } { 1 } \@@_tex_primitive:nnn { scantokens } { input } { 2 } \@@_tex_primitive:nnn { if } { if_test } { 0 } \@@_tex_primitive:nnn { ifcat } { if_test } { 1 } \@@_tex_primitive:nnn { ifnum } { if_test } { 2 } \@@_tex_primitive:nnn { ifdim } { if_test } { 3 } \@@_tex_primitive:nnn { ifodd } { if_test } { 4 } \@@_tex_primitive:nnn { ifvmode } { if_test } { 5 } \@@_tex_primitive:nnn { ifhmode } { if_test } { 5 } \@@_tex_primitive:nnn { ifmmode } { if_test } { 5 } \@@_tex_primitive:nnn { ifinner } { if_test } { 5 } \@@_tex_primitive:nnn { ifvoid } { if_test } { 9 } \@@_tex_primitive:nnn { ifhbox } { if_test } { 9 } \@@_tex_primitive:nnn { ifvbox } { if_test } { 9 } \@@_tex_primitive:nnn { ifx } { if_test } { 12 } \@@_tex_primitive:nnn { ifeof } { if_test } { 13 } \@@_tex_primitive:nnn { iftrue } { if_test } { 14 } \@@_tex_primitive:nnn { iffalse } { if_test } { 15 } \@@_tex_primitive:nnn { ifcase } { if_test } { 16 } \@@_tex_primitive:nnn { ifdefined } { if_test } { 17 } \@@_tex_primitive:nnn { ifcsname } { if_test } { 18 } \@@_tex_primitive:nnn { iffontchar } { if_test } { 19 } \@@_tex_primitive:nnn { ifincsname } { if_test } { 20 } \@@_tex_primitive:nnn { ifprimitive } { if_test } { 21 } \@@_tex_primitive:nnn { ifpdfprimitive } { if_test } { 21 } \@@_tex_primitive:nnn { ifabsnum } { if_test } { 22 } \@@_tex_primitive:nnn { ifpdfabsnum } { if_test } { 22 } \@@_tex_primitive:nnn { ifabsdim } { if_test } { 23 } \@@_tex_primitive:nnn { ifpdfabsdim } { if_test } { 23 } \bool_if:nT { \sys_if_engine_ptex_p: || \sys_if_engine_uptex_p: } { \@@_tex_primitive:nnn { iftdir } { if_test } { 5 } \@@_tex_primitive:nnn { ifydir } { if_test } { 5 } \@@_tex_primitive:nnn { ifddir } { if_test } { 5 } \@@_tex_primitive:nnn { ifmdir } { if_test } { 5 } \@@_tex_primitive:nnn { iftbox } { if_test } { 9 } \@@_tex_primitive:nnn { ifybox } { if_test } { 9 } \@@_tex_primitive:nnn { ifdbox } { if_test } { 9 } \@@_tex_primitive:nnn { ifmbox } { if_test } { 9 } \@@_tex_primitive:nnn { ifjfont } { if_test } { 24 } \@@_tex_primitive:nnn { iftfont } { if_test } { 24 } } \@@_tex_primitive:nnn { fi } { fi_or_else } { 2 } \@@_tex_primitive:nnn { else } { fi_or_else } { 3 } \@@_tex_primitive:nnn { or } { fi_or_else } { 4 } \@@_tex_primitive:nnn { csname } { cs_name } { 0 } \@@_tex_primitive:nnn { lastnamedcs } { cs_name } { 1 } \@@_tex_primitive:nnn { number } { convert } { 0 } \@@_tex_primitive:nnn { romannumeral } { convert } { 1 } \@@_tex_primitive:nnn { string } { convert } { 2 } \@@_tex_primitive:nnn { meaning } { convert } { 3 } \@@_tex_primitive:nnn { fontname } { convert } { 4 } \@@_tex_primitive:nnn { eTeXrevision } { convert } { 5 } \@@_tex_primitive_pdf:nnn { texrevision } { convert } { 6 } \@@_tex_primitive_pdf:nnn { texbanner } { convert } { 7 } \@@_tex_primitive:nnn { pdffontname } { convert } { 8 } \@@_tex_primitive_pdf:nnn { fontobjnum } { convert } { 9 } \@@_tex_primitive_pdf:nnn { fontsize } { convert } { 10 } \@@_tex_primitive_pdf:nnn { pageref } { convert } { 11 } \@@_tex_primitive_pdf:nnn { xformname } { convert } { 12 } \@@_tex_primitive_pdf:nnn { escapestring } { convert } { 13 } \@@_tex_primitive_pdf:nnn { escapename } { convert } { 14 } \@@_tex_primitive:nnn { leftmarginkern } { convert } { 15 } \@@_tex_primitive:nnn { rightmarginkern } { convert } { 16 } \@@_tex_primitive_pdf:nnn { strcmp } { convert } { 17 } \@@_tex_primitive_pdf:nnn { colorstackinit } { convert } { 18 } \@@_tex_primitive_pdf:nnn { escapehex } { convert } { 19 } \@@_tex_primitive_pdf:nnn { unescapehex } { convert } { 20 } \@@_tex_primitive_pdf:nnn { creationdate } { convert } { 21 } \@@_tex_primitive_pdf:nnn { filemoddate } { convert } { 22 } \@@_tex_primitive_pdf:nnn { filesize } { convert } { 23 } \@@_tex_primitive_pdf:nnn { mdfivesum } { convert } { 24 } \@@_tex_primitive_pdf:nnn { filedump } { convert } { 25 } \@@_tex_primitive_pdf:nnn { match } { convert } { 26 } \@@_tex_primitive_pdf:nnn { lastmatch } { convert } { 27 } \@@_tex_primitive_pdf:nnn { uniformdeviate } { convert } { 28 } \@@_tex_primitive_pdf:nnn { normaldeviate } { convert } { 29 } \@@_tex_primitive_pdf:nnn { insertht } { convert } { 30 } \@@_tex_primitive_pdf:nnn { ximagebbox } { convert } { 31 } \@@_tex_primitive:nnn { jobname } { convert } { 32 } \sys_if_engine_luatex:T { \@@_tex_primitive:nnn { directlua } { convert } { 33 } } \@@_tex_primitive:nnn { expanded } { convert } { 34 } \sys_if_engine_luatex:T { \@@_tex_primitive:nnn { luaescapestring } { convert } { 35 } } \bool_if:nT { \sys_if_engine_xetex_p: || \sys_if_engine_ptex_p: || \sys_if_engine_uptex_p: } { \@@_tex_primitive:nnn { Ucharcat } { convert } { 40 } } \@@_tex_primitive:nnn { the } { the } { 0 } \@@_tex_primitive:nnn { unexpanded } { the } { 1 } \@@_tex_primitive:nnn { detokenize } { the } { 5 } \@@_tex_primitive:nnn { topmark } { top_bot_mark } { 0 } \@@_tex_primitive:nnn { firstmark } { top_bot_mark } { 1 } \@@_tex_primitive:nnn { botmark } { top_bot_mark } { 2 } \@@_tex_primitive:nnn { splitfirstmark } { top_bot_mark } { 3 } \@@_tex_primitive:nnn { splitbotmark } { top_bot_mark } { 4 } \@@_tex_primitive:nnn { topmarks } { top_bot_mark } { 5 } \@@_tex_primitive:nnn { firstmarks } { top_bot_mark } { 6 } \@@_tex_primitive:nnn { botmarks } { top_bot_mark } { 7 } \@@_tex_primitive:nnn { splitfirstmarks } { top_bot_mark } { 8 } \@@_tex_primitive:nnn { splitbotmarks } { top_bot_mark } { 9 } % \end{macrocode} % % \subsection{Get next token} % % We define here two functions which fetch the next token in the token % list. % \begin{itemize} % \item \cs{@@_get_next:} sets \cs{l_@@_head_gtl}, \cs{l_@@_head_token}, % and if possible \cs{l_@@_head_tl} (otherwise it is cleared). % \item \cs{@@_get_token:} additionally sets \cs{l_@@_head_cmd_int} and % \cs{l_@@_head_char_int}. % \end{itemize} % The latter is based on \cs{@@_set_cmd:} which derives the % \cs{l_@@_head_cmd_int} and \cs{l_@@_head_char_int} from % \cs{l_@@_head_token}. % % \begin{macro}{\@@_get_next:} % \begin{macro}{\@@_get_next_aux:w} % If the input is empty, insert a frozen \tn{relax} (the alternative % would be either to grab a token in the input stream after % \cs{unravel}, which is tough, or simply produce an error and exit; % perhaps this should be configurable). % Then remove the first token in the input, and store it in % \cs{l_@@_head_gtl}. Set \cs{l_@@_head_token} equal in meaning to % that first token. Then set \cs{l_@@_head_tl} to contain the token, % unless it is a begin-group or end-group character, in which case % this token list is emptied. % \begin{macrocode} \cs_new_protected:Npn \@@_get_next: { \@@_input_if_empty:TF { \@@_error:nnnnn { runaway-unravel } { } { } { } { } \@@_back_input_gtl:N \c_@@_frozen_relax_gtl } { } \@@_input_gpop:N \l_@@_head_gtl \gtl_head_do:NN \l_@@_head_gtl \@@_get_next_aux:w \gtl_if_tl:NTF \l_@@_head_gtl { \tl_set:Ne \l_@@_head_tl { \gtl_head:N \l_@@_head_gtl } \token_if_eq_meaning:NNT \l_@@_head_token \@@_special_relax: \@@_get_next_notexpanded: } { \tl_clear:N \l_@@_head_tl } } \cs_new_protected:Npn \@@_get_next_aux:w { \cs_set_eq:NN \l_@@_head_token } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\@@_get_next_notexpanded:} % \begin{macro}{\@@_notexpanded_test:w, \@@_notexpanded_expand:nN, \@@_notexpanded_expand:NN} % At this point we have likely encountered a special \tn{relax} marker % that we use to mark cases where \tn{noexpand} acts on a control % sequence or an active character. To make sure of that check the % control sequence has the form \cs{notexpanded:\ldots{}}. Since we % don't know the escape character we must use \cs{cs_to_str:N}, but % that function is not meant for active characters and has a runaway % argument if its argument is a space (active since we know its % meaning is the special \tn{relax}). To avoid the runaway we include % an arbitrary delimiter~|Z|. If the token in \cs{l_@@_head_tl} is % not \cs{notexpanded:\ldots{}} we do nothing. Otherwise % \cs{@@_notexpanded_expand:n} reconstructs the token that was hit % with \tn{noexpand} (an active character if the argument is a single % character) and do the job of \cs{@@_get_next:}, setting % \cs{l_@@_head_token} to the special \tn{relax} marker for expandable % commands, as \tn{noexpand} would. % \begin{macrocode} \cs_set_protected:Npn \@@_tmp:w #1 { \cs_new_protected:Npn \@@_get_next_notexpanded: { \tl_if_eq:onTF { \l_@@_head_tl } { \@@_unravel_marker: } { \@@_get_next_marker: } { \exp_args:NNe \use:nn \@@_notexpanded_test:w { \scan_stop: \exp_after:wN \cs_to_str:N \l_@@_head_tl Z } \q_mark \@@_notexpanded_expand:n #1 Z \q_mark \use_none:n \q_stop } } \cs_new_protected:Npn \@@_notexpanded_test:w ##1 #1 ##2 Z \q_mark ##3##4 \q_stop { ##3 {##2} } } \exp_args:Ne \@@_tmp:w { \scan_stop: \tl_to_str:n { notexpanded: } } \group_begin: \char_set_catcode_active:n { 0 } \cs_new_protected:Npn \@@_notexpanded_expand:n #1 { \exp_args:Ne \tl_if_empty:nTF { \str_tail:n {#1} } { \group_begin: \char_set_lccode:nn { 0 } { `#1 } \tex_lowercase:D { \group_end: \@@_notexpanded_expand:N ^^@ } } { \group_begin: \exp_args:NNc \group_end: \@@_notexpanded_expand:N { \use_none:n #1 } } } \group_end: \cs_new_protected:Npn \@@_notexpanded_expand:N #1 { \gtl_set:Nn \l_@@_head_gtl {#1} \tl_set:Nn \l_@@_head_tl {#1} \cs_set_eq:NN \l_@@_head_token \@@_special_relax: } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\@@_get_next_marker:} % This is used to deal with nested unravel. % \begin{macrocode} \cs_new_protected:Npn \@@_get_next_marker: { \@@_get_next: \tl_if_eq:onTF \l_@@_head_tl { \@@:nn } { \@@_error:neeee { nested-unravel } { } { } { } { } } { \@@_error:neeee { internal } { marker~unknown } { } { } { } } \@@_input_gpop_item:NF \l_@@_argi_tl { \@@_error:neeee { internal } { marker~1 } { } { } { } } \@@_input_gpop_item:NF \l_@@_argii_tl { \@@_error:neeee { internal } { marker~2 } { } { } { } } \exp_args:Nno \keys_set:nn { unravel } \l_@@_argi_tl \exp_args:Ne \@@_back_input:n { \exp_not:N \exp_not:n { \exp_not:o \l_@@_argii_tl } } \@@_get_next: } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_get_token:} % Call \cs{@@_get_next:} to set \cs{l_@@_head_gtl}, \cs{l_@@_head_tl} % and \cs{l_@@_head_token}, then call \cs{@@_set_cmd:} to set % \cs{l_@@_head_cmd_int} and \cs{l_@@_head_char_int}. % \begin{macrocode} \cs_new_protected:Npn \@@_get_token: { \@@_get_next: \@@_set_cmd: } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_set_cmd:} % After the call to \cs{@@_get_next:}, we find the command code % \cs{l_@@_head_cmd_int} and the character code % \cs{l_@@_head_char_int}, based only on \cs{l_@@_head_token}. First % set \cs{l_@@_head_meaning_tl} from the \tn{meaning} of the first % token. If the corresponding primitive exists, use the information % to set the two integers. If the token is expandable, it can either % be a macro or be a primitive that we somehow do not know % (\emph{e.g.}, an expandable \XeTeX{} or \LuaTeX{} primitive % perhaps). Otherwise, it can be a control sequence or a character. % \begin{macrocode} \cs_new_protected:Npn \@@_set_cmd: { \@@_set_cmd_aux_meaning: \@@_set_cmd_aux_primitive:oTF { \l_@@_head_meaning_tl } { } { \@@_token_if_expandable:NTF \l_@@_head_token { \token_if_macro:NTF \l_@@_head_token { \@@_set_cmd_aux_macro: } { \@@_set_cmd_aux_unknown: } } { \token_if_cs:NTF \l_@@_head_token { \@@_set_cmd_aux_cs: } { \@@_set_cmd_aux_char: } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_set_cmd_aux_meaning:} % \begin{macro}[EXP]{\@@_set_cmd_aux_meaning:w} % Remove the leading escape character (\cs{@@_strip_escape:w} takes % care of special cases there) from the \tn{meaning} of the first % token, then remove anything after the first~|:|, which is present % for macros, for marks, and for that character too. For any % primitive except \tn{nullfont}, this leaves the primitive's name. % \begin{macrocode} \cs_new_protected:Npn \@@_set_cmd_aux_meaning: { \tl_set:Ne \l_@@_head_meaning_tl { \exp_after:wN \@@_strip_escape:w \token_to_meaning:N \l_@@_head_token \tl_to_str:n { : } } \tl_set:Ne \l_@@_head_meaning_tl { \exp_after:wN \@@_set_cmd_aux_meaning:w \l_@@_head_meaning_tl \q_stop } } \use:e { \cs_new:Npn \exp_not:N \@@_set_cmd_aux_meaning:w #1 \token_to_str:N : #2 \exp_not:N \q_stop {#1} } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro} % {\@@_set_cmd_aux_primitive:nTF, \@@_set_cmd_aux_primitive:oTF} % \begin{macro}{\@@_set_cmd_aux_primitive:nn} % Test if there is any information about the given (cleaned-up) % \tn{meaning}. If there is, use that as the command and character % integers. % \begin{macrocode} \cs_new_protected:Npn \@@_set_cmd_aux_primitive:nTF #1#2 { \cs_if_exist:cTF { c_@@_tex_#1_tl } { \exp_last_unbraced:Nv \@@_set_cmd_aux_primitive:nn { c_@@_tex_#1_tl } #2 } } \cs_generate_variant:Nn \@@_set_cmd_aux_primitive:nTF { o } \cs_new_protected:Npn \@@_set_cmd_aux_primitive:nn #1#2 { \int_set:Nn \l_@@_head_cmd_int {#1} \int_set:Nn \l_@@_head_char_int {#2} } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\@@_set_cmd_aux_macro:} % The token is a macro. There is no need to determine whether the % macro is long/outer. % \begin{macrocode} \cs_new_protected:Npn \@@_set_cmd_aux_macro: { \int_set:Nn \l_@@_head_cmd_int { \@@_tex_use:n { call } } \int_zero:N \l_@@_head_char_int } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_set_cmd_aux_unknown:} % Complain about an unknown primitive, and consider it as if it were % \tn{relax}. % \begin{macrocode} \sys_if_engine_luatex:TF { \cs_new_protected:Npn \@@_set_cmd_aux_unknown: { \exp_last_unbraced:NV \@@_set_cmd_aux_primitive:nn \c_@@_tex_relax_tl \@@_tl_if_in:ooTF \l_@@_head_meaning_tl { \tl_to_str:n { xpandable~luacall } } { } { \@@_error:neeee { unknown-primitive } { \l_@@_head_meaning_tl } { } { } { } } } } { \cs_new_protected:Npn \@@_set_cmd_aux_unknown: { \exp_last_unbraced:NV \@@_set_cmd_aux_primitive:nn \c_@@_tex_relax_tl \@@_error:neeee { unknown-primitive } { \l_@@_head_meaning_tl } { } { } { } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_set_cmd_aux_cs:} % If the \tn{meaning} contains \verb*|elect font|, the control % sequence is \tn{nullfont} or similar (note that we do not search for % \verb*|select font|, as the code to trim the escape character from % the meaning may have removed the leading~|s|). Otherwise, we expect % the \tn{meaning} to be \tn{char} or \tn{mathchar} or similar followed by % |"|~and an uppercase hexadecimal number, or one of \tn{count}, % \tn{dimen}, \tn{skip}, \tn{muskip} or \tn{toks} followed by a % decimal number. % \begin{macrocode} \cs_new_protected:Npn \@@_set_cmd_aux_cs: { \@@_tl_if_in:ooTF \l_@@_head_meaning_tl { \tl_to_str:n { elect~font } } { \exp_last_unbraced:NV \@@_set_cmd_aux_primitive:nn \c_@@_tex_nullfont_tl } { \@@_set_cmd_aux_numeric: } } % \end{macrocode} % \end{macro} % % \begin{macro} % { % \@@_set_cmd_aux_numeric:, % \@@_set_cmd_aux_numeric:w, % \@@_set_cmd_aux_given:n % } % \begin{macro}[rEXP]{\@@_set_cmd_aux_numeric:N} % Insert \cs{q_mark} before the first non-letter (in fact, anything % less than~|A|) in the \tn{meaning} by looping one character at a % time (skipping spaces, but there should be none). We expect the % first part to be |char| or~|mathchar| (or |kchar| or |omathchar| in (u)p\TeX{}), % or one of |count|, |dimen|, % |skip|, |muskip|, or |toks|. In the first two (three) cases, the command is % |char_given| or |math_given|. It is otherwise identical to the % corresponding primitive (\tn{count} \emph{etc.}). We then keep % track of the associated number (part after \cs{q_mark}) in % \cs{l_@@_head_char_int}. For unknown non-expandable primitives, % assuming that their meaning consists solely of letters, the % \cs{q_mark} is inserted at their end, and is followed by~|+0|, so % nothing breaks. % \begin{macrocode} \cs_new_protected:Npn \@@_set_cmd_aux_numeric: { \tl_set:Ne \l_@@_tmpa_tl { \exp_after:wN \@@_set_cmd_aux_numeric:N \l_@@_head_meaning_tl + 0 } \exp_after:wN \@@_set_cmd_aux_numeric:w \l_@@_tmpa_tl \q_stop } \cs_new:Npn \@@_set_cmd_aux_numeric:N #1 { \if_int_compare:w `#1 < `A \exp_stop_f: \exp_not:N \q_mark \exp_after:wN \use_i:nn \fi: #1 \@@_set_cmd_aux_numeric:N } \cs_new_protected:Npn \@@_set_cmd_aux_numeric:w #1 \q_mark #2 \q_stop { \str_case:nnF {#1} { { char } { \@@_set_cmd_aux_given:n { char_given } } { kchar } { \@@_set_cmd_aux_given:n { char_given } } { mathchar } { \@@_set_cmd_aux_given:n { math_given } } { omathchar } { \@@_set_cmd_aux_given:n { math_given } } } { \@@_set_cmd_aux_primitive:nTF {#1} { } { \@@_set_cmd_aux_unknown: } \int_add:Nn \l_@@_head_char_int { 100 000 } } \int_add:Nn \l_@@_head_char_int {#2} } \cs_new_protected:Npn \@@_set_cmd_aux_given:n #1 { \int_set:Nn \l_@@_head_cmd_int { \@@_tex_use:n {#1} } \int_zero:N \l_@@_head_char_int } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\@@_set_cmd_aux_char:} % \begin{macro}{\@@_set_cmd_aux_char:w} % At this point, the \tn{meaning} token list has been shortened by the % code meant to remove the escape character. We thus set it again to % the \tn{meaning} of the leading token. The command is then the % first word (delimited by a space) of the \tn{meaning}, followed by % |_char|, except for category other, where we use |other_char|. For % the character code, there is a need to expand % \cs{@@_token_to_char:N} before placing~|`|. % \begin{macrocode} \cs_new_protected:Npn \@@_set_cmd_aux_char: { \tl_set:Ne \l_@@_head_meaning_tl { \token_to_meaning:N \l_@@_head_token } \token_if_eq_catcode:NNT \l_@@_head_token \c_catcode_other_token { \tl_set:Nn \l_@@_head_meaning_tl { other~ } } \exp_after:wN \@@_set_cmd_aux_char:w \l_@@_head_meaning_tl \q_stop \exp_args:NNe \int_set:Nn \l_@@_head_char_int { ` \@@_token_to_char:N \l_@@_head_token } } \cs_new_protected:Npn \@@_set_cmd_aux_char:w #1 ~ #2 \q_stop { \int_set:Nn \l_@@_head_cmd_int { \@@_tex_use:n { #1_char } } } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Manipulating the input} % % \subsubsection{Elementary operations} % % \begin{macro}[rEXP]{\@@_input_to_str:} % Map \cs{gtl_to_str:c} through the input stack. % \begin{macrocode} \cs_new:Npn \@@_input_to_str: { \int_step_function:nnnN \g_@@_input_int { -1 } { 1 } \@@_input_to_str_aux:n } \cs_new:Npn \@@_input_to_str_aux:n #1 { \gtl_to_str:c { g_@@_input_#1_gtl } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_input_if_empty:TF} % If the input stack is empty, the input contains no token. % Otherwise, check the top of the stack for tokens: if there are, then % the input is non-empty, and if there are none, then we get rid of % the top of stack and loop. % \begin{macrocode} \cs_new_protected:Npn \@@_input_if_empty:TF { \int_compare:nNnTF \g_@@_input_int = 0 { \use_i:nn } { \gtl_if_empty:cTF { g_@@_input_ \int_use:N \g_@@_input_int _gtl } { \int_gdecr:N \g_@@_input_int \@@_input_if_empty:TF } { \@@_input_split: \use_ii:nn } } } % \end{macrocode} % \end{macro} % % ^^A todo: profiling to know if \@@_input_split: is beneficial. % % \begin{macro}{\@@_input_split:} % If the input is completely flat, and is a token list starting with % an |N|-type token, try to unflatten it by splitting at each % occurence of that first token. % \begin{macrocode} \cs_new_protected:Npn \@@_input_split: { \int_compare:nNnT \g_@@_input_int = 1 { \exp_args:Nc \@@_input_split_aux:N { g_@@_input_1_gtl } } } \cs_new_protected:Npn \@@_input_split_aux:N #1 { \gtl_if_tl:NT #1 { \gtl_if_head_is_N_type:NT #1 { \tl_set:Ne \l_@@_input_tmpa_tl { \gtl_left_tl:N #1 } \exp_args:NNe \use:nn \@@_input_split_auxii:N { \tl_head:N \l_@@_input_tmpa_tl } } } } \cs_new_protected:Npn \@@_input_split_auxii:N #1 { \token_if_parameter:NF #1 { \tl_replace_all:Nnn \l_@@_input_tmpa_tl {#1} { \@@_input_split_end: \@@_input_split_auxiii:w #1 } \group_begin: \cs_set:Npn \@@_input_split_auxiii:w ##1 \@@_input_split_end: { + 1 } \int_gset:Nn \g_@@_input_int { 0 \l_@@_input_tmpa_tl \@@_input_split_end: } \group_end: \int_gset_eq:NN \g_@@_input_tmpa_int \g_@@_input_int \l_@@_input_tmpa_tl \@@_input_split_end: } } \cs_new:Npn \@@_input_split_end: { } \cs_new_protected:Npn \@@_input_split_auxiii:w #1 \@@_input_split_end: { \gtl_gclear_new:c { g_@@_input_ \int_use:N \g_@@_input_tmpa_int _gtl } \gtl_gset:cn { g_@@_input_ \int_use:N \g_@@_input_tmpa_int _gtl } {#1} \int_gdecr:N \g_@@_input_tmpa_int } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_input_gset:n} % At first, all of the input is in the same~\texttt{gtl}. % \begin{macrocode} \cs_new_protected:Npn \@@_input_gset:n { \int_gzero:N \g_@@_input_int \@@_back_input:n } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_input_get:N} % \begin{macrocode} \cs_new_protected:Npn \@@_input_get:N #1 { \@@_input_if_empty:TF { \gtl_set:Nn #1 { \q_no_value } } { \gtl_get_left:cN { g_@@_input_ \int_use:N \g_@@_input_int _gtl } #1 } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_input_get_left:N, \@@_input_get_left_aux:nN} % \begin{variable}{\l_@@_input_get_left_tl} % \begin{macrocode} \tl_new:N \l_@@_input_get_left_tl \cs_new_protected:Npn \@@_input_get_left:N #1 { \tl_clear:N #1 \exp_args:NV \@@_input_get_left_aux:nN \g_@@_input_int #1 } \cs_new_protected:Npn \@@_input_get_left_aux:nN #1#2 { \int_compare:nNnF {#1} = 0 { \tl_set:Ne \l_@@_input_get_left_tl { \gtl_left_tl:c { g_@@_input_#1_gtl } } \tl_concat:NNN #2 #2 \l_@@_input_get_left_tl \gtl_if_tl:cT { g_@@_input_#1_gtl } { \exp_args:Nf \@@_input_get_left_aux:nN { \int_eval:n { #1 - 1 } } #2 } } } % \end{macrocode} % \end{variable} % \end{macro} % % \begin{macro}{\@@_input_gpop:N} % Call \cs{@@_input_if_empty:TF} to remove empty levels from the input % stack, then extract the first token from the left-most non-empty % level. % \begin{macrocode} \cs_new_protected:Npn \@@_input_gpop:N #1 { \@@_input_if_empty:TF { \gtl_set:Nn #1 { \q_no_value } } { \gtl_gpop_left:cN { g_@@_input_ \int_use:N \g_@@_input_int _gtl } #1 } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_input_merge:} % Merge the top two levels of input. This requires, but does not % check, that \cs{g_@@_input_int} is at least~$2$. % \begin{macrocode} \cs_new_protected:Npn \@@_input_merge: { \int_gdecr:N \g_@@_input_int \gtl_gconcat:ccc { g_@@_input_ \int_use:N \g_@@_input_int _gtl } { g_@@_input_ \int_eval:n { \g_@@_input_int + 1 } _gtl } { g_@@_input_ \int_use:N \g_@@_input_int _gtl } \gtl_gclear:c { g_@@_input_ \int_eval:n { \g_@@_input_int + 1 } _gtl } } % \end{macrocode} % \end{macro} % % \begin{macro}[TF]{\@@_input_gpop_item:N} % \begin{macro}{\@@_input_gpop_item_aux:NN} % If there is no input, we cannot pop an item. Othewise, try to pop % from the top of the input stack. If this succeeds, or if this % failed and the top of stack has extra end-group characters, or if % the input stack contains only the top-most item, then the answer % given by \cs{gtl_gpop_left_item:NNTF} is the correct one, which we % return. Otherwise, merge the top two levels and repeat. % \begin{macrocode} \prg_new_protected_conditional:Npnn \@@_input_gpop_item:N #1 { F } { \int_compare:nNnTF \g_@@_input_int = 0 { \prg_return_false: } { \exp_args:Nc \@@_input_gpop_item_aux:NN { g_@@_input_ \int_use:N \g_@@_input_int _gtl } #1 } } \cs_new_protected:Npn \@@_input_gpop_item_aux:NN #1#2 { \gtl_gpop_left_item:NNTF #1#2 { \prg_return_true: } { \int_compare:nNnTF { \gtl_extra_end:N #1 } > 0 { \prg_return_false: } { \int_compare:nNnTF \g_@@_input_int = 1 { \prg_return_false: } { \@@_input_merge: \exp_args:Nc \@@_input_gpop_item_aux:NN { g_@@_input_ \int_use:N \g_@@_input_int _gtl } #2 } } } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\@@_input_gpop_tl:N} % \begin{macrocode} \cs_new_protected:Npn \@@_input_gpop_tl:N #1 { \tl_clear:N #1 \@@_input_gpop_tl_aux:N #1 } \cs_new_protected:Npn \@@_input_gpop_tl_aux:N #1 { \int_compare:nNnF \g_@@_input_int = 0 { \exp_args:Nc \@@_input_gpop_tl_aux:NN { g_@@_input_ \int_use:N \g_@@_input_int _gtl } #1 } } \cs_new_protected:Npn \@@_input_gpop_tl_aux:NN #1#2 { \gtl_if_tl:NTF #1 { \tl_put_right:Ne #2 { \gtl_left_tl:N #1 } \gtl_gclear:N #1 \int_gdecr:N \g_@@_input_int \@@_input_gpop_tl_aux:N #2 } { \int_compare:nNnTF \g_@@_input_int > 1 { \int_compare:nNnTF { \gtl_extra_end:N #1 } > 0 } { \use_i:nn } { \tl_put_right:Ne #2 { \gtl_left_tl:N #1 } \gtl_gpop_left_tl:N #1 } { \@@_input_merge: \@@_input_gpop_tl_aux:N #2 } } } % \end{macrocode} % \end{macro} % % \begin{macro}[TF]{\@@_input_if_head_is_group_begin:} % Call \cs{@@_input_if_empty:TF} to remove empty levels from the input % stack, then check if the left-most non-empty level starts with an % explicit begin-group character token. % \begin{macrocode} \prg_new_protected_conditional:Npnn \@@_input_if_head_is_group_begin: { T , F , TF } { \@@_input_if_empty:TF { \prg_return_false: } { \exp_args:Nc \gtl_if_head_is_group_begin:NTF { g_@@_input_ \int_use:N \g_@@_input_int _gtl } { \prg_return_true: } { \prg_return_false: } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_back_input:n, \@@_back_input:V, \@@_back_input:o} % Insert a token list back into the input. Use \cs{gtl_gclear_new:c} % to define the gtl variable if necessary: this happens whenever a % new largest value of \cs{g_@@_input_int} is reached. % \begin{macrocode} \cs_new_protected:Npn \@@_back_input:n { \int_gincr:N \g_@@_input_int \gtl_gclear_new:c { g_@@_input_ \int_use:N \g_@@_input_int _gtl } \gtl_gset:cn { g_@@_input_ \int_use:N \g_@@_input_int _gtl } } \cs_generate_variant:Nn \@@_back_input:n { V , o } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_back_input_gtl:N} % Insert a generalized token list back into the input. % \begin{macrocode} \cs_new_protected:Npn \@@_back_input_gtl:N #1 { \gtl_if_tl:NTF #1 { \exp_args:Ne \@@_back_input:n { \gtl_left_tl:N #1 } } { \gtl_gconcat:cNc { g_@@_input_ \int_use:N \g_@@_input_int _gtl } #1 { g_@@_input_ \int_use:N \g_@@_input_int _gtl } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_back_input:} % Insert the last token read back into the input stream. % \begin{macrocode} \cs_new_protected:Npn \@@_back_input: { \@@_back_input_gtl:N \l_@@_head_gtl } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_back_input_tl_o:} % Insert the \cs{l_@@_head_tl} (may or may not be the last token read) % back into the input stream, after expanding it once. Then print % some diagnostic information. % \begin{macrocode} \cs_new_protected:Npn \@@_back_input_tl_o: { \tl_set:Ne \l_@@_tmpa_tl { \exp_args:NV \exp_not:o \l_@@_head_tl } \@@_back_input:V \l_@@_tmpa_tl \@@_print_expansion:e { \tl_to_str:N \l_@@_head_tl = \tl_to_str:N \l_@@_tmpa_tl } } % \end{macrocode} % \end{macro} % % \subsubsection{Insert token for error recovery} % % \begin{macro}{\@@_insert_relax:} % This function inserts \TeX{}'s |frozen_relax|. It is called when a % conditional is not done finding its condition, but hits the % corresponding \tn{fi} or \tn{or} or \tn{else}, or when \tn{input} % appears while \cs{g_@@_name_in_progress_bool} is \texttt{true}. % \begin{macrocode} \cs_new_protected:Npn \@@_insert_relax: { \@@_back_input: \gtl_set_eq:NN \l_@@_head_gtl \c_@@_frozen_relax_gtl \@@_back_input: \@@_print_action: } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_insert_group_begin_error:} % \begin{macrocode} \cs_new_protected:Npn \@@_insert_group_begin_error: { \tl_set_eq:NN \l_@@_tmpa_tl \l_@@_head_tl \@@_back_input: \gtl_set_eq:NN \l_@@_head_gtl \c_group_begin_gtl \@@_back_input: \@@_tex_error:nV { missing-lbrace } \l_@@_tmpa_tl \@@_print_action: } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_insert_dollar_error:} % \begin{macrocode} \cs_new_protected:Npn \@@_insert_dollar_error: { \@@_back_input: \@@_back_input:n { $ } % $ \@@_error:nnnnn { missing-dollar } { } { } { } { } \@@_print_action: } % \end{macrocode} % \end{macro} % % \subsubsection{Macro calls} % % \begin{macro}[EXP] % { % \@@_macro_prefix:N, % \@@_macro_parameter:N, % \@@_macro_replacement:N % } % \begin{macrocode} \use:e { \exp_not:n { \cs_new:Npn \@@_macro_split_do:NN #1 } { \exp_not:n { \exp_after:wN \@@_macro_split_do:wN } \exp_not:n { \token_to_meaning:N #1 \q_mark { } } \tl_to_str:n { : } \exp_not:n { -> \q_mark \use_none:nnnn } \exp_not:N \q_stop } \exp_not:n { \cs_new:Npn \@@_macro_split_do:wN } \exp_not:n {#1} \tl_to_str:n { : } \exp_not:n { #2 -> } \exp_not:n { #3 \q_mark #4 #5 \q_stop #6 } { \exp_not:n { #4 #6 {#1} {#2} {#3} } } } \cs_new:Npn \@@_macro_prefix:N #1 { \@@_macro_split_do:NN #1 \use_i:nnn } \cs_new:Npn \@@_macro_parameter:N #1 { \@@_macro_split_do:NN #1 \use_ii:nnn } \cs_new:Npn \@@_macro_replacement:N #1 { \@@_macro_split_do:NN #1 \use_iii:nnn } % \end{macrocode} % \end{macro} % % ^^A todo: redoc % \begin{macro}{\@@_macro_call:} % \begin{macro} % { % \@@_macro_call_safe:, % \@@_macro_call_quick:, % \@@_macro_call_quick_loop:NNN, % \@@_macro_call_quick_loop:NN, % \@@_macro_call_quick_runaway:Nw, % } % Macros are simply expanded once. We cannot determine precisely % which tokens a macro will need for its parameters, but we know that % it must form a balanced token list. Thus we can be safe by % extracting the longest balanced prefix in the input and working with % that. We need to check if the argument was braced, to improve the % error recovery for a non-\tn{long} macro receiving \tn{par}. % \begin{macrocode} \cs_new_protected:Npn \@@_macro_call: { \bool_if:NTF \g_@@_speedup_macros_bool { \tl_set:Ne \l_@@_tmpa_tl { ^ \exp_after:wN \@@_macro_parameter:N \l_@@_head_tl } \@@_tl_if_in:ooTF \c_@@_parameters_tl \l_@@_tmpa_tl { \@@_macro_call_quick: } { \@@_macro_call_safe: } } { \@@_macro_call_safe: } \exp_args:NV \@@_back_input:o \l_@@_head_tl \@@_print_expansion: } \cs_new_protected:Npn \@@_macro_call_safe: { \@@_input_gpop_tl:N \l_@@_tmpa_tl \tl_put_right:NV \l_@@_head_tl \l_@@_tmpa_tl } \cs_new_protected:Npn \@@_macro_call_quick: { \exp_after:wN \@@_macro_call_quick_loop:NNN \l_@@_tmpa_tl { ? \use_none_delimit_by_q_stop:w } \q_stop } \cs_new_protected:Npn \@@_macro_call_quick_loop:NNN #1#2#3 { \use_none:n #2 \@@_input_if_head_is_group_begin:TF { \@@_macro_call_quick_loop:NN \prg_do_nothing: } { \@@_macro_call_quick_loop:NN \use:n } #3 } \cs_new_protected:Npn \@@_macro_call_quick_loop:NN #1#2 { \@@_input_gpop_item:NF \l_@@_tmpa_tl { \@@_macro_call_quick_runaway:Nw #2 } \tl_put_right:Ne \l_@@_head_tl { #1 { \exp_not:V \l_@@_tmpa_tl } } \@@_macro_call_quick_loop:NNN #2 } \cs_new_protected:Npn \@@_macro_call_quick_runaway:Nw #1#2 \q_stop { \@@_error:neeee { runaway-macro-parameter } { \tl_to_str:N \l_@@_head_tl } { \tl_to_str:n {#1} } { } { } } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Expand next token} % % \begin{macro}{\@@_expand_do:N} % The argument is a command that will almost always be run to continue % a loop whose aim is to find the next non-expandable token, for % various purposes. The only case where we will end up grabbing the % argument is to suppress the loop by \cs{@@_noexpand:N}. % \begin{itemize} % \item \cs{@@_get_x_next:} when \TeX{} is looking for the first % non-expandable token in the main loop or when looking for numbers, % optional spaces etc. % \item \cs{@@_get_x_or_protected:} at the start of an alignment cell. % \item \cs{@@_get_token_xdef:} in the replacement text of \tn{edef} % and \tn{xdef}. % \item \cs{@@_get_token_x:} in the argument of \tn{message} and the % like. % \item \cs{prg_do_nothing:} in \cs{@@_expandafter:} namely after % \tn{expandafter}. % \end{itemize} % We mimick \TeX{}'s structure, distinguishing macros from other % commands because we find macro arguments very differently from % primitives. % \begin{macrocode} \cs_new_protected:Npn \@@_expand_do:N { \@@_set_action_text: \bool_if:NT \g_@@_internal_debug_bool { \@@_set_cmd: \exp_args:Ne \iow_term:n { Exp:~\int_to_arabic:n { \l_@@_head_cmd_int } } } \token_if_macro:NTF \l_@@_head_token { \@@_macro_call: } { \@@_expand_nonmacro: } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_expand_nonmacro:} % The token is a primitive. We find its (cleaned-up) \tn{meaning}, % and call the function implementing that expansion. If we do not % recognize the meaning then it is probably an unknown primitive. % Then do something similar to what we do for macros: get all tokens % that are not too unlikely to appear in the arguments of the % primitive and expand the resulting token list once before putting it % back into the input stream. % \begin{macrocode} \cs_new_protected:Npn \@@_expand_nonmacro: { \@@_set_cmd_aux_meaning: \@@_set_cmd_aux_primitive:oTF { \l_@@_head_meaning_tl } { \cs_if_exist_use:cF { @@_expandable_ \int_use:N \l_@@_head_cmd_int : } { \@@_error:neeee { internal } { expandable } { } { } { } } } { \@@_set_cmd_aux_unknown: \@@_input_gpop_tl:N \l_@@_tmpa_tl \tl_put_right:NV \l_@@_head_tl \l_@@_tmpa_tl \exp_args:NV \@@_back_input:o \l_@@_head_tl \@@_print_expansion: } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_get_x_next:} % Get a token. If it is expandable, then expand it, and repeat. This % function does not set the |cmd| and |char| integers. It is the % basis of all routines that look for keywords, numbers, equal signs, % filenames, optional spaces etc (in the language of \LaTeX3 these are % situations where \TeX{} \enquote{\texttt{f}-expands}). It is also % the basis of the \cs{@@_main_loop:}. % \begin{macrocode} \cs_new_protected:Npn \@@_get_x_next: { \@@_get_next: \@@_token_if_expandable:NT \l_@@_head_token { \@@_expand_do:N \@@_get_x_next: } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_get_x_or_protected:} % Get a token. If it is expandable, but not protected, then expand % it, and repeat. This function does not set the |cmd| and |char| % integers. This function is not used at present: it will be used at % the start of alignment cells. % \begin{macrocode} \cs_new_protected:Npn \@@_get_x_or_protected: { \@@_get_next: \@@_token_if_protected:NF \l_@@_head_token { \@@_expand_do:N \@@_get_x_or_protected: } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_get_token_xdef:, \@@_get_token_x:} % These are similar to \cs{@@_get_x_next:}, for use when reading the % replacement text of \tn{edef}/\tn{xdef} or the argument of a % primitive like \tn{message} that should be expanded as we read % tokens. Loop until finding a non-expandable token (or protected % macro). % \begin{macrocode} \cs_new_protected:Npn \@@_get_token_xdef: { \@@_get_next: \@@_token_if_protected:NF \l_@@_head_token { \@@_expand_do:N \@@_get_token_xdef: } } \cs_new_protected:Npn \@@_get_token_x: { \@@_get_next: \@@_token_if_protected:NF \l_@@_head_token { \@@_expand_do:N \@@_get_token_x: } } % \end{macrocode} % \end{macro} % % \subsection{Basic scanning subroutines} % % ^^A todo: implement |test_no_ligatures|? % ^^A todo: implement |get_tag_code|? % % \begin{macro}{\@@_get_x_non_blank:} % This function does not set the |cmd| and |char| integers. % \begin{macrocode} \cs_new_protected:Npn \@@_get_x_non_blank: { \@@_get_x_next: \token_if_eq_catcode:NNT \l_@@_head_token \c_space_token { \@@_get_x_non_blank: } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_get_x_non_relax:} % This function does not set the |cmd| and |char| integers. % \begin{macrocode} \cs_new_protected:Npn \@@_get_x_non_relax: { \@@_get_x_next: \token_if_eq_meaning:NNTF \l_@@_head_token \scan_stop: { \@@_get_x_non_relax: } { \token_if_eq_meaning:NNTF \l_@@_head_token \@@_special_relax: { \@@_get_x_non_relax: } { \token_if_eq_catcode:NNT \l_@@_head_token \c_space_token { \@@_get_x_non_relax: } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_skip_optional_space:} % \begin{macrocode} \cs_new_protected:Npn \@@_skip_optional_space: { \@@_get_x_next: \token_if_eq_catcode:NNF \l_@@_head_token \c_space_token { \@@_back_input: } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_scan_optional_equals:} % See \TeX{}'s |scan_optional_equals|. In all cases we forcefully % insert an equal sign in the output, because this sign is required, % as \cs{@@_rescan_something_internal:n} leaves raw numbers in % the previous-input sequence. % \begin{macrocode} \cs_new_protected:Npn \@@_scan_optional_equals: { \@@_get_x_non_blank: \tl_if_eq:NNTF \l_@@_head_tl \c_@@_eq_tl { \@@_prev_input:n { = } } { \@@_prev_input_silent:n { = } \@@_back_input: } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_scan_left_brace:} % The presence of \tn{relax} is allowed before a begin-group token. % If there is no begin-group token, insert one, produce an error, and % scan that begin-group using \cs{@@_get_next:}. % \begin{macrocode} \cs_new_protected:Npn \@@_scan_left_brace: { \@@_get_x_non_relax: \token_if_eq_catcode:NNF \l_@@_head_token \c_group_begin_token { \@@_insert_group_begin_error: \@@_get_next: } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_scan_keyword:n} % \begin{macro}[TF]{\@@_scan_keyword:n} % \begin{macro} % { % \@@_scan_keyword_loop:NNN, % \@@_scan_keyword_test:NNTF, % \@@_scan_keyword_true:, % \@@_scan_keyword_false:w % } % % The details of how \TeX{} looks for keywords are quite tricky to get % right, in particular with respect to expansion, case-insensitivity, % and spaces. We get rid of the case issue by requiring the keyword % to be given in both cases, intertwined: for instance, % \cs{@@_scan_keyword:n} |{| |pPtT| |}|. Then loop through pairs of % letters (which should be matching lowercase and uppercase letters). % The looping auxiliary takes three arguments, the first of which is a % boolean, \texttt{true} if spaces are allowed (no letter of the % keyword has been found yet). At each iteration, get a token, with % expansion, and test whether it is a non-active character equal (in % character code) to either letter of the pair: this happens if the % token is not ``definable'' (neither a control sequence nor an active % character) and it has the right string representation\ldots{} well, % it could also be doubled (macro parameter character), hence we look % at the first character only; spaces become an empty string, but this % works out because no keyword contains a space. So, at each % iteration, if the token is the correct non-active character, add it % to the previous-input sequence (as a generalized token list since % keywords may match begin-group or end-group characters), and % otherwise break with \cs{@@_scan_keyword_false:w}, unless we are % still at the beginning of the keyword and the token is a space. % When the loop reaches the end of the keyword letter pairs, complain % if there were an odd number of letters, and otherwise conclude the % loop with \cs{@@_scan_keyword_true:}, which stores the keyword, % converted to a string. Note that \TeX{}'s skipping of leading % spaces here must be intertwined with the search for keyword, as is % shown by the (plain \TeX{}) example % \begin{verbatim} % \lccode32=`f \lowercase{\def\fspace{ }} % \skip0=1pt plus 1 \fspace il\relax % \message{\the\skip0} % => 1pt plus 1fil % \end{verbatim} % \begin{macrocode} \cs_new_protected:Npn \@@_scan_keyword:n #1 { \@@_scan_keyword:nTF {#1} { } { } } \prg_new_protected_conditional:Npnn \@@_scan_keyword:n #1 { T , F , TF } { \@@_prev_input_gpush_gtl: \@@_scan_keyword_loop:NNN \c_true_bool #1 \q_recursion_tail \q_recursion_tail \q_recursion_stop } \cs_new_protected:Npn \@@_scan_keyword_loop:NNN #1#2#3 { \quark_if_recursion_tail_stop_do:nn {#2} { \@@_scan_keyword_true: } \quark_if_recursion_tail_stop_do:nn {#3} { \@@_error:neeee { internal } { odd-keyword-length } { } { } { } } \@@_get_x_next: \@@_scan_keyword_test:NNTF #2#3 { \@@_prev_input_gtl:N \l_@@_head_gtl \@@_scan_keyword_loop:NNN \c_false_bool } { \token_if_eq_catcode:NNF \l_@@_head_token \c_space_token { \@@_scan_keyword_false:w } \bool_if:NF #1 { \@@_scan_keyword_false:w } \@@_scan_keyword_loop:NNN #1#2#3 } } \prg_new_protected_conditional:Npnn \@@_scan_keyword_test:NN #1#2 { TF } { \@@_gtl_if_head_is_definable:NTF \l_@@_head_gtl { \prg_return_false: } { \str_if_eq:eeTF { \str_head:f { \gtl_to_str:N \l_@@_head_gtl } } {#1} { \prg_return_true: } { \str_if_eq:eeTF { \str_head:f { \gtl_to_str:N \l_@@_head_gtl } } {#2} { \prg_return_true: } { \prg_return_false: } } } } \cs_new_protected:Npn \@@_scan_keyword_true: { \@@_prev_input_gpop_gtl:N \l_@@_tmpb_gtl \@@_prev_input:e { \gtl_to_str:N \l_@@_tmpb_gtl } \prg_return_true: } \cs_new_protected:Npn \@@_scan_keyword_false:w #1 \q_recursion_stop { \@@_back_input: \@@_prev_input_gpop_gtl:N \l_@@_tmpb_gtl \@@_back_input_gtl:N \l_@@_tmpb_gtl \prg_return_false: } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\@@_scan_to:} % Used when |to| is mandatory: after \tn{read} or \tn{readline} and % after \tn{vsplit}. % \begin{macrocode} \cs_new_protected:Npn \@@_scan_to: { \@@_scan_keyword:nF { tToO } { \@@_error:nnnnn { missing-to } { } { } { } { } \@@_prev_input:n { to } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_scan_font_ident:} % Find a font identifier. % \begin{macrocode} \cs_new_protected:Npn \@@_scan_font_ident: { \@@_get_x_non_blank: \@@_set_cmd: \int_case:nnF \l_@@_head_cmd_int { { \@@_tex_use:n { def_font } } { \@@_prev_input:V \l_@@_head_tl } { \@@_tex_use:n { letterspace_font } } { \@@_prev_input:V \l_@@_head_tl } { \@@_tex_use:n { pdf_copy_font } } { \@@_prev_input:V \l_@@_head_tl } { \@@_tex_use:n { set_font } } { \@@_prev_input:V \l_@@_head_tl } { \@@_tex_use:n { def_family } } { \@@_prev_input:V \l_@@_head_tl \@@_scan_int: } } { \@@_error:nnnnn { missing-font-id } { } { } { } { } \@@_back_input: \@@_prev_input:n { \@@_nullfont: } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_scan_font_int:} % Find operands for one of \tn{hyphenchar}'s friends (command code % |assign_font_int=78|). % \begin{macrocode} \cs_new_protected:Npn \@@_scan_font_int: { \int_case:nnF \l_@@_head_char_int { { 0 } { \@@_scan_font_ident: } { 1 } { \@@_scan_font_ident: } { 6 } { \@@_scan_font_ident: } } { \@@_scan_font_ident: \@@_scan_int: } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_scan_font_dimen:} % Find operands for \tn{fontdimen}. % \begin{macrocode} \cs_new_protected:Npn \@@_scan_font_dimen: { \@@_scan_int: \@@_scan_font_ident: } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_rescan_something_internal:n} % \begin{macro}{\@@_scan_something_aux:nwn} % Receives an (explicit) \enquote{level} argument: % \begin{itemize} % \item |int_val=0| for integer values; % \item |dimen_val=1| for dimension values; % \item |glue_val=2| for glue specifications; % \item |mu_val=3| for math glue specifications; % \item |ident_val=4| for font identifiers (this never happens); % \item |tok_val=5| for token lists (after |\the| or |\showthe|). % \end{itemize} % Scans something internal, and places its value, converted to the % given level, to the right of the last item of % the previous-input sequence, then sets \cs{g_@@_val_level_int} to the % found level (level before conversion, so this may be higher than % requested). % % From \cs{@@_thing_case:}, get the information about what level is % produced by the given token once it has received all its operands % (head of \cs{l_@@_tmpa_tl}), and about what to do to find those % operands (tail of \cs{l_@@_tmpa_tl}). If the first token may not % appear after \tn{the} at all, \cs{@@_thing_case:} gives level~$8$. % % If the argument (|#3|~in the auxiliary) is $<4$ but the level that % will be produced (|#1|~in the auxiliary) is $\geq 4$ (that is, % $4$, $5$, or $8$) complain about a missing number and insert a % zero dimension, to get exactly \TeX{}'s error recovery. If the % level produced is~$8$, complain that |\the| cannot do this. % % Otherwise, scan the arguments (in a new input level). If both the % argument and the level produced are $<4$, then get the value with % \cs{@@_thing_use_get:nnNN} which downgrades from glue to dimension % to integer and produces the |incompatible-units| error if needed. % The only remaining case is that the argument is $5$ (since $4$ is % never used) and the level produced is that or less: then the value % found is used with \cs{@@_the:w}. % % Finally, tell the user the tokens that have been found (if there % was a single token, its meaning as well) and their value. Use |=>| % rather than |=| because the value displayed is the value used, not % the actual value (this matters in constructions such as % |\parindent=\parskip| where a skip or a dimen is downgraded to a % dimen or an int, or when there was an error). % \begin{macrocode} \cs_new_protected:Npn \@@_rescan_something_internal:n #1 { \@@_set_cmd: \@@_set_action_text: \tl_set:Nf \l_@@_tmpa_tl { \@@_thing_case: } \exp_after:wN \@@_scan_something_aux:nwn \l_@@_tmpa_tl \q_stop {#1} } \cs_new_protected:Npn \@@_scan_something_aux:nwn #1#2 \q_stop #3 { \int_compare:nT { #3 < 4 <= #1 } { \@@_back_input: \@@_tex_error:nV { missing-number } \l_@@_head_tl \@@_thing_use_get:nnNN { 1 } {#3} \c_zero_dim \l_@@_tmpa_tl \@@_rescan_something_internal_auxii:Vn \l_@@_tmpa_tl { 1 } \@@_break:w } \int_compare:nNnT {#1} = { 8 } { \@@_tex_error:nV { the-cannot } \l_@@_head_tl \@@_rescan_something_internal_auxii:nn 0 { 0 } \@@_break:w } \tl_if_empty:nF {#2} { \@@_prev_input_gpush:N \l_@@_head_tl \@@_print_action: #2 \@@_prev_input_gpop:N \l_@@_head_tl } \int_compare:nNnTF {#3} < { 4 } { \@@_thing_use_get:nnNN {#1} {#3} \l_@@_head_tl \l_@@_tmpa_tl } { \tl_set:Ne \l_@@_tmpa_tl { \@@_the:w \l_@@_head_tl } } \@@_rescan_something_internal_auxii:Vn \l_@@_tmpa_tl {#1} \@@_break_point: \int_compare:nNnT {#3} < { 4 } { \@@_print_action: } } \cs_new_protected:Npn \@@_rescan_something_internal_auxii:nn #1#2 { \@@_prev_input_silent:n {#1} \@@_set_action_text: \@@_set_action_text:e { \g_@@_action_text_str \use:n { ~ => ~ } \tl_to_str:n {#1} } \int_gset:Nn \g_@@_val_level_int {#2} } \cs_generate_variant:Nn \@@_rescan_something_internal_auxii:nn { V } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[EXP] % {\@@_thing_case:, \@@_thing_last_item:, \@@_thing_register:} % This expands to a digit (the level generated by whatever token is % the current |head|), followed by some code to fetch necessary % operands. In most cases, this can be done by simply looking at the % |cmd| integer, but for |last_item|, |set_aux|, |set_shape| and % |register|, the level of the token, or what has to be scanned, % depends on the |char| integer. When the token is not allowed % after \tn{the} (or at any other position where % \cs{@@_rescan_something_internal:n} is called), the resulting level % is~$8$, large enough so that the main function knows it is % forbidden. % \begin{macrocode} \cs_new:Npn \@@_thing_case: { \int_case:nnF \l_@@_head_cmd_int { { 68 } { 0 } % char_given { 69 } { 0 } % math_given { 70 } { \@@_thing_last_item: } % last_item { 71 } { 5 \@@_scan_toks_register: } % toks_register { 72 } { 5 } % assign_toks { 73 } { 0 } % assign_int { 74 } { 1 } % assign_dimen { 75 } { 2 } % assign_glue { 76 } { 3 } % assign_mu_glue { 77 } { 1 \@@_scan_font_dimen: } % assign_font_dimen { 78 } { 0 \@@_scan_font_int: } % assign_font_int { 79 } { \@@_thing_set_aux: } % set_aux { 80 } { 0 } % set_prev_graf { 81 } { 1 } % set_page_dimen { 82 } { 0 } % set_page_int { 83 } { 1 \@@_scan_int: } % set_box_dimen { 84 } { \@@_thing_set_shape: } % set_shape { 85 } { 0 \@@_scan_int: } % def_code { 86 } { 4 \@@_scan_int: } % def_family { 87 } { 4 } % set_font { 88 } { 4 } % def_font { 89 } { \@@_thing_register: } % register {101 } { 4 } % letterspace_font {102 } { 4 } % pdf_copy_font } { 8 } } \cs_new:Npn \@@_thing_set_aux: { \int_compare:nNnTF \l_@@_head_char_int = { 1 } { 1 } { 0 } } \cs_new:Npn \@@_thing_set_shape: { \int_compare:nNnTF \l_@@_head_char_int = 0 { 0 } { 0 \@@_scan_int: } } \cs_new:Npn \@@_thing_last_item: { \int_compare:nNnTF \l_@@_head_char_int < { 26 } { \int_case:nnF \l_@@_head_char_int { { 1 } { 1 } % lastkern { 2 } { 2 } % lastskip } { 0 } % other integer parameters } { \int_case:nnF \l_@@_head_char_int { { 26 } { 0 \@@_scan_normal_glue: } % gluestretchorder { 27 } { 0 \@@_scan_normal_glue: } % glueshrinkorder { 28 } % fontcharwd { 1 \@@_scan_font_ident: \@@_scan_int: } { 29 } % fontcharht { 1 \@@_scan_font_ident: \@@_scan_int: } { 30 } % fontchardp { 1 \@@_scan_font_ident: \@@_scan_int: } { 31 } % fontcharic { 1 \@@_scan_font_ident: \@@_scan_int: } { 32 } { 1 \@@_scan_int: } % parshapelength { 33 } { 1 \@@_scan_int: } % parshapeindent { 34 } { 1 \@@_scan_int: } % parshapedimen { 35 } { 1 \@@_scan_normal_glue: } % gluestretch { 36 } { 1 \@@_scan_normal_glue: } % glueshrink { 37 } { 2 \@@_scan_mu_glue: } % mutoglue { 38 } { 3 \@@_scan_normal_glue: } % gluetomu { 39 } % numepr { 0 \@@_scan_expr:N \@@_scan_int: } { 40 } % dimexpr { 1 \@@_scan_expr:N \@@_scan_normal_dimen: } { 41 } % glueexpr { 2 \@@_scan_expr:N \@@_scan_normal_glue: } { 42 } % muexpr { 3 \@@_scan_expr:N \@@_scan_mu_glue: } } { } } } \cs_new:Npn \@@_thing_register: { \int_eval:n { \l_@@_head_char_int / 1 000 000 - 1 } \int_compare:nNnT { \tl_tail:V \l_@@_head_char_int } = 0 { \@@_scan_int: } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_scan_toks_register:} % A case where getting operands is not completely trivial. % \begin{macrocode} \cs_new_protected:Npn \@@_scan_toks_register: { \int_compare:nNnT \l_@@_head_char_int = 0 { \@@_scan_int: } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_thing_use_get:nnNN} % Given a level found~|#1| and a target level~|#2| (both in $[0,3]$), % turn the token list |#3| into the desired level or less, and store % the result in |#4|. % \begin{macrocode} \cs_new_protected:Npn \@@_thing_use_get:nnNN #1#2#3#4 { \int_compare:nNnTF {#2} < { 3 } { \int_compare:nNnT {#1} = { 3 } { \@@_tex_error:nV { incompatible-units } #3 } \tl_set:Ne #4 { \int_case:nn { \int_min:nn {#1} {#2} } { { 0 } \int_eval:n { 1 } \dim_eval:n { 2 } \skip_eval:n } { \int_compare:nNnT {#1} = { 3 } \tex_mutoglue:D #3 } } } { \int_case:nnF {#1} { { 0 } { \tl_set:Ne #4 { \int_eval:n {#3} } } { 3 } { \tl_set:Ne #4 { \muskip_eval:n {#3} } } } { \@@_tex_error:nV { incompatible-units } #3 \tl_set:Ne #4 { \muskip_eval:n { \tex_gluetomu:D #3 } } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_scan_expr:N} % \begin{macro}{\@@_scan_expr_aux:NN, \@@_scan_factor:N} % \begin{macrocode} \cs_new_protected:Npn \@@_scan_expr:N #1 { \@@_scan_expr_aux:NN #1 \c_false_bool } \cs_new_protected:Npn \@@_scan_expr_aux:NN #1#2 { \@@_get_x_non_blank: \@@_scan_factor:N #1 \@@_scan_expr_op:NN #1#2 } \cs_new_protected:Npn \@@_scan_expr_op:NN #1#2 { \@@_get_x_non_blank: \token_case_meaning:NnF \l_@@_head_tl { \c_@@_plus_tl { \@@_prev_input:V \l_@@_head_tl \@@_scan_expr_aux:NN #1#2 } \c_@@_minus_tl { \@@_prev_input:V \l_@@_head_tl \@@_scan_expr_aux:NN #1#2 } \c_@@_times_tl { \@@_prev_input:V \l_@@_head_tl \@@_get_x_non_blank: \@@_scan_factor:N \@@_scan_int: \@@_scan_expr_op:NN #1#2 } \c_@@_over_tl { \@@_prev_input:V \l_@@_head_tl \@@_get_x_non_blank: \@@_scan_factor:N \@@_scan_int: \@@_scan_expr_op:NN #1#2 } \c_@@_rp_tl { \bool_if:NTF #2 { \@@_prev_input:V \l_@@_head_tl } { \@@_back_input: } } } { \bool_if:NTF #2 { \@@_error:nnnnn { missing-rparen } { } { } { } { } \@@_back_input: \@@_prev_input:V \c_@@_rp_tl } { \token_if_eq_meaning:NNF \l_@@_head_token \scan_stop: { \@@_back_input: } } } } \cs_new_protected:Npn \@@_scan_factor:N #1 { \tl_if_eq:NNTF \l_@@_head_tl \c_@@_lp_tl { \@@_prev_input:V \l_@@_head_tl \@@_scan_expr_aux:NN #1 \c_true_bool } { \@@_back_input: #1 } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\@@_scan_signs:} % Skips blanks, scans signs, and places them to the right of the last % item of \cs{@@_prev_input:n}. % \begin{macrocode} \cs_new_protected:Npn \@@_scan_signs: { \@@_get_x_non_blank: \tl_if_eq:NNTF \l_@@_head_tl \c_@@_plus_tl { \@@_prev_input:V \l_@@_head_tl \@@_scan_signs: } { \tl_if_eq:NNT \l_@@_head_tl \c_@@_minus_tl { \@@_prev_input:V \l_@@_head_tl \@@_scan_signs: } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_scan_int:} % \begin{macro} % {\@@_scan_int_char:, \@@_scan_int_lq:, \@@_scan_int_explicit:n} % \begin{macrocode} \cs_new_protected:Npn \@@_scan_int: { \@@_scan_signs: \@@_set_cmd: \@@_cmd_if_internal:TF { \@@_rescan_something_internal:n { 0 } } { \@@_scan_int_char: } } \cs_new_protected:Npn \@@_scan_int_char: { \token_case_meaning:NnF \l_@@_head_tl { \c_@@_lq_tl { \@@_scan_int_lq: } \c_@@_rq_tl { \@@_prev_input:V \l_@@_head_tl \@@_get_x_next: \@@_scan_int_explicit:Nn \c_false_bool { ' } } \c_@@_dq_tl { \@@_prev_input:V \l_@@_head_tl \@@_get_x_next: \@@_scan_int_explicit:Nn \c_false_bool { " } % " } } { \@@_scan_int_explicit:Nn \c_false_bool { } } } \cs_new_protected:Npn \@@_scan_int_lq: { \@@_get_next: \@@_gtl_if_head_is_definable:NF \l_@@_head_gtl { \tl_set:Ne \l_@@_head_tl { \@@_token_to_char:N \l_@@_head_token } } \tl_set:Ne \l_@@_tmpa_tl { \int_eval:n { \exp_after:wN ` \l_@@_head_tl } } \@@_prev_input_silent:V \l_@@_tmpa_tl \@@_print_action:e { ` \gtl_to_str:N \l_@@_head_gtl = \l_@@_tmpa_tl } \@@_skip_optional_space: } \cs_new_protected:Npn \@@_scan_int_explicit:Nn #1#2 { \if_int_compare:w 1 < #2 1 \exp_after:wN \exp_not:N \l_@@_head_tl \exp_stop_f: \exp_after:wN \use_i:nn \else: \exp_after:wN \use_ii:nn \fi: { \@@_prev_input:V \l_@@_head_tl \@@_get_x_next: \@@_scan_int_explicit:Nn \c_true_bool {#2} } { \token_if_eq_catcode:NNF \l_@@_head_token \c_space_token { \@@_back_input: } \bool_if:NF #1 { \@@_tex_error:nV { missing-number } \l_@@_head_tl \@@_prev_input:n { 0 } } } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\@@_scan_normal_dimen:} % \begin{macrocode} \cs_new_protected:Npn \@@_scan_normal_dimen: { \@@_scan_dimen:nN { 2 } \c_false_bool } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_scan_dimen:nN} % The first argument is $2$ if the unit may not be |mu| and $3$ if % the unit must be |mu| (or |fil|). The second argument is % \cs{c_true_bool} if |fil|, |fill|, |filll| are permitted, and is % otherwise |false|. These arguments are similar to those of % \TeX{}'s own |scan_dimen| procedure, in which |mu| is |bool(#1=3)| % and |inf| is |#2|. The third argument of this procedure is omitted % here, as the corresponding shortcut is provided as a separate % function, \cs{@@_scan_dim_unit:nN}. % % Ideally, \cs{@@_scan_inf_unit_loop:} would produce an \pkg{unravel} % error when reaching the third ``L'', rather than letting TeX produce % the error later on. % \begin{macrocode} \cs_new_protected:Npn \@@_scan_dimen:nN #1#2 { \@@_scan_signs: \@@_prev_input_gpush: \@@_set_cmd: \@@_cmd_if_internal:TF { \int_compare:nNnTF {#1} = { 3 } { \@@_rescan_something_internal:n { 3 } } { \@@_rescan_something_internal:n { 1 } } \int_compare:nNnT \g_@@_val_level_int = { 0 } { \@@_scan_dim_unit:nN {#1} #2 } } { \@@_scan_dimen_char:nN {#1} #2 } \@@_prev_input_gpop:N \l_@@_head_tl \@@_prev_input_silent:V \l_@@_head_tl } \cs_new_protected:Npn \@@_scan_dimen_char:nN #1#2 { \tl_if_eq:NNT \l_@@_head_tl \c_@@_comma_tl { \tl_set_eq:NN \l_@@_head_tl \c_@@_point_tl } \tl_if_eq:NNTF \l_@@_head_tl \c_@@_point_tl { \@@_prev_input:n { . } \@@_scan_decimal_loop: } { \@@_tl_if_in:ooTF { 0123456789 } \l_@@_head_tl { \@@_back_input: \@@_scan_int: \tl_if_eq:NNT \l_@@_head_tl \c_@@_comma_tl { \tl_set_eq:NN \l_@@_head_tl \c_@@_point_tl } \tl_if_eq:NNT \l_@@_head_tl \c_@@_point_tl { \@@_input_gpop:N \l_@@_tmpb_gtl \@@_prev_input:n { . } \@@_scan_decimal_loop: } } { \@@_back_input: \@@_scan_int: } } \@@_scan_dim_unit:nN {#1} #2 } \cs_new_protected:Npn \@@_scan_dim_unit:nN #1#2 { \bool_if:NT #2 { \@@_scan_keyword:nT { fFiIlL } { \@@_scan_inf_unit_loop: \@@_break:w } } \@@_get_x_non_blank: \@@_set_cmd: \@@_cmd_if_internal:TF { \@@_prev_input_gpush: \@@_rescan_something_internal:n {#1} \int_compare:nNnTF \g_@@_val_level_int = { 0 } { \@@_prev_input_join_get:nnN {#1} { sp } \l_@@_tmpa_tl } { \@@_prev_input_join_get:nnN {#1} { } \l_@@_tmpa_tl } \@@_prev_input_gpush:N \l_@@_tmpa_tl \exp_after:wN \use_none:n \@@_break:w } { } \@@_back_input: \int_compare:nNnT {#1} = { 3 } { \@@_scan_keyword:nT { mMuU } { \@@_break:w } \@@_tex_error:nV { missing-mu } \l_@@_head_tl \@@_prev_input:n { mu } \@@_break:w } \@@_scan_keyword:nT { eEmM } { \@@_break:w } \@@_scan_keyword:nT { eExX } { \@@_break:w } \@@_scan_keyword:nT { pPxX } { \@@_break:w } \@@_scan_keyword:nT { tTrRuUeE } { \@@_prepare_mag: } \@@_scan_keyword:nT { pPtT } { \@@_break:w } \@@_scan_keyword:nT { iInN } { \@@_break:w } \@@_scan_keyword:nT { pPcC } { \@@_break:w } \@@_scan_keyword:nT { cCmM } { \@@_break:w } \@@_scan_keyword:nT { mMmM } { \@@_break:w } \@@_scan_keyword:nT { bBpP } { \@@_break:w } \@@_scan_keyword:nT { dDdD } { \@@_break:w } \@@_scan_keyword:nT { cCcC } { \@@_break:w } \@@_scan_keyword:nT { nNdD } { \@@_break:w } \@@_scan_keyword:nT { nNcC } { \@@_break:w } \@@_scan_keyword:nT { sSpP } { \@@_break:w } \@@_tex_error:nV { missing-pt } \l_@@_head_tl \@@_prev_input:n { pt } \@@_break_point: \@@_skip_optional_space: } \cs_new_protected:Npn \@@_scan_inf_unit_loop: { \@@_scan_keyword:nT { lL } { \@@_scan_inf_unit_loop: } } \cs_new_protected:Npn \@@_scan_decimal_loop: { \@@_get_x_next: \tl_if_empty:NTF \l_@@_head_tl { \use_ii:nn } { \@@_tl_if_in:ooTF { 0123456789 } \l_@@_head_tl } { \@@_prev_input:V \l_@@_head_tl \@@_scan_decimal_loop: } { \token_if_eq_catcode:NNF \l_@@_head_token \c_space_token { \@@_back_input: } \@@_prev_input_silent:n { ~ } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_scan_normal_glue:, \@@_scan_mu_glue:} % \begin{macrocode} \cs_new_protected:Npn \@@_scan_normal_glue: { \@@_scan_glue:n { 2 } } \cs_new_protected:Npn \@@_scan_mu_glue: { \@@_scan_glue:n { 3 } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_scan_glue:n} % \begin{macrocode} \cs_new_protected:Npn \@@_scan_glue:n #1 { \@@_prev_input_gpush: \@@_scan_signs: \@@_prev_input_gpush: \@@_set_cmd: \@@_cmd_if_internal:TF { \@@_rescan_something_internal:n {#1} \int_case:nnF \g_@@_val_level_int { { 0 } { \@@_scan_dim_unit:nN {#1} \c_false_bool } { 1 } { } } { \@@_break:w } } { \@@_back_input: \@@_scan_dimen:nN {#1} \c_false_bool } \@@_prev_input_join_get:nnN {#1} { } \l_@@_tmpa_tl \@@_prev_input_gpush: \@@_prev_input_gpush:N \l_@@_tmpa_tl \@@_scan_keyword:nT { pPlLuUsS } { \@@_scan_dimen:nN {#1} \c_true_bool } \@@_scan_keyword:nT { mMiInNuUsS } { \@@_scan_dimen:nN {#1} \c_true_bool } \@@_break_point: \@@_prev_input_join_get:nnN {#1} { } \l_@@_tmpa_tl \@@_prev_input_silent:V \l_@@_tmpa_tl } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_scan_file_name:} % \begin{macrocode} \cs_new_protected:Npn \@@_scan_file_name: { \@@_get_x_non_relax: \token_if_eq_catcode:NNTF \l_@@_head_token \c_group_begin_token { \@@_scan_group_x:N \c_false_bool } { \@@_back_input: \bool_gset_true:N \g_@@_name_in_progress_bool \bool_gset_false:N \g_@@_quotes_bool \@@_get_x_non_blank: \@@_scan_file_name_loop: \bool_gset_false:N \g_@@_name_in_progress_bool \@@_prev_input_silent:n { ~ } } } \cs_new_protected:Npn \@@_scan_file_name_loop: { \@@_gtl_if_head_is_definable:NTF \l_@@_head_gtl { \@@_back_input: } { \tl_set:Ne \l_@@_tmpa_tl { \@@_token_to_char:N \l_@@_head_token } \tl_if_eq:NNT \l_@@_tmpa_tl \c_@@_dq_tl { \bool_if:NTF \g_@@_quotes_bool { \bool_set_false:N } { \bool_set_true:N } \g_@@_quotes_bool } \bool_if:NTF \g_@@_quotes_bool { \use:n } { \tl_if_eq:NNF \l_@@_tmpa_tl \c_space_tl } { \@@_prev_input_silent:V \l_@@_tmpa_tl \@@_get_x_next: \@@_scan_file_name_loop: } } } \bool_new:N \g_@@_quotes_bool % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_scan_r_token:} % This is analogous to \TeX{}'s |get_r_token|. We store in % \cs{l_@@_defined_tl} the token which we found, as this is what will % be defined by the next assignment. % \begin{macrocode} \cs_new_protected:Npn \@@_scan_r_token: { \bool_do_while:nn { \tl_if_eq_p:NN \l_@@_head_tl \c_space_tl } { \@@_get_next: } \@@_gtl_if_head_is_definable:NF \l_@@_head_gtl { \@@_error:nnnnn { missing-cs } { } { } { } { } \@@_back_input: \tl_set:Nn \l_@@_head_tl { \@@_inaccessible:w } } \@@_prev_input_silent:V \l_@@_head_tl \tl_set_eq:NN \l_@@_defined_tl \l_@@_head_tl } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_scan_toks_to_str:} % \begin{macrocode} \cs_new_protected:Npn \@@_scan_toks_to_str: { \@@_prev_input_gpush: \@@_scan_toks:NN \c_false_bool \c_true_bool \@@_prev_input_gpop:N \l_@@_tmpa_tl \@@_prev_input_silent:e { { \exp_after:wN \tl_to_str:n \l_@@_tmpa_tl } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_scan_pdf_ext_toks:} % \begin{macrocode} \cs_new_protected:Npn \@@_scan_pdf_ext_toks: { \@@_prev_input_gpush: \@@_scan_toks:NN \c_false_bool \c_true_bool \@@_prev_input_gpop:N \l_@@_tmpa_tl \@@_prev_input_silent:e { { \exp_not:N \exp_not:n \exp_not:V \l_@@_tmpa_tl } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_scan_toks:NN} % The boolean |#1| is true if we are making a definition (then we % start by scanning the parameter text), false if we are simply % scanning a general text. The boolean |#2| is true if we need to % expand, false otherwise (for instance for \tn{lowercase}). % \begin{macrocode} \cs_new_protected:Npn \@@_scan_toks:NN #1#2 { \bool_if:NT #1 { \@@_scan_param: } \@@_scan_left_brace: \bool_if:NTF #2 { \@@_scan_group_x:N #1 } { \@@_scan_group_n:N #1 } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_scan_param:} % \begin{macro}{\@@_scan_param_aux:} % Collect the parameter text into \cs{l_@@_tmpa_tl}, and when seeing % either a begin-group or an end-group character, put it back into the % input, stop looping, and put what we collected into % \cs{l_@@_defining_tl} and into the |prev_input|. % \begin{macrocode} \cs_new_protected:Npn \@@_scan_param: { \tl_clear:N \l_@@_tmpa_tl \@@_scan_param_aux: \tl_put_right:NV \l_@@_defining_tl \l_@@_tmpa_tl \@@_prev_input_silent:V \l_@@_tmpa_tl } \cs_new_protected:Npn \@@_scan_param_aux: { \@@_get_next: \tl_concat:NNN \l_@@_tmpa_tl \l_@@_tmpa_tl \l_@@_head_tl \tl_if_empty:NTF \l_@@_head_tl { \@@_back_input: } { \@@_scan_param_aux: } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\@@_scan_group_n:N} % The boolean |#1| is true if we are making a definition, false % otherwise. In both cases put the open brace back and grab the first % item. The only difference is that when making a definition we store % the data into \cs{l_@@_defining_tl} as well. % \begin{macrocode} \cs_new_protected:Npn \@@_scan_group_n:N #1 { \gtl_set_eq:NN \l_@@_head_gtl \c_group_begin_gtl \@@_back_input: \@@_input_gpop_item:NF \l_@@_head_tl { \@@_error:nnnnn { runaway-text } { } { } { } { } \@@_exit_hard:w } \tl_set:Ne \l_@@_head_tl { { \exp_not:V \l_@@_head_tl } } \bool_if:NT #1 { \tl_put_right:NV \l_@@_defining_tl \l_@@_head_tl } \@@_prev_input_silent:V \l_@@_head_tl } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_scan_group_x:N} % The boolean |#1| is true if we are making a definition, false % otherwise. % \begin{macrocode} \cs_new_protected:Npn \@@_scan_group_x:N #1 { \@@_input_gpop_tl:N \l_@@_head_tl \@@_back_input:V \l_@@_head_tl \bool_if:NTF #1 { \@@_prev_input_silent:V \c_left_brace_str \tl_put_right:Nn \l_@@_defining_tl { { \if_false: } \fi: } \@@_scan_group_xdef:n { 1 } } { \@@_prev_input_gpush_gtl: \@@_prev_input_gtl:N \l_@@_head_gtl \@@_scan_group_x:n { 1 } \@@_prev_input_gpop_gtl:N \l_@@_tmpb_gtl \@@_prev_input_silent:e { \gtl_left_tl:N \l_@@_tmpb_gtl } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_scan_group_xdef:n} % This is to scan the replacement text of an \tn{edef} or \tn{xdef}. % The integer |#1| counts the brace balance. % \begin{macrocode} \cs_new_protected:Npn \@@_scan_group_xdef:n #1 { \@@_get_token_xdef: \tl_if_empty:NTF \l_@@_head_tl { \gtl_if_head_is_group_begin:NTF \l_@@_head_gtl { \@@_prev_input_silent:V \c_left_brace_str \tl_put_right:Nn \l_@@_defining_tl { { \if_false: } \fi: } \@@_scan_group_xdef:f { \int_eval:n { #1 + 1 } } } { \@@_prev_input_silent:V \c_right_brace_str \tl_put_right:Nn \l_@@_defining_tl { \if_false: { \fi: } } \int_compare:nNnF {#1} = 1 { \@@_scan_group_xdef:f { \int_eval:n { #1 - 1 } } } } } { \@@_prev_input_silent:V \l_@@_head_tl \tl_put_right:Ne \l_@@_defining_tl { \exp_not:N \exp_not:N \exp_not:V \l_@@_head_tl } \@@_scan_group_xdef:n {#1} } } \cs_generate_variant:Nn \@@_scan_group_xdef:n { f } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_scan_group_x:n} % \begin{macrocode} \cs_new_protected:Npn \@@_scan_group_x:n #1 { \@@_get_token_x: \@@_prev_input_gtl:N \l_@@_head_gtl \tl_if_empty:NTF \l_@@_head_tl { \gtl_if_head_is_group_begin:NTF \l_@@_head_gtl { \@@_scan_group_x:f { \int_eval:n { #1 + 1 } } } { \int_compare:nNnF {#1} = 1 { \@@_scan_group_x:f { \int_eval:n { #1 - 1 } } } } } { \@@_scan_group_x:n {#1} } } \cs_generate_variant:Nn \@@_scan_group_x:n { f } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_scan_alt_rule:} % \begin{macrocode} \cs_new_protected:Npn \@@_scan_alt_rule: { \@@_scan_keyword:nTF { wWiIdDtThH } { \@@_scan_normal_dimen: \@@_scan_alt_rule: } { \@@_scan_keyword:nTF { hHeEiIgGhHtT } { \@@_scan_normal_dimen: \@@_scan_alt_rule: } { \@@_scan_keyword:nT { dDeEpPtThH } { \@@_scan_normal_dimen: \@@_scan_alt_rule: } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_scan_spec:} % Some \TeX{} primitives accept the keywords |to| and |spread|, % followed by a dimension. % \begin{macrocode} \cs_new_protected:Npn \@@_scan_spec: { \@@_scan_keyword:nTF { tToO } { \@@_scan_normal_dimen: } { \@@_scan_keyword:nT { sSpPrReEaAdD } { \@@_scan_normal_dimen: } } \@@_scan_left_brace: } % \end{macrocode} % \end{macro} % % \subsection{Working with boxes} % % \begin{macro}{\@@_do_box:N} % When this procedure is called, the last item in % the previous-input sequence is % \begin{itemize} % \item empty if the box is meant to be put in the input stream, % \item \tn{setbox}\meta{int} if it is meant to be stored somewhere, % \item \tn{moveright}\meta{dim}, \tn{moveleft}\meta{dim}, % \tn{lower}\meta{dim}, \tn{raise}\meta{dim} if it is meant to be % shifted, % \item \tn{leaders} or \tn{cleaders} or \tn{xleaders}, in which case % the argument is \cs{c_true_bool} (otherwise \cs{c_false_bool}). % \end{itemize} % If a |make_box| command follows, we fetch the operands. If leaders % are followed by a rule, then this is also ok. In all other cases, % call \cs{@@_do_box_error:} to clean up. % \begin{macrocode} \cs_new_protected:Npn \@@_do_box:N #1 { \@@_get_x_non_relax: \@@_set_cmd: \int_compare:nNnTF \l_@@_head_cmd_int = { \@@_tex_use:n { make_box } } { \@@_do_begin_box:N #1 } { \bool_if:NTF #1 { \int_case:nnTF \l_@@_head_cmd_int { { \@@_tex_use:n { hrule } } { } { \@@_tex_use:n { vrule } } { } } { \@@_do_leaders_rule: } { \@@_do_box_error: } } { \@@_do_box_error: } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_do_box_error:} % Put the (non-|make_box|) command back into the input and complain. % Then recover by throwing away the action (last item of % the previous-input sequence). For some reason (this appears to be what % \TeX{} does), there is no need to remove the after assignment token % here. % \begin{macrocode} \cs_new_protected:Npn \@@_do_box_error: { \@@_back_input: \@@_error:nnnnn { missing-box } { } { } { } { } \@@_prev_input_gpop:N \l_@@_head_tl \@@_print_action:e { \tl_to_str:N \l_@@_head_tl } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_do_begin_box:N} % We have just found a |make_box| command and placed it into the last % item of the previous-input sequence. If it is ``simple'' % (\tn{box}\meta{int}, \tn{copy}\meta{int}, \tn{lastbox}, % \tn{vsplit}\meta{int} \texttt{to} \meta{dim}) then we grab its % operands, then call \cs{@@_do_simple_box:N} to finish up. If it is % \tn{vtop} or \tn{vbox} or \tn{hbox}, we need to work harder. % \begin{macrocode} \cs_new_protected:Npn \@@_do_begin_box:N #1 { \@@_prev_input:V \l_@@_head_tl \int_case:nnTF \l_@@_head_char_int { { 0 } { \@@_scan_int: } % box { 1 } { \@@_scan_int: } % copy { 2 } { } % lastbox { 3 } % vsplit { \@@_scan_int: \@@_scan_to: \@@_scan_normal_dimen: } } { \@@_do_simple_box:N #1 } { \@@_do_box_explicit:N #1 } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_do_simple_box:N} % For leaders, we need to fetch a glue. In all cases, retrieve the % box construction (such as |\raise3pt\vsplit7to5em|). Finally, let % \TeX{} run the code and print what we have done. % In the case of \tn{shipout}, check that \tn{mag} has a value % between $1$ and $32768$. % \begin{macrocode} \cs_new_protected:Npn \@@_do_simple_box:N #1 { \bool_if:NTF #1 { \@@_do_leaders_fetch_skip: } { \@@_prev_input_gpop:N \l_@@_head_tl \tl_if_head_eq_meaning:VNT \l_@@_head_tl \tex_shipout:D { \@@_prepare_mag: } \tl_use:N \l_@@_head_tl \scan_stop: \gtl_gput_right:NV \g_@@_output_gtl \l_@@_head_tl \@@_print_action:e { \tl_to_str:N \l_@@_head_tl } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_do_leaders_fetch_skip:} % \begin{macrocode} \cs_new_protected:Npn \@@_do_leaders_fetch_skip: { \@@_get_x_non_relax: \@@_set_cmd: \int_compare:nNnTF \l_@@_head_cmd_int = { \@@_tex_use:n { \mode_if_vertical:TF { vskip } { hskip } } } { \@@_prev_input_gpop:N \l_@@_tmpa_tl \tl_put_left:NV \l_@@_head_tl \l_@@_tmpa_tl \@@_do_append_glue: } { \@@_back_input: \@@_error:nnnnn { improper-leaders } { } { } { } { } \@@_prev_input_gpop:N \l_@@_head_tl \@@_print_action:e { \tl_to_str:N \l_@@_head_tl } } } % \end{macrocode} % \end{macro} % % ^^A todo: less intrusive hooking into \everyhbox/\everyvbox? % \begin{macro}{\@@_do_box_explicit:N} % At this point, the last item in the previous-input sequence is % typically |\setbox0\hbox| or |\raise 3pt\hbox|. Scan for keywords % |to| and |spread| and a left brace. Install a hook in % \tn{everyhbox} or \tn{everyvbox} (whichever \TeX{} is going to % insert in the box). We then retrieve all the material that led to % the current box into \cs{l_@@_head_tl} in order to print it, then % let \TeX{} perform the box operation (here we need to provide the % begin-group token, as it was scanned but not placed in % the previous-input sequence). \TeX{} inserts \tn{everyhbox} or % \tn{everyvbox} just after the begin-group token, and the hook we did % is such that all that material is collected and put into the input % that we will study. % We must remember to find a glue for leaders, and for this % we use a stack of letters |v|, |h| for vertical/horizontal % leaders, and |Z| for normal boxes. % \begin{macrocode} \cs_new_protected:Npn \@@_do_box_explicit:N #1 { \token_if_eq_meaning:NNTF \l_@@_head_token \@@_hbox:w { \@@_box_hook:N \tex_everyhbox:D } { \@@_box_hook:N \tex_everyvbox:D } \@@_scan_spec: \@@_prev_input_gpop:N \l_@@_head_tl \@@_set_action_text:e { \tl_to_str:N \l_@@_head_tl \iow_char:N \{ } \seq_push:Nf \l_@@_leaders_box_seq { \bool_if:NTF #1 { \mode_if_vertical:TF { v } { h } } { Z } } \gtl_gput_right:NV \g_@@_output_gtl \l_@@_head_tl \gtl_gconcat:NNN \g_@@_output_gtl \g_@@_output_gtl \c_group_begin_gtl \tl_use:N \l_@@_head_tl \c_group_begin_token \@@_box_hook_end: } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_box_hook:N} % \begin{macro}{\@@_box_hook:w, \@@_box_hook_end:} % Used to capture the contents of an \tn{everyhbox} or similar, % without altering \tn{everyhbox} too much (just add one token at the % start). The various \texttt{o}-expansions remove % \cs{prg_do_nothing:}, used to avoid losing braces. % \begin{macrocode} \cs_new_protected:Npn \@@_box_hook:N #1 { \tl_set:NV \l_@@_tmpa_tl #1 \str_if_eq:eeF { \tl_head:N \l_@@_tmpa_tl } { \exp_not:N \@@_box_hook:w } { \exp_args:Ne #1 { \exp_not:n { \@@_box_hook:w \prg_do_nothing: } \exp_not:V #1 } } \cs_gset_protected:Npn \@@_box_hook:w ##1 \@@_box_hook_end: { \exp_args:No #1 {##1} \cs_gset_eq:NN \@@_box_hook:w \prg_do_nothing: \gtl_clear:N \l_@@_after_group_gtl \@@_print_action: \@@_back_input:o {##1} \@@_set_action_text:e { \token_to_meaning:N #1 = \tl_to_str:o {##1} } \tl_if_empty:oF {##1} { \@@_print_action: } } } \cs_new_eq:NN \@@_box_hook:w \prg_do_nothing: \cs_new_eq:NN \@@_box_hook_end: \prg_do_nothing: % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\@@_do_leaders_rule:} % After finding a |vrule| or |hrule| command and looking for |depth|, % |heigh| and |width| keywords, we are in the same situation as after % finding a box. Fetch the required skip accordingly. % \begin{macrocode} \cs_new_protected:Npn \@@_do_leaders_rule: { \@@_prev_input:V \l_@@_head_tl \@@_scan_alt_rule: \@@_do_leaders_fetch_skip: } % \end{macrocode} % \end{macro} % % \subsection{Paragraphs} % % \begin{macro}[TF]{\@@_charcode_if_safe:n} % \begin{macrocode} \prg_new_protected_conditional:Npnn \@@_charcode_if_safe:n #1 { TF } { \bool_if:nTF { \int_compare_p:n { #1 = `! } || \int_compare_p:n { `' <= #1 <= `[ } || \int_compare_p:n { #1 = `] } || \int_compare_p:n { ` ` <= #1 <= `z } } { \prg_return_true: } { \prg_return_false: } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_char:n, \@@_char:V, \@@_char:e} % \begin{macrocode} \cs_new_protected:Npn \@@_char:n #1 { \tex_char:D #1 \scan_stop: \@@_charcode_if_safe:nTF {#1} { \tl_set:Ne \l_@@_tmpa_tl { \char_generate:nn {#1} { 12 } } \gtl_gput_right:NV \g_@@_output_gtl \l_@@_tmpa_tl \@@_print_action:e { \tl_to_str:N \l_@@_tmpa_tl } } { \tl_set:Ne \l_@@_tmpa_tl { \exp_not:N \char \int_eval:n {#1} ~ } \gtl_gput_right:NV \g_@@_output_gtl \l_@@_tmpa_tl \@@_print_action:e { " \char_generate:nn {#1} { 12 } " = \tl_to_str:N \l_@@_tmpa_tl } } } \cs_generate_variant:Nn \@@_char:n { V , e } % \end{macrocode} % \end{macro} % % \begin{macro} % {\@@_char_in_mmode:n, \@@_char_in_mmode:V, \@@_char_in_mmode:e} % \begin{macrocode} \cs_new_protected:Npn \@@_char_in_mmode:n #1 { \int_compare:nNnTF { \tex_mathcode:D #1 } = { \sys_if_engine_luatex:TF { "1000000 } { "8000 } } { % math active \@@_active_do:nn {#1} { \gtl_set:Nn \l_@@_head_gtl } \@@_back_input: \@@_print_action:e { \char_generate:nn {#1} { 12 } ~ active } } { \@@_char:n {#1} } } \cs_generate_variant:Nn \@@_char_in_mmode:n { V , e } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_mathchar:n, \@@_mathchar:e} % ^^A todo: \omathchar support % \begin{macrocode} \cs_new_protected:Npn \@@_mathchar:n #1 { \tex_mathchar:D #1 \scan_stop: \tl_set:Ne \l_@@_tmpa_tl { \exp_not:N \mathchar " \int_to_hex:n {#1} ~ } % " \gtl_gput_right:NV \g_@@_output_gtl \l_@@_tmpa_tl \@@_print_action:e { \tl_to_str:N \l_@@_tmpa_tl } } \cs_generate_variant:Nn \@@_mathchar:n { e } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_new_graf:N} % The argument is a boolean, indicating whether the paragraph should % be indented. We have much less work to do here than \TeX{} itself. % Our only task is to correctly position the \tn{everypar} tokens in % the input that we will read, rather than letting \TeX{} run the code % right away. % \begin{macrocode} \cs_new_protected:Npn \@@_new_graf:N #1 { \tl_set:NV \l_@@_tmpa_tl \@@_everypar:w \@@_everypar:w { } \bool_if:NTF #1 { \tex_indent:D } { \tex_noindent:D } \exp_args:NV \@@_everypar:w \l_@@_tmpa_tl \@@_back_input:V \l_@@_tmpa_tl \@@_print_action:e { \g_@@_action_text_str \c_space_tl : ~ \token_to_str:N \everypar = { \tl_to_str:N \l_@@_tmpa_tl } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_par_if_hmode:} % This is like the |end_graf| procedure in \TeX{}. % \begin{macrocode} \cs_new_protected:Npn \@@_par_if_hmode: { \mode_if_horizontal:T { \@@_par: } } % \end{macrocode} % \end{macro} % % ^^A todo: Do we need to do more? % \begin{macro}{\@@_par:} % \begin{macrocode} \cs_new_protected:Npn \@@_par: { \tex_par:D \gtl_gput_right:Nn \g_@@_output_gtl { \par } \@@_print_action:e { Paragraph~end. } } % \end{macrocode} % \end{macro} % % ^^A todo: do something to the \output routine? % \begin{macro}{\@@_build_page:} % \begin{macrocode} \cs_new_protected:Npn \@@_build_page: { } % \end{macrocode} % \end{macro} % % \subsection{Groups} % % \begin{variable}{\l_@@_choice_int} % Used by \tn{mathchoice} etc to keep % track of which argument we are currently in. % \begin{macrocode} \int_new:N \l_@@_choice_int % \end{macrocode} % \end{variable} % % \begin{macro}{\@@_handle_right_brace:} % When an end-group character is sensed, the result depends on the % current group type. Suppress the |after_group| tokens in % \tn{discretionary} or \tn{mathchoice}. % \begin{macrocode} \cs_new_protected:Npn \@@_handle_right_brace: { \int_compare:nTF { 1 <= \@@_currentgrouptype: <= 13 } { \gtl_gconcat:NNN \g_@@_output_gtl \g_@@_output_gtl \c_group_end_gtl \int_case:nnF \@@_currentgrouptype: { { 10 } { } % disc { 13 } { } % math_choice } { \@@_back_input_gtl:N \l_@@_after_group_gtl } \int_case:nn \@@_currentgrouptype: { { 1 } { \@@_end_simple_group: } % simple { 2 } { \@@_end_box_group: } % hbox { 3 } { \@@_end_box_group: } % adjusted_hbox { 4 } { \@@_par_if_hmode: \@@_end_box_group: } % vbox { 5 } { \@@_par_if_hmode: \@@_end_box_group: } % vtop { 6 } { \@@_end_align_group: } % align { 7 } { \@@_end_no_align_group: } % no_align { 8 } { \@@_end_output_group: } % output { 9 } { \@@_end_simple_group: } % math { 10 } { \@@_end_choice_group:NN 2 \discretionary } % disc { 11 } { \@@_par_if_hmode: \@@_end_simple_group: } % insert { 12 } { \@@_par_if_hmode: \@@_end_simple_group: } % vcenter { 13 } { \@@_end_choice_group:NN 3 \mathchoice } % math_choice } } { % bottom_level, semi_simple, math_shift, math_left \l_@@_head_token \@@_print_action: } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_end_simple_group:} % This command is used to simply end a group, when there are no % specific operations to perform. % \begin{macrocode} \cs_new_protected:Npn \@@_end_simple_group: { \l_@@_head_token \@@_print_action: } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_end_box_group:} % The end of an explicit box (generated by \tn{vtop}, \tn{vbox}, or % \tn{hbox}) can either be simple, or can mean that we need to find a % skip for a \tn{leaders}/\tn{cleaders}/\tn{xleaders} construction. % \begin{macrocode} \cs_new_protected:Npn \@@_end_box_group: { \seq_pop:NN \l_@@_leaders_box_seq \l_@@_tmpa_tl \exp_args:No \@@_end_box_group_aux:n { \l_@@_tmpa_tl } } \cs_new_protected:Npn \@@_end_box_group_aux:n #1 { \str_if_eq:eeTF {#1} { Z } { \@@_end_simple_group: } { \@@_get_x_non_relax: \@@_set_cmd: \int_compare:nNnTF \l_@@_head_cmd_int = { \@@_tex_use:n { #1 skip } } { \tl_put_left:Nn \l_@@_head_tl { \c_group_end_token } \@@_do_append_glue: } { \@@_back_input: \c_group_end_token \group_begin: \group_end: \@@_print_action: } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_end_align_group:} % \begin{macrocode} \cs_new_protected:Npn \@@_end_align_group: { \@@_not_implemented:n { end_align_group } \@@_end_simple_group: } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_end_no_align_group:} % ^^A todo: should start with \@@_par_if_hmode: % \begin{macrocode} \cs_new_protected:Npn \@@_end_no_align_group: { \@@_not_implemented:n { end_no_align_group } \@@_end_simple_group: } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_end_output_group:} % \begin{macrocode} \cs_new_protected:Npn \@@_end_output_group: { \@@_not_implemented:n { end_output_group } \@@_end_simple_group: } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_end_choice_group:NN, \@@_end_choice_group:nN} % \begin{macrocode} \cs_new_protected:Npn \@@_end_choice_group:NN #1#2 { \int_compare:nNnTF \l_@@_choice_int > {#1} { \@@_back_input_gtl:N \l_@@_after_group_gtl \c_group_end_token \@@_print_action:e { \token_to_str:N #2 \prg_replicate:nn { #1 + 1 } { {...} } } } { \exp_args:NV \@@_end_choice_group:nN \l_@@_choice_int #2 } } \cs_new_protected:Npn \@@_end_choice_group:nN #1#2 { \@@_scan_left_brace: \gtl_gconcat:NNN \g_@@_output_gtl \g_@@_output_gtl \c_group_begin_gtl \@@_back_input_gtl:N \l_@@_after_group_gtl \use:n \c_group_end_token \use:n \c_group_begin_token \int_set:Nn \l_@@_choice_int { #1 + 1 } \gtl_clear:N \l_@@_after_group_gtl \@@_print_action:e { \token_to_str:N #2 \prg_replicate:nn {#1} { { ... } } \iow_char:N \{ } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_off_save:} % \begin{macrocode} \cs_new_protected:Npn \@@_off_save: { \int_compare:nNnTF \@@_currentgrouptype: = { 0 } { % bottom-level \@@_error:neeee { extra-close } { \token_to_meaning:N \l_@@_head_token } { } { } { } } { \@@_back_input: \int_case:nnF \@@_currentgrouptype: { { 14 } % semi_simple_group { \gtl_set:Nn \l_@@_head_gtl { \group_end: } } { 15 } % math_shift_group { \gtl_set:Nn \l_@@_head_gtl { $ } } % $ { 16 } % math_left_group { \gtl_set:Nn \l_@@_head_gtl { \tex_right:D . } } } { \gtl_set_eq:NN \l_@@_head_gtl \c_group_end_gtl } \@@_back_input: \@@_error:neeee { off-save } { \gtl_to_str:N \l_@@_head_gtl } { } { } { } } } % \end{macrocode} % \end{macro} % % \subsection{Modes} % ^^A todo: implement \aftergroup and things happening at end-group % % \begin{macro} % {\@@_mode_math:n, \@@_mode_non_math:n, \@@_mode_vertical:n} % \begin{macrocode} \cs_new_protected:Npn \@@_mode_math:n #1 { \mode_if_math:TF {#1} { \@@_insert_dollar_error: } } \cs_new_protected:Npn \@@_mode_non_math:n #1 { \mode_if_math:TF { \@@_insert_dollar_error: } {#1} } \cs_new_protected:Npn \@@_mode_vertical:n #1 { \mode_if_math:TF { \@@_insert_dollar_error: } { \mode_if_horizontal:TF { \@@_head_for_vmode: } {#1} } } \cs_new_protected:Npn \@@_mode_non_vertical:n #1 { \mode_if_vertical:TF { \@@_back_input: \@@_new_graf:N \c_true_bool } {#1} } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_head_for_vmode:} % See \TeX{}'s |head_for_vmode|. % \begin{macrocode} \cs_new_protected:Npn \@@_head_for_vmode: { \mode_if_inner:TF { \token_if_eq_meaning:NNTF \l_@@_head_token \tex_hrule:D { \@@_error:nnnnn { hrule-bad-mode } { } { } { } { } \@@_print_action: } { \@@_off_save: } } { \@@_back_input: \gtl_set:Nn \l_@@_head_gtl { \par } \@@_back_input: } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_goto_inner_math:} % \begin{macrocode} \cs_new_protected:Npn \@@_goto_inner_math: { \@@_box_hook:N \tex_everymath:D $ % $ \@@_box_hook_end: } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_goto_display_math:} % \begin{macrocode} \cs_new_protected:Npn \@@_goto_display_math: { \@@_box_hook:N \tex_everydisplay:D $ $ \@@_box_hook_end: } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_after_math:} % In display math mode, or in a group started by \tn{eqno} or % \tn{leqno} (namely in inner math mode with non-zero % \cs{l_@@_choice_int}), search for another % |$|; otherwise simply close the inner math. % \begin{macrocode} \cs_new_protected:Npn \@@_after_math: { \mode_if_inner:TF { \int_compare:nNnTF \l_@@_choice_int > 0 } { \use_i:nn } { \gtl_gput_right:NV \g_@@_output_gtl \l_@@_head_tl \@@_get_x_next: \token_if_eq_catcode:NNF \l_@@_head_token \c_math_toggle_token { \@@_back_input: \tl_set:Nn \l_@@_head_tl { $ } % $ \@@_error:nnnnn { missing-dollar } { } { } { } { } } \gtl_gput_right:NV \g_@@_output_gtl \l_@@_head_tl \@@_back_input_gtl:N \l_@@_after_group_gtl $ $ } { \gtl_gput_right:NV \g_@@_output_gtl \l_@@_head_tl \@@_back_input_gtl:N \l_@@_after_group_gtl $ % $ } \@@_print_action: } % \end{macrocode} % \end{macro} % % \subsection{Commands} % % We will implement commands in order of their command codes (some of % the more elaborate commands call auxiliaries defined in other % sections). Some cases are forbidden. % % \begin{macro}{\@@_forbidden_case:} % \begin{macrocode} \cs_new_protected:Npn \@@_forbidden_case: { \@@_tex_error:nV { forbidden-case } \l_@@_head_tl } % \end{macrocode} % \end{macro} % % \subsubsection{Characters: from 0 to 15} % % This section is about command codes in the range $[0, 15]$. % \begin{itemize} % \item |relax=0| for \tn{relax}. % \item |begin-group_char=1| for begin-group characters (catcode~$1$). % \item |end-group_char=2| for end-group characters (catcode~$2$). % \item |math_char=3| for math shift (math toggle in \pkg{expl3}) % characters (catcode~$3$). % \item |tab_mark=4| for \tn{span} % \item |alignment_char=4| for alignment tab characters (catcode~$4$). % \item |car_ret=5| for \tn{cr} and \tn{crcr}. % \item |macro_char=6| for macro parameter characters (catcode~$6$). % \item |superscript_char=7| for superscript characters (catcode~$7$). % \item |subscript_char=8| for subscript characters (catcode~$8$). % \item |endv=9| for |?|. % \item |blank_char=10| for blank spaces (catcode~$10$). % \item |the_char=11| for letters (catcode~$11$). % \item |other_char=12| for other characters (catcode~$12$). % \item |par_end=13| for \tn{par}. % \item |stop=14| for \tn{end} and \tn{dump}. % \item |delim_num=15| for \tn{delimiter}. % \end{itemize} % Not implemented at all: |endv|. % % \tn{relax} does nothing. % \begin{macrocode} \@@_new_tex_cmd:nn { relax } % 0 { \token_if_eq_meaning:NNT \l_@@_head_token \@@_special_relax: { \exp_after:wN \@@_token_if_expandable:NTF \l_@@_head_tl { \@@_set_action_text:e { \iow_char:N \\notexpanded: \g_@@_action_text_str } } { } } \@@_print_action: } % \end{macrocode} % % Begin-group characters are sent to the output, as their grouping % behaviour may affect the scope of font changes, for instance. They are % also performed. % \begin{macrocode} \@@_new_tex_cmd:nn { begin-group_char } % 1 { \gtl_gconcat:NNN \g_@@_output_gtl \g_@@_output_gtl \c_group_begin_gtl \@@_print_action: \l_@@_head_token \gtl_clear:N \l_@@_after_group_gtl } % \end{macrocode} % % \begin{macrocode} \@@_new_tex_cmd:nn { end-group_char } % 2 { \@@_handle_right_brace: } % \end{macrocode} % % Math shift characters quit vertical mode, and start math mode. % \begin{macrocode} \@@_new_tex_cmd:nn { math_char } % 3 { \@@_mode_non_vertical:n { \mode_if_math:TF { \int_compare:nNnTF \@@_currentgrouptype: = { 15 } % math_shift_group { \@@_after_math: } { \@@_off_save: } } { \gtl_gput_right:NV \g_@@_output_gtl \l_@@_head_tl \@@_get_next: \token_if_eq_catcode:NNTF \l_@@_head_token \c_math_toggle_token { \mode_if_inner:TF { \@@_back_input: \@@_goto_inner_math: } { \gtl_gput_right:NV \g_@@_output_gtl \l_@@_head_tl \@@_goto_display_math: } } { \@@_back_input: \@@_goto_inner_math: } } } } % \end{macrocode} % % Some commands are errors when they reach \TeX{}'s stomach. Among % others, |tab_mark=alignment_char|, |car_ret| and |macro_char|. We let % \TeX{} insert the proper error. % \begin{macrocode} \@@_new_tex_cmd:nn { alignment_char } % 4 { \l_@@_head_token \@@_print_action: } \@@_new_tex_cmd:nn { car_ret } % 5 { \l_@@_head_token \@@_print_action: } \@@_new_tex_cmd:nn { macro_char } % 6 { \l_@@_head_token \@@_print_action: } % \end{macrocode} % % \begin{macrocode} \@@_new_tex_cmd:nn { superscript_char } % 7 { \@@_mode_math:n { \@@_sub_sup: } } \@@_new_tex_cmd:nn { subscript_char } % 8 { \@@_mode_math:n { \@@_sub_sup: } } \cs_new_protected:Npn \@@_sub_sup: { \@@_prev_input_gpush:N \l_@@_head_tl \@@_print_action: \@@_do_one_atom: } \cs_new_protected:Npn \@@_do_one_atom: { \@@_get_x_non_relax: \@@_set_cmd: \int_case:nnTF \l_@@_head_cmd_int { { \@@_tex_use:n { the_char } } { \@@_prev_input:V \l_@@_head_tl } { \@@_tex_use:n { other_char } } { \@@_prev_input:V \l_@@_head_tl } { \@@_tex_use:n { char_given } } { \@@_prev_input:V \l_@@_head_tl } { \@@_tex_use:n { char_num } } { \@@_prev_input:V \l_@@_head_tl \@@_scan_int: } { \@@_tex_use:n { math_char_num } } { \@@_prev_input:V \l_@@_head_tl \@@_scan_int: } { \@@_tex_use:n { math_given } } { \@@_prev_input:V \l_@@_head_tl } { \@@_tex_use:n { delim_num } } { \@@_prev_input:V \l_@@_head_tl \@@_scan_int: } } { \@@_prev_input_gpop:N \l_@@_head_tl \gtl_gput_right:NV \g_@@_output_gtl \l_@@_head_tl \tl_use:N \l_@@_head_tl \scan_stop: } { \@@_back_input: \@@_scan_left_brace: \@@_prev_input_gpop:N \l_@@_head_tl \gtl_gput_right:NV \g_@@_output_gtl \l_@@_head_tl \gtl_gconcat:NNN \g_@@_output_gtl \g_@@_output_gtl \c_group_begin_gtl \tl_use:N \l_@@_head_tl \c_group_begin_token } \@@_print_action:e { \tl_to_str:N \l_@@_head_tl } } % \end{macrocode} % % \begin{macrocode} \@@_new_tex_cmd:nn { endv } % 9 { \@@_mode_non_math:n { \@@_not_implemented:n { alignments } } } % \end{macrocode} % % Blank spaces are ignored in vertical and math modes in the same way as % \tn{relax} is in all modes. In horizontal mode, add them to the % output. % \begin{macrocode} \@@_new_tex_cmd:nn { blank_char } % 10 { \mode_if_horizontal:T { \gtl_gput_right:Nn \g_@@_output_gtl { ~ } \l_@@_head_token } \@@_print_action: } % \end{macrocode} % % Letters and other characters leave vertical mode. % \begin{macrocode} \@@_new_tex_cmd:nn { the_char } % 11 { \@@_mode_non_vertical:n { \tl_set:Ne \l_@@_tmpa_tl { ` \@@_token_to_char:N \l_@@_head_token } \mode_if_math:TF { \@@_char_in_mmode:V \l_@@_tmpa_tl } { \@@_char:V \l_@@_tmpa_tl } } } \@@_new_eq_tex_cmd:nn { other_char } { the_char } % 12 % \end{macrocode} % % \begin{macrocode} \@@_new_tex_cmd:nn { par_end } % 13 { \@@_mode_non_math:n { \mode_if_vertical:TF { \@@_par: } { % if align_state<0 then off_save; \@@_par_if_hmode: \mode_if_vertical:T { \mode_if_inner:F { \@@_build_page: } } } } } % \end{macrocode} % % ^^A todo: \dump only at bottom-level (\currentgrouptype=0) % \begin{macrocode} \@@_new_tex_cmd:nn { stop } % 14 { \@@_mode_vertical:n { \mode_if_inner:TF { \@@_forbidden_case: } { % ^^A todo: unless its_all_over \int_gdecr:N \g_@@_ends_int \int_compare:nNnTF \g_@@_ends_int > 0 { \@@_back_input: \@@_back_input:n { \@@_hbox:w to \tex_hsize:D { } \tex_vfill:D \tex_penalty:D - '10000000000 ~ } \@@_build_page: \@@_print_action:e { End~everything! } } { \@@_print_outcome: \l_@@_head_token } } } } % \end{macrocode} % % \begin{macrocode} \@@_new_tex_cmd:nn { delim_num } % 15 { \@@_mode_math:n { \@@_prev_input_gpush:N \l_@@_head_tl \@@_print_action: \@@_scan_int: \@@_prev_input_gpop:N \l_@@_head_tl \tl_use:N \l_@@_head_tl \scan_stop: \@@_print_action:e { \tl_to_str:N \l_@@_head_tl } } } % \end{macrocode} % % \subsubsection{Boxes: from 16 to 31} % % \begin{itemize} % \item |char_num=16| for \tn{char} % \item |math_char_num=17| for \tn{mathchar} % \item |mark=18| for \tn{mark} and \tn{marks} % \item |xray=19| for \tn{show}, \tn{showbox}, \tn{showthe}, % \tn{showlists}, \tn{showgroups}, \tn{showtokens}, \tn{showifs}. % \item |make_box=20| for \tn{box}, \tn{copy}, \tn{lastbox}, % \tn{vsplit}, \tn{vtop}, \tn{vbox}, and \tn{hbox}~(106). % \item |hmove=21| for \tn{moveright} and \tn{moveleft}. % \item |vmove=22| for \tn{lower} and \tn{raise}. % \item |un_hbox=23| for \tn{unhbox} and \tn{unhcopy}. % \item |unvbox=24| for \tn{unvbox}, \tn{unvcopy}, \tn{pagediscards}, % and \tn{splitdiscards}. % \item |remove_item=25| for \tn{unpenalty}~(12), \tn{unkern}~(11), % \tn{unskip}~(10). % \item |hskip=26| for \tn{hfil}, \tn{hfill}, \tn{hss}, \tn{hfilneg}, % \tn{hskip}. % \item |vskip=27| for \tn{vfil}, \tn{vfill}, \tn{vss}, \tn{vfilneg}, % \tn{vskip}. % \item |mskip=28| for \tn{mskip}~(5). % \item |kern=29| for \tn{kern}~(1). % \item |mkern=30| for \tn{mkern}~(99). % \item |leader_ship=31| for \tn{shipout}~(99), \tn{leaders}~(100), % \tn{cleaders}~(101), \tn{xleaders}~(102). % \end{itemize} % % \tn{char} leaves vertical mode, then scans an integer operand, then % calls \cs{@@_char_in_mmode:n} or \cs{@@_char:n} depending on the mode. % See implementation of |the_char| and |other_char|. % \begin{macrocode} \@@_new_tex_cmd:nn { char_num } % 16 { \@@_mode_non_vertical:n { \@@_prev_input_gpush:N \l_@@_head_tl \@@_print_action: \@@_scan_int: \@@_prev_input_gpop:N \l_@@_head_tl \mode_if_math:TF { \@@_char_in_mmode:e { \tl_tail:N \l_@@_head_tl } } { \@@_char:e { \tl_tail:N \l_@@_head_tl } } } } % \end{macrocode} % % Only allowed in math mode, \tn{mathchar} reads an integer operand, and % calls \cs{@@_mathchar:n}, which places the corresponding math % character in the \cs{g_@@_output_gtl}, and in the actual output. % \begin{macrocode} \@@_new_tex_cmd:nn { math_char_num } % 17 { \@@_mode_math:n { \@@_prev_input_gpush:N \l_@@_head_tl \@@_print_action: \@@_scan_int: \@@_prev_input_gpop:N \l_@@_head_tl \@@_mathchar:e { \tl_tail:N \l_@@_head_tl } } } % \end{macrocode} % % \begin{macrocode} \@@_new_tex_cmd:nn { mark } % 18 { \@@_prev_input_gpush:N \l_@@_head_tl \@@_print_action: \int_compare:nNnF \l_@@_head_char_int = 0 { \@@_scan_int: } \@@_prev_input_gpush: \@@_scan_toks:NN \c_false_bool \c_true_bool \@@_prev_input_gpop:N \l_@@_tmpa_tl \@@_prev_input_gpop:N \l_@@_head_tl \@@_print_action:e { \tl_to_str:N \l_@@_head_tl \tl_to_str:N \l_@@_tmpa_tl } \tl_put_right:Ne \l_@@_head_tl { { \exp_not:N \exp_not:n \exp_not:V \l_@@_tmpa_tl } } \tl_use:N \l_@@_head_tl } % \end{macrocode} % % We now implement the primitives \tn{show}, \tn{showbox}, \tn{showthe}, % \tn{showlists}, \tn{showgroups}, \tn{showtokens} and \tn{showifs}. % Those with no operand are sent to \TeX{} after printing the action. % Those with operands print first, then scan their operands, then are % sent to \TeX{}. The case of \tn{show} is a bit special, as its % operand is a single token, which cannot easily be put into the % the previous-input sequence in general. Since no expansion can occur, % simply grab the token and show it. % \begin{macrocode} \@@_new_tex_cmd:nn { xray } % 19 { \@@_prev_input_gpush:N \l_@@_head_tl \@@_print_action: \int_case:nnF \l_@@_head_char_int { { 0 } { % show \@@_get_next: \@@_prev_input_gpop:N \l_@@_tmpa_tl \token_if_eq_meaning:NNTF \l_@@_head_token \@@_special_relax: { \exp_after:wN \exp_after:wN \exp_after:wN \l_@@_tmpa_tl \exp_after:wN \exp_not:N \l_@@_head_tl } { \gtl_head_do:NN \l_@@_head_gtl \l_@@_tmpa_tl } } { 2 } { % showthe \@@_get_x_next: \@@_rescan_something_internal:n { 5 } \@@_prev_input_gpop:N \l_@@_head_tl \exp_args:Ne \use:n % better display than \use:e { \tex_showtokens:D { \tl_tail:N \l_@@_head_tl } } } } { % no operand for showlists, showgroups, showifs \int_compare:nNnT \l_@@_head_char_int = 1 % showbox { \@@_scan_int: } \int_compare:nNnT \l_@@_head_char_int = 5 % showtokens { \@@_scan_toks:NN \c_false_bool \c_false_bool } \@@_prev_input_gpop:N \l_@@_head_tl \tl_use:N \l_@@_head_tl \scan_stop: } } % \end{macrocode} % % |make_box=20| for \tn{box}, \tn{copy}, \tn{lastbox}, % \tn{vsplit}, \tn{vtop}, \tn{vbox}, and \tn{hbox}~(106). % \begin{macrocode} \@@_new_tex_cmd:nn { make_box } % 20 { \@@_prev_input_gpush: \@@_back_input: \@@_do_box:N \c_false_bool } % \end{macrocode} % % \begin{macro}{\@@_do_move:} % Scan a dimension and a box, and perform the shift, printing the % appropriate action. % \begin{macrocode} \cs_new_protected:Npn \@@_do_move: { \@@_prev_input_gpush:N \l_@@_head_tl \@@_print_action: \@@_scan_normal_dimen: \@@_do_box:N \c_false_bool } % \end{macrocode} % \end{macro} % % |hmove=21| for \tn{moveright} and \tn{moveleft}. % \begin{macrocode} \@@_new_tex_cmd:nn { hmove } % 21 { \mode_if_vertical:TF { \@@_do_move: } { \@@_forbidden_case: } } % \end{macrocode} % % |vmove=22| for \tn{lower} and \tn{raise}. % \begin{macrocode} \@@_new_tex_cmd:nn { vmove } % 22 { \mode_if_vertical:TF { \@@_forbidden_case: } { \@@_do_move: } } % \end{macrocode} % % \begin{macro}{\@@_do_unpackage:} % \begin{macrocode} \cs_new_protected:Npn \@@_do_unpackage: { \@@_prev_input_gpush:N \l_@@_head_tl \@@_print_action: \@@_scan_int: \@@_prev_input_gpop:N \l_@@_head_tl \tl_use:N \l_@@_head_tl \scan_stop: \@@_print_action:e { \tl_to_str:N \l_@@_head_tl } } % \end{macrocode} % \end{macro} % % |un_hbox=23| for \tn{unhbox} and \tn{unhcopy}. % \begin{macrocode} \@@_new_tex_cmd:nn { un_hbox } % 23 { \@@_mode_non_vertical:n { \@@_do_unpackage: } } % \end{macrocode} % % |unvbox=24| for \tn{unvbox}, \tn{unvcopy}, \tn{pagediscards}, and % \tn{splitdiscards}. The latter two take no operands, so we just let % \TeX{} do its thing, then we show the action. % \begin{macrocode} \@@_new_tex_cmd:nn { un_vbox } % 24 { \@@_mode_vertical:n { \int_compare:nNnTF \l_@@_head_char_int > { 1 } { \l_@@_head_token \@@_print_action: } { \@@_do_unpackage: } } } % \end{macrocode} % % |remove_item=25| for \tn{unpenalty}~(12), \tn{unkern}~(11), % \tn{unskip}~(10). Those commands only act on \TeX{}'s box/glue data % structures, which \pkg{unravel} does not (and cannot) care about. % \begin{macrocode} \@@_new_tex_cmd:nn { remove_item } % 25 { \l_@@_head_token \@@_print_action: } % \end{macrocode} % % \begin{macro}{\@@_do_append_glue:} % For \tn{hfil}, \tn{hfill}, \tn{hss}, \tn{hfilneg} and their vertical % analogs, simply call the primitive then print the action. For % \tn{hskip}, \tn{vskip} and \tn{mskip}, read a normal glue or a mu % glue (\cs{l_@@_head_char_int} is $4$ or~$5$), then call the % primitive with that operand, and print the whole thing as an action. % \begin{macrocode} \cs_new_protected:Npn \@@_do_append_glue: { \int_compare:nNnTF \l_@@_head_char_int < { 4 } { \tl_use:N \l_@@_head_tl \@@_print_action: } { \@@_prev_input_gpush:N \l_@@_head_tl \@@_print_action: \exp_args:Nf \@@_scan_glue:n { \int_eval:n { \l_@@_head_char_int - 2 } } \@@_prev_input_gpop:N \l_@@_head_tl \tl_use:N \l_@@_head_tl \scan_stop: \@@_print_action:e { \tl_to_str:N \l_@@_head_tl } } } % \end{macrocode} % \end{macro} % % |hskip=26| for \tn{hfil}, \tn{hfill}, \tn{hss}, \tn{hfilneg}, % \tn{hskip}. % \begin{macrocode} \@@_new_tex_cmd:nn { hskip } % 26 { \@@_mode_non_vertical:n { \@@_do_append_glue: } } % \end{macrocode} % % |vskip=27| for \tn{vfil}, \tn{vfill}, \tn{vss}, \tn{vfilneg}, % \tn{vskip}. % \begin{macrocode} \@@_new_tex_cmd:nn { vskip } % 27 { \@@_mode_vertical:n { \@@_do_append_glue: } } % \end{macrocode} % % |mskip=28| for \tn{mskip}~(5). % \begin{macrocode} \@@_new_tex_cmd:nn { mskip } % 28 { \@@_mode_math:n { \@@_do_append_glue: } } % \end{macrocode} % % \begin{macro}{\@@_do_append_kern:} % See \cs{@@_do_append_glue:}. This function is used for the % primitives \tn{kern} and \tn{mkern} only. % \begin{macrocode} \cs_new_protected:Npn \@@_do_append_kern: { \@@_prev_input_gpush:N \l_@@_head_tl \@@_print_action: \token_if_eq_meaning:NNTF \l_@@_head_token \tex_kern:D { \@@_scan_dimen:nN { 2 } \c_false_bool } { \@@_scan_dimen:nN { 3 } \c_false_bool } \@@_prev_input_gpop:N \l_@@_head_tl \tl_use:N \l_@@_head_tl \scan_stop: \@@_print_action:e { \tl_to_str:N \l_@@_head_tl } } % \end{macrocode} % \end{macro} % % |kern=29| for \tn{kern}~(1). % \begin{macrocode} \@@_new_tex_cmd:nn { kern } % 29 { \@@_do_append_kern: } % \end{macrocode} % % |mkern=30| for \tn{mkern}~(99). % \begin{macrocode} \@@_new_tex_cmd:nn { mkern } % 30 { \@@_mode_math:n { \@@_do_append_kern: } } % \end{macrocode} % % |leader_ship=31| for \tn{shipout}~(99), \tn{leaders}~(100), % \tn{cleaders}~(101), \tn{xleaders}~(102). % \begin{macrocode} \@@_new_tex_cmd:nn { leader_ship } % 31 { \@@_prev_input_gpush:N \l_@@_head_tl \@@_print_action: \tl_if_head_eq_meaning:VNTF \l_@@_head_tl \tex_shipout:D { \@@_do_box:N \c_false_bool } { \@@_do_box:N \c_true_bool } } % \end{macrocode} % % \subsubsection{From 32 to 47} % % \begin{itemize} % \item |halign=32| % \item |valign=33| % \item |no_align=34| % \item |vrule=35| % \item |hrule=36| % \item |insert=37| % \item |vadjust=38| % \item |ignore_spaces=39| % \item |after_assignment=40| % \item |after_group=41| % \item |break_penalty=42| % \item |start_par=43| % \item |ital_corr=44| % \item |accent=45| % \item |math_accent=46| % \item |discretionary=47| % \end{itemize} % % \begin{macrocode} \@@_new_tex_cmd:nn { halign } % 32 { \@@_not_implemented:n { halign } } \@@_new_tex_cmd:nn { valign } % 33 { \@@_not_implemented:n { valign } } \@@_new_tex_cmd:nn { no_align } % 34 { \l_@@_head_token \@@_print_action: } % \end{macrocode} % % \begin{macrocode} \@@_new_tex_cmd:nn { vrule } % 35 { \@@_mode_non_vertical:n { \@@_do_rule: } } \@@_new_tex_cmd:nn { hrule } % 36 { \@@_mode_vertical:n { \@@_do_rule: } } \cs_new_protected:Npn \@@_do_rule: { \@@_prev_input_gpush:N \l_@@_head_tl \@@_print_action: \@@_scan_alt_rule: \@@_prev_input_gpop:N \l_@@_head_tl \tl_use:N \l_@@_head_tl \scan_stop: \@@_print_action:e { \tl_to_str:N \l_@@_head_tl } } % \end{macrocode} % % \begin{macrocode} \@@_new_tex_cmd:nn { insert } % 37 { \@@_prev_input_gpush:N \l_@@_head_tl \@@_print_action: \@@_scan_int: \@@_begin_insert_or_adjust: } \@@_new_tex_cmd:nn { vadjust } % 38 { \mode_if_vertical:TF { \@@_forbidden_case: } { \@@_prev_input_gpush:N \l_@@_head_tl \@@_print_action: \@@_scan_keyword:nTF { pPrReE } \@@_begin_insert_or_adjust: } } \cs_new_protected:Npn \@@_begin_insert_or_adjust: { \@@_scan_left_brace: \@@_prev_input_gpop:N \l_@@_head_tl \gtl_gput_right:NV \g_@@_output_gtl \l_@@_head_tl \gtl_gconcat:NNN \g_@@_output_gtl \g_@@_output_gtl \c_group_begin_gtl \tl_use:N \l_@@_head_tl \c_group_begin_token \@@_print_action:e { \tl_to_str:N \l_@@_head_tl \iow_char:N \{ } } % \end{macrocode} % % \begin{macrocode} \@@_new_tex_cmd:nn { ignore_spaces } % 39 { \token_if_eq_meaning:NNTF \l_@@_head_token \tex_ignorespaces:D { \@@_print_action: \@@_get_x_non_blank: \@@_set_cmd: \@@_do_step: } { \@@_not_implemented:n { pdfprimitive } } } % \end{macrocode} % % \begin{macrocode} \@@_new_tex_cmd:nn { after_assignment } % 40 { \tl_set_eq:NN \l_@@_tmpa_tl \l_@@_head_tl \@@_get_next: \gtl_gset_eq:NN \g_@@_after_assignment_gtl \l_@@_head_gtl \@@_print_action:e { Afterassignment:~\tl_to_str:N \l_@@_tmpa_tl \gtl_to_str:N \l_@@_head_gtl } } % \end{macrocode} % % Save the next token at the end of \cs{l_@@_after_group_gtl}, unless % we are at the bottom group level, in which case, the token is ignored % completely. % \begin{macrocode} \@@_new_tex_cmd:nn { after_group } % 41 { \tl_set_eq:NN \l_@@_tmpa_tl \l_@@_head_tl \@@_get_next: \int_compare:nNnTF \@@_currentgrouptype: = 0 { \@@_print_action:e { Aftergroup~(level~0~=>~dropped):~ \tl_to_str:N \l_@@_tmpa_tl \gtl_to_str:N \l_@@_head_gtl } } { \gtl_concat:NNN \l_@@_after_group_gtl \l_@@_after_group_gtl \l_@@_head_gtl \@@_print_action:e { Aftergroup:~\tl_to_str:N \l_@@_tmpa_tl \gtl_to_str:N \l_@@_head_gtl } } } % \end{macrocode} % % See \cs{@@_do_append_glue:}. % \begin{macrocode} \@@_new_tex_cmd:nn { break_penalty } % 42 { \@@_prev_input_gpush:N \l_@@_head_tl \@@_print_action: \@@_scan_int: \@@_prev_input_gpop:N \l_@@_head_tl \tl_use:N \l_@@_head_tl \scan_stop: \@@_print_action:e { \tl_to_str:N \l_@@_head_tl } } % \end{macrocode} % % \begin{macrocode} \@@_new_tex_cmd:nn { start_par } % 43 { \mode_if_vertical:TF { \token_if_eq_meaning:NNTF \l_@@_head_token \tex_noindent:D { \@@_new_graf:N \c_false_bool } { \@@_new_graf:N \c_true_bool } } { \int_compare:nNnT \l_@@_head_char_int = { 1 } % indent { \@@_hbox:w width \tex_parindent:D { } \gtl_gput_right:NV \g_@@_output_gtl \l_@@_head_tl } \@@_print_action: } } % \end{macrocode} % % \begin{macrocode} \@@_new_tex_cmd:nn { ital_corr } % 44 { \mode_if_vertical:TF { \@@_forbidden_case: } { \l_@@_head_token \@@_print_action: } } % \end{macrocode} % % \begin{macro}{\@@_do_accent:} % \begin{macrocode} \cs_new_protected:Npn \@@_do_accent: { \@@_prev_input_gpush:N \l_@@_head_tl \@@_print_action: \@@_scan_int: \@@_do_assignments: \bool_if:nTF { \token_if_eq_catcode_p:NN \l_@@_head_token \c_catcode_letter_token || \token_if_eq_catcode_p:NN \l_@@_head_token \c_catcode_other_token || \int_compare_p:nNn \l_@@_head_cmd_int = { \@@_tex_use:n { char_given } } } { \@@_prev_input:V \l_@@_head_tl } { \token_if_eq_meaning:NNTF \l_@@_head_token \tex_char:D { \@@_prev_input:V \l_@@_head_tl \@@_scan_int: } { \@@_break:w } } \@@_prev_input_gpop:N \l_@@_head_tl \gtl_gput_right:NV \g_@@_output_gtl \l_@@_head_tl \tl_use:N \l_@@_head_tl \scan_stop: \@@_print_action:e { \tl_to_str:N \l_@@_head_tl } \@@_break_point: } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_do_math_accent:} % \TeX{} will complain if \cs{l_@@_head_tl} happens to start with % \tn{accent} (the user used \tn{accent} in math mode). % \begin{macrocode} \cs_new_protected:Npn \@@_do_math_accent: { \@@_prev_input_gpush:N \l_@@_head_tl \@@_print_action: \@@_scan_int: \@@_do_one_atom: } % \end{macrocode} % \end{macro} % % \begin{macrocode} \@@_new_tex_cmd:nn { accent } % 45 { \@@_mode_non_vertical:n { \mode_if_math:TF { \@@_do_math_accent: } { \@@_do_accent: } } } % \end{macrocode} % % \begin{macrocode} \@@_new_tex_cmd:nn { math_accent } % 46 { \@@_mode_math:n { \@@_do_math_accent: } } % \end{macrocode} % % \begin{macrocode} \@@_new_tex_cmd:nn { discretionary } % 47 { \@@_mode_non_vertical:n { \int_compare:nNnTF \l_@@_head_char_int = { 1 } { \@@_output_head_token: } { \@@_do_choice: } } } % \end{macrocode} % % \subsubsection{Maths: from 48 to 56} % % \begin{itemize} % \item |eq_no=48| % \item |left_right=49| % \item |math_comp=50| % \item |limit_switch=51| % \item |above=52| % \item |math_style=53| % \item |math_choice=54| % \item |non_script=55| % \item |vcenter=56| % \end{itemize} % % \begin{macrocode} \@@_new_tex_cmd:nn { eq_no } % 48 { \mode_if_math:TF { \mode_if_inner:TF { \@@_off_save: } { \int_compare:nNnTF \tex_currentgrouptype:D = { 15 } { \@@_box_hook:N \tex_everymath:D \gtl_gput_right:NV \g_@@_output_gtl \l_@@_head_tl \l_@@_head_token \@@_box_hook_end: \int_set:Nn \l_@@_choice_int { 1 } } { \@@_off_save: } } } { \@@_forbidden_case: } } % \end{macrocode} % % \begin{macrocode} \@@_new_tex_cmd:nn { left_right } % 49 { \@@_mode_math:n { \@@_prev_input_gpush:N \l_@@_head_tl \@@_print_action: \@@_scan_delimiter: \@@_prev_input_gpop:N \l_@@_head_tl \tl_if_head_eq_meaning:nNTF \l_@@_head_tl \tex_left:D { \gtl_gput_right:NV \g_@@_output_gtl \l_@@_head_tl \tl_use:N \l_@@_head_tl \scan_stop: \@@_print_action:e { \tl_to_str:N \l_@@_head_tl } } { \int_case:nnF \tex_currentgrouptype:D { { 16 } { \gtl_gput_right:NV \g_@@_output_gtl \l_@@_head_tl \@@_back_input_gtl:N \l_@@_after_group_gtl \tl_if_head_eq_meaning:nNTF \l_@@_head_tl \tex_middle:D { \tl_use:N \l_@@_head_tl \scan_stop: \gtl_clear:N \l_@@_after_group_gtl } { \tl_use:N \l_@@_head_tl \scan_stop: } \@@_print_action:e { \tl_to_str:N \l_@@_head_tl } } { 15 } { % todo: this is a TeX error \tl_use:N \l_@@_head_tl \scan_stop: } } { \@@_off_save: } } } } \cs_new_protected:Npn \@@_scan_delimiter: { \@@_get_x_non_relax: \@@_set_cmd: \int_case:nnF \l_@@_head_cmd_int { { \@@_tex_use:n { the_char } } { \@@_prev_input:V \l_@@_head_tl } { \@@_tex_use:n { other_char } } { \@@_prev_input:V \l_@@_head_tl } { \@@_tex_use:n { delim_num } } { \@@_prev_input:V \l_@@_head_tl \@@_scan_int: } } { \@@_back_input: \@@_tex_error:nV { missing-delim } \l_@@_head_tl \@@_prev_input:n { . } } } % \end{macrocode} % % \begin{macrocode} \@@_new_tex_cmd:nn { math_comp } % 50 { \@@_mode_math:n { \@@_sub_sup: } } % \end{macrocode} % % \begin{macrocode} \@@_new_tex_cmd:nn { limit_switch } % 51 { \@@_mode_math:n { \@@_output_head_token: } } \cs_new_protected:Npn \@@_output_head_token: { \gtl_gput_right:NV \g_@@_output_gtl \l_@@_head_tl \l_@@_head_token \@@_print_action: } % \end{macrocode} % % \begin{macrocode} \@@_new_tex_cmd:nn { above } % 52 { \@@_mode_math:n { \@@_not_implemented:n { above } } } % \end{macrocode} % % \begin{macrocode} \@@_new_tex_cmd:nn { math_style } % 53 { \@@_mode_math:n { \@@_output_head_token: } } % \end{macrocode} % % \begin{macrocode} \@@_new_tex_cmd:nn { math_choice } % 54 { \@@_mode_math:n { \@@_do_choice: } } \cs_new_protected:Npn \@@_do_choice: { \@@_prev_input_gpush:N \l_@@_head_tl \@@_print_action: \@@_scan_left_brace: \@@_prev_input_gpop:N \l_@@_head_tl \gtl_gput_right:NV \g_@@_output_gtl \l_@@_head_tl \gtl_gconcat:NNN \g_@@_output_gtl \g_@@_output_gtl \c_group_begin_gtl \tl_use:N \l_@@_head_tl \c_group_begin_token \gtl_clear:N \l_@@_after_group_gtl \int_set:Nn \l_@@_choice_int { 1 } \@@_print_action:e { \tl_to_str:N \l_@@_head_tl \iow_char:N \{ } } % \end{macrocode} % % \begin{macrocode} \@@_new_tex_cmd:nn { non_script } % 55 { \@@_mode_math:n { \@@_output_head_token: } } % \end{macrocode} % % \begin{macrocode} \@@_new_tex_cmd:nn { vcenter } % 56 { \@@_mode_math:n { \@@_not_implemented:n { vcenter } } } % \end{macrocode} % % \subsubsection{From 57 to 70} % % \begin{itemize} % \item |case_shift=57| % \item |message=58| % \item |extension=59| % \item |in_stream=60| % \item |begin_group=61| % \item |end_group=62| % \item |omit=63| % \item |ex_space=64| % \item |no_boundary=65| % \item |radical=66| % \item |end_cs_name=67| % \item |char_given=68| % \item |math_given=69| % \item |last_item=70| % \end{itemize} % % \begin{macrocode} \@@_new_tex_cmd:nn { case_shift } % 57 { \@@_prev_input_gpush:N \l_@@_head_tl \@@_scan_toks:NN \c_false_bool \c_false_bool \@@_prev_input_gpop:N \l_@@_tmpa_tl \exp_after:wN \@@_case_shift:Nn \l_@@_tmpa_tl } \cs_new_protected:Npn \@@_case_shift:Nn #1#2 { #1 { \@@_back_input:n {#2} } \@@_print_action:e { \token_to_meaning:N #1 ~ \tl_to_str:n { {#2} } } } % \end{macrocode} % % \begin{macrocode} \@@_new_tex_cmd:nn { message } % 58 { \@@_prev_input_gpush:N \l_@@_head_tl \@@_print_action: \@@_scan_toks_to_str: \@@_prev_input_gpop:N \l_@@_head_tl \tl_use:N \l_@@_head_tl \@@_print_action:e { \tl_to_str:N \l_@@_head_tl } } % \end{macrocode} % % Extensions are implemented in a later section. % \begin{macrocode} \@@_new_tex_cmd:nn { extension } % 59 { \@@_prev_input_gpush:N \l_@@_head_tl \@@_print_action: \@@_scan_extension_operands: \@@_prev_input_gpop:N \l_@@_head_tl \tl_use:N \l_@@_head_tl \scan_stop: \@@_print_action:e { \tl_to_str:N \l_@@_head_tl } } % \end{macrocode} % % \begin{macrocode} \@@_new_tex_cmd:nn { in_stream } % 60 { \@@_prev_input_gpush:N \l_@@_head_tl \@@_print_action: \token_if_eq_meaning:NNTF \l_@@_head_token \tex_openin:D { \@@_scan_int: \@@_scan_optional_equals: \@@_scan_file_name: } { \@@_scan_int: } \@@_prev_input_gpop:N \l_@@_head_tl \tl_use:N \l_@@_head_tl \scan_stop: \@@_print_action:e { \tl_to_str:N \l_@@_head_tl } } % \end{macrocode} % % \begin{macrocode} \@@_new_tex_cmd:nn { begin_group } % 61 { \gtl_gput_right:NV \g_@@_output_gtl \l_@@_head_tl \l_@@_head_token \gtl_clear:N \l_@@_after_group_gtl \@@_print_action: } \@@_new_tex_cmd:nn { end_group } % 62 { \gtl_gput_right:NV \g_@@_output_gtl \l_@@_head_tl \@@_back_input_gtl:N \l_@@_after_group_gtl \l_@@_head_token \@@_print_action: } % \end{macrocode} % % \begin{macrocode} \@@_new_tex_cmd:nn { omit } % 63 { \l_@@_head_token \@@_print_action: } % \end{macrocode} % % \begin{macrocode} \@@_new_tex_cmd:nn { ex_space } % 64 { \@@_mode_non_vertical:n { \l_@@_head_token \@@_print_action: } } % \end{macrocode} % % \begin{macrocode} \@@_new_tex_cmd:nn { no_boundary } % 65 { \@@_mode_non_vertical:n { \l_@@_head_token \@@_print_action: } } % \end{macrocode} % % \begin{macrocode} \@@_new_tex_cmd:nn { radical } % 66 { \@@_mode_math:n { \@@_do_math_accent: } } % \end{macrocode} % % \begin{macrocode} \@@_new_tex_cmd:nn { end_cs_name } % 67 { \@@_tex_error:nV { extra-endcsname } \l_@@_head_tl \@@_print_action: } % \end{macrocode} % % See |the_char| and |other_char|. % \begin{macrocode} \@@_new_tex_cmd:nn { char_given } % 68 { \@@_mode_non_vertical:n { \mode_if_math:TF { \@@_char_in_mmode:V \l_@@_head_char_int } { \@@_char:V \l_@@_head_char_int } } } % \end{macrocode} % % See |math_char_num|. % \begin{macrocode} \@@_new_tex_cmd:nn { math_given } % 69 { \@@_mode_math:n { \@@_mathchar:e { \int_use:N \l_@@_head_char_int } } } % \end{macrocode} % % \begin{macrocode} \@@_new_tex_cmd:nn { last_item } % 70 { \@@_forbidden_case: } % \end{macrocode} % % \subsubsection{Extensions} % ^^A todo: move this section. % % \begin{macro}{\@@_scan_extension_operands:} % \begin{macrocode} \cs_new_protected:Npn \@@_scan_extension_operands: { \int_case:nnF \l_@@_head_char_int { { 0 } % openout { \@@_scan_int: \@@_scan_optional_equals: \@@_scan_file_name: } { 1 } % write { \@@_scan_int: \@@_scan_toks:NN \c_false_bool \c_false_bool } { 2 } % closeout { \@@_scan_int: } { 3 } % special { \@@_scan_toks_to_str: } { 4 } % immediate { \@@_scan_immediate_operands: } { 5 } % setlanguage { \mode_if_horizontal:TF { \@@_scan_int: } { \@@_error:nnnnn { invalid-mode } { } { } { } { } } } { 6 } % pdfliteral { \@@_scan_keyword:nF { dDiIrReEcCtT } { \@@_scan_keyword:n { pPaAgGeE } } \@@_scan_pdf_ext_toks: } { 7 } % pdfobj { \@@_scan_keyword:nTF { rReEsSeErRvVeEoObBjJnNuUmM } { \@@_skip_optional_space: } { \@@_scan_keyword:nF { uUsSeEoObBjJnNuUmM } { \@@_scan_int: } \@@_scan_keyword:nT { sStTrReEaAmM } { \@@_scan_keyword:nT { aAtTtTrR } { \@@_scan_pdf_ext_toks: } } \@@_scan_keyword:n { fFiIlLeE } \@@_scan_pdf_ext_toks: } } { 8 } % pdfrefobj { \@@_scan_int: } { 9 } % pdfxform { \@@_scan_keyword:nT { aAtTtTrR } { \@@_scan_pdf_ext_toks: } \@@_scan_keyword:nTF { rReEsSoOuUrRcCeEsS } { \@@_scan_pdf_ext_toks: } \@@_scan_int: } { 10 } % pdfrefxform { \@@_scan_int: } { 11 } % pdfximage { \@@_scan_image: } { 12 } % pdfrefximage { \@@_scan_int: } { 13 } % pdfannot { \@@_scan_keyword:nTF { rReEsSeErRvVeEoObBjJnNuUmM } { \@@_scan_optional_space: } { \@@_scan_keyword:nT { uUsSeEoObBjJnNuUmM } { \@@_scan_int: } \@@_scan_alt_rule: \@@_scan_pdf_ext_toks: } } { 14 } % pdfstartlink { \mode_if_vertical:TF { \@@_error:nnnnn { invalid-mode } { } { } { } { } } { \@@_scan_rule_attr: \@@_scan_action: } } { 15 } % pdfendlink { \mode_if_vertical:T { \@@_error:nnnnn { invalid-mode } { } { } { } { } } } { 16 } % pdfoutline { \@@_scan_keyword:nT { aAtTtTrR } { \@@_scan_pdf_ext_toks: } \@@_scan_action: \@@_scan_keyword:nT { cCoOuUnNtT } { \@@_scan_int: } \@@_scan_pdf_ext_toks: } { 17 } % pdfdest { \@@_scan_pdfdest_operands: } { 18 } % pdfthread { \@@_scan_rule_attr: \@@_scan_thread_id: } { 19 } % pdfstartthread { \@@_scan_rule_attr: \@@_scan_thread_id: } { 20 } % pdfendthread { } { 21 } % pdfsavepos { } { 22 } % pdfinfo { \@@_scan_pdf_ext_toks: } { 23 } % pdfcatalog { \@@_scan_pdf_ext_toks: \@@_scan_keyword:n { oOpPeEnNaAcCtTiIoOnN } { \@@_scan_action: } } { 24 } % pdfnames { \@@_scan_pdf_ext_toks: } { 25 } % pdffontattr { \@@_scan_font_ident: \@@_scan_pdf_ext_toks: } { 26 } % pdfincludechars { \@@_scan_font_ident: \@@_scan_pdf_ext_toks: } { 27 } % pdfmapfile { \@@_scan_pdf_ext_toks: } { 28 } % pdfmapline { \@@_scan_pdf_ext_toks: } { 29 } % pdftrailer { \@@_scan_pdf_ext_toks: } { 30 } % pdfresettimer { } { 31 } % pdffontexpand { \@@_scan_font_ident: \@@_scan_optional_equals: \@@_scan_int: \@@_scan_int: \@@_scan_int: \@@_scan_keyword:nT { aAuUtToOeExXpPaAnNdD } { \@@_skip_optional_space: } } { 32 } % pdfsetrandomseed { \@@_scan_int: } { 33 } % pdfsnaprefpoint { } { 34 } % pdfsnapy { \@@_scan_normal_glue: } { 35 } % pdfsnapycomp { \@@_scan_int: } { 36 } % pdfglyphtounicode { \@@_scan_pdf_ext_toks: \@@_scan_pdf_ext_toks: } { 37 } % pdfcolorstack { \@@_scan_pdfcolorstack_operands: } { 38 } % pdfsetmatrix { \@@_scan_pdf_ext_toks: } { 39 } % pdfsave { } { 40 } % pdfrestore { } { 41 } % pdfnobuiltintounicode { \@@_scan_font_ident: } } { } % no other cases. } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_scan_pdfcolorstack_operands:} % \begin{macrocode} \cs_new_protected:Npn \@@_scan_pdfcolorstack_operands: { \@@_scan_int: \@@_scan_keyword:nF { sSeEtT } { \@@_scan_keyword:nF { pPuUsShH } { \@@_scan_keyword:nF { pPoOpP } { \@@_scan_keyword:nF { cCuUrRrReEnNtT } { \@@_error:nnnnn { color-stack-action-missing } { } { } { } { } } } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_scan_rule_attr:} % \begin{macrocode} \cs_new_protected:Npn \@@_scan_rule_attr: { \@@_scan_alt_rule: \@@_scan_keyword:nT { aAtTtTrR } { \@@_scan_pdf_ext_toks: } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_scan_action:} % \begin{macrocode} \cs_new_protected:Npn \@@_scan_action: { \@@_scan_keyword:nTF { uUsSeErR } { \@@_scan_pdf_ext_toks: } { \@@_scan_keyword:nF { gGoOtToO } { \@@_scan_keyword:nF { tThHrReEaAdD } { \@@_error:nnnnn { action-type-missing } { } { } { } { } } } } \@@_scan_keyword:nT { fFiIlLeE } { \@@_scan_pdf_ext_toks: } \@@_scan_keyword:nTF { pPaAgGeE } { \@@_scan_int: \@@_scan_pdf_ext_toks: } { \@@_scan_keyword:nTF { nNaAmMeE } { \@@_scan_pdf_ext_toks: } { \@@_scan_keyword:nTF { nNuUmM } { \@@_scan_int: } { \@@_error:nnnnn { identifier-type-missing } { } { } { } { } } } } \@@_scan_keyword:nTF { nNeEwWwWiInNdDoOwW } { \@@_skip_optional_space: } { \@@_scan_keyword:nT { nNoOnNeEwWwWiInNdDoOwW } { \@@_skip_optional_space: } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_scan_image:} % Used by \tn{pdfximage}. % \begin{macrocode} \cs_new_protected:Npn \@@_scan_image: { \@@_scan_rule_attr: \@@_scan_keyword:nTF { nNaAmMeEdD } { \@@_scan_pdf_ext_toks: } { \@@_scan_keyword:nT { pPaAgGeE } { \@@_scan_int: } } \@@_scan_keyword:nT { cCoOlLoOrRsSpPaAcCeE } { \@@_scan_int: } \@@_scan_pdf_ext_toks: } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_scan_immediate_operands:} % \begin{macrocode} \cs_new_protected:Npn \@@_scan_immediate_operands: { \@@_get_x_next: \@@_set_cmd: \int_compare:nNnTF \l_@@_head_cmd_int = { \@@_tex_use:n { extension } } { \int_compare:nNnTF \l_@@_head_char_int < { 3 } % openout, write, closeout { \@@_scan_immediate_operands_aux: } { \int_case:nnF \l_@@_head_char_int { { 7 } { \@@_scan_extension_operands_aux: } % pdfobj { 9 } { \@@_prepare_mag: \@@_scan_extension_operands_aux: } % pdfxform { 11 } { \@@_scan_extension_operands_aux: } %pdfximage } { \@@_scan_immediate_operands_bad: } } } { \@@_scan_immediate_operands_bad: } } \cs_new_protected:Npn \@@_scan_immediate_operands_aux: { \@@_prev_input:V \l_@@_head_tl \@@_scan_extension_operands: } \cs_new_protected:Npn \@@_scan_immediate_operands_bad: { \@@_back_input: \@@_prev_input_gpop:N \l_@@_head_tl \@@_print_action:e { \tl_to_str:N \l_@@_head_tl ignored } \@@_prev_input_gpush: } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_scan_pdfdest_operands:} % \begin{macrocode} \cs_new_protected:Npn \@@_scan_pdfdest_operands: { \@@_scan_keyword:nTF { nNuUmM } { \@@_scan_int: } { \@@_scan_keyword:nTF { nNaAmMeE } { \@@_scan_pdf_ext_toks: } { \@@_error:nnnnn { identifier-type-missing } { } { } { } { } } } \@@_scan_keyword:nTF { xXyYzZ } { \@@_scan_keyword:nT { zZoOoOmM } { \@@_scan_int: } } { \@@_scan_keyword:nF { fFiItTbBhH } { \@@_scan_keyword:nF { fFiItTbBvV } { \@@_scan_keyword:nF { fFiItTbB } { \@@_scan_keyword:nF { fFiItThHhH } { \@@_scan_keyword:nF { fFiItTvV } { \@@_scan_keyword:nTF { fFiItTrR } { \@@_skip_optional_space: \@@_scan_alt_rule: \use_none:n } { \@@_scan_keyword:nF { fFiItT } { \@@_error:nnnnn { destination-type-missing } { } { } { } { } } } } } } } } } \@@_skip_optional_space: } % \end{macrocode} % \end{macro} % % \subsubsection{Assignments} % % Quoting \texttt{tex.web}: ``Every prefix, and every command code that % might or might not be prefixed, calls the action procedure % |prefixed_command|. This routine accumulates a sequence of prefixes % until coming to a non-prefix, then it carries out the command.'' % We define all those commands in one go, from % |max_non_prefixed_command+1=71| to |max_command=102|. % \begin{macrocode} \cs_set_protected:Npn \@@_tmp:w { \@@_prev_input_gpush: \@@_prefixed_command: } \int_step_inline:nnnn { \@@_tex_use:n { max_non_prefixed_command } + 1 } { 1 } { \@@_tex_use:n { max_command } } { \cs_new_eq:cN { @@_cmd_#1: } \@@_tmp:w } % \end{macrocode} % % \begin{macro}{\@@_prefixed_command:} % Accumulated prefix codes so far are stored as the last item of % the previous-input sequence. % \begin{macrocode} \cs_new_protected:Npn \@@_prefixed_command: { \int_while_do:nNnn \l_@@_head_cmd_int = { \@@_tex_use:n { prefix } } { \@@_prev_input:V \l_@@_head_tl \@@_get_x_non_relax: \@@_set_cmd: \int_compare:nNnF \l_@@_head_cmd_int > { \@@_tex_use:n { max_non_prefixed_command } } { \@@_prev_input_gpop:N \l_@@_tmpa_tl \@@_error:neeee { erroneous-prefixes } { \tl_to_str:N \l_@@_tmpa_tl } { \tl_to_str:N l_@@_head_tl } { } { } \@@_back_input: \@@_omit_after_assignment:w } } % ^^A todo: Discard non-\global prefixes if they are irrelevant % ^^A todo: Adjust for the setting of \globaldefs \cs_if_exist_use:cF { @@_prefixed_ \int_use:N \l_@@_head_cmd_int : } { \@@_error:nnnnn { internal } { prefixed } { } { } { } \@@_omit_after_assignment:w } \@@_after_assignment: } % \end{macrocode} % \end{macro} % We now need to implement prefixed commands, for command codes in the % range $[71,102]$, with the exception of |prefix=93|, which would have % been collected by the \cs{@@_prefixed_command:} loop. % % \begin{macro}{\@@_after_assignment:} % \begin{macro}{\@@_omit_after_assignment:w} % ^^A todo: simplify % \begin{macrocode} \cs_new_protected:Npn \@@_after_assignment: { \@@_back_input_gtl:N \g_@@_after_assignment_gtl \gtl_gclear:N \g_@@_after_assignment_gtl } \cs_new_protected:Npn \@@_omit_after_assignment:w #1 \@@_after_assignment: { } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\@@_prefixed_new:nn} % \begin{macrocode} \cs_new_protected:Npn \@@_prefixed_new:nn #1#2 { \cs_new_protected:cpn { @@_prefixed_ \@@_tex_use:n {#1} : } {#2} } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_assign_token:n} % \begin{macrocode} \cs_new_protected:Npn \@@_assign_token:n #1 { \@@_prev_input_gpop:N \l_@@_head_tl #1 \tl_use:N \l_@@_head_tl \scan_stop: \@@_print_assigned_token: } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_assign_register:} % \begin{macrocode} \cs_new_protected:Npn \@@_assign_register: { \@@_prev_input_gpop:N \l_@@_head_tl \tl_use:N \l_@@_head_tl \scan_stop: \@@_print_assigned_register: } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_assign_value:nn} % \begin{macrocode} \cs_new_protected:Npn \@@_assign_value:nn #1#2 { \tl_if_empty:nF {#1} { \@@_prev_input_gpush:N \l_@@_head_tl \@@_print_action:e { \tl_to_str:N \l_@@_head_tl } #1 \@@_prev_input_gpop:N \l_@@_head_tl } \@@_prev_input:V \l_@@_head_tl \tl_set_eq:NN \l_@@_defined_tl \l_@@_head_tl \@@_scan_optional_equals: #2 \@@_assign_register: } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_assign_toks:} % \begin{macrocode} \@@_prefixed_new:nn { toks_register } % 71 { \int_compare:nNnT \l_@@_head_char_int = 0 { % \toks \@@_prev_input_gpush:N \l_@@_head_tl \@@_print_action: \@@_scan_int: \@@_prev_input_gpop:N \l_@@_head_tl } \@@_assign_toks: } \@@_prefixed_new:nn { assign_toks } % 72 { \@@_assign_toks: } \cs_new_protected:Npn \@@_assign_toks: { \@@_prev_input_silent:V \l_@@_head_tl \@@_print_action: \tl_set_eq:NN \l_@@_defined_tl \l_@@_head_tl \@@_scan_optional_equals: \@@_get_x_non_relax: \@@_set_cmd: \int_compare:nNnTF \l_@@_head_cmd_int = { \@@_tex_use:n { toks_register } } { \@@_prev_input:V \l_@@_head_tl \int_compare:nNnT \l_@@_head_char_int = 0 { \@@_scan_int: } } { \int_compare:nNnTF \l_@@_head_cmd_int = { \@@_tex_use:n { assign_toks } } { \@@_prev_input:V \l_@@_head_tl } { \@@_back_input: \@@_scan_toks:NN \c_false_bool \c_false_bool } } \@@_assign_register: } % \end{macrocode} % \end{macro} % % \begin{macrocode} \@@_prefixed_new:nn { assign_int } % 73 { \@@_assign_value:nn { } { \@@_scan_int: } } \@@_prefixed_new:nn { assign_dimen } % 74 { \@@_assign_value:nn { } { \@@_scan_normal_dimen: } } \@@_prefixed_new:nn { assign_glue } % 75 { \@@_assign_value:nn { } { \@@_scan_normal_glue: } } \@@_prefixed_new:nn { assign_mu_glue } % 76 { \@@_assign_value:nn { } { \@@_scan_mu_glue: } } \@@_prefixed_new:nn { assign_font_dimen } % 77 { \@@_assign_value:nn { \@@_scan_int: \@@_scan_font_ident: } { \@@_scan_normal_dimen: } } \@@_prefixed_new:nn { assign_font_int } % 78 { \@@_assign_value:nn { \@@_scan_font_int: } { \@@_scan_int: } } \@@_prefixed_new:nn { set_aux } % 79 { % prevdepth = 1, spacefactor = 102 \int_compare:nNnTF \l_@@_head_char_int = 1 { \@@_assign_value:nn { } { \@@_scan_normal_dimen: } } { \@@_assign_value:nn { } { \@@_scan_int: } } } \@@_prefixed_new:nn { set_prev_graf } % 80 { \@@_assign_value:nn { } { \@@_scan_int: } } \@@_prefixed_new:nn { set_page_dimen } % 81 { \@@_assign_value:nn { } { \@@_scan_normal_dimen: } } \@@_prefixed_new:nn { set_page_int } % 82 { \@@_assign_value:nn { } { \@@_scan_int: } } \@@_prefixed_new:nn { set_box_dimen } % 83 { \@@_assign_value:nn { \@@_scan_int: } { \@@_scan_normal_dimen: } } % \end{macrocode} % % This is a variant of \cs{@@_assign_value:nn}, with a bit more complication % because the syntax of \tn{parshape} and of \eTeX{} primitives such as % \tn{interlinepenalties} is a bit different. % \begin{macrocode} \@@_prefixed_new:nn { set_shape } % 84 { \@@_prev_input:V \l_@@_head_tl \tl_set_eq:NN \l_@@_defined_tl \l_@@_head_tl \tl_if_head_eq_meaning:VNTF \l_@@_defined_tl \tex_parshape:D { \@@_set_shape:NN 2 \@@_scan_normal_dimen: \@@_print_assigned_parshape: } { \@@_set_shape:NN 1 \@@_scan_int: \@@_print_assigned_set_shape: } } \cs_new_protected:Npn \@@_set_shape:NN #1#2 { \@@_scan_optional_equals: \@@_prev_input_gpush: \@@_scan_int: \@@_prev_input_gpop:N \l_@@_tmpa_tl \@@_prev_input_silent:V \l_@@_tmpa_tl \prg_replicate:nn { \int_max:nn { 0 } { #1 * \l_@@_tmpa_tl } } { \@@_prev_input_silent:n { ~ } #2 } \@@_prev_input_gpop:N \l_@@_tmpa_tl \tl_use:N \l_@@_tmpa_tl \scan_stop: } % \end{macrocode} % % \begin{macrocode} \@@_prefixed_new:nn { def_code } % 85 { \@@_assign_value:nn { \@@_scan_int: } { \@@_scan_int: } } \@@_prefixed_new:nn { def_family } % 86 { \@@_assign_value:nn { \@@_scan_int: } { \@@_scan_font_ident: } } \@@_prefixed_new:nn { set_font } % 87 { \@@_prev_input_gpop:N \l_@@_tmpa_tl \tl_put_left:NV \l_@@_head_tl \l_@@_tmpa_tl \tl_use:N \l_@@_head_tl \scan_stop: \gtl_gput_right:NV \g_@@_output_gtl \l_@@_head_tl \@@_print_action: } \@@_prefixed_new:nn { def_font } % 88 { \@@_prev_input_silent:V \l_@@_head_tl \@@_set_action_text:e { \tl_to_str:N \l_@@_head_tl } \@@_scan_r_token: \@@_print_action:e { \g_@@_action_text_str \tl_to_str:N \l_@@_defined_tl } \@@_scan_optional_equals: \@@_scan_file_name: \bool_gset_true:N \g_@@_name_in_progress_bool \@@_scan_keyword:nTF { aAtT } { \@@_scan_normal_dimen: } { \@@_scan_keyword:nT { sScCaAlLeEdD } { \@@_scan_int: } } \bool_gset_false:N \g_@@_name_in_progress_bool \@@_assign_token:n { } } % \end{macrocode} % % |register=89|, |advance=90|, |multiply=91|, |divide=92| are % implemented elsewhere. |prefix=93| is never needed (see explanation % above). % % |let|, |futurelet| % \begin{macrocode} \@@_prefixed_new:nn { let } % 94 { \@@_prev_input_gpush:N \l_@@_head_tl \token_if_eq_meaning:NNTF \l_@@_head_token \tex_let:D { % |let| \@@_scan_r_token: \@@_prev_input_get:N \l_@@_tmpa_tl \@@_print_action:e { \tl_to_str:N \l_@@_tmpa_tl } \@@_get_next: \bool_while_do:nn { \token_if_eq_catcode_p:NN \l_@@_head_token \c_space_token } { \@@_get_next: } \tl_if_eq:NNT \l_@@_head_tl \c_@@_eq_tl { \@@_get_next: } \token_if_eq_catcode:NNT \l_@@_head_token \c_space_token { \@@_get_next: } } { % |futurelet| \@@_scan_r_token: \@@_prev_input_get:N \l_@@_tmpa_tl \@@_print_action:e { \tl_to_str:N \l_@@_tmpa_tl } \@@_get_next: \gtl_set_eq:NN \l_@@_tmpb_gtl \l_@@_head_gtl \@@_get_next: \@@_back_input: \gtl_set_eq:NN \l_@@_head_gtl \l_@@_tmpb_gtl \@@_back_input: } \@@_prev_input_gpop:N \l_@@_tmpa_tl \tl_put_right:Nn \l_@@_tmpa_tl { = ~ \l_@@_head_token } \@@_prev_input_gpop:N \l_@@_head_tl \use:e { \exp_not:V \l_@@_head_tl \tex_let:D \tl_tail:N \l_@@_tmpa_tl } \@@_print_assigned_token: } % \end{macrocode} % % \begin{macrocode} \@@_prefixed_new:nn { shorthand_def } % 95 { \@@_prev_input_silent:V \l_@@_head_tl \tl_set:Ne \l_@@_prev_action_tl { \tl_to_str:N \l_@@_head_tl } \@@_scan_r_token: \@@_print_action:e { \l_@@_prev_action_tl \tl_to_str:N \l_@@_defined_tl } \exp_after:wN \cs_set_eq:NN \l_@@_defined_tl \scan_stop: \@@_just_print_assigned_token: \@@_scan_optional_equals: \@@_scan_int: \@@_assign_token:n { } } % \end{macrocode} % % \begin{macro}[EXP]{\@@_read_to_cs_safe:nTF, \@@_read_to_cs_safe:fTF} % After \tn{read} or \tn{readline}, find an int, the mandatory % keyword |to|, and an assignable token. The \tn{read} and % \tn{readline} primitives throw a fatal error in \tn{nonstopmode} % and in \tn{batchmode} when trying to read from a stream that is % outside $[0,15]$ or that is not open (according to \tn{ifeof}). We % detect this situation using \cs{@@_read_to_cs_safe:nTF} after % grabbing all arguments of the primitives. If reading is unsafe, % let the user know that \TeX{} would have thrown a fatal error. % \begin{macrocode} \@@_prefixed_new:nn { read_to_cs } % 96 { \@@_prev_input_silent:V \l_@@_head_tl \@@_print_action:e { \tl_to_str:N \l_@@_head_tl } \@@_scan_int: \@@_scan_to: \@@_scan_r_token: \@@_prev_input_get:N \l_@@_tmpa_tl \@@_read_to_cs_safe:fTF { \@@_tl_first_int:N \l_@@_tmpa_tl } { \@@_assign_token:n { } } { \@@_prev_input_gpop:N \l_@@_head_tl \@@_tex_fatal_error:nV { cannot-read } \l_@@_head_tl } } \prg_new_conditional:Npnn \@@_read_to_cs_safe:n #1 { TF } { \int_compare:nNnTF { \tex_interactionmode:D } > { 1 } { \prg_return_true: } { \int_compare:nNnTF {#1} < { 0 } { \prg_return_false: } { \int_compare:nNnTF {#1} > { 15 } { \prg_return_false: } { \tex_ifeof:D #1 \exp_stop_f: \prg_return_false: \else: \prg_return_true: \fi: } } } } \cs_generate_variant:Nn \@@_read_to_cs_safe:nTF { f } % \end{macrocode} % \end{macro} % % \begin{macrocode} \@@_prefixed_new:nn { def } % 97 { \@@_prev_input_get:N \l_@@_tmpa_tl \tl_set:NV \l_@@_defining_tl \l_@@_tmpa_tl \tl_put_right:NV \l_@@_defining_tl \l_@@_head_tl \@@_prev_input_gpush:N \l_@@_head_tl \int_compare:nNnTF \l_@@_head_char_int < 2 { % def/gdef \@@_scan_r_token: \tl_put_right:NV \l_@@_defining_tl \l_@@_defined_tl \@@_scan_toks:NN \c_true_bool \c_false_bool } { % edef/xdef \@@_scan_r_token: \tl_put_right:NV \l_@@_defining_tl \l_@@_defined_tl \@@_scan_toks:NN \c_true_bool \c_true_bool } \@@_prev_input_gpop:N \l_@@_head_tl \@@_prev_input:V \l_@@_head_tl \@@_assign_token:n { \tl_set_eq:NN \l_@@_head_tl \l_@@_defining_tl } } % \end{macrocode} % % \tn{setbox} is a bit special: directly put it in % the previous-input sequence with the prefixes; the box code will take % care of things, and expects a single item containing what it needs to % do. % \begin{macrocode} \@@_prefixed_new:nn { set_box } % 98 { \@@_prev_input:V \l_@@_head_tl \@@_scan_int: \@@_scan_optional_equals: \bool_if:NTF \g_@@_set_box_allowed_bool { \@@_do_box:N \c_false_bool } { \@@_error:nnnnn { improper-setbox } { } { } { } { } \@@_prev_input_gpop:N \l_@@_tmpa_tl \@@_omit_after_assignment:w } } % \end{macrocode} % % ^^A todo: \hyphenation{...{...}...} recovers weirdly (no nesting). % \tn{hyphenation} and \tn{patterns} % \begin{macrocode} \@@_prefixed_new:nn { hyph_data } % 99 { \@@_prev_input:V \l_@@_head_tl \@@_scan_toks:NN \c_false_bool \c_false_bool \@@_assign_token:n { } } % \end{macrocode} % % \begin{macrocode} \@@_prefixed_new:nn { set_interaction } % 100 { \@@_prev_input_gpop:N \l_@@_tmpa_tl \tl_put_left:NV \l_@@_head_tl \l_@@_tmpa_tl \tl_use:N \l_@@_head_tl \scan_stop: \@@_print_assignment:e { \tl_to_str:N \l_@@_head_tl } } % \end{macrocode} % % \begin{macrocode} \@@_prefixed_new:nn { letterspace_font } % 101 { \@@_prev_input_silent:V \l_@@_head_tl \@@_set_action_text:e { \tl_to_str:N \l_@@_head_tl } \@@_scan_r_token: \@@_print_action:e { \g_@@_action_text_str \tl_to_str:N \l_@@_defined_tl } \exp_after:wN \cs_set_eq:NN \l_@@_defined_tl \@@_nullfont: \@@_just_print_assigned_token: \@@_scan_optional_equals: \@@_scan_font_ident: \@@_scan_int: \@@_assign_token:n { } } % \end{macrocode} % % \begin{macrocode} \@@_prefixed_new:nn { pdf_copy_font } % 102 { \@@_prev_input_silent:V \l_@@_head_tl \@@_set_action_text:e { \tl_to_str:N \l_@@_head_tl } \@@_scan_r_token: \@@_print_action:e { \g_@@_action_text_str \tl_to_str:N \l_@@_defined_tl } \exp_after:wN \cs_set_eq:NN \l_@@_defined_tl \@@_nullfont: \@@_just_print_assigned_token: \@@_scan_optional_equals: \@@_scan_font_ident: \@@_assign_token:n { } } % \end{macrocode} % % Changes to numeric registers (\tn{count}, \tn{dimen}, \tn{skip}, % \tn{muskip}, and commands with a built-in number). % \begin{macrocode} \@@_prefixed_new:nn { register } % 89 { \@@_do_register:N 0 } \@@_prefixed_new:nn { advance } % 90 { \@@_do_operation:N 1 } \@@_prefixed_new:nn { multiply } % 91 { \@@_do_operation:N 2 } \@@_prefixed_new:nn { divide } % 92 { \@@_do_operation:N 3 } % \end{macrocode} % % \begin{macro}{\@@_do_operation:N, \@@_do_operation_fail:w} % \begin{macrocode} \cs_new_protected:Npn \@@_do_operation:N #1 { \@@_prev_input_silent:V \l_@@_head_tl \@@_print_action: \@@_get_x_next: \@@_set_cmd: \int_compare:nNnTF \l_@@_head_cmd_int > { \@@_tex_use:n { assign_mu_glue } } { \int_compare:nNnTF \l_@@_head_cmd_int = { \@@_tex_use:n { register } } { \@@_do_register:N #1 } { \@@_do_operation_fail:w } } { \int_compare:nNnTF \l_@@_head_cmd_int < { \@@_tex_use:n { assign_int } } { \@@_do_operation_fail:w } { \@@_prev_input:V \l_@@_head_tl \exp_args:NNf \@@_do_register_set:Nn #1 { \int_eval:n { \l_@@_head_cmd_int - \@@_tex_use:n { assign_toks } } } } } } \cs_new_protected:Npn \@@_do_operation_fail:w { \@@_error:nnnnn { after-advance } { } { } { } { } \@@_prev_input_gpop:N \l_@@_tmpa_tl \@@_omit_after_assignment:w } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_do_register:N, \@@_do_register_aux:Nn} % \begin{macrocode} \cs_new_protected:Npn \@@_do_register:N #1 { \exp_args:NNV \@@_do_register_aux:Nn #1 \l_@@_head_char_int } \cs_new_protected:Npn \@@_do_register_aux:Nn #1#2 { \int_compare:nNnTF { \tl_tail:n {#2} } = 0 { \@@_prev_input_gpush:N \l_@@_head_tl \@@_print_assignment: \@@_scan_int: \@@_prev_input_gpop:N \l_@@_head_tl \@@_prev_input_silent:V \l_@@_head_tl } { \@@_prev_input_silent:V \l_@@_head_tl \@@_print_assignment: } \tl_set_eq:NN \l_@@_defined_tl \l_@@_head_tl \exp_args:NNf \@@_do_register_set:Nn #1 { \int_eval:n { #2 / 1 000 000 } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_do_register_set:Nn} % \begin{macrocode} \cs_new_protected:Npn \@@_do_register_set:Nn #1#2 { \int_compare:nNnTF {#1} = 0 { % truly register command \@@_scan_optional_equals: } { % \advance, \multiply, \divide \@@_scan_keyword:nF { bByY } { \@@_prev_input_silent:n { by } } } \int_compare:nNnTF {#1} < 2 { \int_case:nnF {#2} { { 1 } { \@@_scan_int: } % count { 2 } { \@@_scan_normal_dimen: } % dim { 3 } { \@@_scan_normal_glue: } % glue { 4 } { \@@_scan_mu_glue: } % muglue } { \@@_error:neeee { internal } { do-reg=#2 } { } { } { } } } { \@@_scan_int: } \@@_assign_register: } % \end{macrocode} % \end{macro} % % The following is used for instance when making accents. % \begin{macrocode} \cs_new_protected:Npn \@@_do_assignments: { \@@_get_x_non_relax: \@@_set_cmd: \int_compare:nNnT \l_@@_head_cmd_int > { \@@_tex_use:n { max_non_prefixed_command } } { \bool_gset_false:N \g_@@_set_box_allowed_bool \@@_prev_input_gpush: \@@_prefixed_command: \bool_gset_true:N \g_@@_set_box_allowed_bool \@@_do_assignments: } } % \end{macrocode} % % \subsection{Expandable primitives} % % This section implements expandable primitives, which have the % following command codes: % \begin{itemize} % \item |undefined_cs=103| for undefined control sequences (not quite a % primitive). % \item |expand_after=104| for \tn{expandafter} and \tn{unless}. % \item |no_expand=105| for \tn{noexpand} and \tn{pdfprimitive}. % \item |input=106| for \tn{input}, \tn{endinput} and \tn{scantokens}. % \item |if_test=107| for the conditionals, \tn{if}, \tn{ifcat}, % \tn{ifnum}, \tn{ifdim}, \tn{ifodd}, \tn[index=ifhmode]{if[vhm]mode}, % \tn[index=iftdir]{if[tydm]dir}, % \tn{ifinner}, \tn{ifvoid}, \tn[index=ifhbox]{if[hvtydm]box}, % \tn{ifx}, \tn{ifeof}, \tn{iftrue}, \tn{iffalse}, \tn{ifcase}, % \tn{ifdefined}, \tn{ifcsname}, \tn{iffontchar}, \tn{ifincsname}, % \tn{ifprimitive}, \tn{ifabsnum}, \tn{ifabsdim}, \tn{ifjfont}, \tn{iftfont}. % \item |fi_or_else=108| for \tn{fi}, \tn{else} and \tn{or}. % \item |cs_name=109| for \tn{csname} and \tn{lastnamedcs}. % \item |convert=110| for \tn{number}, \tn{romannumeral}, \tn{string}, % \tn{meaning}, \tn{fontname}, \tn{eTeXrevision}, \tn{pdftexrevision}, % \tn{pdftexbanner}, \tn{pdffontname}, \tn{pdffontobjnum}, % \tn{pdffontsize}, \tn{pdfpageref}, \tn{pdfxformname}, % \tn{pdfescapestring}, \tn{pdfescapename}, \tn{leftmarginkern}, % \tn{rightmarginkern}, \tn{pdfstrcmp}, \tn{pdfcolorstackinit}, % \tn{pdfescapehex}, \tn{pdfunescapehex}, \tn{pdfcreationdate}, % \tn{pdffilemoddate}, \tn{pdffilesize}, \tn{pdfmdfivesum}, % \tn{pdffiledump}, \tn{pdfmatch}, \tn{pdflastmatch}, % \tn{pdfuniformdeviate}, \tn{pdfnormaldeviate}, \tn{pdfinsertht}, % \tn{pdfximagebbox}, \tn{jobname}, \tn{expanded}, and in % \LuaTeX{} \tn{directlua}, \tn{luaescapestring}, and in \XeTeX{}/(u)p\TeX{} \tn{Ucharcat}. % \item |the=111| for \tn{the}, \tn{unexpanded}, and \tn{detokenize}. % \item |top_bot_mark=112| \tn{topmark}, \tn{firstmark}, \tn{botmark}, % \tn{splitfirstmark}, \tn{splitbotmark}, \tn{topmarks}, % \tn{firstmarks}, \tn{botmarks}, \tn{splitfirstmarks}, and % \tn{splitbotmarks}. % \item |call=113| for macro calls, implemented by \cs{@@_macro_call:}. % \item |end_template=117| for \TeX{}'s end template. % \end{itemize} % % Let \TeX{} trigger an error. % \begin{macrocode} \@@_new_tex_expandable:nn { undefined_cs } % 103 { \tl_use:N \l_@@_head_tl \@@_print_expansion: } % \end{macrocode} % % \begin{macro}{\@@_expandafter:, \@@_unless:, \@@_unless_bad:} % \begin{macrocode} \@@_new_tex_expandable:nn { expand_after } % 104 { \token_if_eq_meaning:NNTF \l_@@_head_token \tex_expandafter:D { \@@_expandafter: } { \@@_unless: } } \cs_new_protected:Npn \@@_expandafter: { \gtl_set_eq:NN \l_@@_tmpb_gtl \l_@@_head_gtl \@@_get_next: \gtl_concat:NNN \l_@@_head_gtl \l_@@_tmpb_gtl \l_@@_head_gtl \@@_prev_input_gpush_gtl:N \l_@@_head_gtl \@@_print_expansion:e { \gtl_to_str:N \l_@@_head_gtl } \@@_get_next: \@@_token_if_expandable:NTF \l_@@_head_token { \@@_expand_do:N \prg_do_nothing: } { \@@_back_input: } \@@_prev_input_gpop_gtl:N \l_@@_head_gtl \@@_set_action_text:e { back_input: ~ \gtl_to_str:N \l_@@_head_gtl } \gtl_pop_left:N \l_@@_head_gtl \@@_back_input: \@@_print_expansion: } \cs_new_protected:Npn \@@_unless: { \gtl_set_eq:NN \l_@@_tmpb_gtl \l_@@_head_gtl \@@_get_token: \int_compare:nNnTF \l_@@_head_cmd_int = { \@@_tex_use:n { if_test } } { \token_if_eq_meaning:NNTF \l_@@_head_token \tex_ifcase:D { \@@_unless_bad: } { \tl_put_left:Ne \l_@@_head_tl { \gtl_head_do:NN \l_@@_tmpb_gtl \exp_not:N } \@@_expand_nonmacro: } } { \@@_unless_bad: } } \cs_new_protected:Npn \@@_unless_bad: { \@@_error:nnnnn { bad-unless } { } { } { } { } \@@_back_input: } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_noexpand:N, \@@_noexpand_after:, \@@_pdfprimitive:} % Currently not fully implemented. % % The argument of \cs{@@_noexpand:N} is \cs{prg_do_nothing:} when % \tn{noexpand} is hit by \tn{expandafter}; otherwise it is one of % various loop commands (\cs{@@_get_x_next:}, % \cs{@@_get_x_or_protected:}, \cs{@@_get_token_xdef:}, % \cs{@@_get_token_x:}) that would call \cs{@@_get_next:} and possibly % expand the token more. For these cases we simply stop after % \cs{@@_get_next:} and if the token is expandable we pretend its % meaning is \tn{relax}. % % The case of \tn{expandafter} (so \cs{prg_do_nothing:}) is tougher. % Do nothing if the next token is an explicit non-active character % (begin-group and end-group characters are detected by % \cs{l_@@_head_tl}, the rest by testing if the token is definable). % Otherwise the token must be marked with \cs{notexpanded:} (even if % the token is currently a non-expandable primitive, as its meaning % can be changed by the code skipped over by \tn{expandafter}). That % \cs{notexpanded:} marker should be removed if the token is taken as % the argument of a macro, but we fail to do that. We set the % \cs{notexpanded:\ldots{}} command to be a special \tn{relax} marker % to make it quickly recognizable in \cs{@@_get_next:}. This is % incidentally the same meaning used by \TeX{} for expandable commands. % \begin{macrocode} \@@_new_tex_expandable:nn { no_expand } % 105 { \token_if_eq_meaning:NNTF \l_@@_head_token \tex_noexpand:D { \@@_noexpand:N } { \@@_pdfprimitive: } } \cs_new_protected:Npn \@@_noexpand:N #1 { \@@_get_token: \cs_if_eq:NNTF #1 \prg_do_nothing: { \tl_if_empty:NTF \l_@@_head_tl { \@@_back_input: } { \exp_after:wN \@@_token_if_definable:NTF \l_@@_head_tl { \@@_noexpand_after: } { \@@_back_input: } } } { \@@_back_input: \@@_get_next: \@@_token_if_expandable:NT \l_@@_head_token { \cs_set_eq:NN \l_@@_head_token \@@_special_relax: } } } \cs_new_protected:Npn \@@_noexpand_after: { \group_begin: \@@_set_escapechar:n { 92 } \exp_args:NNc \group_end: \@@_noexpand_after:N { notexpanded: \exp_after:wN \token_to_str:N \l_@@_head_tl } } \cs_new_protected:Npn \@@_noexpand_after:N #1 { \cs_gset_eq:NN #1 \@@_special_relax: \@@_back_input:n {#1} } \cs_new_protected:Npn \@@_pdfprimitive: { \@@_not_implemented:n { pdfprimitive } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_endinput:, \@@_scantokens:, \@@_input:} % \begin{macrocode} \@@_new_tex_expandable:nn { input } % 106 { \int_case:nnF \l_@@_head_char_int { { 1 } { \@@_endinput: } % \endinput { 2 } { \@@_scantokens: } % \scantokens } { % 0=\input \bool_if:NTF \g_@@_name_in_progress_bool { \@@_insert_relax: } { \@@_input: } } } \cs_new_protected:Npn \@@_endinput: { \group_begin: \msg_warning:nn { unravel } { endinput-ignored } \group_end: \@@_print_expansion: } \cs_new_protected:Npn \@@_scantokens: { \@@_prev_input_gpush: \@@_scan_toks:NN \c_false_bool \c_false_bool \@@_prev_input_gpop:N \l_@@_tmpa_tl \exp_last_unbraced:NNNo \tl_set_rescan:Nnn \l_@@_head_tl \prg_do_nothing: \l_@@_tmpa_tl \@@_back_input:V \@@_everyeof:w \@@_back_input:V \l_@@_head_tl \@@_print_expansion:e { \tl_to_str:N \l_@@_tmpa_tl } } \cs_new_protected:Npn \@@_input: { \@@_prev_input_gpush:N \l_@@_head_tl \@@_scan_file_name: \@@_prev_input_gpop:N \l_@@_head_tl \tl_set:Ne \l_@@_tmpa_tl { \tl_tail:N \l_@@_head_tl } \@@_file_get:nN \l_@@_tmpa_tl \l_@@_tmpa_tl \@@_back_input:V \l_@@_tmpa_tl \@@_print_expansion:e { \tl_to_str:N \l_@@_head_tl } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_csname_loop:} % \begin{macrocode} \@@_new_tex_expandable:nn { cs_name } % 109 { \int_compare:nNnTF \l_@@_head_char_int = 0 { \@@_prev_input_gpush:N \l_@@_head_tl \@@_print_expansion: \@@_csname_loop: \@@_prev_input_silent:V \l_@@_head_tl \@@_get_lastnamedcs: \@@_prev_input_gpop:N \l_@@_head_tl \@@_back_input_tl_o: } { \@@_back_input:V \g_@@_lastnamedcs_tl \@@_print_expansion:e { \tl_to_str:N \l_@@_head_tl = \tl_to_str:N \g_@@_lastnamedcs_tl } } } \cs_new_protected:Npn \@@_csname_loop: { \@@_get_x_next: \@@_gtl_if_head_is_definable:NTF \l_@@_head_gtl { \cs_if_eq:NNF \l_@@_head_token \tex_endcsname:D { \@@_back_input: \@@_tex_error:nV { missing-endcsname } \l_@@_head_tl \tl_set:Nn \l_@@_head_tl { \tex_endcsname:D } } } { \@@_prev_input_silent:e { \@@_token_to_char:N \l_@@_head_token } \@@_csname_loop: } } \cs_new_protected:Npn \@@_get_lastnamedcs: { \group_begin: \@@_prev_input_get:N \l_@@_head_tl \tl_gset:No \g_@@_lastnamedcs_tl { \cs:w \exp_after:wN \@@_get_lastnamedcs_check:N \l_@@_head_tl } \group_end: } \cs_new:Npn \@@_get_lastnamedcs_check:N #1 { \if_meaning:w \reverse_if:N #1 \use_i:nn \fi: } % \end{macrocode} % \end{macro} % % \begin{macrocode} \@@_new_tex_expandable:nn { convert } % 110 { \@@_prev_input_gpush:N \l_@@_head_tl \@@_print_expansion: \int_case:nn \l_@@_head_char_int { 0 \@@_scan_int: 1 \@@_scan_int: 2 \@@_convert_string: 3 \@@_convert_meaning:w 4 \@@_scan_font_ident: 8 \@@_scan_font_ident: 9 \@@_scan_font_ident: { 10 } \@@_scan_font_ident: { 11 } \@@_scan_int: { 12 } \@@_scan_int: { 13 } \@@_scan_pdf_ext_toks: { 14 } \@@_scan_pdf_ext_toks: { 15 } \@@_scan_int: { 16 } \@@_scan_int: { 17 } \@@_scan_pdfstrcmp: { 18 } \@@_scan_pdfcolorstackinit: { 19 } \@@_scan_pdf_ext_toks: { 20 } \@@_scan_pdf_ext_toks: { 22 } \@@_scan_pdf_ext_toks: { 23 } \@@_scan_pdf_ext_toks: { 24 } { \@@_scan_keyword:n { fFiIlLeE } \@@_scan_pdf_ext_toks: } { 25 } \@@_scan_pdffiledump: { 26 } \@@_scan_pdfmatch: { 27 } \@@_scan_int: { 28 } \@@_scan_int: { 30 } \@@_scan_int: { 31 } \@@_scan_pdfximagebbox: { 33 } \@@_scan_directlua: { 34 } \@@_scan_pdf_ext_toks: { 35 } \@@_scan_pdf_ext_toks: { 40 } { \@@_scan_int: \@@_prev_input_silent:n { ~ } \@@_scan_int: } } \@@_prev_input_gpop:N \l_@@_head_tl \@@_back_input_tl_o: } \cs_new_protected:Npn \@@_convert_string: { \@@_get_next: \tl_if_empty:NTF \l_@@_head_tl { \@@_prev_input:e { \gtl_to_str:N \l_@@_head_gtl } } { \@@_prev_input:V \l_@@_head_tl } } \cs_new_protected:Npn \@@_convert_meaning:w \@@_prev_input_gpop:N \l_@@_head_tl \@@_back_input_tl_o: { \@@_get_next: \tl_if_empty:NTF \l_@@_head_tl { \gtl_set_eq:NN \l_@@_tmpb_gtl \l_@@_head_gtl \@@_prev_input_gpop:N \l_@@_prev_input_tl \exp_args:NNV \gtl_put_left:Nn \l_@@_tmpb_gtl \l_@@_prev_input_tl \@@_prev_input_gpush_gtl:N \l_@@_tmpb_gtl \@@_print_action:e { \gtl_to_str:N \l_@@_tmpb_gtl } \@@_prev_input_gpop_gtl:N \l_@@_tmpb_gtl \tl_set:Ne \l_@@_tmpa_tl { \gtl_head_do:NN \l_@@_head_gtl \tex_meaning:D } \@@_back_input:V \l_@@_tmpa_tl \@@_print_expansion:e { \gtl_to_str:N \l_@@_tmpb_gtl = \tl_to_str:N \l_@@_tmpa_tl } } { \@@_prev_input:V \l_@@_head_tl \@@_prev_input_gpop:N \l_@@_head_tl \@@_back_input_tl_o: } } \cs_new_protected:Npn \@@_scan_pdfstrcmp: { \@@_scan_toks_to_str: \@@_scan_toks_to_str: } \cs_new_protected:Npn \@@_scan_pdfximagebbox: { \@@_scan_int: \@@_scan_int: } \cs_new_protected:Npn \@@_scan_pdfcolorstackinit: { \@@_scan_keyword:nTF { pPaAgGeE } { \bool_set_true:N \l_@@_tmpa_bool } { \bool_set_false:N \l_@@_tmpb_bool } \@@_scan_keyword:nF { dDiIrReEcCtT } { \@@_scan_keyword:n { pPaAgGeE } } \@@_scan_toks_to_str: } \cs_new_protected:Npn \@@_scan_pdffiledump: { \@@_scan_keyword:nT { oOfFfFsSeEtT } \@@_scan_int: \@@_scan_keyword:nT { lLeEnNgGtThH } \@@_scan_int: \@@_scan_pdf_ext_toks: } \cs_new_protected:Npn \@@_scan_pdfmatch: { \@@_scan_keyword:n { iIcCaAsSeE } \@@_scan_keyword:nT { sSuUbBcCoOuUnNtT } { \@@_scan_int: } \@@_scan_pdf_ext_toks: \@@_scan_pdf_ext_toks: } \sys_if_engine_luatex:T { \cs_new_protected:Npn \@@_scan_directlua: { \@@_get_x_non_relax: \token_if_eq_catcode:NNTF \l_@@_head_token \c_group_begin_token { \@@_back_input: } { \@@_scan_int: \@@_get_x_non_relax: } \@@_scan_pdf_ext_toks: } } % \end{macrocode} % % \begin{macro}{\@@_get_the:N} % |#1| is \cs{@@_get_token_xdef:} in \tn{edef} or \tn{xdef}, % \cs{@@_get_token_x:} in \tn{message} and the like, and can be other % commands. % \begin{macrocode} \@@_new_tex_expandable:nn { the } % 111 { \@@_get_the:N } \cs_new_protected:Npn \@@_get_the:N #1 { \@@_prev_input_gpush:N \l_@@_head_tl \@@_print_expansion: \int_if_odd:nTF \l_@@_head_char_int { % \unexpanded, \detokenize \@@_scan_toks:NN \c_false_bool \c_false_bool \@@_prev_input_gpop:N \l_@@_head_tl \@@_set_action_text:e { \tl_to_str:N \l_@@_head_tl } } { % \the \@@_get_x_next: \@@_rescan_something_internal:n { 5 } \@@_prev_input_gpop:N \l_@@_head_tl \@@_set_action_text:e { \tl_head:N \l_@@_head_tl => \tl_tail:N \l_@@_head_tl } \tl_set:Ne \l_@@_head_tl { \exp_not:N \exp_not:n { \tl_tail:N \l_@@_head_tl } } } \cs_if_eq:NNTF #1 \@@_get_token_xdef: { \tl_put_right:NV \l_@@_defining_tl \l_@@_head_tl \@@_prev_input_silent:e { \l_@@_head_tl } \@@_print_action: } { \cs_if_eq:NNTF #1 \@@_get_token_x: { \exp_args:NNe \gtl_set:Nn \l_@@_tmpb_gtl { \l_@@_head_tl } \@@_prev_input_gtl:N \l_@@_tmpb_gtl } { \tl_set:Ne \l_@@_tmpa_tl { \exp_args:NV \exp_not:o \l_@@_head_tl } \@@_back_input:V \l_@@_tmpa_tl } \@@_print_expansion: } #1 } % \end{macrocode} % \end{macro} % % \begin{macrocode} \@@_new_tex_expandable:nn { top_bot_mark } % 112 { \@@_back_input_tl_o: } % \end{macrocode} % % \begin{macrocode} \@@_new_tex_expandable:nn { end_template } % 117 { \@@_back_input_tl_o: } % \end{macrocode} % % % \subsubsection{Conditionals} % % ^^A todo: simply use \cs{@@_input_gpop:N} right away. % \begin{macro}{\@@_pass_text:} % \begin{macro}{\@@_pass_text_done:w} % \begin{macrocode} \cs_new_protected:Npn \@@_pass_text: { \@@_input_if_empty:TF { \@@_pass_text_empty: } { \@@_input_get:N \l_@@_tmpb_gtl \if_true: \if_case:w \gtl_head_do:NN \l_@@_tmpb_gtl \c_one_int \exp_after:wN \@@_pass_text_done:w \fi: \@@_input_gpop:N \l_@@_tmpb_gtl \exp_after:wN \@@_pass_text: \else: \use:c { fi: } \int_set:Nn \l_@@_if_nesting_int { 1 } \@@_input_gpop:N \l_@@_tmpb_gtl \exp_after:wN \@@_pass_text_nested: \fi: } } \cs_new_protected:Npn \@@_pass_text_done:w { \@@_get_next: \token_if_eq_meaning:NNT \l_@@_head_token \fi: { \if_true: } \else: } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\@@_pass_text_nested:} % Again, if there is no more input we are in trouble. The % construction otherwise essentially results in % \begin{quote} % \cs{if_true:} \cs{if_true:} \cs{else:} \meta{head} \\ % \cs{int_decr:N} \cs{l_@@_if_nesting_int} \cs{use_none:nnnnn} \cs{fi:} \\ % \cs{use_none:nnn} \cs{fi:} \\ % \cs{int_incr:N} \cs{l_@@_if_nesting_int} \cs{fi:} % \end{quote} % If the \meta{head} is a primitive |\if...|, then the \cs{if_true:} % \cs{else:} ends with the second \cs{fi:}, and the nesting integer is % incremented before appropriately closing the \cs{if_true:}. If it % is a normal token or \tn{or} or \tn{else}, \cs{use_none:nnn} cleans % up, leaving the appropriate number of \cs{fi:}. Finally, if it is % \cs{fi:}, the nesting integer is decremented before removing most % \cs{fi:}. % \begin{macrocode} \cs_new_protected:Npn \@@_pass_text_nested: { \@@_input_if_empty:TF { \@@_pass_text_empty: } { \@@_input_get:N \l_@@_tmpb_gtl \if_true: \if_true: \gtl_head_do:NN \l_@@_tmpb_gtl \else: \int_decr:N \l_@@_if_nesting_int \use_none:nnnnn \fi: \use_none:nnn \fi: \int_incr:N \l_@@_if_nesting_int \fi: \@@_input_gpop:N \l_@@_unused_gtl \int_compare:nNnTF \l_@@_if_nesting_int = 0 { \@@_pass_text: } { \@@_pass_text_nested: } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_pass_text_empty:} % \begin{macrocode} \cs_new_protected:Npn \@@_pass_text_empty: { \@@_error:nnnnn { runaway-if } { } { } { } { } \@@_exit_hard:w } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_cond_push:, \@@_cond_pop:} % \begin{macrocode} \cs_new_protected:Npn \@@_cond_push: { \tl_gput_left:Ne \g_@@_if_limit_tl { { \int_use:N \g_@@_if_limit_int } } \int_gincr:N \g_@@_if_depth_int \int_gzero:N \g_@@_if_limit_int } \cs_new_protected:Npn \@@_cond_pop: { \fi: \int_gset:Nn \g_@@_if_limit_int { \tl_head:N \g_@@_if_limit_tl } \tl_gset:Ne \g_@@_if_limit_tl { \tl_tail:N \g_@@_if_limit_tl } \int_gdecr:N \g_@@_if_depth_int } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_change_if_limit:nn} % \begin{macrocode} \cs_new_protected:Npn \@@_change_if_limit:nn #1#2 { \int_compare:nNnTF {#2} = \g_@@_if_depth_int { \int_gset:Nn \g_@@_if_limit_int {#1} } { \tl_clear:N \l_@@_tmpa_tl \prg_replicate:nn { \g_@@_if_depth_int - #2 - 1 } { \tl_put_right:Ne \l_@@_tmpa_tl { { \tl_head:N \g_@@_if_limit_tl } } \tl_gset:Ne \g_@@_if_limit_tl { \tl_tail:N \g_@@_if_limit_tl } } \tl_gset:Ne \g_@@_if_limit_tl { \l_@@_tmpa_tl {#1} \tl_tail:N \g_@@_if_limit_tl } } } % \end{macrocode} % \end{macro} % % \begin{macrocode} \@@_new_tex_expandable:nn { if_test } % 107 { \@@_cond_push: \exp_args:NV \@@_cond_aux:n \g_@@_if_depth_int } % \end{macrocode} % % \begin{macro}{\@@_cond_aux:nn} % \begin{macrocode} \cs_new_protected:Npn \@@_cond_aux:n #1 { \int_case:nnF \l_@@_head_char_int { { 0 } { \@@_test_two_chars:nn { 0 } {#1} } % if { 1 } { \@@_test_two_chars:nn { 1 } {#1} } % ifcat { 12 } { \@@_test_ifx:n {#1} } { 16 } { \@@_test_case:n {#1} } { 20 } { \if_true: \@@_test_incsname:n {#1} } { 21 } { \if_true: \@@_test_pdfprimitive:n {#1} } } { \@@_prev_input_gpush:N \l_@@_head_tl \@@_print_expansion: \int_case:nn \l_@@_head_char_int { { 2 } % ifnum { \@@_test_two_vals:N \@@_scan_int: } { 3 } % ifdim { \@@_test_two_vals:N \@@_scan_normal_dimen: } { 4 } { \@@_scan_int: } % ifodd % { 5 } { } % if[hvm]mode, ifinner, if[tydm]dir { 9 } { \@@_scan_int: } % ifvoid, ifhbox, ifvbox etc { 13 } { \@@_scan_int: } % ifeof % { 14 } { } % iftrue % { 15 } { } % iffalse { 17 } { \@@_test_ifdefined: } % ifdefined { 18 } { \@@_test_ifcsname: } % ifcsname { 19 } % iffontchar { \@@_scan_font_ident: \@@_scan_int: } { 22 } % ifabsnum { \@@_test_two_vals:N \@@_scan_int: } { 23 } % ifabsdim { \@@_test_two_vals:N \@@_scan_normal_dimen: } { 24 } { \@@_scan_font_ident: } % ifjfont, iftfont } \@@_prev_input_gpop:N \l_@@_head_tl \@@_set_action_text:e { \tl_to_str:N \l_@@_head_tl } \l_@@_head_tl \scan_stop: \@@_cond_true:NNNn \else: \@@_cond_false:Nn \fi: {#1} } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_cond_true:NNNn} % \begin{macrocode} \cs_new_protected:Npn \@@_cond_true:NNNn #1#2#3#4 { \@@_change_if_limit:nn { 3 } {#4} % wait for else/fi \@@_print_expansion:e { \g_@@_action_text_str = true } } % \end{macrocode} % \end{macro} % % \begin{macro} % {\@@_cond_false:Nn, \@@_cond_false_loop:n, \@@_cond_false_common:} % \begin{macrocode} \cs_new_protected:Npn \@@_cond_false:Nn #1#2 { \@@_cond_false_loop:n {#2} \@@_cond_false_common: \@@_print_expansion:e { \g_@@_action_text_str = false ~ => ~ skip ~ to ~ \tl_to_str:N \l_@@_head_tl } } \cs_new_protected:Npn \@@_cond_false_loop:n #1 { \@@_pass_text: \int_compare:nNnTF \g_@@_if_depth_int = {#1} { \token_if_eq_meaning:NNT \l_@@_head_token \or: { \@@_error:nnnnn { extra-or } { } { } { } { } \@@_cond_false_loop:n {#1} } } { \token_if_eq_meaning:NNT \l_@@_head_token \fi: { \@@_cond_pop: } \@@_cond_false_loop:n {#1} } } \cs_new_protected:Npn \@@_cond_false_common: { \token_if_eq_meaning:NNTF \l_@@_head_token \fi: { \@@_cond_pop: } { \int_gset:Nn \g_@@_if_limit_int { 2 } } % wait for fi } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_test_two_vals:N} % \begin{macrocode} \cs_new_protected:Npn \@@_test_two_vals:N #1 { #1 \@@_get_x_non_blank: \@@_tl_if_in:ooTF { < = > } \l_@@_head_tl { } { \@@_error:nnnnn { missing-equals } { } { } { } { } \@@_back_input: \tl_set:Nn \l_@@_head_tl { = } } \@@_prev_input:V \l_@@_head_tl #1 } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_test_two_chars:nn, \@@_test_two_chars_get:n, \@@_test_two_chars_gtl:N} % \begin{macrocode} \cs_new_protected:Npn \@@_test_two_chars:nn #1 { \exp_args:NNo \gtl_set:Nn \l_@@_head_gtl { \l_@@_head_tl } \@@_prev_input_gpush_gtl:N \l_@@_head_gtl \@@_print_expansion: \@@_test_two_chars_get:n {#1} \@@_test_two_chars_get:n {#1} \@@_prev_input_gpop_gtl:N \l_@@_head_gtl \@@_set_action_text:e { \gtl_to_str:N \l_@@_head_gtl } \gtl_pop_left_item:NNTF \l_@@_head_gtl \l_@@_head_tl { } { } \exp_args:No \tl_if_head_eq_meaning:nNT \l_@@_head_tl \reverse_if:N { \gtl_pop_left_item:NNTF \l_@@_head_gtl \l_@@_head_tl { } { } \tl_put_left:Nn \l_@@_head_tl { \reverse_if:N } } \gtl_pop_left:NN \l_@@_head_gtl \l_@@_tmpb_gtl \@@_test_two_chars_gtl:N \l_@@_tmpb_gtl \@@_test_two_chars_gtl:N \l_@@_head_gtl \l_@@_head_tl \scan_stop: \@@_cond_true:NNNn \else: \@@_cond_false:Nn \fi: } \cs_new_protected:Npn \@@_test_two_chars_get:n #1 { \@@_get_x_next: \int_compare:nNnT {#1} = 0 { \gtl_if_head_is_N_type:NF \l_@@_head_gtl { \gtl_set:Ne \l_@@_head_gtl { \gtl_to_str:N \l_@@_head_gtl } } } \@@_prev_input_gtl:N \l_@@_head_gtl \@@_print_action:e { \gtl_to_str:N \l_@@_head_gtl } } \cs_new_protected:Npn \@@_test_two_chars_gtl:N #1 { \tl_put_right:Ne \l_@@_head_tl { \gtl_if_head_is_group_begin:NTF #1 { \c_group_begin_token } { \gtl_if_head_is_group_end:NTF #1 { \c_group_end_token } { \exp_not:N \exp_not:N \exp_not:f { \gtl_head_do:NN #1 \exp_stop_f: } } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_test_ifx:n, \@@_test_ifx_str:NN, \@@_test_ifx_aux:NNN, \@@_test_ifx_aux:w} % The token equal to \tn{ifx} is pushed as a previous input to show an % action nicely, then retrieved as \cs{l_@@_tmpa_tl} after getting the % next two tokens as |tmpb| and |head|. Then we call % \cs{l_@@_tmpa_tl} followed by these two tokens. A previous % implementation made sure to get these tokens from unpacking the % |gtl|, presumably (I should have documented, now I might be missing % something) to deal nicely with the master counter in case these % tokens are braces. On the other hand we must take care of tokens % affected by \tn{noexpand} and whose current definition is % expandable, in which case the trustworthy \tn{meaning} is that of % the \cs{l_@@_head_token} or \cs{l_@@_tmpb_token} rather than that of % the token in \cs{l_@@_head_gtl} or \cs{l_@@_tmpb_gtl}. % \begin{macrocode} \cs_new_protected:Npn \@@_test_ifx:n #1 { \@@_prev_input_gpush:N \l_@@_head_tl \@@_print_expansion: \@@_get_next: \gtl_set_eq:NN \l_@@_tmpb_gtl \l_@@_head_gtl \cs_set_eq:NN \l_@@_tmpb_token \l_@@_head_token \@@_get_next: \@@_prev_input_gpop:N \l_@@_tmpa_tl \@@_set_action_text:e { Compare:~ \tl_to_str:N \l_@@_tmpa_tl \@@_test_ifx_str:NN \l_@@_tmpb_token \l_@@_tmpb_gtl \@@_test_ifx_str:NN \l_@@_head_token \l_@@_head_gtl } \@@_test_ifx_aux:NNN \l_@@_tmpb_token \l_@@_tmpb_gtl \@@_test_ifx_aux:w \@@_cond_true:NNNn \else: \@@_cond_false:Nn \fi: {#1} } \cs_new:Npn \@@_test_ifx_str:NN #1#2 { \token_if_eq_meaning:NNT #1 \@@_special_relax: { \iow_char:N \\notexpanded: } \gtl_to_str:N #2 } \cs_new_protected:Npn \@@_test_ifx_aux:NNN #1#2#3 { \token_if_eq_meaning:NNTF #1 \@@_special_relax: { \gtl_head_do:NN #2 \@@_token_if_expandable:NTF { #3 #1 } { \gtl_head_do:NN #2 #3 } } { \gtl_head_do:NN #2 #3 } } \cs_new:Npn \@@_test_ifx_aux:w { \@@_test_ifx_aux:NNN \l_@@_head_token \l_@@_head_gtl \l_@@_tmpa_tl } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_test_case:n, \@@_test_case_aux:nn} % ^^A todo: \ifcase\currentifbranch\show 0\or\show 1\else\show -\fi won't work correctly. % \begin{macrocode} \cs_new_protected:Npn \@@_test_case:n #1 { \if_case:w 0 ~ \@@_prev_input_gpush:N \l_@@_head_tl \@@_print_expansion: \bool_if:NT \g_@@_internal_debug_bool { \iow_term:n { {\ifcase level~#1} } } \@@_scan_int: \@@_prev_input_get:N \l_@@_head_tl \tl_set:Ne \l_@@_head_tl { \tl_tail:N \l_@@_head_tl } % ^^A does text_case_aux use prev_input_seq? \int_compare:nNnF { \l_@@_head_tl } = 0 { \int_compare:nNnTF { \l_@@_head_tl } > 0 { \fi: \if_case:w 1 ~ \or: } { \fi: \if_case:w -1 ~ \else: } } \exp_args:No \@@_test_case_aux:nn { \l_@@_head_tl } {#1} \@@_prev_input_gpop:N \l_@@_head_tl \@@_print_expansion:e { \tl_to_str:N \l_@@_head_tl } } \cs_new_protected:Npn \@@_test_case_aux:nn #1#2 { \int_compare:nNnTF {#1} = 0 { \@@_change_if_limit:nn { 4 } {#2} } { \@@_pass_text: \int_compare:nNnTF \g_@@_if_depth_int = {#2} { \token_if_eq_meaning:NNTF \l_@@_head_token \or: { \exp_args:Nf \@@_test_case_aux:nn { \int_eval:n { #1 - 1 } } {#2} } { \@@_cond_false_common: } } { \token_if_eq_meaning:NNT \l_@@_head_token \fi: { \@@_cond_pop: } \@@_test_case_aux:nn {#1} {#2} } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_test_incsname:n} % \begin{macrocode} \cs_new_protected:Npn \@@_test_incsname:n #1 { \@@_not_implemented:n { ifincsname } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_test_pdfprimitive:n} % \begin{macrocode} \cs_new_protected:Npn \@@_test_pdfprimitive:n #1 { \@@_not_implemented:n { ifpdfprimitive } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_test_ifdefined:} % \begin{macrocode} \cs_new_protected:Npn \@@_test_ifdefined: { \@@_input_if_empty:TF { \@@_pass_text_empty: } { \@@_input_gpop:N \l_@@_tmpb_gtl \@@_set_action_text:e { Conditional:~ \tl_to_str:N \l_@@_head_tl \gtl_to_str:N \l_@@_tmpb_gtl } \@@_prev_input:e { \gtl_if_tl:NTF \l_@@_tmpb_gtl { \gtl_head:N \l_@@_tmpb_gtl } { \gtl_to_str:N \l_@@_tmpb_gtl } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_test_ifcsname:} % \begin{macrocode} \cs_new_protected:Npn \@@_test_ifcsname: { \@@_csname_loop: \@@_prev_input:V \l_@@_head_tl \@@_get_lastnamedcs: } % \end{macrocode} % \end{macro} % % \begin{macrocode} \@@_new_tex_expandable:nn { fi_or_else } % 108 { \int_compare:nNnTF \l_@@_head_char_int > \g_@@_if_limit_int { \int_compare:nNnTF \g_@@_if_limit_int = 0 { \int_compare:nNnTF \g_@@_if_depth_int = 0 { \@@_error:nnnnn { extra-fi-or-else } { } { } { } { } } { \@@_insert_relax: } } { \@@_error:nnnnn { extra-fi-or-else } { } { } { } { } } } { \@@_set_action_text: \int_compare:nNnF \l_@@_head_char_int = 2 { \@@_fi_or_else_loop: \@@_set_action_text:e { \g_@@_action_text_str \c_space_tl => ~ skip ~ to ~ \tl_to_str:N \l_@@_head_tl } } \@@_print_expansion: \@@_cond_pop: } } \cs_new_protected:Npn \@@_fi_or_else_loop: { \int_compare:nNnF \l_@@_head_char_int = 2 { \@@_pass_text: \@@_set_cmd: \@@_fi_or_else_loop: } } % \end{macrocode} % % \subsection{User interaction} % % ^^A Not implemented yet: non-hash-doubling version. % % \subsubsection{Print} % % Let us start with the procedure which prints to the terminal: this % will help me test the code while I'm writing it. % % \begin{macro}{\@@_print_normalize_null:} % \begin{variable}{\l_@@_print_tl} % Change the null character to an explicit |^||^||@| in \LuaTeX{} to % avoid a bug whereby a null character ends a string prematurely. % \begin{macrocode} \tl_new:N \l_@@_print_tl \sys_if_engine_luatex:TF { \cs_new_protected:Npe \@@_print_normalize_null: { \tl_replace_all:Nnn \exp_not:N \l_@@_print_tl { \char_generate:nn { 0 } { 12 } } { \tl_to_str:n { ^ ^ @ } } } } { \cs_new_protected:Npn \@@_print_normalize_null: { } } % \end{macrocode} % \end{variable} % \end{macro} % % \begin{macro}{\@@_print:n, \@@_print:e, \@@_log:n} % \begin{macrocode} \cs_new_protected:Npn \@@_print:n #1 { \tl_set:Nn \l_@@_print_tl {#1} \@@_print_normalize_null: \iow_term:e { \l_@@_print_tl } \tl_if_empty:NF \g_@@_output_file_tl { \iow_now:Ne \g_@@_iow { \l_@@_print_tl } } } \cs_generate_variant:Nn \@@_print:n { e } \cs_new_protected:Npn \@@_log:n #1 { \tl_set:Nn \l_@@_print_tl {#1} \@@_print_normalize_null: \tl_if_empty:NTF \g_@@_output_file_tl { \iow_log:e { \l_@@_print_tl } } { \iow_now:Ne \g_@@_iow { \l_@@_print_tl } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_print_step:n} % Steps are printed or not according to the option value, unaffected % by a prompt such as |s10|. % \begin{macrocode} \cs_new_protected:Npn \@@_print_step:n #1 { \int_compare:nNnF \g_@@_online_int < 0 { \int_compare:nNnTF \g_@@_online_int = 0 { \@@_log:n {#1} } { \@@_print:n {#1} } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_print_message:nn} % The message to be printed should come already detokenized, as~|#2|. % It will be wrapped to 80 characters per line, with~|#1| before each % line. The message is properly suppressed (or sent only to the log) % according to \cs{g_@@_current_online_int} so as to be sensitive to % the prompt. % \begin{macrocode} \cs_new_protected:Npn \@@_print_message:nn #1 #2 { \int_compare:nNnF \g_@@_current_online_int < 0 { \int_compare:nNnTF \g_@@_current_online_int = 0 { \iow_wrap:nnnN { #1 #2 } { #1 } { } \@@_log:n } { \iow_wrap:nnnN { #1 #2 } { #1 } { } \@@_print:n } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_set_action_text:e} % \begin{macrocode} \cs_new_protected:Npn \@@_set_action_text:e #1 { \group_begin: \@@_set_escapechar:n { 92 } \str_gset:Ne \g_@@_action_text_str {#1} \group_end: } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_set_action_text:} % \begin{macrocode} \cs_new_protected:Npn \@@_set_action_text: { \@@_set_action_text:e { \tl_to_str:N \l_@@_head_tl \tl_if_single_token:VT \l_@@_head_tl { = ~ \token_to_meaning:N \l_@@_head_token } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_print_state:} % \begin{macrocode} \cs_new_protected:Npn \@@_print_state: { \group_begin: \@@_set_escapechar:n { 92 } \tl_use:N \g_@@_before_print_state_tl \int_compare:nNnF \g_@@_current_online_int < 0 { \@@_print_state_output: \@@_print_state_prev: \@@_print_state_input: } \group_end: } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_print_state_output:, \@@_print_state_output:n} % Unless empty, print |#1| with each line starting with \verb*+<|~+. % The \cs{@@_str_truncate_left:nn} function trims |#1| if needed, to % fit in a maximum of \cs{g_@@_max_output_int} characters. % \begin{macrocode} \cs_new_protected:Npn \@@_print_state_output: { \exp_args:Ne \@@_print_state_output:n { \gtl_to_str:N \g_@@_output_gtl } } \cs_new_protected:Npn \@@_print_state_output:n #1 { \tl_if_empty:nF {#1} { \@@_print_message:nn { <| ~ } % | { \@@_str_truncate_left:nn {#1} { \g_@@_max_output_int } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_print_state_prev:} % Never trim~|##1|. % \begin{macrocode} \cs_new_protected:Npn \@@_print_state_prev: { \seq_set_map_e:NNn \l_@@_tmpa_seq \g_@@_prev_input_seq { \@@_to_str:Nn ##1 } \seq_remove_all:Nn \l_@@_tmpa_seq { } \seq_if_empty:NTF \l_@@_tmpa_seq { \@@_print_message:nn { || ~ } { } } { \seq_map_inline:Nn \l_@@_tmpa_seq { \@@_print_message:nn { || ~ } {##1} } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_print_state_input:, \@@_print_state_input:n} % Print |#1| with each line starting with \verb*+|>~+. The % \cs{@@_str_truncate_right:nn} function trims |#1| if needed, to fit % in a maximum of \cs{g_@@_max_input_int} characters. % \begin{macrocode} \cs_new_protected:Npn \@@_print_state_input: { \exp_args:Ne \@@_print_state_input:n { \@@_input_to_str: } } \cs_new_protected:Npn \@@_print_state_input:n #1 { \@@_print_message:nn { |> ~ } % | { \@@_str_truncate_right:nn {#1} { \g_@@_max_input_int } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_print_meaning:} % \begin{macrocode} \cs_new_protected:Npn \@@_print_meaning: { \@@_input_if_empty:TF { \@@_print_message:nn { } { Empty~input! } } { \@@_input_get:N \l_@@_tmpb_gtl \@@_print_message:nn { } { \gtl_head_do:NN \l_@@_tmpb_gtl \token_to_str:N = \gtl_head_do:NN \l_@@_tmpb_gtl \token_to_meaning:N } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_print_action:, \@@_print_action:e, \@@_print_assignment:, \@@_print_assignment:e, \@@_print_expansion:, \@@_print_expansion:e, \@@_print_action_aux:N} % Some of these commands are currently synonyms but we may decide to % make some options act differently on them. % \begin{macrocode} \cs_new_protected:Npn \@@_print_action: { \@@_print_action_aux:N \g_@@_trace_other_bool } \cs_new_protected:Npn \@@_print_action:e #1 { \@@_set_action_text:e {#1} \@@_print_action: } \cs_new_protected:Npn \@@_print_assignment: { \@@_print_action_aux:N \g_@@_trace_assigns_bool } \cs_new_protected:Npn \@@_print_assignment:e #1 { \@@_set_action_text:e {#1} \@@_print_assignment: } \cs_new_protected:Npn \@@_print_expansion: { \@@_print_action_aux:N \g_@@_trace_expansion_bool } \cs_new_protected:Npn \@@_print_expansion:e #1 { \@@_set_action_text:e {#1} \@@_print_expansion: } \cs_new_protected:Npn \@@_print_action_aux:N #1 { \int_gdecr:N \g_@@_nonstop_int \int_gincr:N \g_@@_step_int \bool_if:NT #1 { \exp_args:Ne \@@_print_step:n { [===== \bool_if:NT \g_@@_number_steps_bool { ~ Step ~ \int_to_arabic:n { \g_@@_step_int } ~ } =====]~ \int_compare:nNnTF { \str_count:N \g_@@_action_text_str } > { \g_@@_max_action_int } { \str_range:Nnn \g_@@_action_text_str { 1 } { \g_@@_max_action_int - 3 } ... } { \g_@@_action_text_str } } \@@_print_state: \@@_prompt: } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_just_print_assigned_token:, \@@_print_assigned_token:, \@@_print_assigned_register:, \@@_print_assigned_parshape:, \@@_print_assigned_set_shape:} % \begin{macro}[EXP]{\@@_print_assigned_set_shape_aux:n} % \begin{macrocode} \cs_new_protected:Npn \@@_just_print_assigned_token: { \@@_print_assignment:e { Set~ \exp_after:wN \token_to_str:N \l_@@_defined_tl = \exp_after:wN \token_to_meaning:N \l_@@_defined_tl } } \cs_new_protected:Npn \@@_print_assigned_token: { \@@_after_assignment: \@@_just_print_assigned_token: \@@_omit_after_assignment:w } \cs_new:Npn \@@_print_assigned_aux_name: { Set~ \exp_after:wN \token_to_str:N \l_@@_defined_tl \tl_if_single:NT \l_@@_defined_tl { ( \exp_after:wN \token_to_meaning:N \l_@@_defined_tl ) } } \cs_new_protected:Npn \@@_print_assigned_register: { \@@_after_assignment: \exp_args:Ne \@@_print_assignment:e % needed to stringify a \toks { \exp_not:N \@@_print_assigned_aux_name: = \exp_not:N \tl_to_str:n { \@@_the:w \l_@@_defined_tl } } \@@_omit_after_assignment:w } \cs_new_protected:Npn \@@_print_assigned_parshape: { \@@_after_assignment: \tl_set:Nn \l_@@_tmpa_tl { \tex_parshapedimen:D } \@@_print_assignment:e { \@@_print_assigned_aux_name: = \@@_the:w \l_@@_defined_tl \int_step_function:nN { 2 * \l_@@_defined_tl } \@@_print_assigned_set_shape_aux:n } \@@_omit_after_assignment:w } \cs_new_protected:Npn \@@_print_assigned_set_shape: { \@@_after_assignment: \tl_set_eq:NN \l_@@_tmpa_tl \l_@@_defined_tl \@@_print_assignment:e { \@@_print_assigned_aux_name: = \@@_the:w \l_@@_defined_tl 0 \exp_stop_f: \int_step_function:nN { \l_@@_defined_tl 0 } \@@_print_assigned_set_shape_aux:n } \@@_omit_after_assignment:w } \cs_new:Npn \@@_print_assigned_set_shape_aux:n #1 { ~ \@@_the:w \l_@@_tmpa_tl #1 \exp_stop_f: } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\@@_print_welcome:} % Welcome message. % \begin{macrocode} \cs_new_protected:Npn \@@_print_welcome: { \@@_print_message:nn { } { \bool_if:NTF \g_@@_welcome_message_bool { \\ ========~ Welcome~ to~ the~ unravel~ package~ ========\\ \iow_indent:n { "<|"~ denotes~ the~ output~ to~ TeX's~ stomach. \\ "||"~ denotes~ tokens~ waiting~ to~ be~ used. \\ "|>"~ denotes~ tokens~ that~ we~ will~ act~ on. \\ Press~~to~continue;~'h'~~for~help. \\ } } { [=====~Start~=====] } } \@@_print_state: \@@_prompt: } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_print_outcome:} % Final message. % \begin{macrocode} \cs_new_protected:Npn \@@_print_outcome: { \@@_print_message:nn { } { [=====~End~=====] } } % \end{macrocode} % \end{macro} % % \subsubsection{Prompt} % % \begin{macro}{\@@_ior_str_get:NN, \@@_ior_str_get:Nc} % \begin{macrocode} \cs_new_protected:Npn \@@_ior_str_get:NN #1#2 { \tex_readline:D #1 to #2 } \cs_generate_variant:Nn \@@_ior_str_get:NN { Nc } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_prompt:} % \begin{macrocode} \cs_new_protected:Npn \@@_prompt: { \int_compare:nNnF \g_@@_nonstop_int > 0 { \group_begin: \@@_set_escapechar:n { -1 } \int_set:Nn \tex_endlinechar:D { -1 } \tl_use:N \g_@@_before_prompt_tl \@@_prompt_aux: \group_end: } } \cs_new_protected:Npn \@@_prompt_aux: { \clist_if_empty:NTF \g_@@_prompt_input_clist { \int_compare:nNnT { \tex_interactionmode:D } = { 3 } { \bool_if:NTF \g_@@_explicit_prompt_bool { \@@_ior_str_get:Nc \c_@@_prompt_ior } { \@@_ior_str_get:Nc \c_@@_noprompt_ior } { Your~input } \exp_args:Nv \@@_prompt_treat:n { Your~input } } } { \clist_gpop:NN \g_@@_prompt_input_clist \l_@@_tmpa_tl \group_begin: \@@_set_escapechar:n { 92 } \@@_print:e { \bool_if:NT \g_@@_explicit_prompt_bool { Your~input= } \tl_to_str:N \l_@@_tmpa_tl } \group_end: \exp_args:NV \@@_prompt_treat:n \l_@@_tmpa_tl } } \cs_new_protected:Npn \@@_prompt_treat:n #1 { \tl_if_empty:nF {#1} { \str_case:enF { \tl_head:n {#1} } { { m } { \@@_print_meaning: \@@_prompt_aux: } { q } { \int_gset:Nn \g_@@_current_online_int { -1 } \int_gzero:N \g_@@_nonstop_int } { x } { \group_end: \@@_exit_hard:w } { X } { \tex_batchmode:D \tex_read:D -1 to \l_@@_tmpa_tl } { s } { \@@_prompt_scan_int:nn {#1} \@@_prompt_silent_steps:n } { o } { \@@_prompt_scan_int:nn {#1} { \int_gset:Nn \g_@@_current_online_int } } { C } { \use:e { \tl_gset_rescan:Nnn \exp_not:N \g_@@_tmpc_tl { \exp_not:N \ExplSyntaxOn } { \tl_tail:n {#1} } } \tl_gput_left:Nn \g_@@_tmpc_tl { \tl_gclear:N \g_@@_tmpc_tl } \group_insert_after:N \g_@@_tmpc_tl \group_insert_after:N \@@_prompt: } { | } % | { \@@_prompt_scan_int:nn {#1} \@@_prompt_vert:n } { u } { \@@_prompt_until:n {#1} } { a } { \@@_prompt_all: } } { \@@_prompt_help: } } } \cs_new_protected:Npn \@@_prompt_scan_int:nn #1 { \tex_afterassignment:D \@@_prompt_scan_int_after:wn \l_@@_prompt_tmpa_int = \tl_if_head_eq_charcode:fNF { \use_none:n #1 } - { 0 } \use_ii:nn #1 \scan_stop: } \cs_new_protected:Npn \@@_prompt_scan_int_after:wn #1 \scan_stop: #2 { #2 \l_@@_prompt_tmpa_int \tl_if_blank:nF {#1} { \@@_prompt_treat:n {#1} } } \cs_new_protected:Npn \@@_prompt_help: { \@@_print:n { "m":~meaning~of~first~token } \@@_print:n { "a":~print~state~again,~without~truncating } \@@_print:n { "s":~do~~steps~silently } \@@_print:n { "|":~silent~steps~until~~fewer~"||" } \@@_print:n { "u":~silent~steps~until~the~input~starts~with~ } \@@_print:n { "o":~1~=>~log~and~terminal,~0~=>~only~log,~-1~=>~neither.} \@@_print:n { "q":~semi-quiet~(same~as~"o-1") } \@@_print:n { "C":~run~some~expl3~code~immediately } \@@_print:n { "x"/"X":~exit~this~instance~of~unravel/TeX } \@@_prompt_aux: } \cs_new_protected:Npn \@@_prompt_silent_steps:n #1 { \int_compare:nNnF {#1} < 0 { \int_gset:Nn \g_@@_current_online_int { -1 } \tl_gset:Nn \g_@@_before_prompt_tl { \int_gset_eq:NN \g_@@_current_online_int \g_@@_online_int \tl_gclear:N \g_@@_before_prompt_tl } \int_gset:Nn \g_@@_nonstop_int {#1} } } \cs_new_protected:Npn \@@_prompt_vert:n #1 { \int_compare:nNnTF {#1} < { 0 } { \@@_prompt_vert:Nn > {#1} } { \@@_prompt_vert:Nn < {#1} } } \cs_new_protected:Npn \@@_prompt_vert:Nn #1#2 { \int_gset:Nn \g_@@_current_online_int { -1 } \tl_gset:Nf \g_@@_before_print_state_tl { \exp_args:NNf \exp_stop_f: \int_compare:nNnTF { \int_eval:n { \@@_prev_input_count: - #2 } } #1 { \@@_prev_input_count: } { \int_gset:Nn \g_@@_nonstop_int { \int_max:nn { \g_@@_nonstop_int } { 2 } } } { \int_gset_eq:NN \g_@@_current_online_int \g_@@_online_int \tl_gclear:N \g_@@_before_print_state_tl } } } \cs_new_protected:Npn \@@_prompt_all: { \tl_gset:Ne \g_@@_tmpc_tl { \exp_not:n { \tl_gclear:N \g_@@_tmpc_tl \int_gset_eq:NN \g_@@_max_output_int \c_max_int \int_gset_eq:NN \g_@@_max_input_int \c_max_int \@@_print_state: \int_gdecr:N \g_@@_nonstop_int \@@_prompt: } \@@_prompt_all_aux:N \g_@@_max_output_int \@@_prompt_all_aux:N \g_@@_max_input_int } \group_insert_after:N \g_@@_tmpc_tl } \cs_new:Npn \@@_prompt_all_aux:N #1 { \exp_not:n { \int_gset:Nn #1 } { \int_use:N #1 } } % \end{macrocode} % \end{macro} % % ^^A todo: I suspect a bug if given 'a' twice in a row % % \begin{macro}{\@@_prompt_until:n} % \begin{variable}{\g_@@_until_tl} % \begin{macrocode} \tl_new:N \g_@@_until_tl \cs_new_protected:Npn \@@_prompt_until:n #1 { \tl_gset:Ne \g_@@_until_tl { \tl_tail:n {#1} } \int_gset:Nn \g_@@_current_online_int { -1 } \tl_gset:Nn \g_@@_before_print_state_tl { \@@_input_get_left:N \l_@@_tmpa_tl \use:e { \exp_not:N \tl_if_in:nnTF { \exp_not:N \@@:nn \tl_to_str:N \l_@@_tmpa_tl } { \exp_not:N \@@:nn \tl_to_str:N \g_@@_until_tl } } { \int_gzero:N \g_@@_nonstop_int \int_gset_eq:NN \g_@@_current_online_int \g_@@_online_int \tl_gclear:N \g_@@_before_print_state_tl } { \int_gset:Nn \g_@@_nonstop_int { \int_max:nn { \g_@@_nonstop_int } { 2 } } } } } % \end{macrocode} % \end{variable} % \end{macro} % % \subsubsection{Errors} % % \begin{macro}{\@@_not_implemented:n} % \begin{macrocode} \cs_new_protected:Npn \@@_not_implemented:n #1 { \@@_error:nnnnn { not-implemented } {#1} { } { } { } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_error:nnnnn, \@@_error:neeee} % Errors within a group to make sure that none of the \pkg{l3msg} % variables (or others) that may be currently in use in the code being % debugged are modified. % \begin{macrocode} \cs_new_protected:Npn \@@_error:nnnnn #1#2#3#4#5 { \group_begin: \msg_error:nnnnnn { unravel } {#1} {#2} {#3} {#4} {#5} \group_end: } \cs_new_protected:Npn \@@_error:neeee #1#2#3#4#5 { \group_begin: \msg_error:nneeee { unravel } {#1} {#2} {#3} {#4} {#5} \group_end: } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_tex_msg_new:nnn} % This stores a \TeX{} error message. % \begin{macrocode} \cs_new_protected:Npn \@@_tex_msg_new:nnn #1#2#3 { \cs_new:cpn { @@_tex_msg_error_#1: } {#2} \cs_new:cpn { @@_tex_msg_help_#1: } {#3} } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_tex_error:nn, \@@_tex_error:nV} % Throw the |tex-error| message, with arguments: |#2| which triggered % the error, \TeX{}'s error message, and \TeX{}'s help text. % \begin{macrocode} \cs_new_protected:Npn \@@_tex_error:nn #1#2 { \@@_error:neeee { tex-error } { \tl_to_str:n {#2} } { \use:c { @@_tex_msg_error_#1: } } { \use:c { @@_tex_msg_help_#1: } } { } } \cs_generate_variant:Nn \@@_tex_error:nn { nV } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_tex_fatal_error:nn, \@@_tex_fatal_error:nV} % Throw the |tex-fatal| error message, with arguments: |#2| which % triggered the fatal error, \TeX{}'s error message, and \TeX{}'s % help text. % \begin{macrocode} \cs_new_protected:Npn \@@_tex_fatal_error:nn #1#2 { \@@_error:neeee { tex-fatal } { \tl_to_str:n {#2} } { \use:c { @@_tex_msg_error_#1: } } { \use:c { @@_tex_msg_help_#1: } } { } } \cs_generate_variant:Nn \@@_tex_fatal_error:nn { nV } % \end{macrocode} % \end{macro} % % \subsection{Keys} % % Each key needs to be defined twice: for its default setting and for % its setting applying to a single \cs{unravel}. This is due to the % fact that we cannot use grouping to keep settings local to a single % \cs{unravel} since the \meta{code} argument of \cs{unravel} may open % or close groups. % \begin{macrocode} \keys_define:nn { unravel/defaults } { explicit-prompt .bool_gset:N = \g_@@_default_explicit_prompt_bool , internal-debug .bool_gset:N = \g_@@_default_internal_debug_bool , max-action .int_gset:N = \g_@@_default_max_action_int , max-output .int_gset:N = \g_@@_default_max_output_int , max-input .int_gset:N = \g_@@_default_max_input_int , number-steps .bool_gset:N = \g_@@_default_number_steps_bool , online .int_gset:N = \g_@@_default_online_int , output-file .code:n = { \int_gzero:N \g_@@_default_online_int \tl_gset:Nn \g_@@_default_output_file_tl {#1} } , prompt-input .code:n = \@@_prompt_input:Nn \g_@@_default_prompt_input_clist {#1} , trace-assigns .bool_gset:N = \g_@@_default_trace_assigns_bool , trace-expansion .bool_gset:N = \g_@@_default_trace_expansion_bool , trace-other .bool_gset:N = \g_@@_default_trace_other_bool , welcome-message .bool_gset:N = \g_@@_default_welcome_message_bool , } \keys_define:nn { unravel } { explicit-prompt .bool_gset:N = \g_@@_explicit_prompt_bool , internal-debug .bool_gset:N = \g_@@_internal_debug_bool , max-action .int_gset:N = \g_@@_max_action_int , max-output .int_gset:N = \g_@@_max_output_int , max-input .int_gset:N = \g_@@_max_input_int , number-steps .bool_gset:N = \g_@@_number_steps_bool , online .int_gset:N = \g_@@_online_int , output-file .code:n = { \int_gzero:N \g_@@_online_int \tl_gset:Nn \g_@@_output_file_tl {#1} } , prompt-input .code:n = \@@_prompt_input:Nn \g_@@_prompt_input_clist {#1} , trace-assigns .bool_gset:N = \g_@@_trace_assigns_bool , trace-expansion .bool_gset:N = \g_@@_trace_expansion_bool , trace-other .bool_gset:N = \g_@@_trace_other_bool , welcome-message .bool_gset:N = \g_@@_welcome_message_bool , } % \end{macrocode} % % The |machine| and |trace| options are somewhat special so it is % clearer to define them separately. The code is identical for % |unravel/defaults| and |unravel| keys. To be sure of which options % are set, use |.meta:nn| and give the path explicitly. % \begin{macrocode} \tl_map_inline:nn { { /defaults } { } } { \keys_define:nn { unravel #1 } { machine .meta:nn = { unravel #1 } { explicit-prompt = false , internal-debug = false , max-action = \c_max_int , max-output = \c_max_int , max-input = \c_max_int , number-steps = false , welcome-message = false , } , mute .meta:nn = { unravel #1 } { trace-assigns = false , trace-expansion = false , trace-other = false , welcome-message = false , online = -1 , } } } % \end{macrocode} % % \subsection{Main command} % % \begin{macro}{\unravel} % Simply call an underlying code-level command. % \begin{macrocode} \NewDocumentCommand \unravel { O { } +m } { \unravel:nn {#1} {#2} } % \end{macrocode} % \end{macro} % % \begin{macro}{\unravelsetup} % Simply call an underlying code-level command. % \begin{macrocode} \NewDocumentCommand \unravelsetup { m } { \unravel_setup:n {#1} } % \end{macrocode} % \end{macro} % % \begin{macro}{\unravel_setup:n} % Set keys, updating both default values and current values. % \begin{macrocode} \cs_new_protected:Npn \unravel_setup:n #1 { \keys_set:nn { unravel/defaults } {#1} \keys_set:nn { unravel } {#1} } % \end{macrocode} % \end{macro} % % \begin{macro}{\unravel:nn, \@@:nn, \@@_unravel_marker:} % The command starts with \cs{@@_unravel_marker:} to detect nesting of % \tn{unravel} in \tn{unravel} and avoid re-initializing important % variables. % Initialize and setup keys. Initialize and setup other variables % including the input. Welcome the user. Then comes the main loop: % until the input is exhausted, print the current status and do one % step. The main loop is exited by skipping to the first % \cs{@@_exit_point:}, while some abort procedures jump to the second % (and last) one instead. If the main loop finished correctly, print % its outcome and finally test that everything is all right. % \begin{macrocode} \cs_new_protected:Npn \unravel:nn { \@@_unravel_marker: \@@:nn } \cs_new_eq:NN \@@_unravel_marker: \@@_special_relax: \cs_new_protected:Npn \@@:nn #1#2 { \@@_init_key_vars: \keys_set:nn { unravel } {#1} \@@_init_vars: \@@_input_gset:n {#2} \@@_print_welcome: \@@_main_loop: \@@_exit_point: \@@_print_outcome: \@@_final_test: \@@_exit_point: } \cs_new_protected:Npn \unravel_get:nnN #1#2#3 { \unravel:nn {#1} {#2} \tl_set:Ne #3 { \gtl_left_tl:N \g_@@_output_gtl } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_init_key_vars:} % Give variables that are affected by keys their default values (also % controlled by keys). % \begin{macrocode} \cs_new_protected:Npn \@@_init_key_vars: { \sys_if_engine_luatex:T { \tl_gset:No \g_@@_lastnamedcs_tl { \tex_lastnamedcs:D } } \bool_gset_eq:NN \g_@@_explicit_prompt_bool \g_@@_default_explicit_prompt_bool \bool_gset_eq:NN \g_@@_internal_debug_bool \g_@@_default_internal_debug_bool \bool_gset_eq:NN \g_@@_number_steps_bool \g_@@_default_number_steps_bool \int_gset_eq:NN \g_@@_online_int \g_@@_default_online_int \tl_gset_eq:NN \g_@@_output_file_tl \g_@@_default_output_file_tl \clist_gset_eq:NN \g_@@_prompt_input_clist \g_@@_default_prompt_input_clist \bool_gset_eq:NN \g_@@_trace_assigns_bool \g_@@_default_trace_assigns_bool \bool_gset_eq:NN \g_@@_trace_expansion_bool \g_@@_default_trace_expansion_bool \bool_gset_eq:NN \g_@@_trace_other_bool \g_@@_default_trace_other_bool \bool_gset_eq:NN \g_@@_welcome_message_bool \g_@@_default_welcome_message_bool \int_gset_eq:NN \g_@@_max_action_int \g_@@_default_max_action_int \int_gset_eq:NN \g_@@_max_output_int \g_@@_default_max_output_int \int_gset_eq:NN \g_@@_max_input_int \g_@@_default_max_input_int \int_gzero:N \g_@@_nonstop_int \tl_gclear:N \g_@@_before_print_state_tl \tl_gclear:N \g_@@_before_prompt_tl } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_init_vars:} % Give initial values to variables used during the processing. These % have no reason to be modified by the user: neither directly nor % through keys. % \begin{macrocode} \cs_new_protected:Npn \@@_init_vars: { \int_gset_eq:NN \g_@@_current_online_int \g_@@_online_int \tl_if_eq:NNF \g_@@_output_file_tl \g_@@_current_output_file_tl { \iow_close:N \g_@@_iow \iow_open:Nn \g_@@_iow \g_@@_output_file_tl \tl_gset_eq:NN \g_@@_current_output_file_tl \g_@@_output_file_tl } \seq_gclear:N \g_@@_prev_input_seq \gtl_gclear:N \g_@@_output_gtl \int_gzero:N \g_@@_step_int \tl_gclear:N \g_@@_if_limit_tl \int_gzero:N \g_@@_if_limit_int \int_gzero:N \g_@@_if_depth_int \gtl_gclear:N \g_@@_after_assignment_gtl \bool_gset_true:N \g_@@_set_box_allowed_bool \bool_gset_false:N \g_@@_name_in_progress_bool \gtl_clear:N \l_@@_after_group_gtl } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_main_loop:, \@@_get_x_next_or_done:} % Loop forever, getting the next token (with expansion) and performing % the corresponding command. We use \cs{@@_get_x_next_or_done:}, % which is basically \cs{@@_get_x_next:} but with a different % behaviour when there are no more tokens: running out of tokens here % is a successful exit of \cs{unravel}. Note that we cannot put the % logic into \cs{@@_main_loop:} because \cs{@@_expand_do:N} suppresses % the loop when a token is marked with \cs{notexpanded:}, and we don't % want that to suppress the main loop, only the expansion loop. % \begin{macrocode} \cs_new_protected:Npn \@@_get_x_next_or_done: { \@@_input_if_empty:TF { \@@_exit:w } { } \@@_get_next: \@@_token_if_expandable:NT \l_@@_head_token { \@@_expand_do:N \@@_get_x_next_or_done: } } \cs_new_protected:Npn \@@_main_loop: { \@@_get_x_next_or_done: \@@_set_cmd: \@@_do_step: \@@_main_loop: } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_do_step:} % Perform the action if the corresponding command exists. If that % command does not exist, complain, and leave the token in the output. % \begin{macrocode} \cs_new_protected:Npn \@@_do_step: { \@@_set_action_text: \bool_if:NT \g_@@_internal_debug_bool { \iow_term:e { Cmd:~\int_to_arabic:n { \l_@@_head_cmd_int } } } \cs_if_exist_use:cF { @@_cmd_ \int_use:N \l_@@_head_cmd_int : } { \@@_error:neeee { internal } { unknown-command } { } { } { } } } % \end{macrocode} % \end{macro} % % ^^A todo: improve error message % \begin{macro}{\@@_final_test:, \@@_final_bad:} % Make sure that the \cs{unravel} finished correctly. The error % message is a bit primitive. % \begin{macrocode} \cs_new_protected:Npn \@@_final_test: { \@@_input_if_empty:TF { \seq_if_empty:NTF \g_@@_prev_input_seq { \tl_if_empty:NTF \g_@@_if_limit_tl { \int_compare:nNnF \g_@@_if_limit_int = 0 { \@@_final_bad: } } { \@@_final_conditionals: } } { \@@_final_bad: } } { \@@_final_bad: } \@@_final_after_assignment: } \cs_new_protected:Npn \@@_final_bad: { \@@_error:nnnnn { internal } { the-last-unravel-finished-badly } { } { } { } } \cs_new_protected:Npn \@@_final_conditionals: { \group_begin: \msg_warning:nne { unravel } { dangling-conditionals } { \tl_count:N \g_@@_if_limit_tl } \group_end: \tl_greverse:N \g_@@_if_limit_tl \tl_gput_right:NV \g_@@_if_limit_tl \g_@@_if_limit_int \tl_gset:Ne \g_@@_if_limit_tl { \tl_tail:N \g_@@_if_limit_tl } % remove the {0} \prg_replicate:nn { \tl_count:N \g_@@_if_limit_tl } { \fi: } \tl_map_function:NN \g_@@_if_limit_tl \@@_final_cond_aux:n } \cs_new:Npn \@@_final_cond_aux:n #1 { \int_case:nnF {#1} { { 2 } { \if_false: \else: } { 3 } { \if_true: } { 4 } { \if_case:w 0 ~ } } { \@@_final_bad: } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_final_after_assignment:} % Salvage any remaining \tn{afterassignment} token. % \begin{macrocode} \cs_new_protected:Npn \@@_final_after_assignment: { \gtl_if_empty:NF \g_@@_after_assignment_gtl { \gtl_head_do:NN \g_@@_after_assignment_gtl \tex_afterassignment:D } } % \end{macrocode} % \end{macro} % % \subsection{Messages} % % \begin{macrocode} \msg_new:nnnn { unravel } { prev-input } { Internal~error:~unexpected~type~of~``prev_input''~entry. } { Found~type~#2~instead~of~#1~to~assign~to~variable~#3.~Contents:\\ \iow_indent:n {#4} } \msg_new:nnn { unravel } { unknown-primitive } { Internal~error:~the~primitive~'#1'~is~not~known. } \msg_new:nnn { unravel } { extra-fi-or-else } { Extra~fi,~or,~or~else. } \msg_new:nnn { unravel } { missing-dollar } { Missing~dollar~inserted. } \msg_new:nnn { unravel } { unknown-expandable } { Internal~error:~the~expandable~command~'#1'~is~not~known. } \msg_new:nnn { unravel } { missing-font-id } { Missing~font~identifier.~\iow_char:N\\nullfont~inserted. } \msg_new:nnn { unravel } { missing-rparen } { Missing~right~parenthesis~inserted~for~expression. } \msg_new:nnn { unravel } { missing-cs } { Missing~control~sequence.~\iow_char:N\\inaccessible~inserted. } \msg_new:nnn { unravel } { missing-box } { Missing~box~inserted. } \msg_new:nnn { unravel } { missing-to } { Missing~keyword~'to'~inserted. } \msg_new:nnn { unravel } { improper-leaders } { Leaders~not~followed~by~proper~glue. } \msg_new:nnn { unravel } { extra-close } { Extra~right~brace~or~\iow_char:N\\endgroup. } \msg_new:nnn { unravel } { off-save } { Something~is~wrong~with~groups. } \msg_new:nnn { unravel } { hrule-bad-mode } { \iow_char\\hrule~used~in~wrong~mode. } \msg_new:nnn { unravel } { invalid-mode } { Invalid~mode~for~this~command. } \msg_new:nnn { unravel } { color-stack-action-missing } { Missing~color~stack~action. } \msg_new:nnn { unravel } { action-type-missing } { Missing~action~type. } \msg_new:nnn { unravel } { identifier-type-missing } { Missing~identifier~type. } \msg_new:nnn { unravel } { destination-type-missing } { Missing~destination~type. } \msg_new:nnn { unravel } { erroneous-prefixes } { Prefixes~appplied~to~non-assignment~command. } \msg_new:nnn { unravel } { improper-setbox } { \iow_char:N\\setbox~while~fetching~base~of~an~accent. } \msg_new:nnn { unravel } { after-advance } { Missing~register~after~\iow_char:N\\advance,~ \iow_char:N\\multiply,~or~\iow_char:N\\divide. } \msg_new:nnn { unravel } { bad-unless } { \iow_char:N\\unless~not~followed~by~conditional. } \msg_new:nnn { unravel } { runaway-if } { Runaway~\iow_char:N\\if...~Exiting~\iow_char:N\\unravel } \msg_new:nnn { unravel } { runaway-macro-parameter } { Runaway~macro~parameter~\# #2~after \\\\ \iow_indent:n {#1} } \msg_new:nnn { unravel } { runaway-text } { Runaway~braced~argument~for~TeX~primitive.~Exiting~\iow_char:N\\unravel } \msg_new:nnn { unravel } { extra-or } { Extra~\iow_char:N\\or. } \msg_new:nnn { unravel } { missing-equals } { Missing~equals~for~\iow_char:N\\ifnum~or~\iow_char:N\\ifdim. } \msg_new:nnn { unravel } { internal } { Internal~error:~'#1'.~\ Please~report. } \msg_new:nnn { unravel } { not-implemented } { The~following~feature~is~not~implemented:~'#1'. } \msg_new:nnn { unravel } { endinput-ignored } { The~primitive~\iow_char:N\\endinput~was~ignored. } \msg_new:nnn { unravel } { missing-something } { Something~is~missing,~sorry! } \msg_new:nnn { unravel } { nested-unravel } { The~\iow_char:N\\unravel~command~may~not~be~nested. } \msg_new:nnnn { unravel } { tex-error } { TeX~sees~"#1"~and~throws~an~error:\\\\ \iow_indent:n {#2} } { \tl_if_empty:nTF {#3} { TeX~provides~no~further~help~for~this~error. } { TeX's~advice~is:\\\\ \iow_indent:n {#3} } } \msg_new:nnnn { unravel } { tex-fatal } { TeX~sees~"#1"~and~throws~a~fatal~error:\\\\ \iow_indent:n {#2} } { \tl_if_empty:nTF {#3} { TeX~provides~no~further~help~for~this~error. } { TeX's~advice~is:\\\\ \iow_indent:n {#3} } } \msg_new:nnnn { unravel } { runaway-unravel } { Runaway~\iow_char:N\\unravel,~so~\iow_char:N\\relax~inserted. } { Some~TeX~command~expects~input~beyond~the~end~of~ the~argument~of~\iow_char:N\\unravel. } \msg_new:nnn { unravel } { dangling-conditionals } { Attempting~to~issue~#1~dangling~conditionals. } % \end{macrocode} % % Some error messages from \TeX{} itself. % \begin{macrocode} \@@_tex_msg_new:nnn { forbidden-case } { You~can't~use~`\exp_after:wN \token_to_str:N \l_@@_head_tl'~in~ \mode_if_vertical:TF { vertical } { \mode_if_horizontal:TF { horizontal } { \mode_if_math:TF { math } { no } } } ~ mode. } { Sorry,~but~I'm~not~programmed~to~handle~this~case;~ I'll~just~pretend~that~you~didn't~ask~for~it.~ If~you're~in~the~wrong~mode,~you~might~be~able~to~ return~to~the~right~one~by~typing~`I\iow_char:N\}'~or~ `I\iow_char:N\$'~or~`I\iow_char:N\\par'. } \@@_tex_msg_new:nnn { incompatible-mag } { Incompatible~magnification~ ( \int_to_arabic:n { \@@_mag: } );~ the~previous~value~will~be~retained } { I~can~handle~only~one~magnification~ratio~per~job.~So~I've~ reverted~to~the~magnification~you~used~earlier~on~this~run. } \@@_tex_msg_new:nnn { illegal-mag } { Illegal~magnification~has~been~changed~to~1000~ ( \int_to_arabic:n { \@@_mag: } ) } { The~magnification~ratio~must~be~between~1~and~32768. } \@@_tex_msg_new:nnn { missing-number } { Missing~number,~treated~as~zero } { A~number~should~have~been~here;~I~inserted~`0'.~ If~you~can't~figure~out~why~I~needed~to~see~a~number,~ look~up~`weird~error'~in~the~index~to~The~TeXbook. } \@@_tex_msg_new:nnn { the-cannot } { You~can't~use~`\tl_to_str:N\l_@@_head_tl'~after~\iow_char:N\\the } { I'm~forgetting~what~you~said~and~using~zero~instead. } \@@_tex_msg_new:nnn { incompatible-units } { Incompatible~glue~units } { I'm~going~to~assume~that~1mu=1pt~when~they're~mixed. } \@@_tex_msg_new:nnn { missing-mu } { Illegal~unit~of~measure~(mu~inserted) } { The~unit~of~measurement~in~math~glue~must~be~mu.~ To~recover~gracefully~from~this~error,~it's~best~to~ delete~the~erroneous~units;~e.g.,~type~`2'~to~delete~ two~letters.~(See~Chapter~27~of~The~TeXbook.) } \@@_tex_msg_new:nnn { missing-pt } { Illegal~unit~of~measure~(pt~inserted) } { Dimensions~can~be~in~units~of~em,~ex,~in,~pt,~pc,~ cm,~mm,~dd,~cc,~nd,~nc,~bp,~or~sp;~but~yours~is~a~new~one!~ I'll~assume~that~you~meant~to~say~pt,~for~printer's~points.~ To~recover~gracefully~from~this~error,~it's~best~to~ delete~the~erroneous~units;~e.g.,~type~`2'~to~delete~ two~letters.~(See~Chapter~27~of~The~TeXbook.) } \@@_tex_msg_new:nnn { missing-lbrace } { Missing~\iow_char:N\{~inserted } { A~left~brace~was~mandatory~here,~so~I've~put~one~in.~ You~might~want~to~delete~and/or~insert~some~corrections~ so~that~I~will~find~a~matching~right~brace~soon.~ (If~you're~confused~by~all~this,~try~typing~`I\iow_char:N\}'~now.) } \@@_tex_msg_new:nnn { extra-endcsname } { Extra~\token_to_str:c{endcsname} } { I'm~ignoring~this,~since~I~wasn't~doing~a~\token_to_str:c{csname}. } \@@_tex_msg_new:nnn { missing-endcsname } { Missing~\token_to_str:c{endcsname}~inserted } { The~control~sequence~marked~~should~ not~appear~between~\token_to_str:c{csname}~and~ \token_to_str:c{endcsname}. } \@@_tex_msg_new:nnn { missing-delim } { Missing~delimiter~(.~inserted) } { I~was~expecting~to~see~something~like~`('~or~`\token_to_str:N\{'~or~ `\token_to_str:N\}'~here.~If~you~typed,~e.g.,~ `\{'~instead~of~`\token_to_str:N\{',~you~ should~probably~delete~the~`\{'~by~typing~`1'~now,~so~that~ braces~don't~get~unbalanced.~Otherwise~just~proceed.~ Acceptable~delimiters~are~characters~whose~\token_to_str:c{delcode}~is~ nonnegative,~or~you~can~use~`\token_to_str:c{delimiter}~'. } % \end{macrocode} % % Fatal \TeX{} error messages. % \begin{macrocode} \@@_tex_msg_new:nnn { cannot-read } { ***~(cannot~\iow_char:N\\read~from~terminal~in~nonstop~modes) } { } \@@_tex_msg_new:nnn { file-error } { ***~(job~aborted,~file~error~in~nonstop~mode) } { } \@@_tex_msg_new:nnn { interwoven-preambles } { (interwoven~alignment~preambles~are~not~allowed) } { } % \end{macrocode} % % Restore catcodes to their original values. % \begin{macrocode} \@@_setup_restore: % \end{macrocode} % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % \endinput