The short-term answer is: use token lists nested in a sequence.
\RequirePackage{expl3}
\ExplSyntaxOn
\seq_new:N \l_lines_seq
\cs_new:Npn \jb_wrap:n #1 { \exp_not:n { {#1} } }
\seq_clear:N \l_lines_seq
\seq_set_split:Nnn \l_tmpa_seq { \\ } { a & b \\ c & d }
\seq_map_inline:Nn \l_tmpa_seq
{
\seq_set_split:Nnn \l_tmpb_seq { & } {#1} % use ##1 if in def
\seq_put_right:Nx \l_lines_seq
{ \seq_map_function:NN \l_tmpb_seq \jb_wrap:n }
}
% Use it, for instance this shows a, b, next, c, d, next on different lines
\seq_map_inline:Nn \l_lines_seq
{
\tl_map_inline:nn {#1} { \iow_term:n {##1} }
\iow_term:n { next }
}
\stop
The function \seq_set_map:NNn
can also be useful for some of your tasks, but I didn't use it above because was added to expl3
somewhat recently. The functions above are all stable, I think.
A "historical" note: the l3seq
module was rewritten a couple of years ago to use an internal form that would allow nested sequences in principle (since sequence items can be anything). Namely, a sequence is a token list variable of the form \__seq_item:n {...} \__seq_item:n {...} ...
(don't rely on it, it may change without notice). This means that one can safely store the contents of a sequence variable as an item in another sequence variable:
\seq_set_split:Nnn \l_a_seq { , } { a , b , c }
\seq_new:N \l_b_seq
\seq_put_right:NV \l_b_seq \l_a_seq
\seq_show:N \l_b_seq
Retrieving items from the second sequence variable, either through \seq_map_....
functions or through \seq_get_...
, will give you an explicit list of tokens \__seq_item:n {...} ...
, and there is no function in expl3
to manipulate those. I have to admit that I only realized this unfortunate state of affairs much later.
One way to deal with this would be that we add in expl3
some functions which expect n
-type sequence data, both storage into a sequence variable (enough for any non-expandable application), and all expandable functions we currently propose for N
-type ones.
% Extra functions that would be enough for all seq purposes,
% I think. Here '...' denotes `\__seq_item:n ...` material.
\seq_set:Nn <seq> { ... }
\seq_map_function:nN { ... } <func>
\seq_item:nn { ... } { <intexpr> }
\seq_mapthread_function:nnN { ... } { ... } <func>
%
% other useful functions (can be implemented in terms of the above)
%
\seq_if_empty:nTF { ... }
\seq_count:n { ... }
For consistency, something similar would have to be done with property lists, adding
% Minimum set of extra functions for props. Here, `...`
% denotes some `\s__prop <key> \s__prop { <value> }` material.
%
\prop_set:Nn <prop> { ... }
\prop_map_tokens:nn { ... } { <tokens> }
%
% other useful functions (can be implemented in terms of the above)
%
\prop_if_empty:nTF { ... }
\prop_if_in:nnTF { ... } { <key> }
\prop_map_function:nN { ... } <func>
\prop_get:nn { ... } { <key> }
I cannot predict whether we will add those functions for nested sequences/props to expl3
, but I think that we need a way to nest those essential data structures. Perhaps Joseph Wright's l3dt
is a better approach.