5

I am playing around with expl3 and wanted to try some higher order programming. In this example I define an environment and then perform an action on this environment:

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{expl3}
\usepackage{amsmath, amssymb}

\begin{document}

\ExplSyntaxOn
\prop_new:N \l_files_prop
\newcommand{\addConfig}[2]{
    \prop_put:Nnn \l_files_prop {#1} {{#2}}
}
\newcommand{\printConfig}[2]{
    \prop_get:NnN \l_files_prop {#1} \l__files
    \cs_generate_variant:Nn {\regex_split:nnN} {nVN}
    \regex_split:nVN {;} {\l__files} {\l__files_split_tl}
    %\seq_set_split:NnV \l__files_split_tl {o} {\l__files}
  
    %\seq_set_split:Nnn \l__cmd_tl {\textbackslash} {\tl_to_str:n #2}
    \seq_map_inline:Nn \l__files_split_tl {
        \use:c {\cs_to_str:N #2} {##1}\\
    } 
}

\ExplSyntaxOff

\addConfig{config1}{hello; world}
\addConfig{config2}{bye; world}
\addConfig{config3}{testFile.tex; testFile2.tex}

\noindent
\printConfig{config1}{\textit}
\printConfig{config2}{\textbf}
%\printConfig{config3}{\input}

\end{document}

which correctly results in

result1

however, I get the following error along with the correct output:

! LaTeX3 Error: Missing brace inserted when splitting or extracting
(LaTeX3)        submatches.

For immediate help type H <return>.
 ...                                              
                                                  
l.75 \printConfig{config1}{\textit}
                                   

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: 1 left, 1 right.

and I don't really understand why; I try to follow this guide and work with the documentation but I also couldn't find any information why this is occurring.

How can I rewrite the code to remove this error? And (more importantly), why does it appear?

1 Answer 1

7

You are adding a spurious set of braces in your \prop_put:Nnn command:

\prop_put:Nnn \l_files_prop {#1} {{#2}}

Changing that to

\prop_put:Nnn \l_files_prop {#1} {#2}

solves the problem. The error happens because with a token list {hello;world}, splitting it at the ; will leave unbalanced braces in each item ({hello and world}), which is illegal, so l3regex inserts some to balance the token lists.

I also changed some things to conform better with expl3 standards:

  • All variables should be declared, so you need a \tl_new:N \l__files_split_tl and a \tl_new:N \l__files (see next item about this one);
  • Variables should be named \⟨scope⟩_⟨module⟩_⟨name⟩_⟨type⟩ so \l__files is a terrible name. Something like \l__fabian_files_tl is better, assuming fabian is the module;
  • \cs_generate_variant:Nn can be done only once per variant, so you can move it outside the command;
  • Assuming #2 is a control sequence, \use:c {\cs_to_str:N #2} has the same effect as just #2, so you can simplify;
  • The output of \regex_split:nnN is a seq variable, not a tl, so you should use that instead;
  • [bonus] If you just want to split at a ; you don't need regex at all. Instead of the \regex_split:nVN line you can use \seq_set_split:NnV \l__fabian_files_split_seq { ; } \l__fabian_files_tl (the main difference is that \regex_split:nVN will preserve spaces around items, but \seq_set_split:NnV will trim spaces).

Here's the code:

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{xparse}
\usepackage{amsmath, amssymb}

\begin{document}

\ExplSyntaxOn
\prop_new:N \l_fabian_files_prop
\tl_new:N \l__fabian_files_tl
\seq_new:N \l__fabian_files_split_seq
% \cs_generate_variant:Nn \regex_split:nnN { nV }

\NewDocumentCommand \addConfig { mm }
  { \prop_put:Nnn \l_fabian_files_prop {#1} {#2} }
\NewDocumentCommand \printConfig { mm }
  {
    \prop_get:NnN \l_fabian_files_prop {#1} \l__fabian_files_tl
    % \regex_split:nVN { ; } \l__fabian_files_tl \l__fabian_files_split_seq
    \seq_set_split:NnV \l__fabian_files_split_seq { ; } \l__fabian_files_tl
    \seq_map_inline:Nn \l__fabian_files_split_seq { #2 {##1} \\ }
  }

\ExplSyntaxOff

\addConfig{config1}{hello; world}
\addConfig{config2}{bye; world}
\addConfig{config3}{testFile.tex; testFile2.tex}

\noindent
\printConfig{config1}{\textit}
\printConfig{config2}{\textbf}
%\printConfig{config3}{\input}

\end{document}
12
  • 1
    I'd prefer \seq_set_split:NnV, as it also trims white spaces, which is more likely what the OP wants.
    – egreg
    Commented Feb 7, 2021 at 16:36
  • 1
    @FabianSchneider The main problem was {{#2}} in the definition of \addConfig. You don't want the additional braces.
    – egreg
    Commented Feb 7, 2021 at 16:39
  • 2
    @egreg I did mention that in the answer :) Commented Feb 7, 2021 at 16:41
  • 2
    @PhelypeOleinik By the way, the names I have in my code are exactly the same as you chose. ;-)
    – egreg
    Commented Feb 7, 2021 at 16:48
  • 2
    @FabianSchneider In this case no, no mandatory reason, you could have used \newcommand normally (although for example \edef\x{\addConfig{config1}{hello; world}} will break if defined with \newcommand). But nowadays \NewDocumentCommand is part of the LaTeX kernel, so if you have an up-to-date distribution, it doesn't even require xparse, so in this case there is no reason not to use it Commented Feb 8, 2021 at 12:26

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .