summaryrefslogtreecommitdiff
path: root/doc/context/sources/general/manuals/onandon/onandon-expansion.tex
diff options
context:
space:
mode:
Diffstat (limited to 'doc/context/sources/general/manuals/onandon/onandon-expansion.tex')
-rw-r--r--doc/context/sources/general/manuals/onandon/onandon-expansion.tex307
1 files changed, 307 insertions, 0 deletions
diff --git a/doc/context/sources/general/manuals/onandon/onandon-expansion.tex b/doc/context/sources/general/manuals/onandon/onandon-expansion.tex
new file mode 100644
index 000000000..73a0b4953
--- /dev/null
+++ b/doc/context/sources/general/manuals/onandon/onandon-expansion.tex
@@ -0,0 +1,307 @@
+% language=uk
+
+\startcomponent onandon-expansion
+
+\environment onandon-environment
+
+\startchapter[title={More (new) expansion trickery}]
+
+Contrary to what one might expect when looking at macro definitions, \TEX\ is
+pretty efficient. Occasionally I wonder if some extra built in functionality
+could help me write better code but when you program with a bit care there is
+often not much to gain in terms of tokens and performance. \footnote {The long
+trip to the yearly Bacho\TeX\ meeting is always a good opportunity to ponder
+\TEX\ and its features. The new functionality discussed here is a side effect of
+the most recent trip.} Also, some possible extensions probably only would be
+applied a few times which makes them low priority. When you look at the
+extensions brought by \ETEX\ the number is not that large, and \LUATEX\ only
+added a few that deal with the language, for instance \tex {expanded} which is
+like an \tex {edef} without the defining a macro and acts on a token list wrapped
+in (normally) curly braces. Just as reference we mention some of the expansion
+related helpers.
+
+\starttabulate[|l|l|p|]
+\BC command \BC argument \BC
+ comment
+\NC \NR
+\HL
+\NC \tex {expandafter} \NC \type {token} \NC
+ The token after the next token gets expanded (one level only). In tricky
+ \TEX\ code you can often see multiple such commands in sequence which makes a
+ nice puzzle.
+\NC \NR
+\NC \tex {noexpand} \NC \type {token} \NC
+ The token after this command is not expanded in the context of expansion.
+\NC \NR
+\NC \tex {expanded} \NC \type {{tokens}} \NC
+ The given token list is expanded. This command showed up early in \LUATEX\
+ development and was taken from \ETEX\ follow|-|ups. I have mails from 2011
+ mentioning its presence in \PDFTEX\ 1.50 (which was targeted in 2008) but
+ somehow it never ended up in a production version at that time (and we're
+ still not at that version). In \CONTEXT\ we already had a command with that
+ name so there we use \tex {normalexpanded}. Users normally can just use the
+ \CONTEXT\ variant of \type {\expanded}.
+\NC \NR
+\NC \tex {unexpanded} \NC \type {{tokens}} \NC
+ The given token list is hidden from expansion. Again, in \CONTEXT\ we already
+ had a command serving as prefix for definitions so instead we use \tex
+ {normalunexpanded}. In the core of \CONTEXT\ this new \ETEX\ command is hardly
+ used.
+\NC \NR
+\NC \tex {detokenize} \NC \type {{tokens}} \NC
+ The given tokenlist becomes (basically) verbatim \TEX\ code. We had something
+ like that in \CONTEXT\ but have no nameclash. It is used in a few places. It's
+ also an \ETEX\ command.
+\NC \NR
+\NC \tex {scantokens} \NC \type {{tokens}} \NC
+ This primitive interprets its argument as a pseudo file. We don't really use it.
+\NC \NR %
+\NC \tex {scantextokens} \NC \type {{tokens}} \NC
+ This \LUATEX\ primitive does the same but has no end|-|of|-|file side
+ effects. This one is also not really used in \CONTEXT.
+\NC \NR
+\NC \tex {protected} \NC \type {\.def} \NC
+ The definition following this prefix, introduced in \ETEX, is unexpandable in
+ the context of expansion. We already used such a command in \CONTEXT\ but
+ with a completely different meaning so use \tex {normalprotected} as prefix
+ or \tex {unexpanded} which is an alias.
+\NC \NR
+\stoptabulate
+
+Here I will present two other extensions in \LUATEX\ that can come in handy, and
+they are there simply because their effect can hardly be realized otherwise
+(never say never in \TEX). One has to do with immediately applying a definition,
+the other with user defined conditions. The first one relates directly to
+expansion, the second one concerns conditions and relates more to parsing
+branches which on purpose avoids expansion.
+
+For the first one I use some silly examples. I must admit that although I can
+envision useful application, I really need to go over the large amount of
+\CONTEXT\ source code to really find a place where it is making things better.
+Take the following definitions:
+
+\startbuffer
+\newcount\NumberOfCalls
+
+\def\TestMe{\advance\NumberOfCalls1 }
+
+\edef\Tested{\TestMe foo:\the\NumberOfCalls}
+\edef\Tested{\TestMe foo:\the\NumberOfCalls}
+\edef\Tested{\TestMe foo:\the\NumberOfCalls}
+
+\meaning\Tested
+\stopbuffer
+
+\typebuffer
+
+The result is a macro \tex {Tested} that not only has the unexpanded incrementing
+code in its body but also hasn't done any advancing:
+
+\getbuffer
+
+Of course when you're typesetting something, this kind of expansion normally is
+not needed. Instead of the above definition we can define \tex {TestMe} in a way
+that expands the assignment immediately. You need of course to be aware of
+preventing look ahead interference by using a space or \tex {relax} (often an
+expression works better as it doesn't leave an \tex {relax}).
+
+\startbuffer
+\def\TestMe{\immediateassignment\advance\NumberOfCalls1 }
+
+\edef\Tested{\TestMe bar:\the\NumberOfCalls}
+\edef\Tested{\TestMe bar:\the\NumberOfCalls}
+\edef\Tested{\TestMe bar:\the\NumberOfCalls}
+
+\meaning\Tested
+\stopbuffer
+
+\typebuffer
+
+This time the counter gets updated and we don't see interference in the resulting
+\tex {Tested} macro:
+
+\getbuffer
+
+Here is a somewhat silly example of an expanded comparison of two \quote
+{strings}:
+
+\startbuffer
+\def\expandeddoifelse#1#2#3#4%
+ {\immediateassignment\edef\tempa{#1}%
+ \immediateassignment\edef\tempb{#2}%
+ \ifx\tempa\tempb
+ \immediateassignment\def\next{#3}%
+ \else
+ \immediateassignment\def\next{#4}%
+ \fi
+ \next}
+
+\edef\Tested
+ {(\expandeddoifelse{abc}{def}{yes}{nop}/%
+ \expandeddoifelse{abc}{abc}{yes}{nop})}
+
+\meaning\Tested
+\stopbuffer
+
+\typebuffer
+
+I don't remember many cases where I needed such an expanded comparison. We have a
+variant in \CONTEXT\ that uses \LUA\ but that one is not really used in the core.
+Anyway, the above code gives:
+
+\getbuffer
+
+You can do the same assignments as in preambles of \tex {halign} and after \tex
+{accent} which means that assignments to box registers are blocked (boxing
+involves grouping and delayed assignments and so). The error you will get when
+you use a non||assignment command refers to a prefix, because internally such
+commands are called prefixed commands. Leading spaces and \tex {relax} are
+ignored.
+
+In addition to this one|-|time immediate assignment a pseudo token list variant
+is provided, so the above could be rewritten to:
+
+\starttyping
+\def\expandeddoifelse#1#2#3#4%
+ {\immediateassigned {
+ \edef\tempa{#1}
+ \edef\tempb{#2}
+ }%
+ \ifx\tempa\tempb
+ \immediateassignment\def\next{#3}%
+ \else
+ \immediateassignment\def\next{#4}%
+ \fi
+ \next}
+\stoptyping
+
+While \tex {expanded} first builds a token lists that then gets used, the \tex
+{immediateassigned} primitive just walls over the list delimited by curly braces.
+
+A next extension concerns conditions. If you have done a bit of extensive \TEX\
+programming you know that nested conditions need to be properly constructed in
+for instance macro bodies. This is because (for good reason) \TEX\ goes into a
+fast scanning mode when there is a match and it has to skip the \tex {else} upto
+\tex {fi} branch. In order to do that properly a nested \tex {if} in there needs
+to have a matching \tex {fi}.
+
+In practice this is no real problem and careful coding will never give a problem
+here: you can either hide nested code in a macro or somehow jump over nested
+conditions if really needed. Actually you only need to care when you pickup a
+token inside the branch because likely you don't want to pick up for instance a
+\tex {fi} but something that comes after it. Say that we have a sane conditional
+setup like this:
+
+\starttyping
+\newif\iffoo \foofalse
+\newif\ifbar \bartrue
+
+\ifoo
+ \ifbar \else \fi
+\else
+ \ifbar \else \fi
+\fi
+\stoptyping
+
+Here the \tex {iffoo} and \tex {ifbar} need to be equivalent to \tex {iftrue} or
+\tex {iffalse} in order to succeed well and that is what for instance \tex
+{footrue} and \tex {foofalse} will do: change the meaning of \tex {iffoo}.
+
+But imagine that you want something more complex. You want for instance to let
+\tex {ifbar} do some calculations. In that case you want it to behave a bit like
+what a so called \type {vardef} in \METAPOST\ does: the end result is what
+matters. Now, because \TEX\ macros often are a complex mix of expandable and
+non|-|expandable this is not that trivial. One solution is a dedicated definer,
+say \tex {cdef} for defining a macro with conditional properties. I actually
+implemented such a definer a few years ago but left it so long in a folder with
+ideas that I only found it back after I had come up with another solution. It was
+probably proof that it was not that good an idea.
+
+The solution implemented in \LUATEX\ is just a special case of a test: \tex
+{ifcondition}. When looking at the next example, keep in mind that from the
+perspective of \TEX's scanner it only needs to know if something is a token that
+does some test and has a matching \tex {fi}. For that purpose you can consider
+\tex {ifcondition} to be \tex {iftrue}. When \TEX\ actually wants to do a test,
+which is the case in the true branch, then it will simply ignore this \tex
+{ifcondition} primitive and expands what comes after it (which is \TEX's natural
+behaviour). Effectively \tex {ifcondition} has no meaning except from when it has
+to be skipped, in which case it's a token flagged as \tex {if} kind of command.
+
+\starttyping
+\unexpanded\def\something#1#2%
+ {\edef\tempa{#1}%
+ \edef\tempb{#2}
+ \ifx\tempa\tempb}
+
+\ifcondition\something{a}{b}%
+ \ifcondition\something{a}{a}%
+ true 1
+ \else
+ false 1
+ \fi
+\else
+ \ifcondition\something{a}{a}%
+ true 2
+ \else
+ false 2
+ \fi
+\fi
+\stoptyping
+
+Wrapped in a macro you can actually make this fully expandable when you use the
+previously mentioned immediate assignment. Here is another example:
+
+\starttyping
+\unexpanded\def\onoddpage
+ {\ifodd\count0 }
+
+\ifcondition\onoddpage odd \else even \fi page
+\stoptyping
+
+The previously defined comparison macro can now be rewritten as:
+
+\starttyping
+\def\equaltokens#1#2%
+ {\immediateassignment\edef\tempa{#1}%
+ \immediateassignment\edef\tempb{#2}%
+ \ifx\tempa\tempb}
+
+\def\expandeddoifelse#1#2#3#4%
+ {\ifcondition\equaltokens{#1}{#2}%
+ \immediateassignment\def\next{#3}%
+ \else
+ \immediateassignment\def\next{#4}%
+ \fi
+ \next}
+\stoptyping
+
+When used this way it will of course also work without the \tex {ifcondition} but
+when used nested it can be like this. This last example also demonstrates that
+this feature probably only makes sense in more complicated cases where more work
+is done in the \tex {onoddpage} or \tex {equaltokens} macro. And again, I am not
+sure if for instance in \CONTEXT\ I have a real use for it because there are only
+a few cases where nesting like this could benefit. I did some tests with a low
+level macro where it made the code look nicer. It was actually a bit faster but
+most core macros are not called that often. Although the overhead of this feature
+can be neglected, performance should not be the reason for using it: in \CONTEXT\
+for instance one can often only measure such possible speed|-|ups on macros that
+are called tens or hundreds of thousands of times and that seldom happens in a
+real run end even then a change from say 0.827 seconds to 0.815 seconds for 10K
+calls of a complex case is just noise as the opposite can also happen.
+
+Although not strictly necessary these extensions might make some code look better
+so that is why they officially will be available in the 1.09 release of \LUATEX\
+in fall 2018. It might eventually inspire me to go over some code and see where I
+can improve the look and feel.
+
+The last few years I have implemented some more ideas as local experiments, for
+instance \tex {futurelet} variant or a simple (one level) \tex {expand}, but in
+the end rejected them because there is no real benefit in them (no better looking
+code, no gain in performance, hard to document, possible side effects, etc.), so
+it is very unlikely that we will have more extensions like this. After all, we
+could do more than 40 years without them. Although \unknown\ who knows what we
+will provide in \LUATEX\ version~2.
+
+\stopchapter
+
+\stopcomponent