summaryrefslogtreecommitdiff
path: root/doc/context/sources/general/manuals/about/about-luafunctions.tex
diff options
context:
space:
mode:
Diffstat (limited to 'doc/context/sources/general/manuals/about/about-luafunctions.tex')
-rw-r--r--doc/context/sources/general/manuals/about/about-luafunctions.tex292
1 files changed, 292 insertions, 0 deletions
diff --git a/doc/context/sources/general/manuals/about/about-luafunctions.tex b/doc/context/sources/general/manuals/about/about-luafunctions.tex
new file mode 100644
index 000000000..810de10fd
--- /dev/null
+++ b/doc/context/sources/general/manuals/about/about-luafunctions.tex
@@ -0,0 +1,292 @@
+% language=uk
+
+\startcomponent about-properties
+
+\environment about-environment
+
+\startchapter[title=Functions]
+
+\startsection[title=Introduction]
+
+As part of the crited project Luigi and I also tried to identity weak spots in
+the engine and although we found some issues not all were dealt with because
+complicating the machinery makes no sense. However just like the new \type
+{properties} mechanism provides a real simple way to associate extra \LUA\ data
+to a node without bothering about freeing it when a node is flushed, the next
+\type {luafunctions} mechanism provides an additional and fast way to cross the
+\TEX||\LUA\ boundary.
+
+\stopsection
+
+\startsection[title=Callbacks]
+
+In \LUATEX\ we can create more functionality by using \LUA\ which means that we
+end up (at least in \CONTEXT) with a constant switching between \TEX\ macro
+expansion and \LUA\ code interpretation. The magic word in this process is \type
+{callback} and there are two variants:
+
+\startitemize
+
+\startitem At well defined moments in processing its input and node lists, \TEX\
+will check if a specific callback is defined and if so, it will run that code.
+\stopitem
+
+\startitem As part of the input you can have a \type {\directlua} command and
+that one gets expanded and processed. It can print back content into the current
+input buffer. \footnote {Currently this process is somewhat more complex than
+needed, which is a side effect of supporting multiple \LUA\ states in the first
+versions of \LUATEX. We will clean up this mechanism at some point.} \stopitem
+
+\stopitemize
+
+The first type is call a \quote {direct} callback because \TEX\ calls it
+directly, and the second one is an \quote {indirect} one (even if the command is
+\type {\directlua}). It has a deferred cousin \type {\latelua} that results in a
+node being inserted that will become a \LUA\ call during shipout, when the page
+is turned into a \PDF\ stream.
+
+A callback of the first category is pretty fast because the code is already
+translated in \LUA\ bytecode. Checking if a callback has been assigned at all is
+fast too. The second variant is slower because each time the input has to be
+interpreted and checked on validity. Then there is of course some overhead in
+making the call itself.
+
+There is a subtle aspect there. If you have a document that needs say ten calls
+like:
+
+\starttyping
+\directlua{tex.print("[x]")}
+\stoptyping
+
+and you have these calls inlined, you end up with ten times conversion into
+tokens (\TEX's internal view) and ten times conversion back to a string that gets
+fed into \LUA. On the other hand,
+
+\starttyping
+\def\MyCall{\directlua{tex.print("[x]")}}
+\stoptyping
+
+where we call \type {\MyCall} ten times is more efficient because we have already
+tokenized the \type {\directlua}. If we have
+
+\starttyping
+foo foo foo \directlua{tex.print("[1]")} ...
+bar bar bar \directlua{tex.print("[2]")} ...
+\stoptyping
+
+It makes sense to wrap this into a definition:
+
+\starttyping
+\def\MyCall#1{\directlua{tex.print("[#1]")}}
+\stoptyping
+
+and use:
+
+\starttyping
+foo foo foo \MyCall{1} bar bar bar \MyCall{1} ...
+\stoptyping
+
+Of course this is not unique for \type {\directlua} and to be honest, apart from
+convenience (read: less input) the gain often can be neglected. Because a macro
+package wraps functionality in (indeed) macros we already save us the tokenization
+step. We can save some time by wrapping more in a function at the \LUA\ end:
+
+\starttyping
+\startluacode
+function MyFloat(f)
+ tex.print(string.format("%0.5f",f))
+end
+\stopluacode
+
+\def\MyFloat#1%
+ {\directlua{MyFloat(#1)}}
+\stoptyping
+
+This is somewhat more efficient than:
+
+\starttyping
+\def\MyFloat#1%
+ {\directlua{tex.print(string.format("\letterpercent0.5f",#1))}}
+\stoptyping
+
+\stopsection
+
+Of course this is only true when we call this macro a lot of times.
+
+\startsection[title=Shortcuts]
+
+When we talk of \quote {often} or \quote {a lot} we mean many thousands of calls.
+There are some places in \CONTEXT\ where this is indeed the case, for instance
+when we process large registers in critical editions: a few hundred pages of
+references generated in \LUA\ is no exception there. Think of the following:
+
+\starttyping
+\startluacode
+function GetTitle(n)
+ tex.print(Entries[n].title)
+end
+\stopluacode
+
+\def\GetTitle#1%
+ {\directlua{GetTitle(#1)}}
+\stoptyping
+
+If we call \type {\GetTitle} ourselves it's the same as the \type {\MyFloat}
+example, but how about this:
+
+\starttyping
+\def\GetTitle#1%
+ {{\bf \directlua{GetTitle(#1)}}}
+
+\startluacode
+function GetTitle(n)
+ tex.print(Entries[n].title)
+end
+
+function GetEntry(n)
+ if Entries[n] then
+ tex.print("\\directlua{GetTitle(",n,")}")
+ -- some more action
+ end
+end
+\stopluacode
+\stoptyping
+
+Here we have two calls where one is delayed till a later time. This delay results
+in a tokenization and transation to \LUA\ so it will cost time. A way out is this:
+
+\starttyping
+\def\GetTitle#1%
+ {{\bf \luafunction#1}}
+
+\startluacode
+local functions = tex.get_functions_table()
+
+function GetTitle(n)
+ tex.print(Entries[n].title)
+end
+
+function GetEntry(n)
+ if Entries[n] then
+ local m = #functions+1
+ functions[m] = function() GetTitle(n) end
+ tex.print("\\GetTitle{",m,"}")
+ -- some more action
+ end
+end
+\stopluacode
+\stoptyping
+
+We define a function at the \LUA\ end and just print a macro call. That call itself
+calls the defined function using \type {\luafunction}. For a large number
+of calls this is more efficient but it will be clear that you need to make sure that
+used functions are cleaned up. A simple way is to start again at slot one after (say)
+100.000 functions, another method is to reset used functions and keep counting.
+
+\starttyping
+\startluacode
+local functions = tex.get_functions_table()
+
+function GetTitle(n)
+ tex.print(Entries[n].title)
+end
+
+function GetEntry(n)
+ if Entries[n] then
+ local m = #functions+1
+ functions[m] = function(slot) -- the slot number is always
+ GetTitle(n) -- passed as argument so that
+ functions[slot] = nil -- we can reset easily
+ end
+ tex.print("\\GetTitle{",m,"}")
+ -- some more action
+ end
+end
+\stopluacode
+\stoptyping
+
+As you can expect, in \CONTEXT\ users are not expect to deal directly with
+functions at all. Already for years you can so this:
+
+\starttyping
+\def\GetTitle#1%
+ {{\bf#1}}
+
+\startluacode
+function GetEntry(n)
+ if Entries[n] then
+ context(function() context.GetTitle(Entries[n].title) end)
+ -- some more action
+ end
+end
+\stopluacode
+\stoptyping
+
+Upto \LUATEX\ 0.78 we had a \CONTEXT\ specific implementation of functions and
+from 0.79 onwards we use this new mechanism but users won't see that in practice.
+In the \type {cld-mkiv.pdf} manual you can find more about accessing \CONTEXT\
+from the \LUA\ end.
+
+Keep in mind that \type {\luafunction} is not that clever: it doesn't pick up
+arguments. That will be part of future more extensive token handling but of
+course that will then also be a real slow downer because a mix of \TEX\
+tokenization and serialization is subtoptimal (we already did extensive tests
+with that).
+
+\stopsection
+
+\startsection[title=Helpers]
+
+The above mechanism demands some orchestration in the macro package. For instance
+freeing slots should be consistent and therefore user should not mess directly
+with the functions table. If you really want to use this feature you can best do this:
+
+\starttyping
+\startctxfunction MyFunctionA
+ context(" A1 ")
+\stopctxfunction
+
+\startctxfunctiondefinition MyFunctionB
+ context(" B2 ")
+\stopctxfunctiondefinition
+
+\starttext
+ \dorecurse{10000}{\ctxfunction{MyFunctionA}} \page
+ \dorecurse{10000}{\MyFunctionB} \page
+ \dorecurse{10000}{\ctxlua{context(" C3 ")}} \page
+ \dorecurse{10000}{\ctxlua{tex.sprint(" D4 ")}} \page
+\stoptext
+\stoptyping
+
+In case you're curious about performance, here are timing. Given that we have
+10.000 calls the gain is rather neglectable especially because the whole run
+takes 2.328 seconds for 52 processed pages resulting in 22.4 pages per second.
+The real gain is in more complex calls with more tokens involved and in \CONTEXT\
+we have some placed where we run into the hundreds of thousands. A similar
+situation occurs when your input comes from databases and is fetched stepwise.
+
+\starttabulate[|c|c|c|c|]
+\NC \bf A \NC \bf B \NC \bf C \NC \bf D \NC \NR
+\NC 0.053 \NC 0.044 \NC 0.081 \NC 0.081 \NC \NR
+\stoptabulate
+
+So, we can save 50\% runtime but on a simple document like this a few percent is
+not that much. Of course many such few percentages can add up, and it's one of
+the reasons why \CONTEXT\ \MKIV\ is pretty fast in spite of all the switching
+between \TEX\ and \LUA. One objective is that an average complex document should
+be processed with a rate of at least 20 pages per second and in most cases we
+succeed. This fast function accessing can of course trigger new features in
+\CONTEXT, ones we didn't consider useful because of overhead.
+
+Keep in mind that in most cases, especially when programming in \LUA\ directly
+the \type {context} command already does all kind of housekeeping for you. For
+instance it also keeps track of so called trial typesetting runs and can inject
+nodes in the current stream as well. So, be warned: there is no real need to
+complicate your code with this kind of hackery if some high level subsystem
+provides the functionality already.
+
+\stopsection
+
+\stopchapter
+
+\stopcomponent