diff options
author | Karl Berry <karl@freefriends.org> | 2012-07-24 16:55:17 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2012-07-24 16:55:17 +0000 |
commit | 3accf11dd41cac5f2c81b83a2a712c77ffab2b6a (patch) | |
tree | e1b5a34217c371d2c469c5b9d79fe6b12f3624e4 /Master/texmf-dist/source/latex/morewrites | |
parent | c1841336503cc8eb2355c2eab40012c856f18369 (diff) |
morewrites (18jul12)
git-svn-id: svn://tug.org/texlive/trunk@27138 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/source/latex/morewrites')
3 files changed, 1233 insertions, 555 deletions
diff --git a/Master/texmf-dist/source/latex/morewrites/morewrites.dtx b/Master/texmf-dist/source/latex/morewrites/morewrites.dtx index cc8d2d23121..4766f3abe5e 100644 --- a/Master/texmf-dist/source/latex/morewrites/morewrites.dtx +++ b/Master/texmf-dist/source/latex/morewrites/morewrites.dtx @@ -1,6 +1,6 @@ % \iffalse meta-comment % -%% File: morewrites.dtx Copyright (C) 2011 Bruno Le Floch +%% File: morewrites.dtx Copyright (C) 2011-2012 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 @@ -13,8 +13,6 @@ % %<*driver|package> \RequirePackage{l3names} -\GetIdInfo$Id: morewrites.dtx 0.1 2011-09-09 00:00:00Z bruno $ - {Always room for a new write}% %</driver|package> %<*driver> \RequirePackage{morewrites} @@ -22,6 +20,7 @@ \newwrite\foo \newwrite\foo \newwrite\foo \newwrite\foo \newwrite\foo \newwrite\foo \newwrite\foo \newwrite\foo \newwrite\foo \newwrite\foo \newwrite\foo \newwrite\foo +\newcommand{\proc}[1]{\texttt{#1}} \begin{document} \DocInput{\jobname.dtx} \end{document} @@ -30,47 +29,55 @@ % % \title{The \textsf{morewrites} package: \\ % Always room for a new \cs{write}\thanks{This -% file has version number \ExplFileVersion, last revised \ExplFileDate.}} +% file has version number v0.2, last revised 2012-07-10.}} % \author{Bruno Le Floch} -% \date{\ExplFileDate} +% \date{July 10, 2012} % % \maketitle % \tableofcontents % % \begin{documentation} % -% \section{\pkg{morewrites} documentation} -% -% This \LaTeX{} package is meant to be a solution for the error -% \enquote{no room for a new \cs{write}}, which occurs when too -% many macro packages reserve streams to write data to various -% auxiliary files. It is in principle possible to rewrite packages -% so that they are less greedy on resources, but that is often -% unpractical for the end-user. Instead, \pkg{morewrites} hooks -% at the lowest level (\TeX{} primitives). If I did my job -% correctly, you simply need to add the line -% |\usepackage{morewrites}| somewhere near the beginning of your -% \LaTeX{} file, and the \enquote{no room for a new \cs{write}} -% error should vanish. -% -% As this is a rather new package, it has not been tested very -% thoroughly yet. I thus encourage you to check that references -% are correct after loading that package: if they are correct -% without \pkg{morewrites}, but wrong with, please send me a -% minimal file showing the problem, or post a question on the -% \url{tex.stackexchange.com} question and answers website, -% or the \url{comp.text.tex} newsgroup. -% -% This package loads the \pkg{expl3} package, hence the -% \pkg{l3kernel} bundle needs to be up to date. If Heiko -% Oberdiek's package %^^A \pkg{atveryend} -% \pkg{atbegshi} is available, it will be used. -% -% \section{Known deficiencies} -% -% Some distributions of \TeX{} allow a quoted syntax for file -% names with spaces. I haven't yet coded that. A temporary fix -% is to avoid file names with spaces. +% \section{\pkg{morewrites} documentation} +% +% This \LaTeX{} package is meant to be a solution for the error +% \enquote{no room for a new \cs{write}}, which occurs when too many +% macro packages reserve streams to write data to various auxiliary +% files. It is in principle possible to rewrite packages so that they +% are less greedy on resources, but that is often unpractical for the +% end-user. Instead, \pkg{morewrites} hooks at the lowest level (\TeX{} +% primitives). If I did my job correctly, you simply need to add the +% line |\usepackage{morewrites}| somewhere near the beginning of your +% \LaTeX{} file, and the \enquote{no room for a new \cs{write}} error +% should vanish. +% +% I have tried to make the code as robust as possible, but there may +% still be bugs lurking as this package has not been tested very +% thoroughly yet. I thus encourage you to check that references are +% correct after loading that package: if they are correct without +% \pkg{morewrites}, but wrong with, please send me a minimal file +% showing the problem, or post a question on the +% \url{tex.stackexchange.com} question and answers website, or the +% \url{comp.text.tex} newsgroup. +% +% This package loads the \pkg{expl3} package, hence the \pkg{l3kernel} +% bundle needs to be up to date. If Heiko Oberdiek's package +% \pkg{atbegshi} is available, it will be used. +% +% \section{Known deficiencies} +% +% Some distributions of \TeX{} allow a quoted syntax for file names with +% spaces. I haven't yet coded that. A temporary fix is to avoid file +% names with spaces. +% +% The package code is not very legible, and definitely uses too many +% |:D| control sequences, whose name means \enquote{do not use}. The +% author does not see a way to avoid using primitives in this package, +% since hooking into the primitives \tn{immediate}, \tn{write}, +% \emph{etc.} requires having a very strong control on what every +% command does. \emph{Do not take this package as an example of how to +% code with \pkg{expl3}; go and see Joseph Wright's \pkg{siunitx} +% instead.} % % \end{documentation} % @@ -81,637 +88,647 @@ %<*package> % \begin{macrocode} \ProvidesExplPackage - {\ExplFileName}{\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription} -\RequirePackage{expl3} -% \end{macrocode} -% -% Let us recall the syntax of the commands that we need to modify -% (from Chapter 24 of the \TeX{}book): each of those can be preceeded -% by \cs{immediate}, which we will thus also have to modify. + {morewrites} {2012/07/10} {0.2} {Always room for a new write} +\RequirePackage{expl3, primargs} +% \end{macrocode} +% +% \begin{macrocode} +%<@@=morewrites> +% \end{macrocode} +% +% \subsection{Overview of relevant \TeX{} facts} +% +% The aim of the \pkg{morewrites} package is to lift \TeX{}'s +% restriction of only having $16$ files open for writing at the same +% time. We must thus patch $4$ primitives, \tn{openout}, \tn{write}, +% \tn{closeout} and \tn{immediate}, and the \cs{newwrite} macro, defined +% by \LaTeX{} (and plain \TeX{}). Each of those commands must be made +% to accept numbers outside the range $[0,15]$. Let us review the +% syntax of the various commands we need to alter (see Chapter~24 of the +% \TeX{}book). +% +% We start with the three \enquote{actions}. +% \begin{syntax} +% \tn{openout} \meta{integer} \meta{equals} \meta{file name} +% \end{syntax} +% \TeX{} searches the path for a file with a name given by +% \meta{file name}. If found, this file is opened in the writing stream +% \meta{integer}, which must be a number in the range $[0,15]$. +% \begin{syntax} +% \tn{write} \meta{integer} \meta{filler} \meta{general text} +% \end{syntax} +% \TeX{} expands the \meta{general text} as for an \texttt{x}-type +% expansion, with the caveat that macro parameter characters do not need +% to be doubled; converts the result to a string, and writes it in the +% writing stream \meta{integer}. If the writing stream \meta{integer} +% is open (in particular it must be in the range $[0,15]$), then this +% writes to the corresponding file. Otherwise, if the \meta{integer} is +% negative, the text is written to the log file, and a non-negative +% \meta{integer} writes to the terminal. One exception: if the +% \meta{integer} is $18$, the text is sent to a shell to be run as shell +% code. +% \begin{syntax} +% \tn{closeout} \meta{integer} +% \end{syntax} +% If the writing stream \meta{integer} is open, it is closed. +% Otherwise, if the \meta{integer} is not in the range $[0,15]$ an error +% may be raised, or nothing happens. +% +% By default, each one of those three \enquote{actions} are recorded in +% a whatsit node in the current list, and will be performed when the box +% containing the whatsit node is sent to the final \texttt{pdf}, +% \emph{i.e.}, at \enquote{shipout} time. In particular, the +% \meta{general text} for the \tn{write} primitive is expanded at +% shipout time. This behaviour may be modified by putting +% \tn{immediate} before any of the three \enquote{actions} to force +% \TeX{} to perform the action immediately instead of recording it in a +% whatsit node. +% +% Since the \tn{openout}, \tn{write}, and \tn{closeout} primitives +% operate at \tn{shipout} time, we will have to hook into this primitive +% too. It expects to be followed by a box specification such as +% \tn{box}\meta{integer}, or \tn{hbox}\Arg{material to typeset}. +% +% Finally, the \cs{newwrite} macro expects one token as its argument, +% and defines this token (with \tn{chardef}) to be an integer +% corresponding to the first available writing stream. We must extend +% it to let it allocate higher (virtual) write registers. +% +% All of the primitives above perform full expansion of all tokens when +% looking for their operands. In most cases, only the \tn{meaning} of +% tokens encountered in this way matters. Specifically, % \begin{itemize} -% \item \cs{openout} \meta{4-bit number} \meta{equals} \meta{filename} -% \item \cs{write} \meta{number} \meta{general text} -% with a special case when \meta{number} is $18$. -% \item \cs{closeout} \meta{4-bit number} +% \item \meta{integer} denotes an integer in any form that \TeX{} +% accepts as the right-hand side of a primitive integer assignment of +% the form \tn{count}|0=|\meta{integer}; +% \item \meta{equals} is an arbitrary (optional) number of explicit or +% implicit space characters, an optional explicit equal sign of +% category other, and further (optional) explicit or implicit space +% characters; +% \item \meta{file name} is an arbitrary sequence of explicit or implicit +% characters with arbitrary category codes (except active characters, +% which are expanded before reaching \TeX{}'s mouth), ending either +% with a space character (character code $32$, arbitrary non-active +% category code, explicit or implicit), which is removed, or with a +% non-expandable token, with some care needed for the case of a +% \tn{notexpanded:} expandable token; +% \item \meta{filler} is an arbitrary combination of tokens whose +% meaning is \tn{relax} or a character with category code $10$; +% \item \meta{general text} is formed of braced tokens, starting with an +% explicit or implicit begin-group character, and ending with the +% matching explicit end-group character (both with any character +% code), with an equal number of explicit begin-group and end-group +% characters in between: this is precisely the right-hand side of an +% assignment of the form \tn{toks}|0=|\meta{general text}. % \end{itemize} -% Finally, we also need to modify \cs{newwrite} to let it allocate -% more than $16$ streams. % -% \begin{macro}[aux]{\mw_tex_openin:w,\mw_tex_read:w, -% \mw_etex_readline:w,\mw_tex_closein:w} -% \begin{macro}[aux]{\mw_tex_immediate:w,\mw_tex_openout:w, -% \mw_tex_write:w,\mw_tex_closeout:w} -% First save the primitives (including those for input, -% because this package might be extended to cover that -% as well). +% \subsection{Renaming primitives (again)} +% +% \begin{macro}[int] +% { +% \@@_tex_immediate:w , +% \@@_tex_openout:w , +% \@@_tex_write:w , +% \@@_tex_closeout:w +% } +% First save the output-related primitives. % \begin{macrocode} -\cs_new_eq:NN \mw_tex_openin:w \tex_openin:D -\cs_new_eq:NN \mw_tex_read:w \tex_read:D -\cs_new_eq:NN \mw_etex_readline:w \etex_readline:D -\cs_new_eq:NN \mw_tex_closein:w \tex_closein:D -\cs_new_eq:NN \mw_tex_immediate:w \tex_immediate:D -\cs_new_eq:NN \mw_tex_openout:w \tex_openout:D -\cs_new_eq:NN \mw_tex_write:w \tex_write:D -\cs_new_eq:NN \mw_tex_closeout:w \tex_closeout:D -\cs_new_eq:NN \mw_tex_shipout:w \tex_shipout:D +\cs_new_eq:NN \@@_tex_immediate:w \tex_immediate:D +\cs_new_eq:NN \@@_tex_openout:w \tex_openout:D +\cs_new_eq:NN \@@_tex_write:w \tex_write:D +\cs_new_eq:NN \@@_tex_closeout:w \tex_closeout:D % \end{macrocode} % \end{macro} +% +% \begin{macro}[int]{\@@_tex_shipout:w} +% Since the non-\tn{immediate} output primitives act at \tn{shipout} +% time, we need to alter this primitive too. +% %^^A Not sure if the shipout primitive is done right. +% \begin{macrocode} +\cs_new_eq:NN \@@_tex_shipout:w \tex_shipout:D +% \end{macrocode} % \end{macro} % % \subsection{Variables} % -% \begin{variable}{\g_mw_late_write_int} -% The integer \cs{g_mw_late_write_int} labels the various -% non-immediate operations in the order in which they appear -% in the source. -% \begin{macrocode} -\int_new:N \g_mw_late_write_int +% \begin{variable}{\g_@@_late_write_int} +% The integer \cs{g_@@_late_write_int} labels the various +% non-immediate operations in the order in which they appear in the +% source. We can never reuse a number because there is no way to know +% if a whatsit was recorded in a box register, which could be reused +% in a shipped-out box: +% \begin{quote} +% \cs{vbox_set:Nn} \cs{l_my_box} \\ +% ~~|{| \cs{iow_shipout_x:Nn} \cs{c_term_iow} \Arg{text} |}| +% \cs{tex_shipout:D} \cs{tex_copy:D} \cs{l_my_box} +% \cs{tex_shipout:D} \cs{tex_copy:D} \cs{l_my_box} +% \end{quote} +% will print \meta{text} to the terminal twice. +% \begin{macrocode} +\int_new:N \g_@@_late_write_int % \end{macrocode} % \end{variable} % -% \begin{variable}{\g_mw_writes_prop} -% The property list \cs{g_mw_writes_prop} -% associates a file name to each open stream. +% \begin{variable}{\g_@@_iow_prop} +% The property list \cs{g_@@_iow_prop} associates a file name to each +% open stream. % \begin{macrocode} -\prop_new:N \g_mw_writes_prop +\prop_new:N \g_@@_iow_prop % \end{macrocode} % \end{variable} % -% \begin{variable}{\g_mw_read,\c_mw_tmp_file_tl} -% The expansion that \cs{write} performs is impossible -% to emulate with anything else than \cs{write}. We will -% need to write to a file and read back from it for things -% to work properly. +% \begin{variable}{\g_@@_iow, \g_@@_ior, \g_@@_tmp_file_tl} +% The expansion that \tn{write} performs is impossible to emulate with +% anything else than \tn{write}. We will write on the stream +% \cs{g_@@_iow} to the file \cs{g_@@_tmp_file_tl} and read back from +% it in the stream \cs{g_@@_ior} for things to work properly. +% Unfortunately, this means that the file is repeatedly opened and +% closed, leaving a trace of that in the log. % \begin{macrocode} -\newread \g_mw_read -\tl_const:Nn \c_mw_tmp_file_tl { \jobname.mw } -\newwrite \g_mw_write +\newwrite \g_@@_iow +\newread \g_@@_ior +\tl_new:N \g_@@_tmp_file_tl +\tl_gset:Nn \g_@@_tmp_file_tl { \jobname.mw } % \end{macrocode} % \end{variable} % -% \begin{variable}{\g_mw_reserved_writes_seq} -% Some of the writing streams are already allocated when loading -% this package, and we let \LaTeX{} manage them. +% \begin{variable}{\g_@@_reserved_iow_clist} +% Some of the writing streams are already allocated when loading this +% package, and we let the engine manage them. This variable is a +% clist because it only contains integers and the main task is to test +% if a given integer is in the comma list. % \begin{macrocode} -\seq_new:N \g_mw_reserved_writes_seq -\prg_stepwise_inline:nnnn {0} {1} { \g_mw_write - 1 } - { \seq_gput_right:Nn \g_mw_reserved_writes_seq {#1} } -\seq_gput_right:Nn \g_mw_reserved_writes_seq {18} +\clist_new:N \g_@@_reserved_iow_clist +\int_step_inline:nnnn {0} {1} { \g_@@_iow - 1 } + { \clist_gput_right:Nn \g_@@_reserved_iow_clist {#1} } +\clist_gput_right:Nn \g_@@_reserved_iow_clist {18} % \end{macrocode} % \end{variable} % -% \begin{variable}{\l_mw_tmpa_tl,\g_mw_step_int,\g_mw_tmpa_int,\g_mw_box} +% \begin{variable}[int]{\g_@@_stream_int} +% An integer holding the \meta{number} argument of various primitives, +% namely a writing stream. % \begin{macrocode} -\tl_new:N \l_mw_tmpa_tl -\int_new:N \g_mw_step_int -\int_new:N \g_mw_tmpa_int -\newbox \g_mw_box +\int_new:N \g_@@_stream_int % \end{macrocode} % \end{variable} % -% \begin{variable}{\g_mw_stream_int,\g_mw_filename_tl,\g_mw_text_tl} -% What parsing finds is stored in those three variables. +% \begin{macro}[int]{\s_@@} +% A recognizable version of \cs{scan_stop:}. This is inspired +% from\footnote{Historically, this might have happened the other way +% around, since the author of this package is also on the \LaTeX3 +% Team.} scan marks (see the \pkg{l3quark} module of \LaTeX3), but +% note that we don't use \cs{__scan_new:N} directly, since it is +% internal to \LaTeX3. % \begin{macrocode} -\int_new:N \g_mw_stream_int -\tl_new:N \g_mw_filename_tl -\tl_new:N \g_mw_text_tl +\cs_new_eq:NN \s_@@ \scan_stop: % \end{macrocode} -% \end{variable} +% \end{macro} % -% \begin{macro}[aux]{\mw_scan_stop:} -% A recognizable version of \cs{scan_stop:}. +% \begin{variable}[int]{\l_@@_internal_tl} +% Temporary token list, used for scratch purposes. % \begin{macrocode} -\cs_new_eq:NN \mw_scan_stop: \scan_stop: +\tl_new:N \l_@@_internal_tl % \end{macrocode} -% \end{macro} +% \end{variable} % % \subsection{Parsing} % -% \begin{macro}[aux]{\mw_parse_number:Nw} -% Stores a number in \cs{g_mw_stream_int} before performing |#1|. +% \begin{macro}[aux]{\@@_equals_file_name:N} +% Most of the parsing for primitive arguments is done using +% \pkg{primargs}, except for one case we care about: after its +% \meta{number} argument, the \tn{openout} primitive expects an +% \meta{equals} (optional spaces and |=|) and a \meta{file name}. % \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_parse_number:Nw #1 - { - \tex_afterassignment:D #1 - \tex_global:D \g_mw_stream_int - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[aux]{\mw_parse_text:Nw} -% This is used for the \cs{write} command: to grab -% a \meta{general text} we assign a \cs{toks}, within -% a group to avoid clobbering \cs{toks}\texttt{0}. -% \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_parse_text:Nw #1 +\cs_new_protected:Npn \@@_equals_file_name:N #1 { \group_begin: \tex_aftergroup:D #1 - \tex_afterassignment:D \mw_parse_text_aux: - \tex_toks:D \c_zero = - } -\cs_new_protected_nopar:Npn \mw_parse_text_aux: - { - \tl_gset:Nx \g_mw_text_tl { \tex_the:D \tex_toks:D \c_zero } - \group_end: - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[aux]{\mw_parse_equals_filename:Nw} -% Remove a potential \texttt{=} sign. -% \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_parse_equals_filename:Nw #1 - { - \peek_meaning_remove:NTF = - { \mw_parse_filename:Nw #1 } { \mw_parse_filename:Nw #1 } + \primargs_remove_equals:N \@@_parse_file_name: } +\cs_new_protected:Npn \@@_parse_file_name: + { \primargs_get_file_name:N \group_end: } % \end{macrocode} % \end{macro} % -% \begin{macro}[aux]{\mw_parse_filename:Nw} -% Empty the filename, and build it one character at a time. -% We need to expand tokens as in an f-type expansion, but -% without losing spaces. For that, we use \cs{peek_gafter:Nw} -% to find out whether the coming token is expandable or not, -% and distinguish further depending on what kind of -% non-expandable token it is. -% \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_parse_filename:Nw #1 - { - \group_begin: - \group_align_safe_begin: - \tex_aftergroup:D #1 - \tl_gclear:N \g_mw_filename_tl - \mw_parse_filename_loop:w - } -\cs_new_protected_nopar:Npn \mw_parse_filename_loop:w - { \peek_gafter:Nw \mw_parse_filename_loop_test: } -% \end{macrocode} -% The test \cs{token_if_expandable:NTF} should not be used here, -% since it returns false for undefined control sequences (by design). -% But when parsing a file name, undefined control sequences would -% be expanded, and cause errors. In order to keep this behaviour, -% we do the test ourselves. -% -% A non-expandable token is either a non-space character, a space -% character, or a control sequence (either a primitive or a register). -% \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_parse_filename_loop_test: - { - \exp_after:wN \if_meaning:w \exp_not:N \g_peek_token \g_peek_token - \exp_after:wN \use_ii:nn - \else: - \exp_after:wN \use_i:nn - \fi: - { \exp_after:wN \mw_parse_filename_loop:w } - { - \token_if_cs:NTF \g_peek_token - { \mw_parse_filename_end: } - { - \mw_parse_filename_space_test:NTF \g_peek_token - { \mw_parse_filename_space_do:w } - { - \exp_after:wN \mw_parse_filename_push:w - \token_to_meaning:N - } - } - } - } -% \end{macrocode} -% We stop at the first non-expandable non-character token. -% \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_parse_filename_end: - { - \group_align_safe_end: - \group_end: - } -% \end{macrocode} -% We also stop when seeing a space, after removing it. -% \begin{macrocode} - %^^A TeXlive's behaviour (?): space with any catcode -\cs_new_protected_nopar:Npn \mw_parse_filename_space_test:NTF - { \token_if_eq_charcode:NNTF \c_space_token } -\cs_new_protected_nopar:Npn \mw_parse_filename_space_do:w - { - \tex_afterassignment:D \mw_parse_filename_end: - \cs_set_eq:NN \g_peek_token - } -% \end{macrocode} -% Finally, the case of normal characters. It seems -% that e.g., \cs{bgroup} is seen in the same way as an -% explicit left-brace character. Hence, we work with -% the \cs{meaning} of the character we are looking at. -% Most meanings of characters are two words, followed -% by the character. Three exceptions: ``math shift character'', -% ``alignment tab character'', and ``macro parameter character'', -% which have three words. -% \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_parse_filename_push:w #1 ~ % - { - \tl_if_in:nnTF { math ~ alignment ~ macro } { #1 } - { \mw_parse_filename_push_three:w } - { \mw_parse_filename_push_two:w } - } -\cs_new_protected_nopar:Npn \mw_parse_filename_push_two:w #1 ~ #2 - { - \tl_gput_right:Nn \g_mw_filename_tl {#2} - \mw_parse_filename_loop:w - } -\cs_new_protected_nopar:Npn \mw_parse_filename_push_three:w #1 ~ #2 ~ #3 - { - \tl_gput_right:Nn \g_mw_filename_tl {#3} - \mw_parse_filename_loop:w - } -% \end{macrocode} -% \end{macro} -% -% % \subsection{Immediate (writing)} % % In the context of immediate writing, we can store the text % in a token list, and only write it at the corresponding -% \cs{closeout} command. We keep track of a property list, -% \cs{g_mw_writes_prop}, of the writes which are +% \tn{closeout} command. We keep track of a property list, +% \cs{g_@@_iow_prop}, of the writes which are % open (from the point of view of the user), with the % corresponding file name. % -% \subsubsection{What follows \cs{immediate}} -% -% \begin{macro}[aux]{\mw_prg_case_meaning:Nnn} -% \begin{macro}[aux]{\mw_prg_case_meaning_aux:Nw} -% See \cs{prg_case_tl:Nnn}. Here, it is used by \cs{mw_immediate:w} -% to decide whether the following token is a writing command. -% \begin{macrocode} -\cs_new:Npn \mw_prg_case_meaning:Nnn #1#2#3 - { \mw_prg_case_meaning_aux:Nw #1 #2 #1 {#3} \q_recursion_stop } -\cs_new:Npn \mw_prg_case_meaning_aux:Nw #1#2#3 - { - \token_if_eq_meaning:NNTF #1 #2 - { \prg_case_end:nw {#3} } - { \mw_prg_case_meaning_aux:Nw #1 } - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro}[aux]{\mw_exp_after:Nf} -% Eventually, this should be replaced by a slow \cs{futurelet}-based -% expansion which does not lose a space. -% \begin{macrocode} -\cs_new:Npn \mw_exp_after:Nf #1 - { \exp_after:wN #1 \tex_romannumeral:D -`0 } -% \end{macrocode} -% \end{macro} +% \subsubsection{What follows \tn{immediate}} % -% \begin{macro}[aux]{\mw_immediate:w} -% This is a little bit subtle: \TeX{}'s \cs{immediate} primitive +% \begin{macro}[aux]{\@@_immediate:w} +% \begin{macro}[aux]{\@@_immediate_ii:, \@@_immediate_iii:N} +% \begin{macro}[aux]{\@@_immediate_iv:NN} +% \begin{macro}[aux, EXP]{\@@_immediate_v:w} +% This is a little bit subtle: \TeX{}'s \tn{immediate} primitive % raises a flag which is cancelled once \TeX{} sees a non-expandable -% token. This can be emulated by expanding tokens in front of it until -% the first non-expandable token, and checking if it is either -% \cs{openout}, \cs{write}, or \cs{closeout}. We will use f-type -% expansion.\footnote{Unfortunately, this is not quite correct, since it -% loses a space if someone puts \cs{immediate} at places where it -% does not belong, such as \cs{immediate} \cs{c_space_tl}.} -% \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_immediate:w - { - \scan_stop: %^^A? - \mw_exp_after:Nf \mw_immediate_aux:w - } -\cs_new_protected_nopar:Npn \mw_immediate_aux:w +% token. We use \pkg{primargs}'s \texttt{read_x_token} function to +% fully expand in the \TeX{} way, then test for \tn{openout}, +% \tn{write}, or \tn{closeout}. We don't test for the primitives +% themselves, but rather for a recognizable marker, \cs{s_@@}, equal +% to \tn{relax}. If present, replace |morewrites| by +% |morewrites_immediate| in the csname of the second token after it +% (it turns out that this is the correct structure). +% \begin{macrocode} +\cs_new_protected_nopar:Npn \@@_immediate:w + { \primargs_read_x_token:N \@@_immediate_ii: } +\cs_new_protected_nopar:Npn \@@_immediate_ii: { - \peek_meaning:NT \mw_scan_stop: - { \mw_immediate_mw_scan_stop:N } + \token_if_eq_meaning:NNT \g_primargs_token \s_@@ + { \@@_immediate_iii:N } } -\cs_new_protected_nopar:Npn \mw_immediate_mw_scan_stop:N #1 +\cs_new_protected:Npn \@@_immediate_iii:N #1 { - \str_if_eq:nnTF { #1 } { \mw_scan_stop: } - { \mw_immediate_do:NN } + \tl_if_eq:nnTF { #1 } { \s_@@ } + { \@@_immediate_iv:NN } { #1 } } -\cs_new_protected_nopar:Npn \mw_immediate_do:NN #1 #2 +\cs_new_protected:Npn \@@_immediate_iv:NN #1 #2 { - \exp_args:Nc \mw_parse_number:Nw + \exp_args:Nc #1 { - \exp_after:wN \mw_immediate_do_aux:w + \exp_after:wN \@@_immediate_v:w \token_to_str:N #2 } } \use:x { - \cs_new_protected_nopar:Npn \exp_not:N \mw_immediate_do_aux:w - ##1 \tl_to_str:n {mw_} { mw_immediate_ } + \cs_new:Npn \exp_not:N \@@_immediate_v:w + ##1 \tl_to_str:n { @@ } { @@_immediate } } % \end{macrocode} % \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} % % \subsubsection{Immediate closeout} % -% \begin{macro}[aux]{\mw_immediate_closeout_test:} +% \begin{macro}[aux]{\@@_immediate_closeout_test:n} % When the user requests to close a stream, we look in -% \cs{g_mw_reserved_writes_seq} to see if it is a reserved +% \cs{g_@@_reserved_iow_clist} to see if it is a reserved % stream: in this case, we simply use the primitive. % \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_immediate_closeout_test: +\cs_new_protected:Npn \@@_immediate_closeout_test:n #1 { - \seq_if_in:NVTF \g_mw_reserved_writes_seq \g_mw_stream_int - { \mw_tex_immediate:w \mw_tex_closeout:w \g_mw_stream_int } - { \mw_immediate_closeout_aux: } + \int_gset:Nn \g_@@_stream_int {#1} + \clist_if_in:NnTF \g_@@_reserved_iow_clist {#1} + { \@@_tex_immediate:w \@@_tex_closeout:w \g_@@_stream_int } + { \@@_immediate_closeout_aux: } } % \end{macrocode} % \end{macro} % -% \begin{macro}[aux]{\mw_immediate_closeout_aux:} -% We then look in \cs{g_mw_writes_prop} to find -% the file name corresponding to that stream number. If the -% stream does not appear as a key in the property list, -% then it was not open yet, and we do nothing. +% \begin{macro}[aux]{\@@_immediate_closeout_aux:} +% We then look in \cs{g_@@_iow_prop} to find the file name +% corresponding to that stream number. If the stream does not appear +% as a key in the property list, then it was not open yet, and we do +% nothing. Otherwise, the key is removed, and we write the collected +% material to the file. % \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_immediate_closeout_aux: +\cs_new_protected_nopar:Npn \@@_immediate_closeout_aux: { - \exp_args:NNV \prop_pop:NnNT - \g_mw_writes_prop \g_mw_stream_int \l_mw_tmpa_tl + \exp_args:NNV \prop_pop:NnNT \g_@@_iow_prop + \g_@@_stream_int \l_@@_internal_tl { - \mw_tex_immediate:w \mw_tex_openout:w - \g_mw_write \l_mw_tmpa_tl \scan_stop: - \group_begin: - \int_set_eq:NN \tex_newlinechar:D \c_minus_one - \tl_use:c { g_mw_write_ \int_use:N \g_mw_stream_int _tl } - \tl_gclear:c { g_mw_write_ \int_use:N \g_mw_stream_int _tl } - \group_end: - \mw_tex_immediate:w \mw_tex_closeout:w \g_mw_write + \@@_immediate_write_and_close:nn + { \g_@@_stream_int } { \l_@@_internal_tl } } } % \end{macrocode} % \end{macro} % +% \begin{macro}[aux]{\@@_immediate_write_and_close:nn} +% The code to write the material collected so far for a given output +% \meta{stream} is in the token list \cs{g_@@_iow_\meta{stream}_tl}. +% We do this writing in the actual stream \cs{g_@@_iow}, briefly +% opened and closed on the file |#2|. +% \begin{macrocode} +\cs_new_protected:Npn \@@_immediate_write_and_close:nn #1#2 + { + \@@_tex_immediate:w \@@_tex_openout:w + \g_@@_iow #2 \scan_stop: + \group_begin: + \int_set_eq:NN \tex_newlinechar:D \c_minus_one + \tl_use:c { g_@@_iow_ \int_eval:n {#1} _tl } + \tl_gclear:c { g_@@_iow_ \int_eval:n {#1} _tl } + \group_end: + \@@_tex_immediate:w \@@_tex_closeout:w \g_@@_iow + } +% \end{macrocode} +% \end{macro} +% % \subsubsection{Immediate openout} % -% \begin{macro}[aux]{\mw_immediate_openout_test:} -% Read the stream number. If it is one of the reserved streams, -% we use the primitive. Otherwise, parse an optional equal sign, +% \begin{macro}[aux]{\@@_immediate_openout_test:n} +% Read the stream number. If it is one of the reserved streams, we +% use the primitive. Otherwise, parse an optional equal sign, % followed by the file name. % \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_immediate_openout_test: +\cs_new_protected:Npn \@@_immediate_openout_test:n #1 { - \seq_if_in:NVTF \g_mw_reserved_writes_seq \g_mw_stream_int - { \mw_tex_immediate:w \mw_tex_openout:w \g_mw_stream_int } - { \mw_parse_equals_filename:Nw \mw_immediate_openout_aux: } + \int_gset:Nn \g_@@_stream_int {#1} + \clist_if_in:NnTF \g_@@_reserved_iow_clist {#1} + { \@@_tex_immediate:w \@@_tex_openout:w \g_@@_stream_int } + { \@@_equals_file_name:N \@@_immediate_openout_aux:n } } % \end{macrocode} % \end{macro} % -% \begin{macro}[aux]{\mw_immediate_openout_aux:} -% When the user requests to open a stream, it might already be -% open, with another file as its destination. We thus need to -% first close the stream, writing all that we collected so far -% to that other file. This has no effect if the stream was not -% open yet. +% \begin{macro}[aux]{\@@_immediate_openout_aux:n} +% When the user requests to open a stream, it might already be open, +% with another file as its destination. We thus need to first close +% the stream, writing all that we collected so far to that other +% file. This has no effect if the stream was not open yet. % -% We then put the stream and its associated filename in the -% property list, and empty the corresponding token list. +% We then put the stream and its associated file name in the property +% list, and empty/create the corresponding token list. % \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_immediate_openout_aux: +\cs_new_protected:Npn \@@_immediate_openout_aux:n #1 { - \mw_immediate_closeout_aux: - \prop_gput:NVV \g_mw_writes_prop \g_mw_stream_int \g_mw_filename_tl - \tl_gclear_new:c { g_mw_write_ \int_use:N \g_mw_stream_int _tl } + \@@_immediate_closeout_aux: + \prop_gput:NVn \g_@@_iow_prop \g_@@_stream_int {#1} + \tl_gclear_new:c { g_@@_iow_ \int_use:N \g_@@_stream_int _tl } } % \end{macrocode} % \end{macro} % % \subsubsection{Immediate write} % -% \begin{macro}[aux]{\mw_immediate_write_test:} -% Read the stream number. If it is one of the reserved streams, -% we use the primitive. Otherwise, parse the text. +% \begin{macro}[aux]{\@@_immediate_write_test:n} +% Read the stream number. If it is one of the reserved streams, we +% use the primitive. Otherwise, parse the text. % \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_immediate_write_test: +\cs_new_protected:Npn \@@_immediate_write_test:n #1 { - \seq_if_in:NVTF \g_mw_reserved_writes_seq \g_mw_stream_int - { \mw_tex_immediate:w \mw_tex_write:w \g_mw_stream_int } - { \mw_parse_text:Nw \mw_immediate_write_aux: } + \int_gset:Nn \g_@@_stream_int {#1} + \clist_if_in:NnTF \g_@@_reserved_iow_clist {#1} + { \@@_tex_immediate:w \@@_tex_write:w \g_@@_stream_int } + { \primargs_get_general_text:N \@@_immediate_write_aux:n } } % \end{macrocode} % \end{macro} % -% \begin{macro}[aux]{\mw_immediate_write_aux:} -% Only \cs{write} itself can emulate how \cs{write} expands tokens, -% because |#| don't have to be doubled. Hence, we write to a file, -% and re-read from it, setting \cs{endlinechar} to $-1$ to avoid -% spurious characters. Lines will be written one at a time: this -% allows us to set \cs{newlinechar} to $-1$ when writing, so that -% no extra line will be created. +% \begin{macro}[aux]{\@@_immediate_write_aux:n} +% Test whether the stream is allocated or not. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \@@_immediate_write_aux:n + { + \prop_if_in:NVTF \g_@@_iow_prop \g_@@_stream_int + { \@@_immediate_write_open:n } + { \@@_immediate_write_closed:n } + } +% \end{macrocode} +% \end{macro} % -% If the stream \cs{g_mw_stream_int} is not allocated, -% then write either to the terminal or only to the log file. +% \begin{macro}[aux]{\@@_immediate_write_closed:n} +% If the stream \cs{g_@@_stream_int} is not allocated, then write +% either to the terminal or only to the log file, depending on the +% sign. % \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_immediate_write_aux: +\cs_new_protected:Npn \@@_immediate_write_closed:n #1 { - \prop_if_in:NVTF \g_mw_writes_prop \g_mw_stream_int - { - \mw_tex_immediate:w \mw_tex_openout:w \g_mw_write - \c_mw_tmp_file_tl \scan_stop: - \mw_tex_immediate:w \mw_tex_write:w \g_mw_write { \g_mw_text_tl } - \mw_tex_immediate:w \mw_tex_closeout:w \g_mw_write - \group_begin: - \int_set_eq:NN \tex_endlinechar:D \c_minus_one - \mw_tex_openin:w \g_mw_read \c_mw_tmp_file_tl \scan_stop: - \mw_immediate_write_aux_readlines: - \mw_tex_closein:w \g_mw_read - \group_end: - } - { - \mw_tex_immediate:w \mw_tex_write:w - \if_num:w \g_mw_stream_int < \c_zero - -1 - \else: - 16 - \fi: - { \g_mw_text_tl } - } + \@@_tex_immediate:w \@@_tex_write:w + \if_num:w \g_@@_stream_int < \c_zero + -1 + \else: + 16 + \fi: + {#1} + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux]{\@@_immediate_write_open:n} +% \begin{macro}[aux]{\@@_immediate_write_readlines_loop:} +% Only \tn{write} itself can emulate how \tn{write} expands tokens, +% because |#| don't have to be doubled, and because the +% \tn{newlinechar} has to be changed to new lines. Hence, we start by +% writing |#1| to a file, yielding some lines. The lines are then +% read one at a time using \eTeX{}'s \tn{readline} with +% \tn{endlinechar} set to $-1$ to avoid spurious characters. Each +% line becomes a \tn{immediate} \tn{write} statement added to the +% token list \cs{g_@@_iow_\meta{stream}_tl}. This token list will be +% called when it is time to actually write to the file. At that time, +% \tn{newlinechar} will be $-1$, so that writing each line will +% produce no extra line. +% \begin{macrocode} +\cs_new_protected:Npn \@@_immediate_write_open:n #1 + { + \@@_tex_immediate:w \@@_tex_openout:w \g_@@_iow + \g_@@_tmp_file_tl \scan_stop: + \@@_tex_immediate:w \@@_tex_write:w \g_@@_iow {#1} + \@@_tex_immediate:w \@@_tex_closeout:w \g_@@_iow + \group_begin: + \int_set_eq:NN \tex_endlinechar:D \c_minus_one + \tex_openin:D \g_@@_ior \g_@@_tmp_file_tl \scan_stop: + \@@_immediate_write_readlines_loop: + \tex_closein:D \g_@@_ior + \group_end: } -\cs_new_protected_nopar:Npn \mw_immediate_write_aux_readlines: +\cs_new_protected_nopar:Npn \@@_immediate_write_readlines_loop: { - \mw_etex_readline:w \g_mw_read to \l_tmpa_tl - \ior_if_eof:NF \g_mw_read + \etex_readline:D \g_@@_ior to \l_@@_internal_tl + \ior_if_eof:NF \g_@@_ior { \tl_gput_right:cx - { g_mw_write_ \int_use:N \g_mw_stream_int _tl } - { \mw_tex_immediate:w \mw_tex_write:w \g_mw_write { \l_tmpa_tl } } - \mw_immediate_write_aux_readlines: + { g_@@_iow_ \int_use:N \g_@@_stream_int _tl } + { + \@@_tex_immediate:w \@@_tex_write:w \g_@@_iow + { \l_@@_internal_tl } + } + \@@_immediate_write_readlines_loop: } } % \end{macrocode} % \end{macro} +% \end{macro} % % % \subsection{Non-immediate writing} % -% This is trickier, because the expansion of the text for -% a non-immediate \cs{write} takes place immediately after -% the page containing it is shipped out. We store each -% non-immediate \cs{openout}, \cs{write}, or \cs{closeout} -% without expansion in separate token lists -% \cs{g_mw_late_write_}\meta{N}\texttt{_tl} to be used later, -% and instead write |`(|\meta{N}|)| to a file (including the -% strange delimiters). After each shipout, we can read the -% file to see which output operations we need to perform, -% and in what order. -% -% \begin{macro}[aux]{\mw_late_aux:nN} -% Store the action to be done at shipout in a token list, -% and non-immediately write the label \cs{g_mw_late_write_int} -% of the output operation to the temporary file. -% Here, |#1| holds an assignment similar to the lines above it, -% and |#2| holds the relevant immediate action to be performed -% after shipout. -% \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_late_aux:nN #1 #2 +% This is trickier, because the expansion of the text for a +% non-immediate \tn{write} takes place immediately after the page +% containing it is shipped out. We store each non-immediate +% \tn{openout}, \tn{write}, or \tn{closeout} without expansion in +% separate token lists \cs{g_@@_late_write_\meta{stream}_tl} to be used +% later, and instead write |`(|\meta{stream}|)| to a file (including the +% strange delimiters). After each shipout, we can read the file to see +% which output operations we need to perform, and in what order. +% +% ^^A todo: check "_nopar"? +% +% \subsubsection{Replacement for primitives} +% +% \begin{macro}[aux]{\@@_late:n} +% Store the action to be done at shipout in a token list, and +% non-immediately write the label \cs{g_@@_late_write_int} of the +% output operation to the temporary file. Here, |#1| holds an +% assignment similar to the lines above it, and |#2| holds the +% relevant immediate action to be performed after shipout. +% \begin{macrocode} +\cs_new_protected:Npn \@@_late:n #1 { - \int_gincr:N \g_mw_late_write_int - \tl_const:cx { c_mw_late_write_ \int_use:N \g_mw_late_write_int _tl } + \int_gincr:N \g_@@_late_write_int + \tl_const:cx { - \int_gset:Nn \exp_not:N \g_mw_stream_int - { \exp_not:V \g_mw_stream_int } - #1 - \exp_not:N #2 + c_@@_late_write_ + \int_use:N \g_@@_late_write_int + _tl + } + { + \int_gset:Nn \exp_not:N \g_@@_stream_int + { \exp_not:V \g_@@_stream_int } + \exp_not:n {#1} } - \exp_args:NNx \mw_tex_write:w \g_mw_write - { `( \int_use:N \g_mw_late_write_int ) } + \exp_args:NNx \@@_tex_write:w \g_@@_iow + { `( \int_use:N \g_@@_late_write_int ) } } % \end{macrocode} % \end{macro} % -% \begin{macro}[aux]{\mw_openout:w} -% \begin{macro}[aux]{\mw_openout_test:,\mw_openout_aux:} -% \cs{openout} tests if the number to come is among reserved streams. -% If it is, use the primitive, otherwise, parse a filename. +% \begin{macro}[aux]{\@@_openout:w} +% \begin{macro}[aux]{\@@_openout_test:n, \@@_openout_aux:n} +% \tn{openout} tests if the number to come is among reserved streams. +% If it is, use the primitive, otherwise, parse a file name. % \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_openout:w - { \mw_scan_stop: \mw_parse_number:Nw \mw_openout_test: } -\cs_new_protected_nopar:Npn \mw_openout_test: - { - \seq_if_in:NVTF \g_mw_reserved_writes_seq \g_mw_stream_int - { \mw_tex_openout:w \g_mw_stream_int } - { \mw_parse_equals_filename:Nw \mw_openout_aux: } - } -\cs_new_protected_nopar:Npn \mw_openout_aux: +\cs_new_protected_nopar:Npn \@@_openout:w + { \s_@@ \primargs_get_number:N \@@_openout_test:n } +\cs_new_protected:Npn \@@_openout_test:n #1 { - \mw_late_aux:nN - { - \tl_gset:Nn \exp_not:N \g_mw_filename_tl - { \exp_not:V \g_mw_filename_tl } - } - \mw_immediate_openout_aux: + \int_gset:Nn \g_@@_stream_int {#1} + \clist_if_in:NnTF \g_@@_reserved_iow_clist {#1} + { \@@_tex_openout:w \g_@@_stream_int } + { \@@_equals_file_name:N \@@_openout_aux:n } } +\cs_new_protected:Npn \@@_openout_aux:n #1 + { \@@_late:n { \@@_immediate_openout_aux:n {#1} } } % \end{macrocode} % \end{macro} % \end{macro} % -% \begin{macro}[aux]{\mw_write:w} -% \begin{macro}[aux]{\mw_write_test:,\mw_write_aux:} -% Same idea for \cs{write}, except that we parse a text. +% \begin{macro}[aux]{\@@_write:w} +% \begin{macro}[aux]{\@@_write_test:n, \@@_write_aux:n} +% Same idea for \tn{write}, except that we parse a text. % \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_write:w - { \mw_scan_stop: \mw_parse_number:Nw \mw_write_test: } -\cs_new_protected_nopar:Npn \mw_write_test: +\cs_new_protected_nopar:Npn \@@_write:w + { \s_@@ \primargs_get_number:N \@@_write_test:n } +\cs_new_protected:Npn \@@_write_test:n #1 { - \seq_if_in:NVTF \g_mw_reserved_writes_seq \g_mw_stream_int - { \mw_tex_write:w \g_mw_stream_int } - { \mw_parse_text:Nw \mw_write_aux: } - } -\cs_new_protected_nopar:Npn \mw_write_aux: - { - \mw_late_aux:nN - { - \tl_gset:Nn \exp_not:N \g_mw_text_tl - { \exp_not:V \g_mw_text_tl } - } - \mw_immediate_write_aux: + \int_gset:Nn \g_@@_stream_int {#1} + \clist_if_in:NnTF \g_@@_reserved_iow_clist {#1} + { \@@_tex_write:w \g_@@_stream_int } + { \primargs_get_general_text:N \@@_write_aux:n } } +\cs_new_protected:Npn \@@_write_aux:n #1 + { \@@_late:n { \@@_immediate_write_aux:n {#1} } } % \end{macrocode} % \end{macro} % \end{macro} % -% \begin{macro}[aux]{\mw_closeout:w} -% \begin{macro}[aux]{\mw_closeout_test:,\mw_closeout_aux:} -% Same idea for \cs{closeout}, and we don't need to parse +% \begin{macro}[aux]{\@@_closeout:w} +% \begin{macro}[aux]{\@@_closeout_test:n, \@@_closeout_aux:} +% Same idea for \tn{closeout}, and we don't need to parse % anything else than the number. % \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_closeout:w - { \mw_scan_stop: \mw_parse_number:Nw \mw_closeout_test: } -\cs_new_protected_nopar:Npn \mw_closeout_test: +\cs_new_protected_nopar:Npn \@@_closeout:w + { \s_@@ \primargs_get_number:N \@@_closeout_test:n } +\cs_new_protected:Npn \@@_closeout_test:n #1 { - \seq_if_in:NVTF \g_mw_reserved_writes_seq \g_mw_stream_int - { \mw_tex_closeout:w \g_mw_stream_int } - { \mw_closeout_aux: } + \int_gset:Nn \g_@@_stream_int {#1} + \clist_if_in:NnTF \g_@@_reserved_iow_clist {#1} + { \@@_tex_closeout:w \g_@@_stream_int } + { \@@_closeout_aux: } } -\cs_new_protected_nopar:Npn \mw_closeout_aux: - { \mw_late_aux:nN { } \mw_immediate_closeout_aux: } +\cs_new_protected_nopar:Npn \@@_closeout_aux: + { \@@_late:n { \@@_immediate_closeout_aux: } } % \end{macrocode} % \end{macro} % \end{macro} % -% \begin{macro}[aux]{\mw_before_shipout:} -% Immediately before the shipout, we must open the writing stream -% \cs{g_mw_write}. Each delayed output operation has been replaced -% by \cs{write} \cs{g_mw_write} |{`(|\meta{operation number}|}|. -% The delimiters we chose to put around numbers must be at least -% two distinct characters on the left (then \cs{tex_newlinechar:D} -% cannot be equal to the delimiter), and at least one non-digit -% character on the right. -% \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_before_shipout: - { - \mw_tex_immediate:w \mw_tex_openout:w \g_mw_write - \c_mw_tmp_file_tl \scan_stop: - } -% \end{macrocode} -% \end{macro} +% \subsubsection{Shipout business} % -% \begin{macro}[aux]{\mw_after_shipout:} -% After all the \cs{write}s are performed, we read the file, -% and grab the relevant pieces in a seq. -% \begin{macrocode} -\seq_new:N \g_mw_operations_seq -\cs_new_protected_nopar:Npn \mw_after_shipout: - { - \mw_tex_immediate:w \mw_tex_closeout:w \g_mw_write +% \begin{macro}[aux]{\@@_before_shipout:} +% Immediately before the shipout, we must open the writing stream +% \cs{g_@@_iow}. Each delayed output operation has been replaced by +% \tn{write} \cs{g_@@_iow} |{`(|\meta{operation number}|}|. The +% delimiters we chose to put around numbers must be at least two +% distinct characters on the left (then \cs{tex_newlinechar:D} cannot +% be equal to the delimiter), and at least one non-digit character on +% the right. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \@@_before_shipout: + { + \@@_tex_immediate:w \@@_tex_openout:w \g_@@_iow + \g_@@_tmp_file_tl \scan_stop: + } +% \end{macrocode} +% \end{macro} +% +%% ^^A todo: \shipout{\immediate\write4{}} => bug. +% \begin{macro}[aux]{\@@_after_shipout:} +% \begin{macro}[aux, rEXP]{\@@_after_shipout_loop:ww} +% Immediately after all the \tn{write}s are performed, close the file, +% then read the file with \tn{endlinechar} set to +% \tn{newlinechar}\footnote{Note that the \tn{newlinechar} used by +% \tn{write}s at \tn{shipout} time are those in effect when the page +% is shipped out, \emph{i.e.}, just after the closing brace of the +% \tn{shipout} construction, which is exactly where we have added +% this hook.} to get exactly the original characters that have been +% written, possibly with extra characters between |`(|\ldots{}|)| +% groups. The file is then read with all the appropriate category +% codes set up (no other character can appear in the file). The +% looping auxiliary \cs{@@_after_shipout_loop:ww} extract the +% \meta{operation} numbers from the file, and makes a token list out +% of those. This token list is then used in a mapping function to +% perform the appropriate \tn{write} operations. Note that those +% operations may reuse the file, so we have to fully parse the file +% before moving on. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \@@_after_shipout: + { + \@@_tex_immediate:w \@@_tex_closeout:w \g_@@_iow \group_begin: \int_set_eq:NN \tex_endlinechar:D \tex_newlinechar:D + \char_set_catcode_other:n { \tex_endlinechar:D } \tl_map_inline:nn { `(0123456789) } { \char_set_catcode_other:n {`##1} } \etex_everyeof:D { `() \exp_not:N } - \tl_gset:Nx \g_mw_operations_seq - { \if_false: } \fi: - \exp_after:wN \mw_after_shipout_loop:ww - \tex_input:D \c_mw_tmp_file_tl \scan_stop: + \exp_args:NNx \group_end: - \seq_map_inline:Nn \g_mw_operations_seq + \tl_map_inline:nn { - \tl_use:c - { c_mw_late_write_ ##1 _tl } + \exp_after:wN \@@_after_shipout_loop:ww + \tex_input:D \g_@@_tmp_file_tl \c_space_tl %^^A bug? } + { \tl_use:c { c_@@_late_write_ ##1 _tl } } } -\cs_new:Npn \mw_after_shipout_loop:ww #1 `( #2 ) +\cs_new:Npn \@@_after_shipout_loop:ww #1 `( #2 ) { - \tl_if_empty:nTF {#2} - { \if_false: { \fi: } } + \tl_if_empty:nF {#2} { - \exp_not:N \seq_item:n {#2} - \mw_after_shipout_loop:ww + {#2} + \@@_after_shipout_loop:ww } } % \end{macrocode} % \end{macro} +% \end{macro} % % \begin{macro}[aux]{\shipout} -% \begin{macro}[aux]{\mw_shipout:w} +% \begin{macro}[aux]{\@@_shipout:w} +% \begin{variable}{\g_@@_group_level_int, \g_@@_shipout_box} % If \pkg{atbegshi} is available, patch it by adding -% \cs{mw_after_shipout:} at the right place. Otherwise, -% redefine \cs{shipout} to add a hook. +% \cs{@@_before_shipout:} and \cs{@@_after_shipout:} at the right +% place: the two transformations are needed to cover several versions +% of the package. Otherwise, redefine \tn{shipout} to add a hook (see +% Heiko's \pkg{atbegshi} for details). % \begin{macrocode} \IfFileExists{atbegshi.sty} { @@ -719,120 +736,142 @@ \tl_replace_once:Nnn \AtBegShi@Output { \AtBegShi@OrgShipout \box \AtBeginShipoutBox } { - \mw_before_shipout: + \@@_before_shipout: \AtBegShi@OrgShipout \box \AtBeginShipoutBox - \mw_after_shipout: + \@@_after_shipout: } \tl_replace_once:Nnn \AtBegShi@Output { \AtBeginShipoutOriginalShipout \box \AtBeginShipoutBox } { - \mw_before_shipout: + \@@_before_shipout: \AtBeginShipoutOriginalShipout \box \AtBeginShipoutBox - \mw_after_shipout: + \@@_after_shipout: } } { - \cs_new_protected_nopar:Npn \mw_shipout:w + \int_new:N \g_@@_group_level_int + \box_new:N \g_@@_shipout_box + \cs_new_protected_nopar:Npn \@@_shipout:w { - \int_gset_eq:NN \g_mw_tmpa_int \etex_currentgrouplevel:D - \tex_afterassignment:D \mw_shipout_aux: - \tex_global:D \tex_setbox:D \g_mw_box + \int_gset_eq:NN \g_@@_group_level_int \etex_currentgrouplevel:D + \tex_afterassignment:D \@@_shipout_i: + \tex_global:D \tex_setbox:D \g_@@_shipout_box } - \cs_new_protected_nopar:Npn \mw_shipout_aux: + \cs_new_protected_nopar:Npn \@@_shipout_i: { - \int_compare:nNnTF { \g_mw_tmpa_int } + \int_compare:nNnTF { \g_@@_group_level_int } = { \etex_currentgrouplevel:D } - { \mw_shipout_aux_ii: } - { \tex_aftergroup:D \mw_shipout_aux_ii: } + { \@@_shipout_ii: } + { \tex_aftergroup:D \@@_shipout_ii: } } - \cs_new_protected_nopar:Npn \mw_shipout_aux_ii: + \cs_new_protected_nopar:Npn \@@_shipout_ii: { - \mw_before_shipout: - \mw_tex_shipout:w \tex_box:D \g_mw_box - \mw_after_shipout: + \@@_before_shipout: + \@@_tex_shipout:w \tex_box:D \g_@@_shipout_box + \@@_after_shipout: } - \cs_gset_eq:NN \shipout \mw_shipout:w + \AtBeginDocument { \cs_gset_eq:NN \shipout \@@_shipout:w } } % \end{macrocode} +% \end{variable} % \end{macro} % \end{macro} % % \subsection{Hook at the very end} % -% \begin{macro}[aux]{\mw_close_all:} +% \begin{variable}{\g_@@_at_end_int} +% At the end of the run, we try very hard to put some material at the +% |\@@end|. This integer controls how many times to call +% \cs{@@_close_all_at_end:w}, to avoid infinite loops in case two +% packages compete for that last place. +% \begin{macrocode} +\int_new:N \g_@@_at_end_int +\int_gset:Nn \g_@@_at_end_int { 10 } +% \end{macrocode} +% \end{variable} +% +% \begin{macro}[aux]{\@@_close_all:} % At the end of the document, close all the files. % \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_close_all: +\cs_new_protected_nopar:Npn \@@_close_all: { - \prop_map_inline:Nn \g_mw_writes_prop - { - \mw_tex_immediate:w \mw_tex_openout:w \g_mw_write ##2 \scan_stop: - \group_begin: - \int_set_eq:NN \tex_newlinechar:D \c_minus_one - \tl_use:c { g_mw_write_ ##1 _tl } - \tl_gclear:c { g_mw_write_ ##1 _tl } - \group_end: - \mw_tex_immediate:w \mw_tex_closeout:w \g_mw_write - } - \prop_gclear:N \g_mw_writes_prop + \prop_map_function:NN \g_@@_iow_prop + \@@_immediate_write_and_close:nn + \prop_gclear:N \g_@@_iow_prop } % \end{macrocode} % \end{macro} % -% \begin{macro}[aux]{\mw_put_at_end:Nw} +% \begin{macro}[aux]{\@@_close_all_at_end:w} % This pushes its first argument to the very end of the \LaTeX{} run, -% recursively, just in case some other code adds things there. -% If \pkg{atveryend} is available, we use it instead. -% \begin{macrocode} - % \IfFileExists{atveryend.sty} { \use_i:nn } { \use_ii:nn } - % { - % \RequirePackage {atveryend} [2011/06/29] - % \AtVeryVeryEnd { \mw_close_all: } - % } - % { - \cs_new_protected:Npn \mw_put_at_end:Nw #1 #2 \@@end +% recursively (at most $10$ times, initial value of +% \cs{g_@@_at_end_int}), just in case some other code adds things +% there. +% \begin{macrocode} +\cs_set:Npn \@@_tmp:w #1 + { + \cs_new_protected:Npn \@@_close_all_at_end:w ##1 #1 { - \tl_if_empty:nTF {#2} - { #1 \@@end } - { #2 \mw_put_at_end:Nw #1 \@@end } + \int_gdecr:N \g_@@_at_end_int + \int_compare:nNnTF \g_@@_at_end_int > \c_zero + { + \tl_if_empty:nTF {##1} + { ##1 \@@_close_all: } + { ##1 \@@_close_all_at_end:w } + } + { \@@_close_all: ##1 } + #1 } - \AtEndDocument { \mw_put_at_end:Nw \mw_close_all: } - % } + } +\exp_args:Nc \@@_tmp:w { @ @ end } +\AtEndDocument { \@@_close_all_at_end:w } % \end{macrocode} % \end{macro} % % \subsection{Modified \cs{newwrite}} % +% \begin{variable}{\g_@@_alloc_int} +% The counter that \LaTeXe{} uses to allocate \tn{write} registers. +% \begin{macrocode} +\tex_countdef:D \g_@@_alloc_int 17 \scan_stop: +% \end{macrocode} +% \end{variable} +% % \begin{macro}[aux]{\newwrite} % We need to allow \cs{newwrite} to allocate more than $16$ writes, % but beware that $18$ is reserved, and that packages might expect -% $16$ or $17$ to write to the terminal. So instead skip until $20$. +% $16$ or $17$ to write to the terminal. So instead skip until $20$, +% to be on the safe side. +% % ^^A todo: redefine the LaTeX3 things too? % \begin{macrocode} -\countdef \mw_write_int 17 \scan_stop: -\cs_new:Npn \mw_newwrite:N #1 +\cs_new:Npn \@@_newwrite:N #1 { - \if_num:w \mw_write_int = \c_sixteen - \int_gset:Nn \mw_write_int { 20 } + \int_gincr:N \g_@@_alloc_int + \if_num:w \g_@@_alloc_int = \c_sixteen + \int_gset:Nn \g_@@_alloc_int { 20 } \fi: - \int_set_eq:NN \allocationnumber \mw_write_int - \int_gincr:N \mw_write_int + \int_set_eq:NN \allocationnumber \g_@@_alloc_int \cs_undefine:N #1 \int_const:Nn #1 { \allocationnumber } - \wlog {\string #1=\string \write \the \allocationnumber } + \wlog + { + \token_to_str:N #1 + = \token_to_str:N \write \int_use:N \allocationnumber + } } % \end{macrocode} % \end{macro} % % \subsection{Redefining the ``normal'' control sequences} % -% \begin{macro}[aux]{\immediate,\openout,\write,\closeout,\newwrite} -% \cs{shipout} has been redefined earlier. +% \begin{macro}[aux]{\immediate, \openout, \write, \closeout, \newwrite} +% \tn{shipout} has been redefined earlier. % \begin{macrocode} -\cs_gset_eq:NN \immediate \mw_immediate:w -\cs_gset_eq:NN \openout \mw_openout:w -\cs_gset_eq:NN \write \mw_write:w -\cs_gset_eq:NN \closeout \mw_closeout:w -\cs_gset_eq:NN \newwrite \mw_newwrite:N +\cs_gset_eq:NN \immediate \@@_immediate:w +\cs_gset_eq:NN \openout \@@_openout:w +\cs_gset_eq:NN \write \@@_write:w +\cs_gset_eq:NN \closeout \@@_closeout:w +\cs_gset_eq:NN \newwrite \@@_newwrite:N % \end{macrocode} % \end{macro} % diff --git a/Master/texmf-dist/source/latex/morewrites/morewrites.ins b/Master/texmf-dist/source/latex/morewrites/morewrites.ins index 814fd46a489..d9e17d55e00 100644 --- a/Master/texmf-dist/source/latex/morewrites/morewrites.ins +++ b/Master/texmf-dist/source/latex/morewrites/morewrites.ins @@ -1,4 +1,23 @@ -\input docstrip.tex +\iffalse meta-comment + +File morewrites.ins Copyright (C) 2012 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 + +This file is part of the "morewrites bundle" (The Work in LPPL) +and all files in that bundle must be distributed together. + +The released version of this bundle is available from CTAN. + +\fi + +\let\jobname\relax +\input l3docstrip.tex \askforoverwritefalse \preamble @@ -15,5 +34,6 @@ to Bruno Le Floch (blflatex@gmail.com). % the package \generate{\file{morewrites.sty}{\from{morewrites.dtx}{package}}} +\generate{\file{primargs.sty}{\from{primargs.dtx}{package}}} \endbatchfile diff --git a/Master/texmf-dist/source/latex/morewrites/primargs.dtx b/Master/texmf-dist/source/latex/morewrites/primargs.dtx new file mode 100644 index 00000000000..a88bc6169e8 --- /dev/null +++ b/Master/texmf-dist/source/latex/morewrites/primargs.dtx @@ -0,0 +1,619 @@ +% \iffalse meta-comment +% +%% File: primargs.dtx Copyright (C) 2012 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|package> +\RequirePackage{l3names} +%</driver|package> +%<*driver> +\documentclass[full]{l3doc} +\newcommand{\proc}[1]{\texttt{#1}} +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +%</driver> +% \fi +% +% \title{The \textsf{primargs} package: \\ +% Parsing arguments of primitives\thanks{This +% file has version number v0.1, last revised 2012-07-10.}} +% \author{Bruno Le Floch} +% \date{2012-07-10} +% +% \maketitle +% \tableofcontents +% +% \begin{documentation} +% +% \section{\pkg{primargs} documentation} +% +% This \LaTeX{} package is currently used by \pkg{morewrites} when +% redefining primitives: it allows to read arguments of primitives in +% place of \TeX{}. Of course, this is much slower than letting \TeX{} +% do things directly, but it may not be possible. +% +% \subsection{Read one token} +% +% \begin{variable}{\g_primargs_token} +% The result of \cs{primargs_read_x_token:N} and +% \cs{primargs_read_token:N}. +% \end{variable} +% +% \begin{function}{\primargs_read_x_token:N} +% \begin{syntax} +% \cs{primargs_read_x_token:N} \meta{function} +% \end{syntax} +% Expands tokens recursively with \cs{exp_after:wN}, until +% encountering a non-expandable token, and calls the \meta{function} +% afterwards. The non-expandable token found is stored as +% \cs{g_primargs_token}. +% \end{function} +% +% \begin{function}{\primargs_read_token:N} +% \begin{syntax} +% \cs{primargs_read_token:N} \meta{function} +% \end{syntax} +% Sets \cs{g_primargs_token} globally to the value of the next token, +% then calls the \meta{function}. +% \end{function} +% +% \subsection{Grabbing arguments} +% +% A whole lot of functions have the same syntax: +% \begin{syntax} +% \cs{primargs_get_\meta{foo}:N} \meta{function} +% \end{syntax} +% Finds what \TeX{}'s grammar calls a \meta{foo} in the input stream, +% and feeds its value as a braced argument to the \meta{function}. Here +% is a list of the functions currently defined: +% \begin{itemize} +% \item \cs{primargs_get_number:N} +% \item \cs{primargs_get_dimen:N} +% \item \cs{primargs_get_glue:N} +% \item \cs{primargs_get_mudimen:N} (\emph{not implemented properly}) +% \item \cs{primargs_get_muglue:N} +% \item \cs{primargs_get_general_text:N} +% \item \cs{primargs_get_file_name:N} +% \end{itemize} +% +% \subsection{Discarding tokens} +% +% \begin{function}{\primargs_remove_token:N} +% \begin{syntax} +% \cs{primargs_remove_token:N} \meta{function} +% \end{syntax} +% Removes one \meta{token} following the \meta{function}, which is +% performed next. +% \end{function} +% +% \begin{function}{\primargs_remove_one_optional_space:N} +% \begin{syntax} +% \cs{primargs_remove_one_optional_space:N} \meta{function} +% \end{syntax} +% Removes \meta{one optional space} after the \meta{function}. +% \end{function} +% +% \begin{function}{\primargs_remove_optional_spaces:N} +% \begin{syntax} +% \cs{primargs_remove_optional_spaces:N} \meta{function} +% \end{syntax} +% Removes \meta{optional spaces} after the \meta{function}. +% \end{function} +% +% \begin{function}{\primargs_remove_equals:N} +% \begin{syntax} +% \cs{primargs_remove_equals:N} \meta{function} +% \end{syntax} +% Removes \meta{equals}, namely \meta{optional spaces} followed +% optionally by an explicit |=| character with category other. +% \end{function} +% +% \subsection{Afterassignment and \tn{globaldefs}} +% +% The \tn{globaldefs} parameter is not taken into account yet, and +% setting it to a non-zero value may make everything crash. It is +% straightforward to fix that for negative \tn{globaldefs}, but positive +% \tn{globaldefs} make things complicated. +% +% Tokens inserted using \tn{afterassignment} may be lost when using this +% package, since it uses \tn{afterassignment} internally. +% +% \subsection{Internal functions} +% +% \begin{function}{\@@_get_rhs:NnN, \@@_get_rhs:NoN} +% \begin{syntax} +% \cs{@@_get_rhs:NnN} \meta{register} \Arg{register rhs} \meta{function} +% \end{syntax} +% Use the \meta{register} to find a right-hand side of a valid +% assignment for this type of variable, and feed the value found to +% the \meta{function}. The value of the \meta{register} is then +% restored using \meta{register} |=| \meta{register rhs}, where the +% \meta{register rhs} should be the initial value of the +% \meta{register}. All those assignments are performed within a +% group, but some are automatically global, and \tn{globaldefs} may +% cause trouble with others. +% \end{function} +% +% \end{documentation} +% +% \begin{implementation} +% +% \section{\pkg{primargs} implementation} +% +%<*package> +% \begin{macrocode} +\ProvidesExplPackage + {primargs}{2012/07/10}{0.1}{Parsing~arguments~of~primitives} +\RequirePackage{expl3} +% \end{macrocode} +% +% \begin{macrocode} +%<@@=primargs> +% \end{macrocode} +% +% \subsection{Helpers} +% +% \begin{macro}[aux]{\g_@@_code_tl} +% Used to contain temporary code. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \g_@@_code_tl { } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux]{\@@_no_afterassignment:} +% Supersedes any \tn{afterassignment} token. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \@@_no_afterassignment: + { \tex_afterassignment:D \tex_relax:D } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux]{\@@_no_localdefs:} +% This function, which must be called in a group, ensures that +% \tn{global} indeed makes definitions global. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \@@_no_localdefs: + { + \@@_no_afterassignment: + \int_compare:nNnF \tex_globaldefs:D > \c_zero + { \tex_globaldefs:D = \c_zero } + } +% \end{macrocode} +% \end{macro} +% +% \subsection{Read token with or without expansion} +% +% \TeX{} often calls the \proc{get_x_token} procedure when parsing +% various parts of its grammar. This expands tokens recursively until +% reaching a non-expandable token. We emulate this by expanding with +% \tn{expandafter}, then checking whether the upcoming token is +% expandable or not using \tn{futurelet}, and if it is, expanding again. +% +% One thing to be careful about is that +% \begin{quote} +% \tn{expandafter} \tn{show} \tn{noexpand} \cs{space} +% \end{quote} +% shows the \tn{meaning} of the \tn{notexpanded:} \cs{space}, +% namely \tn{relax} (frozen, in fact, hence a bit different from the +% normal \tn{relax}), while expanding twice with +% \begin{quote} +% \tn{expandafter} \tn{expandafter} \tn{expandafter} \tn{show} +% \tn{noexpand} \cs{space} +% \end{quote} +% expands the \cs{space} to the underlying space character token. +% What this means is that we must first check if the token is expandable +% or not, and only then expand, and that the token should not be queried +% again using \tn{futurelet}. On this latter point, run +% \begin{verbatim} +% \def \test { \show \next \futurelet \next \test } +% \expandafter \test \noexpand \space +% \end{verbatim} +% to see how \cs{next} changes from \tn{relax} to becoming a macro. +% +% \begin{macro}{\primargs_read_x_token:N} +% \begin{macro}[aux]{\@@_read_x_token:N, \@@_read_x_token_ii:N} +% First query the following token. Then test whether it is +% expandable, using a variant of the \cs{token_if_expandable:NTF} +% test.\footnote{This \LaTeX3 test returns \texttt{false} for +% undefined tokens (by design), but \TeX{}'s \proc{get_x_token} +% expands those undefined tokens, causing errors, so we should as +% well.} If the token is expandable, \cs{exp_not:N} will change its +% \tn{meaning} to \tn{relax}, the test is \texttt{false}, we expand, +% and call the loop. Otherwise, we stop. Interestingly, we don't +% ever need to take the user's function as an argument. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \primargs_read_x_token:N + { + \group_begin: + \@@_no_localdefs: + \@@_read_x_token:N + } +\cs_new_protected_nopar:Npn \@@_read_x_token:N + { + \tex_afterassignment:D \@@_read_x_token_ii:N + \tex_global:D \tex_futurelet:D \g_primargs_token + } +\cs_new_protected_nopar:Npn \@@_read_x_token_ii:N + { + \exp_after:wN + \if_meaning:w \exp_not:N \g_primargs_token \g_primargs_token + \group_end: + \exp_after:wN \use_none:nnn + \fi: + \exp_after:wN \@@_read_x_token:N \exp_after:wN + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\primargs_read_token:N} +% The same without expansion, for use when we already know that what +% follows is expanded. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \primargs_read_token:N + { + \group_begin: + \@@_no_localdefs: + \tex_afterassignment:D \group_end: + \tex_global:D \tex_futurelet:D \g_primargs_token + } +% \end{macrocode} +% \end{macro} +% +% \subsection{Removing tokens} +% +% \subsubsection{One token} +% +% \begin{macro}{\primargs_remove_token:N} +% Remove token using \tn{let} (note the presence of |=| and a space, +% to correctly remove explicit space characters), then insert the +% \meta{function} with \tn{afterassignment}. +% \begin{macrocode} +\cs_new_protected:Npn \primargs_remove_token:N #1 + { + \group_begin: + \@@_no_localdefs: + \tex_aftergroup:D #1 + \tex_afterassignment:D \group_end: + \tex_global:D \tex_let:D \g_primargs_token = ~ + } +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Optional space token} +% +% ^^A Not sure of the interaction with \notexpanded: +% +% \begin{macro}{\primargs_remove_one_optional_space:N} +% \begin{macro}[aux]{\@@_remove_one_optional_space:} +% Start a group: we will insert the \meta{function} at its end. +% \begin{macrocode} +\cs_new_protected:Npn \primargs_remove_one_optional_space:N #1 + { + \group_begin: + \@@_no_localdefs: + \tex_aftergroup:D #1 + \primargs_read_x_token:N \@@_remove_one_optional_space: + } +\exp_args:NNo \cs_new_protected_nopar:Npn \@@_remove_one_optional_space: + { + \use:n { \if_catcode:w } ~ \exp_not:N \g_primargs_token + \exp_after:wN \primargs_remove_token:N + \fi: + \group_end: + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\primargs_remove_optional_spaces:N} +% \begin{macro}[aux] +% {\@@_remove_optional_spaces:, \@@_remove_optional_spaces_ii:} +% Start a group: we will insert the \meta{function} at its end. +% \begin{macrocode} +\cs_new_protected:Npn \primargs_remove_optional_spaces:N #1 + { + \group_begin: + \@@_no_localdefs: + \tex_aftergroup:D #1 + \@@_remove_optional_spaces: + } +\cs_new_protected_nopar:Npn \@@_remove_optional_spaces: + { \primargs_read_x_token:N \@@_remove_optional_spaces_ii: } +\exp_args:NNo \cs_new_protected_nopar:Npn \@@_remove_optional_spaces_ii: + { + \use:n { \if_catcode:w } ~ \exp_not:N \g_primargs_token + \exp_after:wN \primargs_remove_token:N + \exp_after:wN \@@_remove_optional_spaces: + \else: + \exp_after:wN \group_end: + \fi: + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\primargs_remove_equals:N} +% Remove \meta{optional spaces}, then test for an explicit |=|, both +% in \tn{meaning} and as a token list (once we know its \tn{meaning}, +% we can grab it safely). +% \begin{macrocode} +\cs_new_protected:Npn \primargs_remove_equals:N #1 + { + \group_begin: + \tex_aftergroup:D #1 + \primargs_remove_optional_spaces:N \@@_remove_equals: + } +\cs_new_protected_nopar:Npn \@@_remove_equals: + { + \if_meaning:w = \g_primargs_token + \exp_after:wN \@@_remove_equals_ii:NN + \fi: + \group_end: + } +\cs_new_protected:Npn \@@_remove_equals_ii:NN #1#2 + { \tl_if_eq:nnTF { #2 } { = } { #1 } { #1 #2 } } +% \end{macrocode} +% \end{macro} +% +% \subsection{Right-hand sides of assignments} +% +% The naive approach to reading an integer, or a general text, is to let +% \TeX{} perform an assignment to a \tn{count}, or a \tn{toks}, register +% and regain control using \tn{afterassignment}. The question is then +% to know which \tn{count} or \tn{toks} register to use. One might +% think that any can be used as long as the assignment happens in a +% group. +% +% However, there comes the question of the \tn{globaldefs} parameter. +% If this parameter is positive, every assignment is global, including +% assignments to the parameter itself, preventing us from setting it to +% zero locally; hence, we are stuck with global assignments (if +% \tn{globaldefs} is negative, we can change it, locally, to whatever +% value pleases us, as done by \cs{@@_no_localdefs:}). We may thus not +% use scratch registers to parse integers, general texts, and other +% pieces of \TeX{}'s grammar. +% +% For integers, we will use \tn{deadcycles}, a parameter which is +% automatically assigned globally, and we revert it to its previous +% value afterwards. For general text, we will use \tn{errhelp}, which +% we will assign locally if possible (\tn{globaldefs} negative or zero), +% and otherwise reset to be empty. +% +% \begin{macro}[int]{\@@_get_rhs:NnN, \@@_get_rhs:NoN} +% The last two lines of this function are the key: assign to |#1|, +% then take control using \tn{afterassignment}. After the assignment, +% we expand the value found, |\tex_the:D #1|, within a brace group, +% then restore |#1| using its initial value |#2|, and end the group. +% The earlier use of \cs{tex_aftergroup:D} inserts the \meta{function} +% |#3| before the brace group containing the value found. +% \begin{macrocode} +\cs_new_protected:Npn \@@_get_rhs:NnN #1#2#3 + { + \group_begin: + \@@_no_localdefs: + \tex_aftergroup:D #3 + \tl_gset:Nn \g_@@_code_tl + { + \use:x + { + \exp_not:n { #1 = #2 \group_end: } + { \tex_the:D #1 } + } + } + \tex_afterassignment:D \g_@@_code_tl + #1 = + } +\cs_generate_variant:Nn \@@_get_rhs:NnN { No } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\primargs_get_number:N} +% We use the general \cs{@@_get_rhs:NoN}, using the internal register +% \tn{deadcycles}, for which all assignments are global: thus, +% restoring its value will not interact badly with groups. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \primargs_get_number:N + { + \@@_get_rhs:NoN \tex_deadcycles:D + { \tex_the:D \tex_deadcycles:D } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\primargs_get_dimen:N} +% Use \tn{hoffset} as a register since it is not too likely to be +% changed locally (anyways, which register we use is not that +% important since normally, \tn{globaldefs} is zero, and everything is +% done within a group). +% \begin{macrocode} +\cs_new_protected_nopar:Npn \primargs_get_dimen:N + { + \@@_get_rhs:NoN \tex_hoffset:D + { \tex_the:D \tex_hoffset:D } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\primargs_get_glue:N} +% Use \tn{topskip}. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \primargs_get_glue:N + { + \@@_get_rhs:NoN \tex_topskip:D + { \tex_the:D \tex_topskip:D } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\primargs_get_mudimen:N} +% There is no such thing as a \meta{mudimen variable}, so we're on our +% own to parse a \meta{mudimen}. Warn about that problem, and parse a +% \meta{muglue} instead. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \primargs_get_mudimen:N + { + \msg_warning:nn { primargs } { get-mudimen } + \primargs_get_muglue:N + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\primargs_get_muglue:N} +% Use \tn{thinmuskip}. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \primargs_get_muglue:N + { + \@@_get_rhs:NoN \tex_thinmuskip:D + { \tex_the:D \tex_thinmuskip:D } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\primargs_get_general_text:N} +% For a general text, use \tn{errhelp}, since it shouldn't be a big +% problem if that's changed. We don't revert it to its value, but to +% be empty (note the extra braces, though, since it's a token +% register), because it is probably better to have no help than the +% wrong help hanging around. Besides, \tn{errhelp} is always set +% for immediate use. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \primargs_get_general_text:N + { \@@_get_rhs:NoN \tex_errhelp:D { { } } } +% \end{macrocode} +% \end{macro} +% +% \subsection{Get file name} +% +% ^^A todo: Figure out when \group_align_safe_begin: is needed. +% +% \begin{variable}{\g_@@_file_name_tl} +% Token list used to build a file name, one character at a time. +% \begin{macrocode} +\tl_new:N \g_@@_file_name_tl +% \end{macrocode} +% \end{variable} +% +% \begin{macro}{\primargs_get_file_name:N} +% Empty the file name (globally), and build it one character at a time. +% The \meta{function} is added at the end of a group, started here. +% As described in the \TeX{}book, a \meta{file name} should start with +% \meta{optional spaces}, which we remove, then character tokens, +% ending with a non-expandable character or control sequence. After +% space removal, \cs{g_primargs_token} contains the next token, so no +% need for \cs{primargs_read_token:N}. +% \begin{macrocode} +\cs_new_protected:Npn \primargs_get_file_name:N #1 + { + \group_begin: + \@@_no_localdefs: + \tex_aftergroup:D #1 + \tl_gclear:N \g_@@_file_name_tl + \primargs_remove_optional_spaces:N \@@_get_file_name_test: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux]{\@@_get_file_name_test:} +% The token read is in \cs{g_primargs_token}, and is non-expandable. +% If it is a control sequence, end the \meta{file name}. If it is a +% space, the the \meta{file name} ends after its removal. Otherwise, +% we extract the character from the \tn{meaning} of the \meta{token}, +% which we remove anyways: in that case, we'll recurse. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \@@_get_file_name_test: + { + \token_if_cs:NTF \g_primargs_token + { \@@_get_file_name_end: } + { + \token_if_eq_charcode:NNTF \c_space_token \g_primargs_token + { \primargs_remove_token:N \@@_get_file_name_end: } + { \primargs_remove_token:N \@@_get_file_name_char: } + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux]{\@@_get_file_name_end:} +% When the end of the file name is reached, end the group, after +% expanding the contents of \cs{g_@@_file_name_tl}. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \@@_get_file_name_end: + { \exp_args:No \group_end: \g_@@_file_name_tl } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux]{\@@_get_file_name_char:} +% \begin{macro}[aux, EXP] +% {\@@_get_file_name_char_ii:w, \@@_get_file_name_char_iii:w} +% With an explicit character, applying \tn{string} would give the +% character code. Here, implicit characters have to be converted too, +% so we must work with the \tn{meaning}, which is two or three words +% separated by spaces, then the character. The \texttt{ii} auxiliary +% removes the first two words, and duplicates the remainder (either +% one character, or a word and a character), and the second auxiliary +% leaves the second piece in the definition (in both cases, the +% character). Then loop with expansion. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \@@_get_file_name_char: + { + \tl_gput_right:Nx \g_@@_file_name_tl + { + \exp_after:wN \@@_get_file_name_char_ii:w + \token_to_meaning:N \g_primargs_token + \q_stop + } + \primargs_read_x_token:N \@@_get_file_name_test: + } +\cs_new:Npn \@@_get_file_name_char_ii:w #1 ~ #2 ~ #3 \q_stop + { \@@_get_file_name_char_iii:w #3 ~ #3 ~ \q_stop } +\cs_new:Npn \@@_get_file_name_char_iii:w #1 ~ #2 ~ #3 \q_stop {#2} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \section{Examples} +% +% [Old text which I don't want to remove yet.] A few examples of what +% this package can parse. +% \begin{itemize} +% \item \meta{integer} denotes an integer in any form that \TeX{} +% accepts as the right-hand side of a primitive integer assignment +% of the form \tn{count}|0=|\meta{integer}; +% \item \meta{equals} is an arbitrary (optional) number of explicit or +% implicit space characters, an optional explicit equal sign of +% category other, and further (optional) explicit or implicit space +% characters; +% \item \meta{file name} is an arbitrary sequence of explicit or +% implicit characters with arbitrary category codes (except active +% characters, which are expanded before reaching \TeX{}'s mouth), +% ending either with a space character (character code $32$, +% arbitrary non-active category code, explicit or implicit), which +% is removed, or with a non-expandable token, with some care needed +% for the case of a \tn{notexpanded:} expandable token; +% \item \meta{filler} is an arbitrary combination of tokens each of +% whose meaning is \tn{relax} or a character with category code +% $10$; +% \item \meta{general text} is a \meta{filler}, followed by braced +% tokens, starting with an explicit or implicit begin-group +% character, and ending with the matching explicit end-group +% character (both with any character code), with an equal number of +% explicit begin-group and end-group characters in between: this is +% precisely the right-hand side of an assignment of the form +% \tn{toks}|0=|\meta{general text}. +% \end{itemize} +% +%</package> +% +% \end{implementation} +% +% \endinput |