summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/latex/morewrites/primargs.dtx
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2017-04-16 22:19:40 +0000
committerKarl Berry <karl@freefriends.org>2017-04-16 22:19:40 +0000
commitcacc7d77918eb955a4bc5bed894e8ea1445a6ba4 (patch)
tree54307a98b0d3dafd1998f2135dcfe9237f38f9af /Master/texmf-dist/source/latex/morewrites/primargs.dtx
parentb1e4ebb4febbc920047445c1f1416bb2f6a563f7 (diff)
morewrites (14apr17)
git-svn-id: svn://tug.org/texlive/trunk@43862 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/source/latex/morewrites/primargs.dtx')
-rw-r--r--Master/texmf-dist/source/latex/morewrites/primargs.dtx685
1 files changed, 491 insertions, 194 deletions
diff --git a/Master/texmf-dist/source/latex/morewrites/primargs.dtx b/Master/texmf-dist/source/latex/morewrites/primargs.dtx
index f6c3f78ce33..851263575a6 100644
--- a/Master/texmf-dist/source/latex/morewrites/primargs.dtx
+++ b/Master/texmf-dist/source/latex/morewrites/primargs.dtx
@@ -1,6 +1,6 @@
% \iffalse meta-comment
%
-%% File: primargs.dtx Copyright (C) 2012-2013 Bruno Le Floch
+%% File: primargs.dtx Copyright (C) 2012-2017 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
@@ -21,10 +21,9 @@
% \fi
%
% \title{The \textsf{primargs} package: \\
-% Parsing arguments of primitives\thanks{This
-% file has version number v0.2e, last revised 2013/01/08.}}
+% Parsing arguments of primitives}
% \author{Bruno Le Floch}
-% \date{2013/01/08}
+% \date{2017/04/10}
%
% \maketitle
% \tableofcontents
@@ -33,102 +32,195 @@
%
% \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.
+% This \TeX{} and \LaTeX{} package is currently used by \pkg{morewrites}
+% when redefining primitives: it allows to read arguments of primitives
+% in place of \TeX{}, which is useful to add hooks to primitives. Of
+% course, this is much slower than letting \TeX{} do things directly.
%
-% \subsection{Read one token}
+% All assignments done by this package are global. While a negative
+% value of the \tn{globaldefs} (primitive) parameter normally makes all
+% assignments local, this package makes sure \tn{globaldefs} is
+% non-negative before assignments.
%
-% \begin{variable}{\g_primargs_token}
-% The result of \cs{primargs_read_x_token:N} and
-% \cs{primargs_read_token:N}.
+% \subsection{Reading one token without removing it}
+%
+% \begin{variable}[tested = primargs001]{\g_primargs_token}
+% The token read by \cs{primargs_read_token:N} or
+% \cs{primargs_read_x_token:N}. Its value is always set globally.
+% It can be an \tn{outer} macro.
% \end{variable}
%
-% \begin{function}{\primargs_read_x_token:N}
+% \begin{function}[tested = primargs001]{\primargs_read_token:N}
% \begin{syntax}
-% \cs{primargs_read_x_token:N} \meta{function}
+% \cs{primargs_read_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}.
+% Sets \cs{g_primargs_token} equal to the token following the
+% \meta{function}, then calls the \meta{function}. The token
+% following the \meta{function} is not removed.
+% \begin{texnote}
+% This is essentially \tn{global} \tn{futurelet}
+% \cs{g_primargs_token} \meta{function}, with the added guarantee
+% that the assignment is global even when $\tn{globaldefs}$ is negative.
+% \end{texnote}
% \end{function}
%
-% \begin{function}{\primargs_read_token:N}
+% \begin{function}[tested = primargs001]{\primargs_read_x_token:N}
% \begin{syntax}
-% \cs{primargs_read_token:N} \meta{function}
+% \cs{primargs_read_x_token:N} \meta{function}
% \end{syntax}
-% Sets \cs{g_primargs_token} globally to the value of the next token,
-% then calls the \meta{function}.
+% Expands tokens recursively with \cs{exp_after:wN} until encountering
+% a non-expandable token and afterwards calls the \meta{function}.
+% The non-expandable token following the \meta{function} is not
+% removed and \cs{g_primargs_token} is also set (globally) equal to
+% that token.
% \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}
+% \subsection{Removing tokens}
%
-% \begin{function}{\primargs_remove_token:N}
+% \begin{function}[tested = primargs005]{\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.
+% Removes the \meta{token} which follows the \meta{function}, then
+% calls the \meta{function}. This also sets \cs{g_primargs_token}
+% (globally) equal to the removed token.
% \end{function}
%
-% \begin{function}{\primargs_remove_one_optional_space:N}
+% \begin{function}[tested = primargs005]{\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}.
+% Expands tokens following the \meta{function} until a non-expandable
+% token is found, and sets \cs{g_primargs_token} (globally) equal to this token,
+% then removes the token if it has catcode~$10$ (space). Finally,
+% call the \meta{function}.
% \end{function}
%
-% \begin{function}{\primargs_remove_optional_spaces:N}
+% \begin{function}[tested = primargs005]{\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}.
+% Expands tokens following the \meta{function}, removing any token
+% with catcode~$10$ (space), then sets \cs{g_primargs_token} (globally) equal to
+% the first non-space token and calls the \meta{function}.
% \end{function}
%
-% \begin{function}{\primargs_remove_equals:N}
+% \begin{function}[tested = primargs005]{\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.
+% Expands tokens following the \meta{function}, removing any token
+% with catcode~$10$ (space), then sets \cs{g_primargs_token} (globally) equal to
+% the first non-space token. If this token is an explicit~|=|
+% character token with catcode~$12$ (other), then it is removed as
+% well. Finally, calls the \meta{function}.
% \end{function}
%
-% \subsection{Afterassignment and \tn{globaldefs}}
+% \begin{function}[added = 2014-08-06, tested = primargs005]{\primargs_remove_filler:N}
+% \begin{syntax}
+% \cs{primargs_remove_filler:N} \meta{function}
+% \end{syntax}
+% Expands tokens following the \meta{function}, removing any token
+% with catcode~$10$ (space) or equal to \tn{relax}, then sets
+% \cs{g_primargs_token} (globally) equal to the next
+% token. Finally, calls the \meta{function}.
+% \end{function}
%
-% 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.
+% \subsection{Grabbing arguments}
%
-% Tokens inserted using \tn{afterassignment} may be lost when using this
-% package, since it uses \tn{afterassignment} internally.
+% \begin{function}[tested = primargs002]
+% {
+% \primargs_get_number:N,
+% \primargs_get_dimen:N,
+% \primargs_get_glue:N,
+% \primargs_get_mudimen:N,
+% \primargs_get_muglue:N,
+% }
+% \begin{syntax}
+% \cs{primargs_get_number:N} \meta{function}
+% \end{syntax}
+% Reads a number/dimension/glue/math dimension/math glue following the
+% \meta{function}, then calls the \meta{function} with a braced
+% argument containing the value found. For instance,
+% \begin{verbatim}
+% \primargs_get_glue:N \test 3sp plus \numexpr 2-3 fill X
+% \end{verbatim}
+% yields
+% \begin{verbatim}
+% \test {3sp plus -1fill}X
+% \end{verbatim}
+% A word of warning: the \cs{primargs_get_mudimen:N} function
+% currently parses a \meta{muskip} instead of a \meta{mudimen}.
+% \end{function}
+%
+% \begin{function}[updated = 2014-08-06, tested = primargs003]
+% {\primargs_get_general_text:N}
+% \begin{syntax}
+% \cs{primargs_get_general_text:N} \meta{function}
+% \end{syntax}
+% Finds what \TeX{}'s grammar calls a \meta{general text} (that is, a
+% \meta{filler}, a catcode~$1$ token, a \meta{balanced text}, and an
+% explicit catcode~$2$ token) following the \meta{function}, and calls
+% the \meta{function} with the \meta{balanced text} as a braced
+% argument.
+% \end{function}
%
-% \subsection{Internal functions}
+% \begin{function}[updated = 2017-04-10, tested = primargs004]{\primargs_get_file_name:N, \primargs_get_input_file_name:N}
+% \begin{syntax}
+% \cs{primargs_get_file_name:N} \meta{function}
+% \cs{primargs_get_input_file_name:N} \meta{function}
+% \end{syntax}
+% Reads a \meta{file name} following the \meta{function} and calls the
+% \meta{function} with this \meta{file name} as a braced argument.
+% The two functions are identical except in the \LuaTeX{} engine where
+% \cs{primargs_get_input_file_name:N} allows braced file names:
+% \LuaTeX{} allows such braced file names for some primitives (\tn{input}
+% and \tn{openin}) but not others (\tn{openout}).
+% \begin{texnote}
+% When braced file names are disallowed, the file name is obtained
+% by discarding \meta{optional spaces} then repeatedly doing the
+% following. Fully expand what follows in the input stream. If
+% the next token is an explicit or implicit character token
+% (regardless of its catcode) then add that character to the
+% file name and remove it from the input stream, and go back to
+% expanding tokens, except in one case: if the character code
+% is~$32$ (space) and the number of quote characters (code~$34$)
+% already in the file name is even, then the space is removed from
+% the input stream, not included in the file name, and parsing
+% ends. Finally, if the next token is a non-expandable command
+% (be it a control sequence or an active character) then the file
+% name ends and the command is left in the input stream.
+%
+% When braced file names are allowed, the following steps are added
+% prior to the procedure above. First remove a \meta{filler}. If
+% the next token is of catcode~$1$ then fully expand tokens one by
+% one and add their string representation (with \cs{tl_to_str:n},
+% not \cs{token_to_str:N}) to the file name.
+% \end{texnote}
+% \end{function}
+%
+% \subsection{Comments and internal functions}
+%
+% This package is not idiomatic \pkg{expl3} and should not be used as an
+% example of good coding practices. It uses \cs[no-index]{\ldots{}:D}
+% primitives directly:
+% \begin{itemize}
+% \item to cope with \tn{outer} tokens, since this package is meant to
+% be used quite broadly;
+% \item for primitives with (rightfully) no \pkg{expl3} interface (or a
+% slightly incomplete interface), namely \tn{afterassignment},
+% \tn{globaldefs}, \tn{aftergroup}, \tn{the}, \tn{deadcycles},
+% \tn{hoffset}, \tn{topskip}, \tn{thinmuskip}, \tn{unexpanded};
+% \item to test that a token's meaning is a given primitive when the
+% \pkg{expl3} interface is not (or not obviously) a copy of the primitive.
+% \end{itemize}
+% As a result, \emph{do not take this package as an example of how to
+% code with \pkg{expl3}; go and see Joseph Wright's \pkg{siunitx} for
+% instance.}
%
-% \begin{function}{\@@_get_rhs:NnN, \@@_get_rhs:NoN}
+% \begin{function}{\__primargs_get_rhs:NnN, \__primargs_get_rhs:NoN}
% \begin{syntax}
-% \cs{@@_get_rhs:NnN} \meta{register} \Arg{register rhs} \meta{function}
+% \cs{__primargs_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
@@ -140,6 +232,28 @@
% cause trouble with others.
% \end{function}
%
+% Despite large efforts expended to make this package robust against
+% changes to the \tn{globaldefs} parameter, setting it to a non-zero
+% value may make some parts of this package crash.
+%
+% Tokens inserted using \tn{afterassignment} may be lost when using this
+% package, since it uses \tn{afterassignment} internally.
+%
+% Todo list.
+% \begin{itemize}
+% \item Test all functions within alignments and understand their
+% interaction with the master counter.
+% \item Correct the parsing of \meta{mudimen}.
+% \item Perhaps parse \meta{muglue} and \meta{glue} by hand to avoid bad
+% interactions with \tn{globaldefs}. Otherwise put up a warning about
+% \tn{globaldefs} when relevant. Better partial fix: declare a skip and a muskip.
+% \item Write tests of engine behaviour, especially \LuaTeX{}'s \tn{input},
+% \tn{openin}, \tn{openout} including behaviour of |#| and spaces and
+% character-code-zero, to detect unexpected changes. In
+% \tn{input}|{|\ldots{}\tn{input}\ldots{}|}|, \LuaTeX{} expands the
+% inner \tn{input} but uses the inner file name as the outer file name.
+% \end{itemize}
+%
% \end{documentation}
%
% \begin{implementation}
@@ -148,41 +262,47 @@
%
%<*package>
% \begin{macrocode}
-\RequirePackage {expl3} [2012/08/14]
+\RequirePackage {expl3} [2017/03/18]
\ProvidesExplPackage
- {primargs} {2013/01/08} {0.2e} {Parsing arguments of primitives}
+ {primargs} {2017/04/10} {} {Parsing arguments of primitives}
% \end{macrocode}
%
% \begin{macrocode}
%<@@=primargs>
% \end{macrocode}
%
-% \subsection{Helpers}
+% \subsection{Variables and helpers}
%
% \begin{macro}[aux]{\g_@@_code_tl}
% Used to contain temporary code.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \g_@@_code_tl { }
+\tl_new:N \g_@@_code_tl
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[aux]{\@@_no_afterassignment:}
-% Supersedes any \tn{afterassignment} token.
+% \begin{variable}[int]{\g_@@_file_name_tl, \g_@@_file_name_level_tl}
+% Token list used to build a file name, one character at a time.
+% Token list holding the level of nesting in quotes or braces.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \@@_no_afterassignment:
- { \tex_afterassignment:D \tex_relax:D }
+\tl_new:N \g_@@_file_name_tl
+\tl_new:N \g_@@_file_name_level_tl
% \end{macrocode}
-% \end{macro}
+% \end{variable}
%
-% \begin{macro}[aux]{\@@_no_localdefs:}
-% This function, which must be called in a group, ensures that
-% \tn{global} indeed makes definitions global.
+% \begin{macro}[aux]{\@@_safe:}
+% This function, which must be called in a group, cancels any
+% \tn{afterassignment} token and makes the \tn{globaldefs} parameter
+% non-negative. This ensures that assignments prefixed with
+% \tn{global} are indeed global. When \tn{globaldefs} is positive,
+% every assignment is global, and it is not possible to safely
+% (locally) set it to zero.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \@@_no_localdefs:
+\cs_new_protected:Npn \@@_safe:
{
- \@@_no_afterassignment:
- \int_compare:nNnF \tex_globaldefs:D > \c_zero
- { \tex_globaldefs:D = \c_zero }
+ \tex_afterassignment:D \tex_relax:D
+ \if_int_compare:w 0 > \tex_globaldefs:D
+ \int_zero:N \tex_globaldefs:D
+ \fi:
}
% \end{macrocode}
% \end{macro}
@@ -191,22 +311,23 @@
%
% \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.
+% reaching a non-expandable token. We emulate this by reading the next
+% token with \tn{futurelet}, checking whether it is expandable or not by
+% comparing its meaning to its meaning when acted upon by \tn{noexpand},
+% and expanding it with \tn{expandafter} if it is expandable.
%
% One thing to be careful about is that
% \begin{quote}
-% \tn{expandafter} \tn{show} \tn{noexpand} \cs{space}
+% \tn{expandafter} \tn{show} \tn{noexpand} \tn{space}
% \end{quote}
-% shows the \tn{meaning} of the \tn{notexpanded:} \cs{space},
+% shows the \tn{meaning} of the |\notexpanded: \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}
+% \tn{noexpand} \tn{space}
% \end{quote}
-% expands the \cs{space} to the underlying space character token.
+% expands the \tn{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
@@ -214,52 +335,73 @@
% \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.
+% to see how \tn{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}
+% \begin{macro}[aux]
+% {
+% \@@_read_x_token:N, \@@_read_x_token_aux:N,
+% \@@_read_x_token_std:N, \@@_read_x_token_file:N
+% }
+% This is a bit messy, because we need to support the fact that
+% \TeX{} does not consider \tn{input} as expandable when it is
+% looking for a file name. This variation is encapsulated by
+% letting \cs{@@_read_x_token_aux:N} equal to either a standard
+% (\texttt{std}) version or a version specific to file names
+% (\texttt{file}).
+%
% 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
+% 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.
+% and call the loop. Otherwise, we stop. In the \texttt{file}
+% version there is an extra test for \cs{tex_input:D}. By default
+% use the standard version.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \primargs_read_x_token:N
+\cs_new_protected:Npn \primargs_read_x_token:N
{
\group_begin:
- \@@_no_localdefs:
+ \@@_safe:
\@@_read_x_token:N
}
-\cs_new_protected_nopar:Npn \@@_read_x_token:N
+\cs_new_protected:Npn \@@_read_x_token:N
{
- \tex_afterassignment:D \@@_read_x_token_ii:N
+ \tex_afterassignment:D \@@_read_x_token_aux:N
\tex_global:D \tex_futurelet:D \g_primargs_token
}
-\cs_new_protected_nopar:Npn \@@_read_x_token_ii:N
+\cs_new_protected:Npn \@@_read_x_token_std:N
{
\exp_after:wN
\if_meaning:w \exp_not:N \g_primargs_token \g_primargs_token
- \group_end:
- \exp_after:wN \use_none:nnn
+ \group_end: \use_i:nnnn
\fi:
\exp_after:wN \@@_read_x_token:N \exp_after:wN
}
+\cs_new_eq:NN \@@_read_x_token_aux:N
+ \@@_read_x_token_std:N
+\cs_new_protected:Npn \@@_read_x_token_file:N
+ {
+ \if_meaning:w \tex_input:D \g_primargs_token
+ \use_i_ii:nnn \group_end:
+ \fi:
+ \@@_read_x_token_std:N
+ }
% \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.
+% The same without expansion, useful for instance when we already know
+% that what follows is expanded. Interestingly, we don't ever need to
+% take the user's function as an argument.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \primargs_read_token:N
+\cs_new_protected:Npn \primargs_read_token:N
{
\group_begin:
- \@@_no_localdefs:
+ \@@_safe:
\tex_afterassignment:D \group_end:
\tex_global:D \tex_futurelet:D \g_primargs_token
}
@@ -268,17 +410,15 @@
%
% \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}.
+% \meta{function} after closing the group.
% \begin{macrocode}
\cs_new_protected:Npn \primargs_remove_token:N #1
{
\group_begin:
- \@@_no_localdefs:
+ \@@_safe:
\tex_aftergroup:D #1
\tex_afterassignment:D \group_end:
\tex_global:D \tex_let:D \g_primargs_token = ~
@@ -286,10 +426,6 @@
% \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.
@@ -297,13 +433,13 @@
\cs_new_protected:Npn \primargs_remove_one_optional_space:N #1
{
\group_begin:
- \@@_no_localdefs:
+ \@@_safe:
\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:
+\cs_new_protected:Npn \@@_remove_one_optional_space:
{
- \use:n { \if_catcode:w } ~ \exp_not:N \g_primargs_token
+ \if_catcode:w \c_space_token \exp_not:N \g_primargs_token
\exp_after:wN \primargs_remove_token:N
\fi:
\group_end:
@@ -314,21 +450,23 @@
%
% \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.
+% {\@@_remove_optional_spaces:, \@@_remove_optional_spaces_aux:}
+% Start a group, make assignments safe, then recursively expand tokens
+% and remove any token with catcode~$10$ (space). Once another token
+% is found, close the group hence insert the \meta{function}~|#1|.
% \begin{macrocode}
\cs_new_protected:Npn \primargs_remove_optional_spaces:N #1
{
\group_begin:
- \@@_no_localdefs:
+ \@@_safe:
\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:
+\cs_new_protected:Npn \@@_remove_optional_spaces:
+ { \primargs_read_x_token:N \@@_remove_optional_spaces_aux: }
+\cs_new_protected:Npn \@@_remove_optional_spaces_aux:
{
- \use:n { \if_catcode:w } ~ \exp_not:N \g_primargs_token
+ \if_catcode:w \c_space_token \exp_not:N \g_primargs_token
\exp_after:wN \primargs_remove_token:N
\exp_after:wN \@@_remove_optional_spaces:
\else:
@@ -340,9 +478,10 @@
% \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{macro}[aux]{\@@_remove_equals:, \@@_remove_equals_aux:NN}
+% 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
{
@@ -350,17 +489,60 @@
\tex_aftergroup:D #1
\primargs_remove_optional_spaces:N \@@_remove_equals:
}
-\cs_new_protected_nopar:Npn \@@_remove_equals:
+\cs_new_protected:Npn \@@_remove_equals:
{
\if_meaning:w = \g_primargs_token
- \exp_after:wN \@@_remove_equals_ii:NN
+ \exp_after:wN \@@_remove_equals_aux:NN
\fi:
\group_end:
}
-\cs_new_protected:Npn \@@_remove_equals_ii:NN #1#2
+\cs_new_protected:Npn \@@_remove_equals_aux:NN #1#2
{ \tl_if_eq:nnTF { #2 } { = } { #1 } { #1 #2 } }
% \end{macrocode}
% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\primargs_remove_filler:N}
+% \begin{macro}[aux]
+% {
+% \@@_remove_filler:,
+% \@@_remove_filler_aux:,
+% \@@_remove_filler_end:NNNNN
+% }
+% Within a group remove a \meta{filler}, and insert the user's |#1|
+% after closing the group. A \meta{filler} consists of tokens with
+% catcode~$10$ (space) or equal to \tn{relax} or to the
+% \enquote{frozen \tn{relax}} command.
+% \begin{macrocode}
+\cs_new_protected:Npn \primargs_remove_filler:N #1
+ {
+ \group_begin:
+ \@@_safe:
+ \tex_aftergroup:D #1
+ \@@_remove_filler:
+ }
+\cs_new_protected:Npn \@@_remove_filler:
+ { \primargs_read_x_token:N \@@_remove_filler_aux: }
+\cs_new_protected:Npn \@@_remove_filler_aux:
+ {
+ \if_catcode:w \c_space_token \exp_not:N \g_primargs_token
+ \else:
+ \if_meaning:w \tex_relax:D \g_primargs_token
+ \else:
+ \exp_after:wN
+ \if_meaning:w \exp_not:N \prg_do_nothing: \g_primargs_token
+ \else:
+ \@@_remove_filler_end:NNNNN
+ \fi:
+ \fi:
+ \fi:
+ \primargs_remove_token:N \@@_remove_filler:
+ }
+\cs_new_protected:Npn \@@_remove_filler_end:NNNNN #1#2#3#4#5
+ { #1 #2 #3 \group_end: }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
%
% \subsection{Right-hand sides of assignments}
%
@@ -376,15 +558,13 @@
% 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
+% value pleases us, as done by \cs{@@_safe:}). 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.
+% value afterwards.
%
% \begin{macro}[int]{\@@_get_rhs:NnN, \@@_get_rhs:NoN}
% The last two lines of this function are the key: assign to |#1|,
@@ -397,7 +577,7 @@
\cs_new_protected:Npn \@@_get_rhs:NnN #1#2#3
{
\group_begin:
- \@@_no_localdefs:
+ \@@_safe:
\tex_aftergroup:D #3
\tl_gset:Nn \g_@@_code_tl
{
@@ -419,7 +599,7 @@
% \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
+\cs_new_protected:Npn \primargs_get_number:N
{
\@@_get_rhs:NoN \tex_deadcycles:D
{ \tex_the:D \tex_deadcycles:D }
@@ -433,7 +613,7 @@
% 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
+\cs_new_protected:Npn \primargs_get_dimen:N
{
\@@_get_rhs:NoN \tex_hoffset:D
{ \tex_the:D \tex_hoffset:D }
@@ -444,7 +624,7 @@
% \begin{macro}{\primargs_get_glue:N}
% Use \tn{topskip}.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \primargs_get_glue:N
+\cs_new_protected:Npn \primargs_get_glue:N
{
\@@_get_rhs:NoN \tex_topskip:D
{ \tex_the:D \tex_topskip:D }
@@ -457,7 +637,7 @@
% 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
+\cs_new_protected:Npn \primargs_get_mudimen:N
{
\msg_warning:nn { primargs } { get-mudimen }
\primargs_get_muglue:N
@@ -470,7 +650,7 @@
% \begin{macro}{\primargs_get_muglue:N}
% Use \tn{thinmuskip}.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \primargs_get_muglue:N
+\cs_new_protected:Npn \primargs_get_muglue:N
{
\@@_get_rhs:NoN \tex_thinmuskip:D
{ \tex_the:D \tex_thinmuskip:D }
@@ -479,44 +659,72 @@
% \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{macro}[aux]
+% {\@@_get_general_text:, \@@_get_general_text_error:n}
+% Getting a \meta{general text} is more tricky, as an assignment to
+% \tn{errhelp} (for instance) would also allow constructions such as
+% |\toks0|. Instead, we remove a \meta{filler} then test whether the
+% next token (already expanded) is a catcode~$1$ token, in which case
+% we replace it by an explicit left brace before calling the function.
+% When the next token is not of catcode~$1$, we produce an error,
+% attempting to imitate as closely as possible the \TeX{} error.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \primargs_get_general_text:N
- { \@@_get_rhs:NoN \tex_errhelp:D { { } } }
+\cs_new_protected:Npn \primargs_get_general_text:N #1
+ {
+ \group_begin:
+ \@@_safe:
+ \tex_aftergroup:D #1
+ \tex_aftergroup:D { \if_false: } \fi:
+ \primargs_remove_filler:N \@@_get_general_text:
+ }
+\cs_new_protected:Npn \@@_get_general_text:
+ {
+ \if_catcode:w \c_group_begin_token \g_primargs_token
+ \exp_after:wN \primargs_remove_token:N
+ \else:
+ \group_begin:
+ \tex_aftergroup:D \@@_get_general_text_error:n
+ \if_catcode:w \c_group_end_token \g_primargs_token
+ \tex_aftergroup:D {
+ \tex_aftergroup:D }
+ \fi:
+ \fi:
+ \group_end:
+ }
+\cs_new_protected:Npn \@@_get_general_text_error:n #1
+ {
+ \exp_after:wN \group_end:
+ \etex_unexpanded:D \if_int_compare:w `{ = \c_zero \fi: #1 }
+ }
% \end{macrocode}
% \end{macro}
+% \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,
+% \meta{optional spaces} (\LuaTeX{} changes that to \meta{filler}),
+% 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}.
+% When \TeX{} reads a file name, the \tn{input} primitive is
+% temporarily not expandable, so we temporarily change
+% \cs{primargs_read_x_token:N} to not expand this primitive. This is
+% reverted by \cs{@@_get_file_name_end:}.
% \begin{macrocode}
\cs_new_protected:Npn \primargs_get_file_name:N #1
{
\group_begin:
- \@@_no_localdefs:
+ \@@_safe:
+ \cs_gset_eq:NN \@@_read_x_token_aux:N
+ \@@_read_x_token_file:N
\tex_aftergroup:D #1
\tl_gclear:N \g_@@_file_name_tl
+ \tl_gset:Nn \g_@@_file_name_level_tl { 0 }
\primargs_remove_optional_spaces:N \@@_get_file_name_test:
}
% \end{macrocode}
@@ -524,18 +732,18 @@
%
% \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,
+% If it is a control sequence, end the \meta{file name}. Spaces are
+% special (quotes too, but that is treated elsewhere). 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:
+\cs_new_protected: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_space: }
{ \primargs_remove_token:N \@@_get_file_name_char: }
}
}
@@ -543,17 +751,42 @@
% \end{macro}
%
% \begin{macro}[aux]{\@@_get_file_name_end:}
-% When the end of the file name is reached, end the group, after
+% When the end of the file name is reached, reinstate the original
+% definition of |read_x_token| so as to make \tn{input} expandable
+% again, then 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 }
+\cs_new_protected:Npn \@@_get_file_name_end:
+ {
+ \cs_gset_eq:NN \@@_read_x_token_aux:N
+ \@@_read_x_token_std:N
+ \exp_args:No \group_end: \g_@@_file_name_tl
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\@@_get_file_name_space:}
+% We have already removed the space from the input stream. If
+% there is an odd number of quotes so far, add a space to the
+% file name and continue. Otherwise the file name ends.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_get_file_name_space:
+ {
+ \int_if_odd:nTF { \g_@@_file_name_level_tl }
+ {
+ \tl_gput_right:Nn \g_@@_file_name_tl { ~ }
+ \primargs_read_x_token:N \@@_get_file_name_test:
+ }
+ { \@@_get_file_name_end: }
+ }
% \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}
+% Check for a quote, which switches \cs{g_@@_file_name_level_tl}
+% from $0$ to $1$ or back.
% 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
@@ -564,8 +797,13 @@
% character). Then loop with expansion. This technique would fail if
% the character could be a space (character code~$32$).
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \@@_get_file_name_char:
+\cs_new_protected:Npn \@@_get_file_name_char:
{
+ \token_if_eq_charcode:NNT " \g_primargs_token
+ {
+ \tl_set:Nx \g_@@_file_name_level_tl
+ { \int_eval:n { 1 - \g_@@_file_name_level_tl } }
+ }
\tl_gput_right:Nx \g_@@_file_name_tl
{
\exp_after:wN \@@_get_file_name_char_ii:w
@@ -581,39 +819,98 @@
% \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}
+% \begin{macro}{\primargs_get_input_file_name:N}
+% \begin{macro}[aux]
+% {
+% \@@_get_input_file_name_first:,
+% \@@_get_input_file_name_loop:, \@@_get_input_file_name_test:,
+% \@@_get_input_file_name_brace:, \@@_get_input_file_name_aux:N
+% }
+% For most engines this is an alias of \cs{primargs_get_file_name:N}.
+% In \LuaTeX{} we test for a catcode $1$ token (after a filler) then
+% expand and collect tokens (turned to strings) one by one, counting
+% begin-group and end-group tokens in \cs{g_@@_file_name_level_tl}.
+% The control sequence \tn{par} is ignored. After removing a filler
+% or after expansion, \cs{g_primargs_token} cannot be \tn{outer} hence
+% the tests are safe. We use primitives to cope with outer macro
+% hidden by \tn{noexpand} upon first expansion.
+% \begin{macrocode}
+\sys_if_engine_luatex:TF
+ {
+ \cs_new_protected:Npn \primargs_get_input_file_name:N #1
+ {
+ \group_begin:
+ \@@_safe:
+ \tex_aftergroup:D #1
+ \tl_gclear:N \g_@@_file_name_tl
+ \tl_gset:Nn \g_@@_file_name_level_tl { 1 }
+ \primargs_remove_filler:N \@@_get_input_file_name_first:
+ }
+ \cs_new_protected:Npn \@@_get_input_file_name_first:
+ {
+ \token_if_eq_catcode:NNTF \g_primargs_token \c_group_begin_token
+ { \primargs_remove_token:N \@@_get_input_file_name_loop: }
+ { \primargs_get_file_name:N \group_end: }
+ }
+ \cs_new_protected:Npn \@@_get_input_file_name_loop:
+ { \primargs_read_x_token:N \@@_get_input_file_name_test: }
+ \cs_new_protected:Npn \@@_get_input_file_name_test:
+ {
+ \token_if_eq_catcode:NNTF \g_primargs_token \c_group_begin_token
+ {
+ \tl_gset:Nx \g_@@_file_name_level_tl
+ { \int_eval:n { \g_@@_file_name_level_tl + 1 } }
+ \primargs_remove_token:N \@@_get_input_file_name_brace:
+ }
+ {
+ \token_if_eq_catcode:NNTF \g_primargs_token \c_group_end_token
+ {
+ \tl_gset:Nx \g_@@_file_name_level_tl
+ { \int_eval:n { \g_@@_file_name_level_tl - 1 } }
+ \int_compare:nNnTF { \g_@@_file_name_level_tl } > 0
+ { \primargs_remove_token:N \@@_get_input_file_name_brace: }
+ { \primargs_remove_token:N \@@_get_file_name_end: }
+ }
+ {
+ \token_if_eq_meaning:NNTF \g_primargs_token \c_space_token
+ {
+ \tl_gput_right:Nn \g_@@_file_name_tl { ~ }
+ \primargs_remove_token:N \@@_get_input_file_name_loop:
+ }
+ { \exp_after:wN \@@_get_input_file_name_aux:N \exp_not:N }
+ }
+ }
+ }
+ \cs_new_protected:Npn \@@_get_input_file_name_brace:
+ {
+ \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
+ }
+ \@@_get_input_file_name_loop:
+ }
+ \cs_new_protected:Npn \@@_get_input_file_name_aux:N #1
+ {
+ \exp_after:wN \str_if_eq_x:nnT
+ \exp_after:wN { \token_to_str:N #1 } { \token_to_str:N \par }
+ { \use_none:nnn }
+ \tex_xdef:D \g_@@_file_name_tl
+ {
+ \g_@@_file_name_tl
+ \exp_after:wN \tl_to_str:n \exp_after:wN { \exp_not:N #1 }
+ }
+ \@@_get_input_file_name_loop:
+ }
+ }
+ { \cs_new_eq:NN \primargs_get_input_file_name:N \primargs_get_file_name:N }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
%
-%</package>
+%</package>
%
% \end{implementation}
%
-% \endinput
+% \PrintIndex