diff options
Diffstat (limited to 'Master/texmf-dist/source/latex/l3kernel/l3expan.dtx')
-rw-r--r-- | Master/texmf-dist/source/latex/l3kernel/l3expan.dtx | 367 |
1 files changed, 260 insertions, 107 deletions
diff --git a/Master/texmf-dist/source/latex/l3kernel/l3expan.dtx b/Master/texmf-dist/source/latex/l3kernel/l3expan.dtx index 5c853a3a321..d912433ee8d 100644 --- a/Master/texmf-dist/source/latex/l3kernel/l3expan.dtx +++ b/Master/texmf-dist/source/latex/l3kernel/l3expan.dtx @@ -35,7 +35,7 @@ % %<*driver|package> \RequirePackage{l3bootstrap} -\GetIdInfo$Id: l3expan.dtx 4073 2012-08-10 01:33:13Z bruno $ +\GetIdInfo$Id: l3expan.dtx 4149 2012-08-28 10:50:40Z bruno $ {L3 Argument expansion} %</driver|package> %<*driver> @@ -116,7 +116,7 @@ % % \section{Methods for defining variants} % -% \begin{function}[updated = 2012-06-21]{\cs_generate_variant:Nn} +% \begin{function}[updated = 2012-08-28]{\cs_generate_variant:Nn} % \begin{syntax} % \cs{cs_generate_variant:Nn} \meta{parent control sequence} \Arg{variant argument specifiers} % \end{syntax} @@ -598,7 +598,7 @@ % with other functions using temporary variables. % \end{variable} % -% \begin{function}{\::n, \::N, \::c, \::o, \::f, \::x, \::v, \::V, \:::} +% \begin{function}{\::n, \::N, \::p, \::c, \::o, \::f, \::x, \::v, \::V, \:::} % \begin{syntax} % |\cs_set_nopar:Npn \exp_args:Ncof { \::c \::o \::f \::: }| % \end{syntax} @@ -644,11 +644,7 @@ % argument handling is defined. These general expansion functions are % expandable unless |x| is used. (Any version of |x| is going to have % to use one of the \LaTeX3 names for \cs{cs_set_nopar:Npx} at some -% point, and so is never going to be expandable.\footnote{Some -% primitives have certain characteristics that means that their -% arguments undergo an expansion similar to an \texttt{x} type -% expansion but the primitive is in fact still expandable. We make it -% very clear when such a function is expandable.}) +% point, and so is never going to be expandable.) % % The definition of expansion functions with this technique happens % in section~\ref{sec:gendef}. @@ -671,6 +667,8 @@ % \cs{:::} serves as an end marker for the list of manipulations, |#2| % is the carried over result of the previous expansion steps and |#3| is % the argument about to be processed. +% One exception to this rule is \cs{::p}, which has to grab an argument +% delimited by a left brace. % % \begin{macro}[aux, EXP]{\@@_arg_next:nnn} % \begin{macro}[aux, EXP]{\@@_arg_next:Nnn} @@ -713,9 +711,18 @@ % \end{macrocode} % \end{macro} % +% \begin{macro}[int, EXP]{\::p} +% This function is used to skip an argument that is delimited by a +% left brace and doesn't need to be expanded. It should not be +% wrapped in braces in the result. +% \begin{macrocode} +\cs_new:Npn \::p #1 \::: #2#3# { #1 \::: {#2#3} } +% \end{macrocode} +% \end{macro} +% % \begin{macro}[int, EXP]{\::c} % This function is used to skip an argument that is turned into -% as control sequence without expansion. +% a control sequence without expansion. % \begin{macrocode} \cs_new:Npn \::c #1 \::: #2#3 { \exp_after:wN \@@_arg_next:Nnn \cs:w #3 \cs_end: {#1} {#2} } @@ -890,17 +897,14 @@ % \end{macro} % \end{macro} % -% \begin{macro}[EXP]{\exp_args:Nc} -% In \pkg{l3basics} -%\end{macro} +% \begin{macro}[EXP]{\exp_args:Nc, \exp_args:cc} +% In \pkg{l3basics}. +% \end{macro} % -% \begin{macro}[EXP] -% {\exp_args:cc, \exp_args:NNc, \exp_args:Ncc, \exp_args:Nccc} -% Here are the functions that turn their argument into csnames but -% are expandable. +% \begin{macro}[EXP]{\exp_args:NNc, \exp_args:Ncc, \exp_args:Nccc} +% Here are the functions that turn their argument into csnames but are +% expandable. % \begin{macrocode} -\cs_new:Npn \exp_args:cc #1#2 - { \cs:w #1 \exp_after:wN \cs_end: \cs:w #2 \cs_end: } \cs_new:Npn \exp_args:NNc #1#2#3 { \exp_after:wN #1 \exp_after:wN #2 \cs:w # 3\cs_end: } \cs_new:Npn \exp_args:Ncc #1#2#3 @@ -1283,43 +1287,60 @@ \exp_after:wN #1 \exp_after:wN \@@_generate_variant:nnNN \exp_after:wN #1 - \etex_detokenize:D {#2} , ? , \q_recursion_stop + \etex_detokenize:D {#2} , \scan_stop: , \q_recursion_stop } % \end{macrocode} % \end{macro} % % \begin{macro}[aux]{\@@_generate_variant:N} -% \begin{macro}[aux]{\@@_generate_variant:w} -% The idea here is to pick up protected parent functions, using the -% nature of the meaning string that they generate. If |\protected| -% appears in the meaning, the first \cs{q_mark} is taken as an -% argument, and |#3| is \cs{cs_new_protected_nopar:Npx}, otherwise it +% \begin{macro}[aux]{\@@_generate_variant:ww, \@@_generate_variant:wwNw} +% The goal here is to pick up protected parent functions. There are +% four cases: the parent function can be a primitive or a macro, and +% can be expandable or not. For non-expandable primitives, all +% variants should be protected; skipping the \cs{else:} branch is safe +% because all primitive \TeX{} conditionals are expandable. +% +% The other case where variants should be protected is when the parent +% function is a protected macro: then |protected| appears in the +% meaning before the fist occurrence of |macro|. The |ww| auxiliary +% removes everything in the meaning string after the first |ma|. We +% use |ma| rather than the full |macro| because the meaning of the +% \tn{firstmark} primitive (and four others) can contain an arbitrary +% string after a leading |firstmark:|. Then, look for |pr| in the +% part we extracted: no need to look for anything longer: the only +% strings we can have are an empty string, \verb*|\long |, +% \verb*|\protected |, \verb*|\protected\long |, |\first|, |\top|, +% |\bot|, |\splittop|, or |\splitbot|, with |\| replaced by the +% appropriate escape character. If |pr| appears in the part before +% |ma|, the first \cs{q_mark} is taken as an argument of the |wwNw| +% auxiliary, and |#3| is \cs{cs_new_protected_nopar:Npx}, otherwise it % is \cs{cs_new_nopar:Npx}. % \begin{macrocode} \group_begin: - \tex_lccode:D `\Z = `\d \scan_stop: - \tex_lccode:D `\? =`\\ \scan_stop: + \tex_catcode:D `\M = 12 \scan_stop: + \tex_catcode:D `\A = 12 \scan_stop: \tex_catcode:D `\P = 12 \scan_stop: \tex_catcode:D `\R = 12 \scan_stop: - \tex_catcode:D `\O = 12 \scan_stop: - \tex_catcode:D `\T = 12 \scan_stop: - \tex_catcode:D `\E = 12 \scan_stop: - \tex_catcode:D `\C = 12 \scan_stop: - \tex_catcode:D `\Z = 12 \scan_stop: \tex_lowercase:D { \group_end: \cs_new_protected:Npn \@@_generate_variant:N #1 { - \exp_after:wN \@@_generate_variant:w - \token_to_meaning:N #1 - \q_mark \cs_new_protected_nopar:Npx - ? PROTECTEZ - \q_mark \cs_new_nopar:Npx - \q_stop + \exp_after:wN \if_meaning:w \exp_not:N #1 #1 + \cs_set_eq:NN \@@_tmp:w \cs_new_protected_nopar:Npx + \else: + \exp_after:wN \@@_generate_variant:ww + \token_to_meaning:N #1 MA \q_mark + \q_mark \cs_new_protected_nopar:Npx + PR + \q_mark \cs_new_nopar:Npx + \q_stop + \fi: } - \cs_new_protected:Npn \@@_generate_variant:w - #1 ? PROTECTEZ #2 \q_mark #3 #4 \q_stop + \cs_new_protected:Npn \@@_generate_variant:ww #1 MA #2 \q_mark + { \@@_generate_variant:wwNw #1 } + \cs_new_protected:Npn \@@_generate_variant:wwNw + #1 PR #2 \q_mark #3 #4 \q_stop { \cs_set_eq:NN \@@_tmp:w #3 } @@ -1335,12 +1356,20 @@ % \item Boolean. % \item Base function. % \end{arguments} -% We discard the boolean and then set off a loop through the desired -% variant forms. The original function is retained as |#4| for +% If the boolean is \cs{c_false_bool}, the base function has no colon +% and we abort with an error; otherwise, set off a loop through the +% desired variant forms. The original function is retained as |#4| for % efficiency. % \begin{macrocode} \cs_new_protected:Npn \@@_generate_variant:nnNN #1#2#3#4 - { \@@_generate_variant:Nnnw #4 {#1}{#2} } + { + \if_meaning:w \c_false_bool #3 + \__msg_kernel_error:nnx { kernel } { missing-colon } + { \token_to_str:c {#1} } + \exp_after:wN \use_none_delimit_by_q_recursion_stop:w + \fi: + \@@_generate_variant:Nnnw #4 {#1}{#2} + } % \end{macrocode} % \end{macro} % @@ -1351,108 +1380,232 @@ % \item Base signature. % \item Beginning of variant signature. % \end{arguments} -% First check whether to terminate the loop over variant forms. Then -% build the variant function name once, to avoid repeating this -% relatively expensive operation. Then recurse, calling -% \cs{@@_generate_variant:Nnnw} with the three same arguments (and a -% new item from the comma list of variant forms). -% -% For each variant form, construct a new function name using the +% First check whether to terminate the loop over variant forms. Then, +% for each variant form, construct a new function name using the % original base name, the variant signature consisting of $l$ letters % and the last $k-l$ letters of the base signature (of length $k$). % For example, for a base function \cs{prop_put:Nnn} which needs a -% |cV| variant form, we want the new signature to be |cVn|. This -% could be done by placing the variant form letters, then -% \cs{use_none:nn} followed by the signature (the choice of -% \cs{use_none:nn} would depend on the variant form). However, this -% would crash badly if the base signature is mistakenly shorter than -% the variant form (this includes cases where the base function had no -% colon). Instead, we do a loop which at each step removes a -% character from the base signature and leaves one from the variant -% form behind it in a \cs{cs:w} \ldots{} \cs{cs_end:} construction. -% This \texttt{c}-type expansion is not done using \cs{exp_args:NNc} -% because some error-reporting mechanism must escape out of this -% construction. +% |cV| variant form, we want the new signature to be |cVn|. +% +% There are further subtleties: +% \begin{itemize} +% \item In \cs{cs_generate_variant:Nn} |\foo:nnTF| |{xxTF}|, it +% would be better to define |\foo:xxTF| using |\exp_args:Nxx|, +% rather than a hypothetical |\exp_args:NxxTF|. Thus, we wish to +% trim a common trailing part from the base signature and the +% variant signature. +% \item In \cs{cs_generate_variant:Nn} |\foo:on| |{ox}|, the +% function |\foo:ox| should be defined using |\exp_args:Nnx|, not +% |\exp_args:Nox|, to avoid double |o| expansion. +% \item Lastly, \cs{cs_generate_variant:Nn} |\foo:on| |{xn}| should +% trigger an error, because we do not have a means to replace +% |o|-expansion by |x|-expansion. +% \end{itemize} +% All this boils down to a few rules. Only |n| and |N|-type +% arguments can be replaced by \cs{cs_generate_variant:Nn}. Other +% argument types are allowed to be passed unchanged from the base +% form to the variant: in the process they are changed to |n| +% (except for two cases: |N| and |p|-type arguments). A common +% trailing part is ignored. +% +% We compare the base and variant signatures one character at a time +% within |x|-expansion. The result is given to +% \cs{@@_generate_variant:wwNN} in the form \meta{processed variant +% signature} \cs{q_mark} \meta{errors} \cs{q_stop} \meta{base +% function} \meta{new function}. If all went well, \meta{errors} +% is empty; otherwise, it is a kernel error message, followed by +% some clean-up code (\cs{use_none:nnnn}). +% +% Note the space after |#3| and after the following brace group. +% Those are ignored by \TeX{} when fetching the last argument for +% \cs{@@_generate_variant_loop:nNwN}, but can be used as a delimiter +% for \cs{@@_generate_variant_loop_end:nwwwNNnn}. % \begin{macrocode} \cs_new_protected:Npn \@@_generate_variant:Nnnw #1#2#3#4 , { - \if:w ? #4 + \if_meaning:w \scan_stop: #4 \exp_after:wN \use_none_delimit_by_q_recursion_stop:w \fi: - \exp_after:wN \@@_generate_variant:NNn - \exp_after:wN #1 - \cs:w - #2 : - \@@_generate_variant_loop:NwN - ? #3 - \q_mark #4 \@@_generate_variant_loop_end:w - \q_mark \@@_generate_variant_loop_error:wnNNnn - \q_stop - \cs_end: - {#4} + \use:x + { + \exp_not:N \@@_generate_variant:wwNN + \@@_generate_variant_loop:nNwN { } + #4 + \@@_generate_variant_loop_end:nwwwNNnn + \q_mark + #3 ~ + { ~ { } \fi: \@@_generate_variant_loop_long:wNNnn } ~ + { } + \q_stop + \exp_not:N #1 {#2} {#4} + } \@@_generate_variant:Nnnw #1 {#2} {#3} } % \end{macrocode} % \end{macro} % -% \begin{macro}[aux, rEXP]{\@@_generate_variant_loop:NwN} % \begin{macro}[aux, EXP] % { -% \@@_generate_variant_loop_end:w, -% \@@_generate_variant_loop_error:wnNNnn +% \@@_generate_variant_loop:nNwN, +% \@@_generate_variant_loop_same:w, +% \@@_generate_variant_loop_end:nwwwNNnn, +% \@@_generate_variant_loop_long:wNNnn, +% \@@_generate_variant_loop_invalid:NNwNNnn, % } -% Normally, the loop takes one character of the base signature and one -% from the variant form (after \cs{q_mark}), and leaves the latter one -% in the input stream. This stops when |#3| is the \texttt{loop_end} -% auxiliary, which, once left in the input stream, cleans up the rest -% of the csname construction. In case the base signature was in fact -% shorter, one reaches the point where |#1| is the \cs{q_mark} which -% is supposed to separate the base signature from the variant form. -% Then |#2| is delimited by the next \cs{q_mark}, and the -% \texttt{loop_error} auxiliary is taken as |#3|. This function -% fetches appropriate arguments for an error message, and places it -% outside the csname construction. -% -% Note in the definition of \cs{@@_generate_variant:Nnnw} the base -% signature is preceeded by a question mark. This shifts the base -% signature and variant form to compensate for the presence of the -% \texttt{loop_end} auxiliary at the end of the variant form. +% \begin{arguments} +% \item Last few (consecutive) letters common between the base +% and variant (in fact, \cs{@@_generate_variant_same:N} +% \meta{letter} for each letter). +% \item Next variant letter. +% \item Remainder of variant form. +% \item Next base letter. +% \end{arguments} +% The first argument is populated by +% \cs{@@_generate_variant_loop_same:w} when a variant letter and a +% base letter match. It is flushed into the input stream whenever the +% two letters are different: if the loop ends before, the argument is +% dropped, which means that trailing common letters are ignored. +% +% The case where the two letters are different is only allowed with a +% base letter of |N| or |n|. Otherwise, call +% \cs{@@_generate_variant_loop_invalid:NNwNNnn} to remove the end of +% the loop, get arguments at the end of the loop, and place an +% appropriate error message as a second argument of +% \cs{@@_generate_variant:wwNN}. If the letters are distinct and +% the base letter is indeed |n| or |N|, leave in the input stream +% whatever argument was collected, and the next variant letter |#2|, +% then loop by calling \cs{@@_generate_variant_loop:nNwN}. +% +% The loop can stop in three ways. +% \begin{itemize} +% \item If the end of the variant form is encountered first, |#2| is +% \cs{@@_generate_variant_loop_end:nwwwNNnn} (expanded by the +% conditional \cs{if:w}), which inserts some tokens to end the +% conditional; grabs the \meta{base name} as |#7|, the +% \meta{variant signature} |#8|, the \meta{next base letter} |#1| +% and the part |#3| of the base signature that wasn't read yet; +% and combines those into the \meta{new function} to be defined. +% \item If the end of the base form is encountered first, |#4| is +% |~{}\fi:| which ends the conditional (with an empty expansion), +% followed by \cs{@@_generate_variant_loop_long:wNNnn}, which +% places an error as the second argument of +% \cs{@@_generate_variant:wwNN}. +% \item The loop can be interrupted early if the requested expansion +% is unavailable, namely when the variant and base letters differ +% and the base is neither |n| nor |N|. Again, an error is placed +% as the second argument of \cs{@@_generate_variant:wwNN}. +% \end{itemize} +% Note that if the variant form has the same length as the base form, +% |#2| is as described in the first point, and |#4| as described in +% the second point above. The \cs{@@_generate_variant_loop_end:nwwwNNnn} +% breaking function takes the empty brace group in |#4| as its first +% argument: this empty brace group produces the correct signature for +% the full variant. % \begin{macrocode} -\cs_new:Npn \@@_generate_variant_loop:NwN #1 #2 \q_mark #3 - { #3 \@@_generate_variant_loop:NwN #2 \q_mark } -\cs_new:Npn \@@_generate_variant_loop_end:w #1#2 \q_mark #3 \q_stop {#2} -\cs_new:Npn \@@_generate_variant_loop_error:wnNNnn - #1 \q_stop \cs_end: #2 #3#4#5#6 +\cs_new:Npn \@@_generate_variant_loop:nNwN #1#2#3 \q_mark #4 + { + \if:w #2 #4 + \exp_after:wN \@@_generate_variant_loop_same:w + \else: + \if:w N #4 \else: + \if:w n #4 \else: + \@@_generate_variant_loop_invalid:NNwNNnn #4#2 + \fi: + \fi: + \fi: + #1 + \prg_do_nothing: + #2 + \@@_generate_variant_loop:nNwN { } #3 \q_mark + } +\cs_new:Npn \@@_generate_variant_loop_same:w + #1 \prg_do_nothing: #2#3#4 + { + #3 { #1 \@@_generate_variant_same:N #2 } + } +\cs_new:Npn \@@_generate_variant_loop_end:nwwwNNnn + #1#2 \q_mark #3 ~ #4 \q_stop #5#6#7#8 { - \cs_end: {#2} - \__msg_kernel_error:nnxx { kernel } { variant-too-long } - { \tl_to_str:n {#2} } { \token_to_str:N #4 } - #3 #4 {#5} {#6} + \scan_stop: \scan_stop: \fi: + \exp_not:N \q_mark + \exp_not:N \q_stop + \exp_not:N #6 + \exp_not:c { #7 : #8 #1 #3 } + } +\cs_new:Npn \@@_generate_variant_loop_long:wNNnn #1 \q_stop #2#3#4#5 + { + \exp_not:n + { + \q_mark + \__msg_kernel_error:nnxx { kernel } { variant-too-long } + {#5} { \token_to_str:N #3 } + \use_none:nnnn + \q_stop + #3 + #3 + } + } +\cs_new:Npn \@@_generate_variant_loop_invalid:NNwNNnn + #1#2 \fi: \fi: \fi: #3 \q_stop #4#5#6#7 + { + \fi: \fi: \fi: + \exp_not:n + { + \q_mark + \__msg_kernel_error:nnxxxx { kernel } { invalid-variant } + {#7} { \token_to_str:N #5 } {#1} {#2} + \use_none:nnnn + \q_stop + #5 + #5 + } } % \end{macrocode} % \end{macro} +% +% \begin{macro}[aux, rEXP]{\@@_generate_variant_same:N} +% When the base and variant letters are identical, don't do any +% expansion. For most argument types, we can use the |n|-type +% no-expansion, but the |N| and |p| types require a slightly different +% behaviour with respect to braces. +% \begin{macrocode} +\cs_new:Npn \@@_generate_variant_same:N #1 + { + \if:w N #1 + N + \else: + \if:w p #1 + p + \else: + n + \fi: + \fi: + } +% \end{macrocode} % \end{macro} % -% \begin{macro}[aux]{\@@_generate_variant:NNn} +% \begin{macro}[aux]{\@@_generate_variant:wwNN} % If the variant form has already been defined, log its existence. % Otherwise, make sure that the |\exp_args:N #3| form is defined, and % if it contains |x|, change \cs{@@_tmp:w} locally to % \cs{cs_new_protected_nopar:Npx}. Then define the variant by % combining the |\exp_args:N #3| variant and the base function. % \begin{macrocode} -\cs_new_protected:Npn \@@_generate_variant:NNn #1 #2 #3 +\cs_new_protected:Npn \@@_generate_variant:wwNN + #1 \q_mark #2 \q_stop #3#4 { - \cs_if_free:NTF #2 + #2 + \cs_if_free:NTF #4 { \group_begin: - \@@_generate_internal_variant:n {#3} - \@@_tmp:w #2 { \exp_not:c { exp_args:N #3 } \exp_not:N #1 } + \@@_generate_internal_variant:n {#1} + \@@_tmp:w #4 { \exp_not:c { exp_args:N #1 } \exp_not:N #3 } \group_end: } { \iow_log:x { - Variant~\token_to_str:N #2~% + Variant~\token_to_str:N #4~% already~defined;~ not~ changing~ it~on~line~% \tex_the:D \tex_inputlineno:D } |