3

This is a followup to a previous question.

I'm trying to hack the cell parsing of \matrix so that at some point in my code I can hold and manipulate the whole cell content to decide how to render it.

I'm actually pretty close to that:

\input tikz
\catcode`@ = 11

\tikzset{
  my matrix/.style={
    matrix,
    execute at begin cell=\myBeginCell,
    execute at empty cell=\myEmptyCell,
  }
}

\def\myNil{\NOPE}

\def\myBeginCell{
  \let\\\myNewLine
  \pgfutil@ifnextchar\relax{}{\myDoTail}
}

\def\myNewLine{
  \pgfutil@ifnextchar\myNil
    {\expandafter\pgfmatrixendrow\pgfutil@gobble}
    {\pgfmatrixendrow}
}

\bgroup
\catcode`&=\active

\gdef\myDoTail#1\\{
  % \def\TAIL{#1}\show\TAIL%
  \pgfutil@ifnextchar\myNil
    {\myDoHead#1\\}
    {\myDoHead#1&\myNil\\\myNil}
}

\gdef\myDoHead#1&{
  % \def\HEAD{#1}\show\HEAD%
  \node{#1};
  \pgfutil@ifnextchar\myNil{\pgfutil@gobble}{&}
}

\egroup

\def\myEmptyCell{\node{$\emptyset$};}

% \def\myEmptyCell#1[#2#3{
  % \node{$\emptyset$};%
  % \pgfutil@ifnextchar\myNil%
    % {#1[#2#3\pgfutil@gobble}
    % {#1[#2#3}
% }

\catcode`@ = 12

\tikz\matrix[my matrix]{
  1 & 2 & 3 \\
    & 4 & 5 \\
  6 &   & 7 \\
  8 & 9 &   \\
};

\bye

What's happening?

At the beginning of each cell the remainder of the row gets marked with some terminators. E.g.: A & B \\ becomes A & B & \myNil \\ \myNil. The first one is needed to detect when the row is over and allows matching the cells with \gdef\myDoHead#1&{...} by adding a buffer cell at the end that gets detected and ignored. The second terminator is needed to detect whether the row is already marked and a redefinition of \\ gets rid of it when necessary.

What's my problem?

Empty cells are parsed in a different way. This means that when the last cell of a row is empty and the buffer cell (containing \myNil) is added, then it does not get detected and erased. This means that \myNil gets expanded and everything crashes. Making \myNil a no-op is not an acceptable solution: there still is an additional empty cell, effectively doubling all the empty cells at row ends.

What have I tried?

The best I could come up with is the definition commented above:

\def\myEmptyCell#1[#2#3{
  \node{$\emptyset$};%
  \pgfutil@ifnextchar\myNil%
    {#1[#2#3\pgfutil@gobble}
    {#1[#2#3}
}

Those bizarre parameters are needed to hop over the cell closing tokens:

\pgfsys@endscope \let \pgf@matrix@signal@cell@end =\pgf@matrix@signal@cell@end \pgfutil@ifnextchar [{\pgf@matrix@cell@cont }{\pgf@matrix@cell@cont [0pt]}\myNil \\\myNil

For reference (I think) that's the expansion of

\pgfsys@endscope\pgfmatrixnextcell\myNil\\\myNil

This tentative solution is incomplete (does not handle the second \myNil) but it still seems not to be doing what I'd want (throws the usual argument mismatch error I can't make sense of).

Also, I'm not sure this is a good idea but I can't think of anything else short of fiddling directly with pgfmodulematrix.code.tex, and I'd rather avoid that if possible.

What am I asking for?

Either a working definition for \myEmpyCell or a proof that it's impossible and I should ditch the plan. The question is tagged plain-tex because I need a solution as universal as TikZ itself.

Musings and suggestions are also more than welcome!

5
  • 1
    tex-core is not meant for format independence. Use plain-tex if you really need to.
    – percusse
    Oct 11 '16 at 17:13
  • @percusse Ok. I misinterpreted the tag description "{tex-core} is for questions that are independent of formats". Thanks for the pointer+edit. Oct 11 '16 at 17:18
  • Isn't plain a format?
    – cfr
    Oct 12 '16 at 1:19
  • However, I'm sure the tag wiki for tex-core used to say something more helpful.
    – cfr
    Oct 12 '16 at 1:20
  • In fact, it does say something more helpful: {tex-core} is for questions that are independent of formats and macro packages. ... This question is hardly independent of macro packages! However, I think the emphasis on formats is definitely misleading.
    – cfr
    Oct 12 '16 at 1:22
3

Here you go:

\input tikz
\catcode`@ = 11



\tikzset{
  my matrix/.style={
    matrix,
    execute at begin cell=\myBeginCell,
    execute at empty cell=\myEmptyCell,
  }
}

\def\myNil{\NOPE}

\def\myBeginCell{
  \let\\\myNewLine
  \pgfutil@ifnextchar\relax{}{\myDoTail}
}

\def\myNewLine{
  \pgfutil@ifnextchar\myNil
    {\expandafter\pgfmatrixendrow\pgfutil@gobble}
    {\pgfmatrixendrow}
}

\bgroup
\catcode`&=\active

\gdef\myDoTail#1\\{
  \pgfutil@ifnextchar\myNil
    {\myDoHead#1\\}
    {\myDoHead#1&\myNil\\\myNil}
}

\gdef\myDoHead#1&{
  \node{#1};
  \pgfutil@ifnextchar\myNil{\pgfutil@gobble}{&}
}
\egroup


% Here's the new chunk:
% Take up to the \pgfutil@ifnextchar
\def\myEmptyCell#1\pgfutil@ifnextchar[#2#3{
    % evaluate the \pgfutil@ifnextchar so we can be assured of having a pair [#2].
    % We can delete #1 which just has the no-op \let\pgf@matrix@signal@cell@end=\pgf@matrix@signal@cell@end .
    \pgfutil@ifnextchar[{\myEmptyCell@#2}{\myEmptyCell@#3}
}
% Now we know there is "[brackets]" right before the beginning of the next cell, so we can find it.
\def\myEmptyCell@#1[#2]{
    \pgfutil@ifnextchar\myNil{% If the next cell is the terminal cell,
        % Don't reinsert #1[#2] because it would make this code run again on the fake last cell where it would throw an error.
        \myEmptyCell@endline % and eat the \myNil's
    }{
        #1[#2] % Not the last cell, so just reinsert the start a new cell code.
    }
}
\def\myEmptyCell@endline#1\myNil#2\myNil{#1#2}




\tikz
\matrix[my matrix,nodes=draw]{
  1 &[10pt]  &[6pt]  \\[5pt]
  3 &  & 5\\
  6 & 7 &\\
};
\bye

How does it work? We need to check whether we are the last cell. The trick is to eat tokens up to the beginning of the next cell, then check if the cell begins with \myNil. If so, we find the two \myNil's and remove both of them.

The only problem is that the & ending the current cell has already been expanded in the process of it checking whether the cell was empty so to get to the beginning of the next cell, we can't vacuum up to the next ampersand as you might expect. Instead, look at the code of &:

> &=macro:
->\let \pgf@matrix@signal@cell@end =\pgf@matrix@signal@cell@end \pgfutil@ifnextchar [{\pgf@matrix@cell@cont }{\pgf@matrix@cell@cont [0pt]}.

What we see here is a no op \let\pgf@matrix@signal@cell@end\pgf@matrix@signal@cell@end that is used by pgf to tell whether the cell is empty (that's why & is already expanded), followed by a check that handles the optional argument to &.

So we absorb up to the \pgfutil@ifnextchar and it's three arguments. What is next is either the beginning of the next cell or the optional argument to &. We run the \pgfutil@ifnextchar check and make sure brackets are inserted, then we can stick this back in and scan up to [#2] and we know that the next character is the first character of the new cell.

Now, we check if the next character is \myNil. If it's \myNil, remove both the \myNil's and remove the start new cell code we absorbed in order to get up to the next cell (we don't want the terminal & we added to make a second empty cell).

Otherwise, reinsert the start new cell code.

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.