% language=uk

\startcomponent mk-dirtytricks

\environment mk-environment

\chapter {Dirty tricks}

If you ever laid your hands on the \TeX book, the words \quote {dirty tricks} will
forever be associated with an appendix of that book. There is no doubt that you need
to know a bit of the internals of \TEX\ in order to master this kind of trickyness.

In this chaper I will show a few dirty \LUATEX\ tricks. It also gives an impression
of what kind of discussions Taco and I had when discussing what kind of support should
be build in the interface.

\subject{afterlua}

When we look at \LUA\ from the \TEX\ end, we can do things like:

\startbuffer
\def\test#1{%
    \setbox0=\hbox{\directlua0{tex.sprint(math.pi*#1)}}%
    pi: \the\wd0\space\the\ht0\space\the\dp0\par
}
\stopbuffer

\typebuffer \blank \getbuffer \blank

But what if we are at the \LUA\ end and want to let \TEX\ handle things?  Imagine
the following call:

\startbuffer
\setbox0\hbox{} \dimen0=0pt \ctxlua {
    tex.sprint("\string\\setbox0=\string\\hbox{123}")
    tex.sprint("\string\\the\string\\wd0")
}
\stopbuffer

\typebuffer

This gives: \ignorespaces \getbuffer. This may give you the impression that \TEX\
kicks in immediately, but the following example demonstrates otherwise:

\startbuffer
\setbox0\hbox{} \dimen0=0pt \ctxlua {
    tex.sprint("\string\\setbox0=\string\\hbox{123}")
    tex.dimen[0] = tex.box[0].width
    tex.sprint("\string\\the\string\\dimen0")
}
\stopbuffer

\typebuffer

This gives: \getbuffer. When still in \LUA, we never get to see the width
of the box.

A way out of this is the following rather straightforward approach:

\starttyping
function test(n)
    function follow_up()
        tex.sprint(tex.box[0].width)
    end
    tex.sprint("\\setbox0=\\hbox{123}\\directlua 0 {follow_up()}")
end
\stoptyping

We can provide a more convenient solution for this:

\starttyping
after_lua = { } -- could also be done with closures

function the_afterlua(...)
    for _, fun in ipairs(after_lua) do
        fun(...)
    end
    after_lua = { }
end

function afterlua(f)
    after_lua[#after_lua+1] = f
end

function theafterlua(...)
    tex.sprint("\\directlua 0 {the_afterlua("
        .. table.concat({...},',') .. ")}")
end
\stoptyping

If you look closely, you will see that we can (optionally) pass arguments
to the function \type {theafterlua}. Usage now becomes:

\starttyping
function test(n)
    afterlua(function(...)
        tex.sprint(string.format("pi: %s %s %s\\par",...     ))
    end)
    afterlua(function(wd,ht,dp)
        tex.sprint(string.format("ip: %s %s %s\\par",dp,ht,wd))
    end)
    tex.sprint(string.format("\\setbox0=\\hbox{%s}",math.pi*n))
    local box_0 = tex.box[0]
    theafterlua(box_0.width,box_0.height,box_0.depth)
end
\stoptyping

The last call may confuse you but since it does a print to \TEX, it is
in fact a delayed action. A cleaner implementation is the following:

\starttyping
local delayed = { }

local function flushdelayed(...)
    delayed = { }
    for i=1, #t do
        t[i](...)
    end
end

function lua.delay(f)
    delayed[#delayed+1] = f
end

function lua.flush(...)
    tex.sprint("\\directlua{flushdelayed(" ..
        table.concat({...},',') .. ")}")
end
\stoptyping

Usage is similar:

\starttyping
function test(n)
    lua.delay(function(...)
        tex.sprint(string.format("pi: %s %s %s\\par",...))
    end)
    tex.sprint(string.format("\\setbox0=\\hbox{%s}",math.pi*n))
    local box_0 = tex.box[0]
    lua.flush(box_0.width,box_0.height,box_0.depth)
end
\stoptyping

\stopcomponent
