9

I've been trying to use a macro which abstracts the structure of an \halign object to a number of columns, as mentioned in my previous question "Can I use a macro in \halign's preamble?." For that part of the problem, \span worked well, but I haven't been able to get an integrated solution working.

I tried to use this code:

\count1=4 \countdef\columns=1
\def\subpre{
  & ##
  \advance\columns by -1 \ifnum\columns>0 \span\subpre \fi
}
\def\preamb{
  ##
  \advance \columns by -1
  \the\columns
  \ifnum\columns>0 \span\subpre \fi
  \cr
}
\halign{\span\preamb
  first line \cr
  second line \cr
  third line \cr
}
\bye

to create an \halign which can be modified by changing the number which is assigned to count1, but this results in the error:

! TeX capacity exceeded, sorry [input stack size=5000].
\subpre ...by -1 \ifnum \columns >0 \span \subpre 
                                                  \fi 
\subpre ...by -1 \ifnum \columns >0 \span \subpre 
                                                  \fi 
\subpre ...by -1 \ifnum \columns >0 \span \subpre 
                                                  \fi 
\subpre ...by -1 \ifnum \columns >0 \span \subpre 
                                                  \fi 
\subpre ...by -1 \ifnum \columns >0 \span \subpre 
                                                  \fi 
\subpre ...by -1 \ifnum \columns >0 \span \subpre 
                                                  \fi 
...
l.13 \halign{\span\preamb

I think this is because the \span\subpre in \subpre automatically expands even if it's not supposed to be evaluated by the if statement, but I can't figure out how to expand it conditionally like in a "standard" recursive loop. What's the best way to conditionally expand the code within the if statement on line 4? I'm using Plain TeX.

4
  • 3
    I'm pretty sure that \preamb has to be fully expandable. Can you use e-TeX features? Jun 17 '19 at 1:49
  • @HenriMenke I'd prefer to use just plain TeX because I'm trying to learn more about the underlying engine, but I'm just using this for a personal project, so I'd be able to. Which features would be necessary? Jun 17 '19 at 2:31
  • See for example: tex.stackexchange.com/questions/428675/… Jun 17 '19 at 3:03
  • 2
    all your columns are the same so you don't need a loop or count you could just use \halign&#\cr (or usually with some glue \halign&#\hfill\cr ) Jun 17 '19 at 6:55
7

As Henri Menke wrote in a comment, your preamble has to be expandable (and every token that should be expanded has to be preceded by \span). So how do we build a fully expandable loop only using plain TeX? We use \romannumeral. If \count1 is some integer, then we can use \the\count1 000 to get 1000*\count1. This is expressed as a roman numeral by exactly \count1 times the letter m. \romannumeral is expandable, so we can use this in our preamble. Then our macro is repeated, consuming one m at every iteration, until the ms run out:

\count1=4
\def\subpre#1{
  \span\if m#1
    & ##
    \span\expandafter
    \span\expandafter\subpre
  \fi
}
\def\gobble#1{}
\def\preamb{
  ##
  \span\expandafter
  \span\expandafter\expandafter
  \expandafter\span\expandafter\expandafter\expandafter\subpre
  \expandafter\gobble\romannumeral\the\count1 000x
  \cr
}
\halign{\span\preamb
  first& line&val&x \cr
}
\bye

To understand all the \expandafters in \preamb, try to think as if you were TeX: In \preamb, first we add ## to the preamble. Then there is \span, so the next token is expanded. It is \expandafter, so we skip one token and expand the next, which (again) is \expandafter. This repeats 6 times, expanding the first, second, fourth, fifth, seventh and eighth \expandafter in \preamb until finally expanding \romannumeral\the\count1 000x. This expands to \romannumeral4000x and therefore mmmmx. Without the \expandafters and \romannumeral expanded, we are left with the tokens

\span\expandafter
\span\expandafter\subpre\gobble mmmmx

Again, we have \span, so we expand this. The remaining \expandafters end up expanding \gobble which gobbles the first m. This is the equivalent to \advance \columns by -1 in your original code. So what remains is

\span\subpre mmmx

which finally starts expanding \subpre.

On the other hand, it is often much easier to not restrict yourself to expandable constructs. Instead you can construct the preamble first (e.g.in a token list), and only insert the finished preamble into \halign: (based on David Carlisle's answer to your original question)

\newcount\columns
\newtoks\preambtoks
\def\myhalign{%
  \afterassignment\xmyhalign
  \columns=%
}
\def\xmyhalign{\afterassignment\xxmyhalign\let\tmp=}
\def\subpre{
  \preambtoks\expandafter{\the\preambtoks & ##}
  \advance\columns by -1 \ifnum\columns>0 \subpre \fi
}
\def\xxmyhalign{%
  \preambtoks{##}%
  \advance \columns by -1
  \ifnum\columns>0 \subpre \fi
  \halign\bgroup\span\the\preambtoks\cr
}

\myhalign3{
first line & second column \cr
1& 2&3 \cr
}
\bye
2
  • Very nice answer, +1! So, the \afterassignment...\let\tmp= in the last example is only there to gobble an opening brace, which wouldn't work with \gobble defined as \def\gobble#1{}, as that would swallow the whole braced group...
    – frougon
    Jun 17 '19 at 7:46
  • 1
    @frougon Right, the alternative would be to read the entire group as an argument and then using #1} in xxmyhalign, but that would fix the catcodes, messing up verbatim and similar stuff. Jun 17 '19 at 8:47
2

The primitive \span in the context of a \halign preamble, performs a single expansion step.

You need some more sophisticated code if you want to allocate a variable number of columns.

Here I define \preamb to take as argument a column template (in the example, \hfil#\hfil); this template is repeated as many times as the current value of \columns (assumed to be positive).

The trick is to use \span to expand \romannumeral-`@, that does recursive expansion until finding an unexpandable token.

The \subpre macro is called \columns-1 times and each time the third argument contains the preamble-so-far augmented by a further column.

% syntactic sugar
\long\def\firstoftwo#1#2{#1}
\long\def\secondoftwo#1#2{#2}

% the code

\newcount\columns

\def\preamb#1{#1\span\romannumeral-`@\subpre\columns{#1}{}}

\def\subpre#1#2#3{%
  \ifnum\numexpr#1-1=0
    \expandafter\firstoftwo
  \else
    \expandafter\secondoftwo
  \fi
  {#3\cr}%
  {\expandafter\subpre\expandafter{\the\numexpr#1-1}{#2}{#3&#2}}%
}

% the test

\tabskip=10pt
\columns=4
\halign{\span\preamb{\hfil#\hfil}%
  1&2&3&4\cr
  AAA&BB&CCCC&DDDD\cr
  u&v&w&x\cr
}

\bye

enter image description here

1
  • In my opinion an excellent user should not, at least when I am there, have zero scores when his answers are always by 110 e lode.
    – Sebastiano
    Jun 20 '19 at 19:59

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.