olsak / OpTeX

OpTeX - LuaTeX format with extended Plain TeX macros
http://petr.olsak.net/optex/
33 stars 13 forks source link

Unexpected whitespace when using `\crx` / `\crtop` via autoload #165

Closed robertbachmann closed 4 months ago

robertbachmann commented 5 months ago

Example to reproduce:

\fontfam[lm]
%\loadtrick\crx \loadtrick\crtop

\frame{\table{ll}{\crx 
1&2\crx 
3&4 \crx
5&6
}}

\bigskip \bigskip

\frame{\table{ll}{\crtop
1&2\crmid
3&4 \cr
5&6\crbot
}}

\bye

Result:

image

Note: If we uncomment line 2, everything works as expected.

olsak commented 5 months ago

IMHO, this problem hasn't simple solution. The core of the problem can be demonstrated in the following example. Compare:

\hrule
\halign{#\cr \crcr
aha\cr
}
\bye

and

\hrule
\halign{#\cr \relax \crcr
aha\cr
}
\bye

The macro \crtop (or \crx) have to be started by \crcr primitive, because it can be used after \cr (and we needn't to create a new row in the table) or it can be used at the end of the row and we want to close this row. As soon as we add an unexpandable primitive command (\relax in the example above) between \cr and \crcr then the new row is created. This is feature of the \halign primitive. You can replace the \relax in the example by \def\a{} and you get the same empty row. On the other hand \immediateassignment\def\a{} doesn't create empty row. But we want to run \_loadtrick macro if an autoloaded macro is firstly used and \_loadtrick runs \setbox. The \setbox assignment cannot be prefixed by \immediateasignment.

I can add a comment to these two optex tricks that the autoloading doesn't work inside the table (under certain circumstances) but I am unable to solve this problem by an elegant macro set.

robertbachmann commented 5 months ago

I think documenting this restriction should be good enough.

Udi-Fogiel commented 5 months ago

Maybe we can use tex.runtoks?

\fontfam[lm]

\_def\_regtrick#1{\_ifx#1\_undefined\_def#1{%
    \_directlua{tex.runtoks(token.get_next)}%
    \_loadtrick#1\_endlocalcontrol#1}\_else\_badtrick\_fi}

\_let \crx \_undefined
\_let \crtop \_undefined

\_xargs \_regtrick \crx \crtop ;

\frame{\table{ll}{\crx 
1&2\crx 
3&4 \crx
5&6
}}

\bigskip \bigskip

\frame{\table{ll}{\crtop
1&2\crmid
3&4 \cr
5&6\crbot
}}

\bye
Udi-Fogiel commented 5 months ago

It is also possible to define a new "pseudoprimitive" based on tex.runtoks, that will allow to create blocks of code that gets executed (not just expanded) during the expansion stage. For example, here I define \_beglocalcontrol, which starts the block (\_endlocalcontrol is already defined in LuaTeX ):

\fontfam[lm]

\_directlua{
optex.define_lua_command("_beglocalcontrol", function()
    return tex.runtoks(token.get_next)
end)
}

\_def\_regtrick#1{\_ifx#1\_undefined\_def#1{%
        \_beglocalcontrol\_loadtrick#1\_endlocalcontrol#1}%
    \_else\_badtrick\_fi}

\_let \crx \_undefined
\_let \crtop \_undefined

\_xargs \_regtrick \crx \crtop ;

\frame{\table{ll}{\crx 
1&2\crx 
3&4 \crx
5&6
}}

\bigskip \bigskip

\frame{\table{ll}{\crtop
1&2\crmid
3&4 \cr
5&6\crbot
}}

\bye
olsak commented 5 months ago

Thank you very much for noticing about tex.runtoks. I'll experiment with it and use it for autoloading optex tricks.

olsak commented 5 months ago

I hope that my last commit solves this issue. Thanks you very much Udi Fogiel.