From 9e813f489cbf83fd824dc82a10271b0cd5529451 Mon Sep 17 00:00:00 2001 From: Bruno Le Floch Date: Mon, 27 Nov 2017 13:28:18 -0500 Subject: [PATCH] Make \cs_generate_variant:Nn stricter (forbid n to c etc) (fixes #418) This forbids variants that try to change N to n,o,f,V,v,x or n to N,c. It also makes it an error to try and use unknown letters (more precisely, that's only an error if the letter changes). --- l3kernel/l3expan.dtx | 70 +++++++++------- l3kernel/testfiles/m3expan001.lvt | 25 +++++- l3kernel/testfiles/m3expan001.tlg | 129 +++++++++++++++++++++++++++++- 3 files changed, 192 insertions(+), 32 deletions(-) diff --git a/l3kernel/l3expan.dtx b/l3kernel/l3expan.dtx index d4a1c0c4ea..2d98707ec5 100644 --- a/l3kernel/l3expan.dtx +++ b/l3kernel/l3expan.dtx @@ -1611,32 +1611,34 @@ % % 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|, +% \item In \cs{cs_generate_variant:Nn} |\foo:nnTF| |{xxTF}|, we must 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 +% function |\foo:ox| must 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 +% \item Lastly, \cs{cs_generate_variant:Nn} |\foo:on| |{xn}| must % trigger an error, because we do not have a means to replace % |o|-expansion by |x|-expansion. +% More generally, we can only convert |N| to |c|, or convert |n| +% to |V|, |v|, |o|, |f|, |x|. % \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. +% except for |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:nnn}). +% \cs{@@_generate_variant:wwNN} (defined later) 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 and some clean-up code. % % Note the space after |#3| and after the following brace group. % Those are ignored by \TeX{} when fetching the last argument for @@ -1666,7 +1668,7 @@ % \end{macrocode} % \end{macro} % -% \begin{macro}[aux, EXP] +% \begin{macro}[aux, rEXP] % { % \@@_generate_variant_loop:nNwN, % \@@_generate_variant_loop_same:w, @@ -1675,8 +1677,8 @@ % \@@_generate_variant_loop_invalid:NNwNNnn, % } % \begin{arguments} -% \item Last few (consecutive) letters common between the base -% and variant (in fact, \cs{@@_generate_variant_same:N} +% \item Last few consecutive letters common between the base and +% variant (more precisely, \cs{@@_generate_variant_same:N} % \meta{letter} for each letter). % \item Next variant letter. % \item Remainder of variant form. @@ -1688,15 +1690,16 @@ % 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 +% The case where the two letters are different is only allowed if the +% base is |N| and the variant is |c|, or when the base is |n| and the +% variant is |o|, |V|, |v|, |f| or |x|. 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}. +% \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 |#1| 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} @@ -1714,8 +1717,9 @@ % \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}. +% and the base is not the right one (|n| or |N| to support the +% variant). In that case too 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 @@ -1729,10 +1733,22 @@ \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 + \if:w #4 + \if:w c #2 N \else: + \if:w o #2 n \else: + \if:w V #2 n \else: + \if:w v #2 n \else: + \if:w f #2 n \else: + \if:w x #2 n \else: + \scan_stop: + \fi: + \fi: + \fi: + \fi: + \fi: \fi: + \else: + \@@_generate_variant_loop_invalid:NNwNNnn #4#2 \fi: \fi: #1 @@ -1768,9 +1784,9 @@ } } \cs_new:Npn \@@_generate_variant_loop_invalid:NNwNNnn - #1#2 \fi: \fi: \fi: #3 \q_stop #4#5#6#7 + #1#2 \fi: \fi: #3 \q_stop #4#5#6#7 { - \fi: \fi: \fi: + \fi: \fi: \exp_not:n { \q_mark diff --git a/l3kernel/testfiles/m3expan001.lvt b/l3kernel/testfiles/m3expan001.lvt index c302826f63..0ab3023937 100644 --- a/l3kernel/testfiles/m3expan001.lvt +++ b/l3kernel/testfiles/m3expan001.lvt @@ -1,6 +1,6 @@ % % Copyright (C) 2008 Frank Mittelbach -% 2009-2012 The LaTeX3 Project +% 2009-2012,2017 The LaTeX3 Project % % Input the regression test macros for LaTeX @@ -51,7 +51,6 @@ \foo:oo { \c_aa_tl } { \c_bb_tl } \foo:VV \c_aa_tl \c_bb_tl \foo:xx { \c_aa_tl } { \c_bb_tl } - \foo:cc { a } { b } \foo:nx { a } { \c_b_tl } \foo:fx { \c_aaa_tl } { \c_bbb_tl } \foo:xf { \c_aaa_tl } { \c_bbb_tl } @@ -107,4 +106,26 @@ \iow_term:x { \cs_meaning:N \foo: } } +\cs_new:Npn \foobar:Nn { } +\TEST { Incorrect~letters } + { + \cs_generate_variant:Nn \foobar:Nn { pn , :Nn } + \TYPE + { + \cs_meaning:N \foobar:pn , + \cs_meaning:N \foobar::Nn , + } + } + +\TEST { Issue~418:~changing~N/n~to~incompatible~variants } + { + \cs_generate_variant:Nn \foobar:Nn { on , Nc , n } + \TYPE + { + \cs_meaning:N \foobar:on , + \cs_meaning:N \foobar:Nc , + \cs_meaning:N \foobar:nn , + } + } + \END diff --git a/l3kernel/testfiles/m3expan001.tlg b/l3kernel/testfiles/m3expan001.tlg index 3d548fbbbb..89c2aa638c 100644 --- a/l3kernel/testfiles/m3expan001.tlg +++ b/l3kernel/testfiles/m3expan001.tlg @@ -11,7 +11,25 @@ Defining \foo:nn on line ... Defining \foo:oo on line ... Defining \foo:VV on line ... Defining \foo:xx on line ... -Defining \foo:cc on line ... +Variant \foo:nn already defined; not changing it on line ... +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +! +! LaTeX error: "kernel/invalid-variant" +! +! Variant form 'cc' invalid for base form '\foo:nn'. +! +! See the LaTeX3 documentation for further information. +! +! For immediate help type H . +!............................................... +l. ... { oo , VV , xx , cc , nx , fx , xf } +|''''''''''''''''''''''''''''''''''''''''''''''' +| This is a coding error. +| +| LaTeX has been asked to create a variant of the function '\foo:nn' with a +| signature starting with 'cc', but cannot change an argument from type 'n' to +| type 'c'. +|............................................... Defining \foo:nx on line ... Defining \exp_args:Nfx on line ... Defining \foo:fx on line ... @@ -26,7 +44,7 @@ TEST 1: protection \long macro:->\exp_args:Noo \foo:nn \long macro:->\exp_args:NVV \foo:nn \protected\long macro:->\exp_args:Nxx \foo:nn -\long macro:->\exp_args:Ncc \foo:nn +undefined \protected\long macro:->\exp_args:Nnx \foo:nn \protected\long macro:->\exp_args:Nfx \foo:nn \protected\long macro:->\exp_args:Nxf \foo:nn @@ -41,7 +59,6 @@ TEST 2: expansion (\c_a_tl )(\c_b_tl ) (\c_a_tl )(\c_b_tl ) (A)(B) -(\a )(\b ) (a)(B) (A\c_a_tl )(BB) (AA)(B\c_b_tl ) @@ -183,3 +200,109 @@ l. ... } |............................................... undefined ============================================================ +Defining \foobar:Nn on line ... +============================================================ +TEST 9: Incorrect letters +============================================================ +Variant \foobar:Nn already defined; not changing it on line ... +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +! +! LaTeX error: "kernel/invalid-variant" +! +! Variant form 'pn' invalid for base form '\foobar:Nn'. +! +! See the LaTeX3 documentation for further information. +! +! For immediate help type H . +!............................................... +l. ... } +|''''''''''''''''''''''''''''''''''''''''''''''' +| This is a coding error. +| +| LaTeX has been asked to create a variant of the function '\foobar:Nn' with a +| signature starting with 'pn', but cannot change an argument from type 'N' to +| type 'p'. +|............................................... +Variant \foobar:Nn already defined; not changing it on line ... +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +! +! LaTeX error: "kernel/invalid-variant" +! +! Variant form ':Nn' invalid for base form '\foobar:Nn'. +! +! See the LaTeX3 documentation for further information. +! +! For immediate help type H . +!............................................... +l. ... } +|''''''''''''''''''''''''''''''''''''''''''''''' +| This is a coding error. +| +| LaTeX has been asked to create a variant of the function '\foobar:Nn' with a +| signature starting with ':Nn', but cannot change an argument from type 'N' +| to type ':'. +|............................................... +undefined,undefined, +============================================================ +============================================================ +TEST 10: Issue 418: changing N/n to incompatible variants +============================================================ +Variant \foobar:Nn already defined; not changing it on line ... +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +! +! LaTeX error: "kernel/invalid-variant" +! +! Variant form 'on' invalid for base form '\foobar:Nn'. +! +! See the LaTeX3 documentation for further information. +! +! For immediate help type H . +!............................................... +l. ... } +|''''''''''''''''''''''''''''''''''''''''''''''' +| This is a coding error. +| +| LaTeX has been asked to create a variant of the function '\foobar:Nn' with a +| signature starting with 'on', but cannot change an argument from type 'N' to +| type 'o'. +|............................................... +Variant \foobar:Nn already defined; not changing it on line ... +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +! +! LaTeX error: "kernel/invalid-variant" +! +! Variant form 'Nc' invalid for base form '\foobar:Nn'. +! +! See the LaTeX3 documentation for further information. +! +! For immediate help type H . +!............................................... +l. ... } +|''''''''''''''''''''''''''''''''''''''''''''''' +| This is a coding error. +| +| LaTeX has been asked to create a variant of the function '\foobar:Nn' with a +| signature starting with 'Nc', but cannot change an argument from type 'n' to +| type 'c'. +|............................................... +Variant \foobar:Nn already defined; not changing it on line ... +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +! +! LaTeX error: "kernel/invalid-variant" +! +! Variant form 'n' invalid for base form '\foobar:Nn'. +! +! See the LaTeX3 documentation for further information. +! +! For immediate help type H . +!............................................... +l. ... } +|''''''''''''''''''''''''''''''''''''''''''''''' +| This is a coding error. +| +| LaTeX has been asked to create a variant of the function '\foobar:Nn' with a +| signature starting with 'n', but cannot change an argument from type 'N' to +| type 'n'. +|............................................... +undefined,undefined,undefined, +============================================================