[latex3-commits] [git/LaTeX3-latex3-latex3] main: Fix error check for unbalanced braces in regex replacement (see #377) (9ede50e4c)

Bruno Le Floch blflatex at gmail.com
Thu May 13 00:12:33 CEST 2021


Repository : https://github.com/latex3/latex3
On branch  : main
Link       : https://github.com/latex3/latex3/commit/9ede50e4cbb98b7189af3349d1ca1f9a413aaa00

>---------------------------------------------------------------

commit 9ede50e4cbb98b7189af3349d1ca1f9a413aaa00
Author: Bruno Le Floch <blflatex at gmail.com>
Date:   Thu May 13 00:12:33 2021 +0200

    Fix error check for unbalanced braces in regex replacement (see #377)


>---------------------------------------------------------------

9ede50e4cbb98b7189af3349d1ca1f9a413aaa00
 l3kernel/l3regex.dtx              |  90 +++++++++++++++++++++++----------
 l3kernel/testfiles/m3regex006.lvt |   2 +-
 l3kernel/testfiles/m3regex006.tlg | 104 +++++++++++++++++++++++++++++---------
 3 files changed, 145 insertions(+), 51 deletions(-)

diff --git a/l3kernel/l3regex.dtx b/l3kernel/l3regex.dtx
index 37ba50587..3cdb00818 100644
--- a/l3kernel/l3regex.dtx
+++ b/l3kernel/l3regex.dtx
@@ -6349,6 +6349,15 @@
 %    \end{macrocode}
 % \end{variable}
 %
+% \begin{variable}{\l_@@_added_begin_int, \l_@@_added_end_int}
+%   Keep track of the number of left/right braces to add when performing
+%   a regex operation such as a replacement.
+%    \begin{macrocode}
+\int_new:N \l_@@_added_begin_int
+\int_new:N \l_@@_added_end_int
+%    \end{macrocode}
+% \end{variable}
+%
 % \begin{macro}{\@@_return:}
 %   This function triggers either \cs{prg_return_false:} or
 %   \cs{prg_return_true:} as appropriate to whether a match was found or
@@ -6717,36 +6726,65 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}{\@@_group_end_replace:N}
-%   If the brace balance is not $0$, raise an error. Then set the user's
-%   variable |#1| to the \texttt{x}-expansion of
-%   \cs{l_@@_internal_a_tl}, adding the appropriate braces to produce
-%   a balanced result. And end the group.
+% \begin{macro}
+%   {
+%     \@@_group_end_replace:N, \@@_group_end_replace_try:,
+%     \@@_group_end_replace_check:w, \@@_group_end_replace_check:n
+%   }
+%   At this stage \cs{l_@@_internal_a_tl} (|x|-expands to the desired
+%   result).  Guess from \cs{l_@@_balance_int} the number of braces to
+%   add before or after the result then try expanding.  The simplest
+%   case is when \cs{l_@@_internal_a_tl} together with the braces we
+%   insert via \cs{prg_replicate:nn} give a balanced result, and the
+%   assignment ends at the \cs{if_false:} |{| \cs{fi:} |}| construction:
+%   then \cs{@@_group_end_replace_check:w} sees that there is no
+%   material left and we successfully found the result.  The harder case
+%   is that expanding \cs{l_@@_internal_a_tl} may produce extra closing
+%   braces and end the assignment early.  Then we grab the remaining code
+%   using; importantly, what follows has not yet been expanded so that
+%   \cs{@@_group_end_replace_check:n} grabs everything until the last
+%   brace in \cs{@@_group_end_replace_try:}, letting us try again with
+%   an extra surrounding pair of braces.
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_group_end_replace:N #1
   {
-    \if_int_compare:w \l_@@_balance_int = 0 \exp_stop_f:
-    \else:
-      \__kernel_msg_error:nnxxx { regex } { result-unbalanced }
-        { replacing }
-        { \int_max:nn { - \l_@@_balance_int } { 0 } }
-        { \int_max:nn { \l_@@_balance_int } { 0 } }
-    \fi:
-    \use:x
+    \int_set:Nn \l_@@_added_begin_int
+      { \int_max:nn { - \l_@@_balance_int } { 0 } }
+    \int_set:Nn \l_@@_added_end_int
+      { \int_max:nn { \l_@@_balance_int } { 0 } }
+    \@@_group_end_replace_try:
+    \int_compare:nNnT { \l_@@_added_begin_int + \l_@@_added_end_int } > 0
       {
-        \group_end:
-        \tl_set:Nn \exp_not:N #1
-          {
-            \if_int_compare:w \l_@@_balance_int < 0 \exp_stop_f:
-              \prg_replicate:nn { - \l_@@_balance_int }
-                { { \if_false: } \fi: }
-            \fi:
-            \l_@@_internal_a_tl
-            \if_int_compare:w \l_@@_balance_int > 0 \exp_stop_f:
-              \prg_replicate:nn { \l_@@_balance_int }
-                { \if_false: { \fi: } }
-            \fi:
-          }
+        \__kernel_msg_error:nnxxx { regex } { result-unbalanced }
+          { replacing } { \int_use:N \l_@@_added_begin_int }
+          { \int_use:N \l_@@_added_end_int }
+      }
+    \group_end:
+    \tl_set_eq:NN #1 \g_@@_internal_tl
+  }
+\cs_new_protected:Npn \@@_group_end_replace_try:
+  {
+    \tex_afterassignment:D \@@_group_end_replace_check:w
+    \__kernel_tl_gset:Nx \g_@@_internal_tl
+      {
+        \prg_replicate:nn { \l_@@_added_begin_int } { { \if_false: } \fi: }
+        \l_@@_internal_a_tl
+        \prg_replicate:nn { \l_@@_added_end_int } { \if_false: { \fi: } }
+        \if_false: { \fi: }
+      }
+  }
+\cs_new_protected:Npn \@@_group_end_replace_check:w
+  {
+    \exp_after:wN \@@_group_end_replace_check:n
+    \exp_after:wN { \if_false: } \fi:
+  }
+\cs_new_protected:Npn \@@_group_end_replace_check:n #1
+  {
+    \tl_if_empty:nF {#1}
+      {
+        \int_incr:N \l_@@_added_begin_int
+        \int_incr:N \l_@@_added_end_int
+        \@@_group_end_replace_try:
       }
   }
 %    \end{macrocode}
diff --git a/l3kernel/testfiles/m3regex006.lvt b/l3kernel/testfiles/m3regex006.lvt
index 2b4290454..42f69b8d5 100644
--- a/l3kernel/testfiles/m3regex006.lvt
+++ b/l3kernel/testfiles/m3regex006.lvt
@@ -145,7 +145,7 @@
     \test:nnnTF { ([^c])a   } { \1 \cB< \0   } { ab{c}ade } \TRUE  \ERROR
     \test:nnnTF { ([^c])..a } { \c{\1}       } { ab{c}ade } \TRUE  \ERROR
     \test:nnnTF { [c-z]     } { \x00 \x{02}  } { ab{c}ade } \TRUE  \ERROR
-    \test:nnnTF { (.) b(.)  } { \cB\x5c\2\2  } { aba{b}#d } \TRUE  \ERROR
+    \test:nnnTF { (.) b(.)  } { \}\}\{\{\cB\x5c\2\2 } { aba{b}#d } \TRUE  \ERROR
     \test:nnnTF { \# (.*?)c } { \c{use:n} \1 } { a#{b}c#d } \TRUE  \ERROR
   }
 
diff --git a/l3kernel/testfiles/m3regex006.tlg b/l3kernel/testfiles/m3regex006.tlg
index 14da26466..ea8f261b7 100644
--- a/l3kernel/testfiles/m3regex006.tlg
+++ b/l3kernel/testfiles/m3regex006.tlg
@@ -556,80 +556,136 @@ For immediate help type H <return>.
 l. ...  }
 LaTeX was asked to do some regular expression operation, and the resulting
 token list would not have the same number of begin-group and end-group tokens.
-Braces were inserted: 0 left, 1 right.
+Braces were inserted: 2 left, 3 right.
 TRUE
-macro:->\aa{b}##d}
+macro:->{{}}{{\aa{b}##d}}}
 ! LaTeX3 Error: Missing brace inserted when replacing.
 For immediate help type H <return>.
  ...                                              
 l. ...  }
 LaTeX was asked to do some regular expression operation, and the resulting
 token list would not have the same number of begin-group and end-group tokens.
-Braces were inserted: 0 left, 1 right.
+Braces were inserted: 2 left, 3 right.
 TRUE
-macro:->\aa{b}##d}
+macro:->{{}}{{\aa{b}##d}}}
 ! LaTeX3 Error: Missing brace inserted when replacing.
 For immediate help type H <return>.
  ...                                              
 l. ...  }
 LaTeX was asked to do some regular expression operation, and the resulting
 token list would not have the same number of begin-group and end-group tokens.
-Braces were inserted: 0 left, 1 right.
-macro:->\aa{b}##d}
+Braces were inserted: 2 left, 3 right.
+macro:->{{}}{{\aa{b}##d}}}
 ! LaTeX3 Error: Missing brace inserted when replacing.
 For immediate help type H <return>.
  ...                                              
 l. ...  }
 LaTeX was asked to do some regular expression operation, and the resulting
 token list would not have the same number of begin-group and end-group tokens.
-Braces were inserted: 0 left, 1 right.
-macro:->\aa{b}##d}
+Braces were inserted: 2 left, 3 right.
+macro:->{{}}{{\aa{b}##d}}}
 ! LaTeX3 Error: Missing brace inserted when replacing.
 For immediate help type H <return>.
  ...                                              
 l. ...  }
 LaTeX was asked to do some regular expression operation, and the resulting
 token list would not have the same number of begin-group and end-group tokens.
-Braces were inserted: 0 left, 1 right.
+Braces were inserted: 2 left, 3 right.
 TRUE
-macro:->\aa{b}##d}
+macro:->{{}}{{\aa{b}##d}}}
 ! LaTeX3 Error: Missing brace inserted when replacing.
 For immediate help type H <return>.
  ...                                              
 l. ...  }
 LaTeX was asked to do some regular expression operation, and the resulting
 token list would not have the same number of begin-group and end-group tokens.
-Braces were inserted: 0 left, 1 right.
+Braces were inserted: 2 left, 3 right.
 TRUE
-macro:->\aa{b}##d}
+macro:->{{}}{{\aa{b}##d}}}
 ! LaTeX3 Error: Missing brace inserted when replacing.
 For immediate help type H <return>.
  ...                                              
 l. ...  }
 LaTeX was asked to do some regular expression operation, and the resulting
 token list would not have the same number of begin-group and end-group tokens.
-Braces were inserted: 0 left, 1 right.
-macro:->\aa{b}##d}
+Braces were inserted: 2 left, 3 right.
+macro:->{{}}{{\aa{b}##d}}}
 ! LaTeX3 Error: Missing brace inserted when replacing.
 For immediate help type H <return>.
  ...                                              
 l. ...  }
 LaTeX was asked to do some regular expression operation, and the resulting
 token list would not have the same number of begin-group and end-group tokens.
-Braces were inserted: 0 left, 1 right.
-macro:->\aa{b}##d}
+Braces were inserted: 2 left, 3 right.
+macro:->{{}}{{\aa{b}##d}}}
+! LaTeX3 Error: Missing brace inserted when replacing.
+For immediate help type H <return>.
+ ...                                              
+l. ...  }
+LaTeX was asked to do some regular expression operation, and the resulting
+token list would not have the same number of begin-group and end-group tokens.
+Braces were inserted: 2 left, 2 right.
 TRUE
-macro:->\aa\}}##d
+macro:->{{}}{{\aa}}{{\}}##d}}
+! LaTeX3 Error: Missing brace inserted when replacing.
+For immediate help type H <return>.
+ ...                                              
+l. ...  }
+LaTeX was asked to do some regular expression operation, and the resulting
+token list would not have the same number of begin-group and end-group tokens.
+Braces were inserted: 2 left, 2 right.
 TRUE
-macro:->\aa\}}##d
-macro:->\aa\}}##d
-macro:->\aa\}}##d
+macro:->{{}}{{\aa}}{{\}}##d}}
+! LaTeX3 Error: Missing brace inserted when replacing.
+For immediate help type H <return>.
+ ...                                              
+l. ...  }
+LaTeX was asked to do some regular expression operation, and the resulting
+token list would not have the same number of begin-group and end-group tokens.
+Braces were inserted: 2 left, 2 right.
+macro:->{{}}{{\aa}}{{\}}##d}}
+! LaTeX3 Error: Missing brace inserted when replacing.
+For immediate help type H <return>.
+ ...                                              
+l. ...  }
+LaTeX was asked to do some regular expression operation, and the resulting
+token list would not have the same number of begin-group and end-group tokens.
+Braces were inserted: 2 left, 2 right.
+macro:->{{}}{{\aa}}{{\}}##d}}
+! LaTeX3 Error: Missing brace inserted when replacing.
+For immediate help type H <return>.
+ ...                                              
+l. ...  }
+LaTeX was asked to do some regular expression operation, and the resulting
+token list would not have the same number of begin-group and end-group tokens.
+Braces were inserted: 2 left, 2 right.
 TRUE
