[latex3-commits] [git/LaTeX3-latex3-latex3] main: Validate internal structure when showing complex datatypes (fixes #884) (d4253c7db)
Bruno Le Floch
blflatex at gmail.com
Thu Apr 29 22:08:23 CEST 2021
Repository : https://github.com/latex3/latex3
On branch : main
Link : https://github.com/latex3/latex3/commit/d4253c7dbdb308e01aecf3a91e111f45fee62a62
>---------------------------------------------------------------
commit d4253c7dbdb308e01aecf3a91e111f45fee62a62
Author: Bruno Le Floch <blflatex at gmail.com>
Date: Thu Apr 29 01:07:58 2021 +0200
Validate internal structure when showing complex datatypes (fixes #884)
I opted not to fix the fact that \int_show:N, \dim_show:N etc are
equivalent (hence accept the wrong type of data) because this is messy
to check.
>---------------------------------------------------------------
d4253c7dbdb308e01aecf3a91e111f45fee62a62
l3kernel/CHANGELOG.md | 1 +
l3kernel/l3clist.dtx | 27 ++++--
l3kernel/l3fp-assign.dtx | 18 +++-
l3kernel/l3fp.dtx | 4 +-
l3kernel/l3kernel-functions.dtx | 13 +++
l3kernel/l3msg.dtx | 25 +++++
l3kernel/l3prg.dtx | 16 +++-
l3kernel/l3prop.dtx | 25 ++++-
l3kernel/l3regex.dtx | 159 +++++++++++++++++++++++++++++++-
l3kernel/l3seq.dtx | 21 ++++-
l3kernel/l3str.dtx | 16 +++-
l3kernel/l3tl.dtx | 50 +++++++++-
l3kernel/testfiles/m3show003.luatex.tlg | 137 +++++++++++++++++++++++++++
l3kernel/testfiles/m3show003.lvt | 31 +++++++
l3kernel/testfiles/m3show003.tlg | 137 +++++++++++++++++++++++++++
15 files changed, 646 insertions(+), 34 deletions(-)
diff --git a/l3kernel/CHANGELOG.md b/l3kernel/CHANGELOG.md
index c628e5746..388fe7d2c 100644
--- a/l3kernel/CHANGELOG.md
+++ b/l3kernel/CHANGELOG.md
@@ -25,6 +25,7 @@ this project uses date-based 'snapshot' version identifiers.
- Show printable characters explicitly in `\regex_show:n`
- Regex replacement now errors when using a submatch (`\1` etc) for which
the regex has too few groups
+- Showing complex datatypes now validates their internal structure (issue #884)
### Fixed
- Evalutate integer constants only once (issue #861)
diff --git a/l3kernel/l3clist.dtx b/l3kernel/l3clist.dtx
index 6299dd115..36ec3bacc 100644
--- a/l3kernel/l3clist.dtx
+++ b/l3kernel/l3clist.dtx
@@ -703,7 +703,7 @@
%
% \section{Viewing comma lists}
%
-% \begin{function}[updated = 2015-08-03]{\clist_show:N, \clist_show:c}
+% \begin{function}[updated = 2021-04-29]{\clist_show:N, \clist_show:c}
% \begin{syntax}
% \cs{clist_show:N} \meta{comma list}
% \end{syntax}
@@ -717,7 +717,7 @@
% Displays the entries in the comma list in the terminal.
% \end{function}
%
-% \begin{function}[added = 2014-08-22, updated = 2015-08-03]{\clist_log:N, \clist_log:c}
+% \begin{function}[added = 2014-08-22, updated = 2021-04-29]{\clist_log:N, \clist_log:c}
% \begin{syntax}
% \cs{clist_log:N} \meta{comma list}
% \end{syntax}
@@ -2009,8 +2009,9 @@
% \subsection{Viewing comma lists}
%
% \begin{macro}{\clist_show:N, \clist_show:c, \clist_log:N, \clist_log:c, \@@_show:NN}
-% Apply the general \cs{__kernel_chk_defined:NT} and
-% \cs{msg_show:nnnnnn}.
+% Apply the general \cs{__kernel_chk_tl_type:NnnT} with \cs{exp_not:o}
+% |#2| serving as a dummy code to prevent a check performed by this
+% auxiliary.
% \begin{macrocode}
\cs_new_protected:Npn \clist_show:N { \@@_show:NN \__kernel_msg_show:nnxxxx }
\cs_generate_variant:Nn \clist_show:N { c }
@@ -2018,12 +2019,20 @@
\cs_generate_variant:Nn \clist_log:N { c }
\cs_new_protected:Npn \@@_show:NN #1#2
{
- \__kernel_chk_defined:NT #2
+ \__kernel_chk_tl_type:NnnT #2 { clist } { \exp_not:o #2 }
{
- #1 { clist } { show }
- { \token_to_str:N #2 }
- { \clist_map_function:NN #2 \msg_show_item:n }
- { } { }
+ \int_compare:nNnTF { \clist_count:N #2 }
+ = { \exp_args:No \clist_count:n #2 }
+ {
+ #1 { clist } { show }
+ { \token_to_str:N #2 }
+ { \clist_map_function:NN #2 \msg_show_item:n }
+ { } { }
+ }
+ {
+ \__kernel_msg_error:nnxx { kernel } { non-clist }
+ { \token_to_str:N #2 } { \tl_to_str:N #2 }
+ }
}
}
% \end{macrocode}
diff --git a/l3kernel/l3fp-assign.dtx b/l3kernel/l3fp-assign.dtx
index 30d9cc9ad..7da8b1dfa 100644
--- a/l3kernel/l3fp-assign.dtx
+++ b/l3kernel/l3fp-assign.dtx
@@ -165,6 +165,7 @@
% \subsection{Showing values}
%
% \begin{macro}{\fp_show:N, \fp_show:c, \fp_log:N, \fp_log:c, \@@_show:NN}
+% \begin{macro}[EXP]{\@@_show_validate:w}
% This shows the result of computing its argument by
% passing the right data to \cs{tl_show:n} or \cs{tl_log:n}.
% \begin{macrocode}
@@ -174,11 +175,26 @@
\cs_generate_variant:Nn \fp_log:N { c }
\cs_new_protected:Npn \@@_show:NN #1#2
{
- \__kernel_chk_defined:NT #2
+ \__kernel_chk_tl_type:NnnT #2 { fp }
+ {
+ \str_if_eq:eeTF { \tl_head:N #2 } { \s_@@_tuple } { \exp_not:o #2 }
+ {
+ \exp_after:wN \@@_show_validate:w #2
+ \s_@@ \@@_chk:w ??? ; \s_@@_stop
+ }
+ }
{ \exp_args:Nx #1 { \token_to_str:N #2 = \fp_to_tl:N #2 } }
}
+\cs_new:Npn \@@_show_validate:w
+ #1 \s_@@ \@@_chk:w #2#3#4#5 ; #6 \s_@@_stop
+ {
+ \token_if_eq_meaning:NNTF #2 1
+ { \s_@@ \@@_chk:w #2 #3 {#4} #5 ; }
+ { \s_@@ \@@_chk:w #2 #3 #4 #5 ; }
+ }
% \end{macrocode}
% \end{macro}
+% \end{macro}
%
% \begin{macro}{\fp_show:n, \fp_log:n}
% Use general tools.
diff --git a/l3kernel/l3fp.dtx b/l3kernel/l3fp.dtx
index 4be73958d..0deb4e25f 100644
--- a/l3kernel/l3fp.dtx
+++ b/l3kernel/l3fp.dtx
@@ -769,7 +769,7 @@
%
% \section{Viewing floating points}
%
-% \begin{function}[added = 2012-05-08, updated = 2015-08-07,
+% \begin{function}[added = 2012-05-08, updated = 2021-04-29,
% tested = m3fp002]{\fp_show:N, \fp_show:c, \fp_show:n}
% \begin{syntax}
% \cs{fp_show:N} \meta{fp~var}
@@ -779,7 +779,7 @@
% result in the terminal.
% \end{function}
%
-% \begin{function}[added = 2014-08-22, updated = 2015-08-07]
+% \begin{function}[added = 2014-08-22, updated = 2021-04-29]
% {\fp_log:N, \fp_log:c, \fp_log:n}
% \begin{syntax}
% \cs{fp_log:N} \meta{fp~var}
diff --git a/l3kernel/l3kernel-functions.dtx b/l3kernel/l3kernel-functions.dtx
index 09a3f3022..32ae13ce9 100644
--- a/l3kernel/l3kernel-functions.dtx
+++ b/l3kernel/l3kernel-functions.dtx
@@ -92,6 +92,19 @@
% purposes.
% \end{function}
%
+% \begin{function}{\__kernel_chk_tl_type:NnnT}
+% \begin{syntax}
+% \cs{__kernel_chk_tl_type:NnnT} \meta{control sequence} \Arg{specific type} \\
+% \ \ \Arg{reconstruction} \Arg{true code}
+% \end{syntax}
+% Helper to test that the \meta{control sequence} is a variable of the
+% given \meta{specific type} of token list. Produces suitable error
+% messages if the \meta{control sequence} does not exist, or if it is
+% not a token list variable at all, or if the \meta{control sequence}
+% differs from the result of |x|-expanding \meta{reconstruction}. If
+% all of these tests succeed then the \meta{true code} is run.
+% \end{function}
+%
% \begin{function}{\__kernel_cs_parm_from_arg_count:nnF}
% \begin{syntax}
% \cs{__kernel_cs_parm_from_arg_count:nnF} \Arg{follow-on} \Arg{args} \Arg{false code}
diff --git a/l3kernel/l3msg.dtx b/l3kernel/l3msg.dtx
index 258af244b..3f7fe9af1 100644
--- a/l3kernel/l3msg.dtx
+++ b/l3kernel/l3msg.dtx
@@ -2015,6 +2015,31 @@
LaTeX~has~been~asked~to~show~a~variable~#1,~but~this~has~not~
been~defined~yet.
}
+\__kernel_msg_new:nnnn { kernel } { bad-type }
+ { Variable~'#1'~is~not~a~valid~#3. }
+ {
+ \c_@@_coding_error_text_tl
+ The~variable~'#1'~with~\tl_if_empty:nTF {#4} {meaning} {value}\\\\
+ \iow_indent:n {#2}\\\\
+ should~be~a~#3~variable,~but~
+ \tl_if_empty:nTF {#4}
+ { it~is~not \str_if_eq:nnF {#3} { bool } { ~a~short~macro } . }
+ {
+ it~does~not~have~the~correct~
+ \str_if_eq:nnTF {#2} {#4}
+ { category~codes. }
+ { internal~structure:\\\\\iow_indent:n {#4} }
+ }
+ }
+\__kernel_msg_new:nnnn { kernel } { non-clist }
+ { Variable~'#1'~is~not~a~valid~clist. }
+ {
+ \c_@@_coding_error_text_tl
+ The~variable~'#1'~with~value\\\\
+ \iow_indent:n {#2}\\\\
+ should~be~a~clist~variable,~but~it~includes~empty~or~blank~items~
+ without~braces.
+ }
% \end{macrocode}
%
% Some errors are only needed in package mode if debugging is enabled by
diff --git a/l3kernel/l3prg.dtx b/l3kernel/l3prg.dtx
index 0b6eecbbc..1f9f08679 100644
--- a/l3kernel/l3prg.dtx
+++ b/l3kernel/l3prg.dtx
@@ -313,7 +313,7 @@
% based on this result.
% \end{function}
%
-% \begin{function}[added = 2012-02-09, updated = 2015-08-01]{\bool_show:N, \bool_show:c}
+% \begin{function}[added = 2012-02-09, updated = 2021-04-29]{\bool_show:N, \bool_show:c}
% \begin{syntax}
% \cs{bool_show:N} \meta{boolean}
% \end{syntax}
@@ -328,7 +328,7 @@
% terminal.
% \end{function}
%
-% \begin{function}[added = 2014-08-22, updated = 2015-08-03]{\bool_log:N, \bool_log:c}
+% \begin{function}[added = 2014-08-22, updated = 2021-04-29]{\bool_log:N, \bool_log:c}
% \begin{syntax}
% \cs{bool_log:N} \meta{boolean}
% \end{syntax}
@@ -987,7 +987,17 @@
\cs_new_protected:Npn \@@_show:NN #1#2
{
\__kernel_chk_defined:NT #2
- { \exp_args:Nx #1 { \token_to_str:N #2 = \@@_to_str:n {#2} } }
+ {
+ \token_case_meaning:NnF #2
+ {
+ \c_true_bool { \exp_args:Nx #1 { \token_to_str:N #2 = true } }
+ \c_false_bool { \exp_args:Nx #1 { \token_to_str:N #2 = false } }
+ }
+ {
+ \__kernel_msg_error:nnxxx { kernel } { bad-type }
+ { \token_to_str:N #2 } { \token_to_meaning:N #2 } { bool }
+ }
+ }
}
% \end{macrocode}
% \end{macro}
diff --git a/l3kernel/l3prop.dtx b/l3kernel/l3prop.dtx
index f55de55fa..7cc144073 100644
--- a/l3kernel/l3prop.dtx
+++ b/l3kernel/l3prop.dtx
@@ -480,14 +480,14 @@
%
% \section{Viewing property lists}
%
-% \begin{function}[updated = 2015-08-01]{\prop_show:N, \prop_show:c}
+% \begin{function}[updated = 2021-04-29]{\prop_show:N, \prop_show:c}
% \begin{syntax}
% \cs{prop_show:N} \meta{property list}
% \end{syntax}
% Displays the entries in the \meta{property list} in the terminal.
% \end{function}
%
-% \begin{function}[added = 2014-08-12, updated = 2015-08-01]{\prop_log:N, \prop_log:c}
+% \begin{function}[added = 2014-08-12, updated = 2021-04-29]{\prop_log:N, \prop_log:c}
% \begin{syntax}
% \cs{prop_log:N} \meta{property list}
% \end{syntax}
@@ -1346,8 +1346,10 @@
%
% \begin{macro}[tested = m3show001]
% {\prop_show:N, \prop_show:c, \prop_log:N, \prop_log:c}
-% Apply the general \cs{__kernel_chk_defined:NT} and
-% \cs{msg_show:nnnnnn}. Contrarily to sequences and comma lists,
+% \begin{macro}{\@@_show:NN}
+% \begin{macro}[rEXP]{\@@_show_validate:w}
+% Apply the general \cs{__kernel_chk_tl_type:NnnT}.
+% Contrarily to sequences and comma lists,
% we use \cs{msg_show_item:nn} to format both the key and the value
% for each pair.
% \begin{macrocode}
@@ -1357,7 +1359,12 @@
\cs_generate_variant:Nn \prop_log:N { c }
\cs_new_protected:Npn \@@_show:NN #1#2
{
- \__kernel_chk_defined:NT #2
+ \__kernel_chk_tl_type:NnnT #2 { prop }
+ {
+ \s_@@
+ \exp_after:wN \use_i:nn \exp_after:wN \@@_show_validate:w #2
+ \@@_pair:wn \q_recursion_tail \s_@@ { } \q_recursion_stop
+ }
{
#1 { prop } { show }
{ \token_to_str:N #2 }
@@ -1365,8 +1372,16 @@
{ } { }
}
}
+\cs_new:Npn \@@_show_validate:w #1 \@@_pair:wn #2 \s_@@ #3
+ {
+ \quark_if_recursion_tail_stop:n {#2}
+ \exp_not:N \@@_pair:wn \tl_to_str:n {#2} \s_@@ \exp_not:n { {#3} }
+ \@@_show_validate:w
+ }
% \end{macrocode}
% \end{macro}
+% \end{macro}
+% \end{macro}
%
% \begin{macrocode}
%</package>
diff --git a/l3kernel/l3regex.dtx b/l3kernel/l3regex.dtx
index 8cf3071a0..30d4abd78 100644
--- a/l3kernel/l3regex.dtx
+++ b/l3kernel/l3regex.dtx
@@ -544,7 +544,7 @@
% which never change.
% \end{function}
%
-% \begin{function}[added = 2021-04-26]
+% \begin{function}[added = 2021-04-26, updated = 2021-04-29]
% {\regex_show:N, \regex_show:n, \regex_log:N, \regex_log:n}
% \begin{syntax}
% \cs{regex_show:n} \Arg{regex}
@@ -3626,6 +3626,160 @@
%
% \subsubsection{Showing regexes}
%
+% \begin{macro}[rEXP]
+% {
+% \@@_clean_bool:n, \@@_clean_int:n, \@@_clean_int_aux:N,
+% \@@_clean_regex:n, \@@_clean_regex_loop:w, \@@_clean_branch:n,
+% \@@_clean_branch_loop:n, \@@_clean_assertion:Nn,
+% \@@_clean_class:NnnnN, \@@_clean_group:nnnN, \@@_clean_class:n,
+% \@@_clean_class_loop:nnn, \@@_clean_exact_cs:n,
+% \@@_clean_exact_cs:w
+% }
+% Before showing a regex we check that it is \enquote{clean} in the
+% sense that it has the correct internal structure. We do this (in
+% the implementation of \cs{regex_show:N} and \cs{regex_log:N}) by
+% comparing it with a cleaned-up version of the same regex. Along the
+% way we also need similar functions for other types: all
+% \cs[no-index]{@@_clean_\meta{type}:n} functions produce valid
+% \meta{type} tokens (bool, explicit integer, etc.\@) from arbitrary
+% input, and the output coincides with the input if that was valid.
+% \begin{macrocode}
+\cs_new:Npn \@@_clean_bool:n #1
+ {
+ \tl_if_single:nTF {#1}
+ { \bool_if:NTF #1 \c_true_bool \c_false_bool }
+ { \c_true_bool }
+ }
+\cs_new:Npn \@@_clean_int:n #1
+ {
+ \tl_if_head_eq_meaning:nNTF {#1} -
+ { - \exp_args:No \@@_clean_int:n { \use_none:n #1 } }
+ { \int_eval:n { 0 \str_map_function:nN {#1} \@@_clean_int_aux:N } }
+ }
+\cs_new:Npn \@@_clean_int_aux:N #1
+ {
+ \if_int_compare:w 1 < 1 #1 ~
+ #1
+ \else:
+ \exp_after:wN \str_map_break:
+ \fi:
+ }
+\cs_new:Npn \@@_clean_regex:n #1
+ {
+ \@@_clean_regex_loop:w #1
+ \@@_branch:n { \q_recursion_tail } \q_recursion_stop
+ }
+\cs_new:Npn \@@_clean_regex_loop:w #1 \@@_branch:n #2
+ {
+ \quark_if_recursion_tail_stop:n {#2}
+ \@@_branch:n { \@@_clean_branch:n {#2} }
+ \@@_clean_regex_loop:w
+ }
+\cs_new:Npn \@@_clean_branch:n #1
+ {
+ \@@_clean_branch_loop:n #1
+ ? ? ? ? ? ? \prg_break_point:
+ }
+\cs_new:Npn \@@_clean_branch_loop:n #1
+ {
+ \tl_if_single:nF {#1} { \prg_break: }
+ \token_case_meaning:NnF #1
+ {
+ \@@_command_K: { #1 \@@_clean_branch_loop:n }
+ \@@_assertion:Nn { #1 \@@_clean_assertion:Nn }
+ \@@_class:NnnnN { #1 \@@_clean_class:NnnnN }
+ \@@_group:nnnN { #1 \@@_clean_group:nnnN }
+ \@@_group_no_capture:nnnN { #1 \@@_clean_group:nnnN }
+ \@@_group_resetting:nnnN { #1 \@@_clean_group:nnnN }
+ }
+ { \prg_break: }
+ }
+\cs_new:Npn \@@_clean_assertion:Nn #1#2
+ {
+ \@@_clean_bool:n {#1}
+ \tl_if_single:nF {#2} { { \@@_A_test: } \prg_break: }
+ \token_case_meaning:NnTF #2
+ {
+ \@@_A_test: { }
+ \@@_G_test: { }
+ \@@_Z_test: { }
+ \@@_b_test: { }
+ }
+ { {#2} }
+ { { \@@_A_test: } \prg_break: }
+ \@@_clean_branch_loop:n
+ }
+\cs_new:Npn \@@_clean_class:NnnnN #1#2#3#4#5
+ {
+ \@@_clean_bool:n {#1}
+ { \@@_clean_class:n {#2} }
+ { \int_max:nn { 0 } { \@@_clean_int:n {#3} } }
+ { \int_max:nn { -1 } { \@@_clean_int:n {#4} } }
+ \@@_clean_bool:n {#5}
+ \@@_clean_branch_loop:n
+ }
+\cs_new:Npn \@@_clean_group:nnnN #1#2#3#4
+ {
+ { \@@_clean_regex:n {#1} }
+ { \int_max:nn { 0 } { \@@_clean_int:n {#2} } }
+ { \int_max:nn { -1 } { \@@_clean_int:n {#3} } }
+ \@@_clean_bool:n {#4}
+ \@@_clean_branch_loop:n
+ }
+\cs_new:Npn \@@_clean_class:n #1
+ { \@@_clean_class_loop:nnn #1 ????? \prg_break_point: }
+\cs_new:Npn \@@_clean_class_loop:nnn #1#2#3
+ {
+ \tl_if_single:nF {#1} { \prg_break: }
+ \token_case_meaning:NnTF #1
+ {
+ \@@_item_cs:n { #1 { \@@_clean_regex:n {#2} } }
+ \@@_item_exact_cs:n { #1 { \@@_clean_exact_cs:n {#2} } }
+ \@@_item_caseful_equal:n { #1 { \@@_clean_int:n {#2} } }
+ \@@_item_caseless_equal:n { #1 { \@@_clean_int:n {#2} } }
+ \@@_item_reverse:n { #1 { \@@_clean_class:n {#2} } }
+ }
+ { \@@_clean_class_loop:nnn {#3} }
+ {
+ \token_case_meaning:NnTF #1
+ {
+ \@@_item_caseful_range:nn { }
+ \@@_item_caseless_range:nn { }
+ \@@_item_exact:nn { }
+ }
+ { #1 { \@@_clean_int:n {#2} } { \@@_clean_int:n {#3} } }
+ {
+ \token_case_meaning:NnTF #1
+ {
+ \@@_item_catcode:nT { }
+ \@@_item_catcode_reverse:nT { }
+ }
+ {
+ #1 { \@@_clean_int:n {#2} } { \@@_clean_class:n {#3} }
+ \@@_clean_class_loop:nnn
+ }
+ { \prg_break: }
+ }
+ }
+ }
+\cs_new:Npn \@@_clean_exact_cs:n #1
+ {
+ \exp_last_unbraced:Nf \use_none:n
+ {
+ \@@_clean_exact_cs:w #1
+ \scan_stop: \q_recursion_tail \scan_stop:
+ \q_recursion_stop
+ }
+ }
+\cs_new:Npn \@@_clean_exact_cs:w #1 \scan_stop:
+ {
+ \quark_if_recursion_tail_stop:n {#1}
+ \scan_stop: \tl_to_str:n {#1}
+ \@@_clean_exact_cs:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
% \begin{macro}{\@@_show:N}
% Within a group and within \cs{tl_build_begin:N} \ldots{} \cs{tl_build_end:N} we
% redefine all the function that can appear in a compiled regex, then
@@ -6045,7 +6199,8 @@
\cs_new_protected:Npn \regex_log:N { \@@_show:NN \__kernel_msg_log:nnxxxx }
\cs_new_protected:Npn \@@_show:NN #1#2
{
- \__kernel_chk_defined:NT #2
+ \__kernel_chk_tl_type:NnnT #2 { regex }
+ { \exp_args:No \@@_clean_regex:n {#2} }
{
\@@_show:N #2
#1 { regex } { show }
diff --git a/l3kernel/l3seq.dtx b/l3kernel/l3seq.dtx
index a0a806589..3c03b042b 100644
--- a/l3kernel/l3seq.dtx
+++ b/l3kernel/l3seq.dtx
@@ -988,14 +988,14 @@
%
% \section{Viewing sequences}
%
-% \begin{function}[updated = 2015-08-01]{\seq_show:N, \seq_show:c}
+% \begin{function}[updated = 2021-04-29]{\seq_show:N, \seq_show:c}
% \begin{syntax}
% \cs{seq_show:N} \meta{sequence}
% \end{syntax}
% Displays the entries in the \meta{sequence} in the terminal.
% \end{function}
%
-% \begin{function}[added = 2014-08-12, updated = 2015-08-01]{\seq_log:N, \seq_log:c}
+% \begin{function}[added = 2014-08-12, updated = 2021-04-29]{\seq_log:N, \seq_log:c}
% \begin{syntax}
% \cs{seq_log:N} \meta{sequence}
% \end{syntax}
@@ -2359,8 +2359,9 @@
% \subsection{Viewing sequences}
%
% \begin{macro}{\seq_show:N, \seq_show:c, \seq_log:N, \seq_log:c, \@@_show:NN}
+% \begin{macro}[rEXP]{\@@_show_validate:nn}
% \UnitTested
-% Apply the general \cs{msg_show:nnnnnn}.
+% Apply the general \cs{__kernel_chk_tl_type:NnnT}.
% \begin{macrocode}
\cs_new_protected:Npn \seq_show:N { \@@_show:NN \__kernel_msg_show:nnxxxx }
\cs_generate_variant:Nn \seq_show:N { c }
@@ -2368,7 +2369,12 @@
\cs_generate_variant:Nn \seq_log:N { c }
\cs_new_protected:Npn \@@_show:NN #1#2
{
- \__kernel_chk_defined:NT #2
+ \__kernel_chk_tl_type:NnnT #2 { seq }
+ {
+ \s_@@
+ \exp_after:wN \use_i:nn \exp_after:wN \@@_show_validate:nn #2
+ \q_recursion_tail \q_recursion_tail \q_recursion_stop
+ }
{
#1 { seq } { show }
{ \token_to_str:N #2 }
@@ -2376,8 +2382,15 @@
{ } { }
}
}
+\cs_new:Npn \@@_show_validate:nn #1#2
+ {
+ \quark_if_recursion_tail_stop:n {#2}
+ \@@_wrap_item:n {#2}
+ \@@_show_validate:nn
+ }
% \end{macrocode}
% \end{macro}
+% \end{macro}
%
% \subsection{Scratch sequences}
%
diff --git a/l3kernel/l3str.dtx b/l3kernel/l3str.dtx
index 953d36646..8b3dfccb5 100644
--- a/l3kernel/l3str.dtx
+++ b/l3kernel/l3str.dtx
@@ -797,7 +797,7 @@
%
% \section{Viewing strings}
%
-% \begin{function}[added = 2015-09-18]
+% \begin{function}[added = 2015-09-18, updated = 2021-04-29]
% {\str_show:N, \str_show:c, \str_show:n}
% \begin{syntax}
% \cs{str_show:N} \meta{str~var}
@@ -805,7 +805,7 @@
% Displays the content of the \meta{str~var} on the terminal.
% \end{function}
%
-% \begin{function}[added = 2019-02-15]
+% \begin{function}[added = 2019-02-15, updated = 2021-04-29]
% {\str_log:N, \str_log:c, \str_log:n}
% \begin{syntax}
% \cs{str_log:N} \meta{str~var}
@@ -1983,10 +1983,18 @@
% Displays a string on the terminal.
% \begin{macrocode}
\cs_new_eq:NN \str_show:n \tl_show:n
-\cs_new_eq:NN \str_show:N \tl_show:N
+\cs_new_protected:Npn \str_show:N #1
+ {
+ \__kernel_chk_tl_type:NnnT #1 { str } { \tl_to_str:N #1 }
+ { \tl_show:N #1 }
+ }
\cs_generate_variant:Nn \str_show:N { c }
\cs_new_eq:NN \str_log:n \tl_log:n
-\cs_new_eq:NN \str_log:N \tl_log:N
+\cs_new_protected:Npn \str_log:N #1
+ {
+ \__kernel_chk_tl_type:NnnT #1 { str } { \tl_to_str:N #1 }
+ { \tl_log:N #1 }
+ }
\cs_generate_variant:Nn \str_log:N { c }
% \end{macrocode}
% \end{macro}
diff --git a/l3kernel/l3tl.dtx b/l3kernel/l3tl.dtx
index 15c26d379..dd11b5159 100644
--- a/l3kernel/l3tl.dtx
+++ b/l3kernel/l3tl.dtx
@@ -1169,7 +1169,7 @@
%
% \section{Viewing token lists}
%
-% \begin{function}[updated = 2015-08-01]{\tl_show:N, \tl_show:c}
+% \begin{function}[updated = 2021-04-29]{\tl_show:N, \tl_show:c}
% \begin{syntax}
% \cs{tl_show:N} \meta{tl~var}
% \end{syntax}
@@ -1191,7 +1191,7 @@
% \end{texnote}
% \end{function}
%
-% \begin{function}[added = 2014-08-22, updated = 2015-08-01]{\tl_log:N, \tl_log:c}
+% \begin{function}[added = 2014-08-22, updated = 2021-04-29]{\tl_log:N, \tl_log:c}
% \begin{syntax}
% \cs{tl_log:N} \meta{tl~var}
% \end{syntax}
@@ -3531,8 +3531,16 @@
{
\__kernel_chk_defined:NT #2
{
- \exp_args:Ne #1
- { \token_to_str:N #2 = \__kernel_exp_not:w \exp_after:wN {#2} }
+ \exp_args:Nf \tl_if_empty:nTF
+ { \cs_prefix_spec:N #2 \cs_argument_spec:N #2 }
+ {
+ \exp_args:Ne #1
+ { \token_to_str:N #2 = \__kernel_exp_not:w \exp_after:wN {#2} }
+ }
+ {
+ \__kernel_msg_error:nnxxx { kernel } { bad-type }
+ { \token_to_str:N #2 } { \token_to_meaning:N #2 } { tl }
+ }
}
}
% \end{macrocode}
@@ -3584,6 +3592,40 @@
% \end{macrocode}
% \end{macro}
%
+% \begin{macro}{\__kernel_chk_tl_type:NnnT}
+% Helper for checking that |#1| has the correct internal structure to
+% be of a certain type. Make sure that it is defined and that it is a
+% token list, namely a macro with no \tn{long} nor \tn{protected}
+% prefix. Then compare |#1| to an attempt at reconstructing a valid
+% structure of the given type using |#2| (see implementation of
+% \cs{seq_show:N} for instance). If that is successful run the
+% requested code~|#4|.
+% \begin{macrocode}
+\cs_new_protected:Npn \__kernel_chk_tl_type:NnnT #1#2#3#4
+ {
+ \__kernel_chk_defined:NT #1
+ {
+ \exp_args:Nf \tl_if_empty:nTF
+ { \cs_prefix_spec:N #1 \cs_argument_spec:N #1 }
+ {
+ \tl_set:Nx \l_@@_internal_a_tl {#3}
+ \tl_if_eq:NNTF #1 \l_@@_internal_a_tl
+ {#4}
+ {
+ \__kernel_msg_error:nnxxxx { kernel } { bad-type }
+ { \token_to_str:N #1 } { \tl_to_str:N #1 }
+ {#2} { \tl_to_str:N \l_@@_internal_a_tl }
+ }
+ }
+ {
+ \__kernel_msg_error:nnxxx { kernel } { bad-type }
+ { \token_to_str:N #1 } { \token_to_meaning:N #1 } {#2}
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
% \subsection{Internal scan marks}
%
% \begin{variable}{\s_@@_nil,\s_@@_mark,\s_@@_stop}
diff --git a/l3kernel/testfiles/m3show003.luatex.tlg b/l3kernel/testfiles/m3show003.luatex.tlg
index 89b88b09a..bfa810551 100644
--- a/l3kernel/testfiles/m3show003.luatex.tlg
+++ b/l3kernel/testfiles/m3show003.luatex.tlg
@@ -251,3 +251,140 @@ l. ... }
<recently read> }
l. ... }
============================================================
+============================================================
+TEST 17: Bad types
+============================================================
+! LaTeX3 Error: Variable 'A' is not a valid bool.
+For immediate help type H <return>.
+ ...
+l. ... }
+This is a coding error.
+The variable 'A' with meaning
+ the letter A
+should be a bool variable, but it is not.
+! LaTeX3 Error: Variable '\c_empty_tl' is not a valid bool.
+For immediate help type H <return>.
+ ...
+l. ... }
+This is a coding error.
+The variable '\c_empty_tl' with meaning
+ macro:->
+should be a bool variable, but it is not.
+! LaTeX3 Error: Variable '\l_tmpa_tl' is not a valid clist.
+For immediate help type H <return>.
+ ...
+l. ... }
+This is a coding error.
+The variable '\l_tmpa_tl' with value
+ a,
+should be a clist variable, but it includes empty or blank items without
+braces.
+! LaTeX3 Error: Variable '\l_tmpa_tl' is not a valid str.
+For immediate help type H <return>.
+ ...
+l. ... }
+This is a coding error.
+The variable '\l_tmpa_tl' with value
+ a,
+should be a str variable, but it does not have the correct category codes.
+> \l_tmpa_int=0.
+<recently read> }
+l. ... }
+> \l_tmpa_dim=0.0pt.
+! LaTeX3 Error: Variable '\l_tmpa_tl' is not a valid fp.
+For immediate help type H <return>.
+ ...
+l. ... }
+This is a coding error.
+The variable '\l_tmpa_tl' with value
+ a,
+should be a fp variable, but it does not have the correct internal structure:
+ \s__fp \__fp_chk:w ???;
+! LaTeX3 Error: Variable '\l_tmpa_tl' is not a valid fp.
+For immediate help type H <return>.
+ ...
+l. ... }
+This is a coding error.
+The variable '\l_tmpa_tl' with value
+ \s__fp \__fp_chk:w 10{1}{1000}{0000}{0000}{0000};;
+should be a fp variable, but it does not have the correct internal structure:
+ \s__fp \__fp_chk:w 10{1}{1000}{0000}{0000}{0000};
+! LaTeX3 Error: Variable '\l_tmpa_tl' is not a valid prop.
+For immediate help type H <return>.
+ ...
+l. ... }
+This is a coding error.
+The variable '\l_tmpa_tl' with value
+ \s__fp \__fp_chk:w 10{1}{1000}{0000}{0000}{0000};;
+should be a prop variable, but it does not have the correct internal
+structure:
+ \s__prop
+! LaTeX3 Error: Variable '\l_tmpa_tl' is not a valid seq.
+For immediate help type H <return>.
+ ...
+l. ... }
+This is a coding error.
+The variable '\l_tmpa_tl' with value
+ \s__fp \__fp_chk:w 10{1}{1000}{0000}{0000}{0000};;
+should be a seq variable, but it does not have the correct internal structure:
+ \s__seq \__seq_item:n {1}\__seq_item:n {1}\__seq_item:n
+ {0000}\__seq_item:n {0000}\__seq_item:n {;}
+! LaTeX3 Error: Variable '\l_tmpa_tl' is not a valid seq.
+For immediate help type H <return>.
+ ...
+l. ... }
+This is a coding error.
+The variable '\l_tmpa_tl' with value
+ \s__seq \__seq_item:n {a}\__seq_item:n {b}?{c}
+should be a seq variable, but it does not have the correct internal structure:
+ \s__seq \__seq_item:n {a}\__seq_item:n {b}\__seq_item:n {c}
+! LaTeX3 Error: Variable '\use:n' is not a valid tl.
+For immediate help type H <return>.
+ ...
+l. ... }
+This is a coding error.
+The variable '\use:n' with meaning
+ \long macro:#1->#1
+should be a tl variable, but it is not a short macro.
+============================================================
+============================================================
+TEST 18: Bad types for regex
+============================================================
+! LaTeX3 Error: Variable '\use:n' is not a valid regex.
+For immediate help type H <return>.
+ ...
+l. ... }
+This is a coding error.
+The variable '\use:n' with meaning
+ \long macro:#1->#1
+should be a regex variable, but it is not a short macro.
+> Compiled regex variable \l_tmpa_tl:.
+<recently read> }
+l. ... }
+! LaTeX3 Error: Variable '\l_tmpa_tl' is not a valid regex.
+For immediate help type H <return>.
+ ...
+l. ... }
+This is a coding error.
+The variable '\l_tmpa_tl' with value
+ {\__regex_class:NnnnN \c_true_bool {\__regex_item_caseful_equal:n
+ {97}}{1}{0}\c_false_bool }\__regex_branch:n {\__regex_class:NnnnN
+ \c_true_bool {\__regex_item_caseful_equal:n {98}}{1}{0}\c_false_bool
+ }\__regex_branch:n {\__regex_group:nnnN {\__regex_branch:n
+ {\__regex_class:NnnnN \c_true_bool {\__regex_item_caseful_equal:n
+ {99}}{1}{0}\c_false_bool \__regex_class:NnnnN \c_true_bool
+ {\__regex_item_caseful_equal:n {100}}{1}{0}\c_false_bool
+ \__regex_class:NnnnN \c_true_bool {\__regex_item_caseful_equal:n
+ {101}\__regex_item_caseful_equal:n {102}}{0}{-1}\c_true_bool
+ \__regex_command_K: }}{1}{0}\c_false_bool }
+should be a regex variable, but it does not have the correct internal
+structure:
+ \__regex_branch:n {\__regex_class:NnnnN \c_true_bool
+ {\__regex_item_caseful_equal:n {98}}{1}{0}\c_false_bool }\__regex_branch:n
+ {\__regex_group:nnnN {\__regex_branch:n {\__regex_class:NnnnN \c_true_bool
+ {\__regex_item_caseful_equal:n {99}}{1}{0}\c_false_bool
+ \__regex_class:NnnnN \c_true_bool {\__regex_item_caseful_equal:n
+ {100}}{1}{0}\c_false_bool \__regex_class:NnnnN \c_true_bool
+ {\__regex_item_caseful_equal:n {101}\__regex_item_caseful_equal:n
+ {102}}{0}{-1}\c_true_bool \__regex_command_K: }}{1}{0}\c_false_bool }
+============================================================
diff --git a/l3kernel/testfiles/m3show003.lvt b/l3kernel/testfiles/m3show003.lvt
index f824df3f6..9da17d853 100644
--- a/l3kernel/testfiles/m3show003.lvt
+++ b/l3kernel/testfiles/m3show003.lvt
@@ -225,4 +225,35 @@
}
}
+
+\TEST { Bad~types }
+ {
+ \bool_show:N A
+ \bool_log:N \c_empty_tl
+ \tl_set:Nn \l_tmpa_tl { a , }
+ \clist_show:N \l_tmpa_tl
+ \str_show:N \l_tmpa_tl
+ \dim_show:N \l_tmpa_int % not caught
+ \int_log:N \l_tmpa_dim % not caught
+ \fp_show:N \l_tmpa_tl
+ \tl_set:Nx \l_tmpa_tl { \c_one_fp ; }
+ \fp_log:N \l_tmpa_tl
+ \prop_show:N \l_tmpa_tl
+ \seq_show:N \l_tmpa_tl
+ \seq_set_split:Nnn \l_tmpa_seq { } { ab }
+ \tl_set:Nx \l_tmpa_tl { \exp_not:V \l_tmpa_seq ? { c } }
+ \seq_show:N \l_tmpa_tl
+ \tl_show:N \use:n
+ }
+
+\TEST { Bad~types~for~regex }
+ {
+ \regex_show:N \use:n
+ \regex_show:N \l_tmpa_tl
+ \regex_set:Nn \l_tmpa_regex { a | b | (cd[ef]*?\K) }
+ \tl_set:Nx \l_tmpa_tl
+ { \exp_after:wN \use_none:n \l_tmpa_regex }
+ \regex_show:N \l_tmpa_tl
+ }
+
\END
diff --git a/l3kernel/testfiles/m3show003.tlg b/l3kernel/testfiles/m3show003.tlg
index a1d997f53..0123e0e25 100644
--- a/l3kernel/testfiles/m3show003.tlg
+++ b/l3kernel/testfiles/m3show003.tlg
@@ -249,3 +249,140 @@ l. ... }
<recently read> }
l. ... }
============================================================
+============================================================
+TEST 17: Bad types
+============================================================
+! LaTeX3 Error: Variable 'A' is not a valid bool.
+For immediate help type H <return>.
+ ...
+l. ... }
+This is a coding error.
+The variable 'A' with meaning
+ the letter A
+should be a bool variable, but it is not.
+! LaTeX3 Error: Variable '\c_empty_tl' is not a valid bool.
+For immediate help type H <return>.
+ ...
+l. ... }
+This is a coding error.
+The variable '\c_empty_tl' with meaning
+ macro:->
+should be a bool variable, but it is not.
+! LaTeX3 Error: Variable '\l_tmpa_tl' is not a valid clist.
+For immediate help type H <return>.
+ ...
+l. ... }
+This is a coding error.
+The variable '\l_tmpa_tl' with value
+ a,
+should be a clist variable, but it includes empty or blank items without
+braces.
+! LaTeX3 Error: Variable '\l_tmpa_tl' is not a valid str.
+For immediate help type H <return>.
+ ...
+l. ... }
+This is a coding error.
+The variable '\l_tmpa_tl' with value
+ a,
+should be a str variable, but it does not have the correct category codes.
+> \l_tmpa_int=0.
+<recently read> }
+l. ... }
+> \l_tmpa_dim=0.0pt.
+! LaTeX3 Error: Variable '\l_tmpa_tl' is not a valid fp.
+For immediate help type H <return>.
+ ...
+l. ... }
+This is a coding error.
+The variable '\l_tmpa_tl' with value
+ a,
+should be a fp variable, but it does not have the correct internal structure:
+ \s__fp \__fp_chk:w ???;
+! LaTeX3 Error: Variable '\l_tmpa_tl' is not a valid fp.
+For immediate help type H <return>.
+ ...
+l. ... }
+This is a coding error.
+The variable '\l_tmpa_tl' with value
+ \s__fp \__fp_chk:w 10{1}{1000}{0000}{0000}{0000};;
+should be a fp variable, but it does not have the correct internal structure:
+ \s__fp \__fp_chk:w 10{1}{1000}{0000}{0000}{0000};
+! LaTeX3 Error: Variable '\l_tmpa_tl' is not a valid prop.
+For immediate help type H <return>.
+ ...
+l. ... }
+This is a coding error.
+The variable '\l_tmpa_tl' with value
+ \s__fp \__fp_chk:w 10{1}{1000}{0000}{0000}{0000};;
+should be a prop variable, but it does not have the correct internal
+structure:
+ \s__prop
+! LaTeX3 Error: Variable '\l_tmpa_tl' is not a valid seq.
+For immediate help type H <return>.
+ ...
+l. ... }
+This is a coding error.
+The variable '\l_tmpa_tl' with value
+ \s__fp \__fp_chk:w 10{1}{1000}{0000}{0000}{0000};;
+should be a seq variable, but it does not have the correct internal structure:
+ \s__seq \__seq_item:n {1}\__seq_item:n {1}\__seq_item:n
+ {0000}\__seq_item:n {0000}\__seq_item:n {;}
+! LaTeX3 Error: Variable '\l_tmpa_tl' is not a valid seq.
+For immediate help type H <return>.
+ ...
+l. ... }
+This is a coding error.
+The variable '\l_tmpa_tl' with value
+ \s__seq \__seq_item:n {a}\__seq_item:n {b}?{c}
+should be a seq variable, but it does not have the correct internal structure:
+ \s__seq \__seq_item:n {a}\__seq_item:n {b}\__seq_item:n {c}
+! LaTeX3 Error: Variable '\use:n' is not a valid tl.
+For immediate help type H <return>.
+ ...
+l. ... }
+This is a coding error.
+The variable '\use:n' with meaning
+ \long macro:#1->#1
+should be a tl variable, but it is not a short macro.
+============================================================
+============================================================
+TEST 18: Bad types for regex
+============================================================
+! LaTeX3 Error: Variable '\use:n' is not a valid regex.
+For immediate help type H <return>.
+ ...
+l. ... }
+This is a coding error.
+The variable '\use:n' with meaning
+ \long macro:#1->#1
+should be a regex variable, but it is not a short macro.
+> Compiled regex variable \l_tmpa_tl:.
+<recently read> }
+l. ... }
+! LaTeX3 Error: Variable '\l_tmpa_tl' is not a valid regex.
+For immediate help type H <return>.
+ ...
+l. ... }
+This is a coding error.
+The variable '\l_tmpa_tl' with value
+ {\__regex_class:NnnnN \c_true_bool {\__regex_item_caseful_equal:n
+ {97}}{1}{0}\c_false_bool }\__regex_branch:n {\__regex_class:NnnnN
+ \c_true_bool {\__regex_item_caseful_equal:n {98}}{1}{0}\c_false_bool
+ }\__regex_branch:n {\__regex_group:nnnN {\__regex_branch:n
+ {\__regex_class:NnnnN \c_true_bool {\__regex_item_caseful_equal:n
+ {99}}{1}{0}\c_false_bool \__regex_class:NnnnN \c_true_bool
+ {\__regex_item_caseful_equal:n {100}}{1}{0}\c_false_bool
+ \__regex_class:NnnnN \c_true_bool {\__regex_item_caseful_equal:n
+ {101}\__regex_item_caseful_equal:n {102}}{0}{-1}\c_true_bool
+ \__regex_command_K: }}{1}{0}\c_false_bool }
+should be a regex variable, but it does not have the correct internal
+structure:
+ \__regex_branch:n {\__regex_class:NnnnN \c_true_bool
+ {\__regex_item_caseful_equal:n {98}}{1}{0}\c_false_bool }\__regex_branch:n
+ {\__regex_group:nnnN {\__regex_branch:n {\__regex_class:NnnnN \c_true_bool
+ {\__regex_item_caseful_equal:n {99}}{1}{0}\c_false_bool
+ \__regex_class:NnnnN \c_true_bool {\__regex_item_caseful_equal:n
+ {100}}{1}{0}\c_false_bool \__regex_class:NnnnN \c_true_bool
+ {\__regex_item_caseful_equal:n {101}\__regex_item_caseful_equal:n
+ {102}}{0}{-1}\c_true_bool \__regex_command_K: }}{1}{0}\c_false_bool }
+============================================================
More information about the latex3-commits
mailing list.