3

I'm trying to programmatically typeset rows inside a tblr table (from tabularray), but I'm unable to solve a Misplaced alignment tab character & error.

This minimal example reproduces my exact situation, including the structure of the macro calls I'm doing:

\documentclass{article}
\usepackage{xparse,tabularray}

\begin{document}
  \ExplSyntaxOn

  \seq_new:N \entries_seq

  \NewDocumentCommand { \entry } { m m m } {
    \seq_put_right:Nn \entries_seq { { #1 } { #2 } { #3 } }
  }

  \cs_new:Nn \make_row:nnn {
    % NOTE: this macro will also contain some complex logic for rendering the cells content
    #1 & #2 & #3 \\
  }

  \NewDocumentEnvironment { mytblr } {} {} {
    \begin{tblr}{lll}
      A & B & C \\
      \seq_map_inline:Nn \entries_seq { \make_row:nnn ##1 }
      X & Y & Z \\
    \end{tblr}
  }

  \ExplSyntaxOff

  \begin{mytblr}
    \entry{1}{2}{3}
    \entry{4}{5}{6}
  \end{mytblr}

\end{document}

This minimal example reproduces just the error, disregarding the current organisation of my code:

\documentclass{article}
\usepackage{tabularray}

\begin{document}
  \ExplSyntaxOn

    \cs_new:Nn \make_test_row: {
      1 & 2 & 3 \\
    }

    \begin{tblr}{lll}
      \make_test_row:
    \end{tblr}

  \ExplSyntaxOff
\end{document}

I'm aware of https://tex.stackexchange.com/a/338918/82186 and https://tex.stackexchange.com/a/243627/82186 but tabularray seems to be introducing some difference I can't pinpoint (In fact those solutions don't work replacing tabular with tblr).

It might be enough to solve the problem just on the shorter example to clarify the technique I need to use, but I included the longer example because that's the situation where I'll need to use it.

3
2

[In your examples you did not obey all expl3-conventions for naming control sequences.
In my coding-examples I renamed things to fit the conventions.]

In your example the \entry-macro is used for accumulating things within a sequence-variable. You use it within the mytblr-environment. At the end of that environment the things stored in the variable are to be nested inside a tblr-environment. You did not specify the desired behavior in case the mytblr-environment contains something other than calls to the \entry-macro.

E.g., you didn't specify the desired output for

 \begin{mytblr}
    \entry{1}{2}{3}%
    A&B&C\\
    \entry{4}{5}{6}%
  \end{mytblr}

where A&B&C\\ will not be turned into an element of the sequence-variable and therefore attempting to typeset takes place immediately and outside the tblr-environment.

As you did not specify this I assume that instances of your mytblr-environment only contain calls to \entry and nothing else.

The tblr-environment of the package tabularray needs its body to be expanded.

Thus although it is said that with expl3-stuff you don't need to care about expansion-issues, you have to ensure that stuff is passed to the tblr-environment expanded.

You use a sequence-variable \entries_seq while in interface3.pdf I didn't find any expandable function documented for mapping function-calls to the items of a sequence-variable in a way where you can obtain the entire sequence of function-calls in one go without further expansion-trickery.

(In expansion-contexts the difference between
\map{{A}{B}{C}}{\thing} yielding \thing{A}\map{{B}{C}}{\thing}
and
\map{{A}{B}{C}}{\thing} in one go yielding \thing{A}\thing{B}\thing{C}
is important.
Seems with expl3-mapping-functions you get something like the further.
You also may need to cope with controlling the level of expansion of \thing.)

You can work around this, e.g., by mapping the content of the sequence-variable \l__MyModule_entries_seq to a token-list-variable \l__MyModule_entries_tl, hereby prepending \__MyModule_MakeRow:nnn to each element of the sequence and once expanding that command.

After mapping, via \use_ii_i:nn you exchange the content of the token-list-variable \l__MyModule_entries_tl with the tokens \begin{tblr}{lll} A & B & C \\.

\documentclass{article}
\usepackage{xparse,tabularray}

  \ExplSyntaxOn

  % Probably you can use one of the scratch-variables \l_tmpa_seq / \l_tmpb_seq  instead:
  \seq_new:N \l__MyModule_entries_seq
  % Probably you can use one of the scratch-variables \l_tmpa_tl / \l_tmpb_tl  instead:
  \tl_new:N \l__MyModule_entries_tl

  \NewDocumentCommand { \entry } { m m m } {
    \seq_put_right:Nn \l__MyModule_entries_seq { { #1 } { #2 } { #3 } }
  }

  \cs_new:Nn \__MyModule_MakeRow:nnn {
    % NOTE: this macro will also contain some complex logic for rendering the cells content
    #1 & #2 & #3 \\
  }

  % \cs_new:Nn \use_ii_i:nn { #2 #1 }

  \NewDocumentEnvironment { mytblr } {} {\seq_clear:N \l__MyModule_entries_seq} {
    \tl_clear:N \l__MyModule_entries_tl
    \seq_map_inline:Nn \l__MyModule_entries_seq {  \tl_put_right:No \l__MyModule_entries_tl {\__MyModule_MakeRow:nnn ##1} }
    \exp_args:NV \use_ii_i:nn  { \l__MyModule_entries_tl }
    { \begin{tblr}{lll}
        A & B & C \\  }
      X & Y & Z \\
    \end{tblr}
  }

  \ExplSyntaxOff

\begin{document}

  \begin{mytblr}%
    \entry{1}{2}{3}%
    \entry{4}{5}{6}%
  \end{mytblr}

\end{document}

Actually this is unnecessarily complicated. You can do without a sequence-variable, using only a token-list-variable:

\documentclass{article}
\usepackage{xparse,tabularray}

  \ExplSyntaxOn

  % Probably you can use one of the scratch-variables \l_tmpa_tl / \l_tmpb_tl  instead:
  \tl_new:N \l__MyModule_entries_tl


  \NewDocumentCommand { \entry } { m m m } {
    \tl_put_right:No \l__MyModule_entries_tl {\__MyModule_MakeRow:nnn { #1 } { #2 } { #3 } }
  }

  \cs_new:Nn \__MyModule_MakeRow:nnn {
    % NOTE: this macro will also contain some complex logic for rendering the cells content
    #1 & #2 & #3 \\
  }

  % \cs_new:Nn \use_ii_i:nn { #2 #1 }

  \NewDocumentEnvironment { mytblr } {} {\tl_clear:N \l__MyModule_entries_tl} {
    \exp_args:NV \use_ii_i:nn { \l__MyModule_entries_tl }
    { \begin{tblr}{lll}
        A & B & C \\  }
      X & Y & Z \\
    \end{tblr}
  }

  \ExplSyntaxOff

\begin{document}

  \begin{mytblr}%
    \entry{1}{2}{3}%
    \entry{4}{5}{6}%
  \end{mytblr}

\end{document}

I just realized: In expl3 release 2019-08-30 \seq_map_tokens:Nn was introdcuced.

Using this you can do with a sequence-variable only, no need for a token-list-variable:

\documentclass{article}
\RequirePackage{xparse}
\usepackage{tabularray}

  \ExplSyntaxOn

  % Probably you can use one of the scratch-variables \l_tmpa_seq / \l_tmpb_seq  instead:
  \seq_new:N \l__MyModule_entries_seq

  \cs_new:Nn \MyModule_JoinArgsAndCallExpNot:Nnnn {
     % #1 some \exp_args...-command to be applied to #2#4#3
     % #2 tokens to prepend to element of sequence
     % #3 tokens to append to element of sequence
     % #4 element of sequence
     #1 \exp_not:n {#2#4#3}
  }

  \NewDocumentCommand { \entry } { m m m } {
    \seq_put_right:Nn \l__MyModule_entries_seq { { #1 } { #2 } { #3 } }
  }

  \cs_new:Nn \__MyModule_MakeRow:nnn {
    % NOTE: this macro will also contain some complex logic for rendering the cells content
    #1 & #2 & #3 \\
  }

  % \cs_new:Nn \use_ii_i:nn { #2 #1 }

  \NewDocumentEnvironment { mytblr } {} {\seq_clear:N \l__MyModule_entries_seq} {
    \exp_args:Nx \use_ii_i:nn  { 
      \seq_map_tokens:Nn  \l__MyModule_entries_seq { \MyModule_JoinArgsAndCallExpNot:Nnnn \exp_args:No {\__MyModule_MakeRow:nnn} {} }
    }{ \begin{tblr}{lll}
        A & B & C \\  }
      X & Y & Z \\
    \end{tblr}
  }

  \ExplSyntaxOff

\begin{document}

  \begin{mytblr}%
    \entry{1}{2}{3}%
    \entry{4}{5}{6}%
  \end{mytblr}

\end{document}

With the example above I used o-expansion on purpose:

expl3.pdf says:

o One-level-expanded token or braced token list.
This means that the argument is expanded one level, as by \expandafter, and the expansion is passed to the function as a braced token list. Note that if the original argument is a braced token list then only the first token in that list is expanded. In general, using V should be preferred to using o for simple variable retrieval


With all these examples the output is:

enter image description here


These examples are just a starting-point.

The modifications needed depend on the implications of your comment about \make_row:nnn:

% NOTE: this macro will also contain some complex logic for rendering the cells content

In any case the chain of processing and expanding things must work out in a way where the content of the token-list variable, which is to be passed behind the sequence \begin{tblr}{lll} A & B & C \\, consists of a sequence of tokens expanded as far as needed with the body of the tblr-environment.

2

You want to use an expandable mapping, so \seq_map_function:NN.

\documentclass{article}
%\usepackage{xparse} % not needed with LaTeX 2020-10-01 or later
\usepackage{tabularray}

\ExplSyntaxOn

\seq_new:N \pb_mytblr_entries_seq

\NewDocumentCommand { \entry } { m m m }
 {
  \seq_put_right:Nn \pb_mytblr_entries_seq { { #1 } { #2 } { #3 } }
 }

\cs_new:Nn \pb_mytblr_make_row:n
 {
  \pb_mytblr_make_row:nnn #1
 }

\cs_new:Nn \pb_mytblr_make_row:nnn
 {
  % NOTE: this macro will also contain some complex logic for rendering the cells content
  #1 & #2 & #3 \\
 }

\NewDocumentEnvironment { mytblr } {}
 {
  % do nothing at the beginning
 } 
 {
  \use:e
   {
    \exp_not:N \begin{tblr}{lll}
    A & B & C \\
    \seq_map_function:NN \pb_mytblr_entries_seq \pb_mytblr_make_row:n
    X & Y & Z \\
    \exp_not:N \end{tblr}
   }
 }

\ExplSyntaxOff

\begin{document}

  \begin{mytblr}
    \entry{1}{2}{3}
    \entry{4}{5}{6}
  \end{mytblr}

\end{document}

Please, stick with the recommended naming scheme.

enter image description here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.