-macro:->\aa\}}##d
+macro:->{{}}{{\aa}}{{\}}##d}}
+! LaTeX3 Error: Missing brace inserted when replacing.
+For immediate help type H <return>.
+ ...                                              
+l. ...  }
+LaTeX was asked to do some regular expression operation, and the resulting
+token list would not have the same number of begin-group and end-group tokens.
+Braces were inserted: 2 left, 2 right.
 TRUE
-macro:->\aa\}}##d
-macro:->\aa\}}##d
-macro:->\aa\}}##d
+macro:->{{}}{{\aa}}{{\}}##d}}
+! LaTeX3 Error: Missing brace inserted when replacing.
+For immediate help type H <return>.
+ ...                                              
+l. ...  }
+LaTeX was asked to do some regular expression operation, and the resulting
+token list would not have the same number of begin-group and end-group tokens.
+Braces were inserted: 2 left, 2 right.
+macro:->{{}}{{\aa}}{{\}}##d}}
+! LaTeX3 Error: Missing brace inserted when replacing.
+For immediate help type H <return>.
+ ...                                              
+l. ...  }
+LaTeX was asked to do some regular expression operation, and the resulting
+token list would not have the same number of begin-group and end-group tokens.
+Braces were inserted: 2 left, 2 right.
+macro:->{{}}{{\aa}}{{\}}##d}}
 macro:->aba{b}##d
 TRUE
 macro:->a\use:n {b}##d





More information about the latex3-commits mailing list.