diff options
author | Hans Hagen <pragma@wxs.nl> | 2019-09-27 20:24:34 +0200 |
---|---|---|
committer | Context Git Mirror Bot <phg@phi-gamma.net> | 2019-09-27 20:24:34 +0200 |
commit | 0a5f59a9aa25b3de7e9659b39ad201aaf7eb5a67 (patch) | |
tree | a652bb083d6bc6a9b6309908dd5d09a539ac3859 /doc/context/sources/general/manuals/lowlevel | |
parent | 58c7c9288160407c874930aac789ef6ef3faa6b5 (diff) | |
download | context-0a5f59a9aa25b3de7e9659b39ad201aaf7eb5a67.tar.gz |
2019-09-27 18:10:00
Diffstat (limited to 'doc/context/sources/general/manuals/lowlevel')
5 files changed, 2904 insertions, 0 deletions
diff --git a/doc/context/sources/general/manuals/lowlevel/lowlevel-boxes.tex b/doc/context/sources/general/manuals/lowlevel/lowlevel-boxes.tex new file mode 100644 index 000000000..986d07b1b --- /dev/null +++ b/doc/context/sources/general/manuals/lowlevel/lowlevel-boxes.tex @@ -0,0 +1,698 @@ +% language=us + +% \hfil \hss +% spread + +\environment lowlevel-style + +\startdocument + [title=boxes, + color=middlered] + +\startsection[title=Preamble] + +\startsubsection[title=Introduction] + +An average \CONTEXT\ user will not use the low level box primitives but a basic +understanding of how \TEX\ works doesn't hurt. In fact, occasionally using a box +command might bring a solution not easily achieved otherwise, simply because a +more high level interface can also be in the way. + +The best reference is of course The \TeX book so if you're really interested in +the details you should get a copy of that book. Below I will not go into details +about all kind of glues, kerns and penalties, just boxes it is. + +This explanation will be extended when I feel the need (or users have questions +that can be answered here). + +\stopsubsection + +\startsubsection[title=Boxes] + +This paragraph of text is made from lines that contain words that themselves +contain symbolic representations of characters. Each line is wrapped in a so +called horizontal box and eventually those lines themselves get wrapped in what +we call a vertical box. + +\startbuffer +\vbox \bgroup + \hsize 5cm + \raggedright + This is a rather narrow paragraph blown up a bit. Here we use a flush left, + aka ragged right, approach. +\egroup +\stopbuffer + +When we expose some details of a paragraph it looks like this: + +\startlinecorrection +\startcombination[2*1] + {\scale[width=8cm]{\showmakeup[boxes]\getbuffer}} {} + {\scale[width=8cm]{\showmakeup\getbuffer}} {} +\stopcombination +\stoplinecorrection + +The left only shows the boxes, the variant at the right shows (font) kerns and +glue too. Because we flush left, there is rather strong right skip glue at the +right boundary of the box. If font kerns show up depends on the font, not all +fonts have them (or have only a few). The glyphs themselves are also kind of +boxed, as their dimensions determine the area that they occupy: + +\startlinecorrection + \scale[width=\textwidth]{\showglyphs\hbox{This is a rather ...}} +\stoplinecorrection + +But, internally they are not really boxed, as they already are a single quantity. +The same is true for rules: they are just blobs with dimensions. A box on the +other hand wraps a linked list of so called nodes: glyphs, kerns, glue, +penalties, rules, boxes, etc. It is a container with properties like width, +height, depth and shift. + +\stopsubsection + +\stopsection + +\startsection[title={\TEX\ primitives}] + +The box model is reflected in \TEX's user interface but not by that many +commands, most noticeably \type {\hbox}, \type {\vbox} and \type {\vtop}. Here is +an example of the first one: + +\starttyping[option=TEX] +\hbox width 10cm{text} +\hbox width 10cm height 1cm depth 5mm{text} +text \raise5mm\hbox{text} text +\stoptyping + +The \type {\raise} and \type {\lower} commands behave the same but in opposite +directions. One could as well have been defined in terms of the other. + +\startbuffer +text \raise 5mm \hbox to 2cm {text} +text \lower -5mm \hbox to 2cm {text} +text \raise -5mm \hbox to 2cm {text} +text \lower 5mm \hbox to 2cm {text} +\stopbuffer + +\typebuffer[option=TEX] + +\startlinecorrection +{\dontcomplain\showboxes\getbuffer} +\stoplinecorrection + +A box can be moved to the left or right but, believe it or not, in \CONTEXT\ we +never use that feature, probably because the consequences for the width are such +that we can as well use kerns. Here are some examples: + +\startbuffer +text \vbox{\moveleft 5mm \hbox {left}}text ! +text \vbox{\moveright 5mm \hbox{right}}text ! +\stopbuffer + +\typebuffer[option=TEX] + +\startlinecorrection +{\dontcomplain\getbuffer} +\stoplinecorrection + +\startbuffer +text \vbox{\moveleft 25mm \hbox {left}}text ! +text \vbox{\moveright 25mm \hbox{right}}text ! +\stopbuffer + +\typebuffer[option=TEX] + +\startlinecorrection +{\dontcomplain\getbuffer} +\stoplinecorrection + +Code like this will produce a complaint about an underfull box but we can easily +get around that: + +\startbuffer +text \raise 5mm \hbox to 2cm {\hss text} +text \lower -5mm \hbox to 2cm {text\hss} +text \raise -5mm \hbox to 2cm {\hss text} +text \lower 5mm \hbox to 2cm {text\hss} +\stopbuffer + +\typebuffer[option=TEX] + +The \type {\hss} primitive injects a glue that when needed will fill up the +available space. So, here we force the text to the right or left. + +\startlinecorrection +{\dontcomplain\showboxes\getbuffer} +\stoplinecorrection + +We have three kind of boxes: \type {\hbox}, \type {\vbox} and \type {\vtop}: + +\startbuffer +\hbox{\strut height and depth\strut} +\vbox{\hsize 4cm \strut height and depth\par and width\strut} +\vtop{\hsize 4cm \strut height and depth\par and width\strut} +\stopbuffer + +\typebuffer[option=TEX] + +A \type {\vbox} aligns at the bottom and a \type {\vtop} at the top. I have added +some so called struts to enforce a consistent height and depth. A strut is an +invisible quantity (consider it a black box) that enforces consistent line +dimensions: height and depth. + + +\startlinecorrection +{\dontcomplain\hbox{\showstruts\showboxes\getbuffer}} +\stoplinecorrection + +You can store a box in a register but you need to be careful not to use a +predefined one. If you need a lot of boxes you can reserve some for your own: + +\starttyping +\newbox\MySpecialBox +\stoptyping + +but normally you can do with one of the scratch registers, like 0, 2, 4, 6 or 8, +for local boxes, and 1, 3, 5, 7 and 9 for global ones. Registers are used like: + +\starttyping + \setbox0\hbox{here} +\global\setbox1\hbox{there} +\stoptyping + +In \CONTEXT\ you can also use + +\starttyping +\setbox\scratchbox \hbox{here} +\setbox\scratchboxone\hbox{here} +\setbox\scratchboxtwo\hbox{here} +\stoptyping + +and some more. In fact, there are quite some predefined scratch registers (boxes, +dimensions, counters, etc). Feel free to investigate further. + +When a box is stored, you can consult its dimensions with \type {\wd}, \type +{\ht} and \type {\dp}. You can of course store them for later use. + +\starttyping +\scratchwidth \wd\scratchbox +\scratchheight\ht\scratchbox +\scratchdepth \dp\scratchbox +\scratchtotal \dimexpr\ht\scratchbox+\dp\scratchbox\relax +\scratchtotal \htdp\scratchbox +\stoptyping + +The last line is \CONTEXT\ specific. You can also set the dimensions + +\starttyping +\wd\scratchbox 10cm +\ht\scratchbox 10mm +\dp\scratchbox 5mm +\stoptyping + +So you can cheat! A box is placed with \type {\copy}, which keeps the original +intact or \type {\box} which just inserts the box and then wipes the register. In +practice you seldom need a copy, which is more expensive in runtime anyway. Here +we use copy because it serves the examples. + +\starttyping +\copy\scratchbox +\box \scratchbox +\stoptyping + +\stopsection + +\startsection[title={\ETEX\ primitives}] + +The \ETEX\ extensions don't add something relevant for boxes, apart from that you +can use the expressions mechanism to mess around with their dimensions. There is +a mechanism for typesetting r2l within a paragraph but that has limited +capabilities and doesn't change much as it's mostly a way to trick the backend +into outputting a stretch of text in the other direction. This feature is not +available in \LUATEX\ because it has an alternative direction mechanism. + +\stopsection + +\startsection[title={\LUATEX\ primitives}] + +The concept of boxes is the same in \LUATEX\ as in its predecessors but there are +some aspects to keep in mind. When a box is typeset this happens in \LUATEX: + +\startitemize[n] + \startitem + A list of nodes is constructed. In \LUATEX\ this is a double linked + list (so that it can easily be manipulated in \LUA) but \TEX\ itself + only uses the forward links. + \stopitem + \startitem + That list is hyphenated, that is: so called discretionary nodes are + injected. This depends on the language properties of the glyph + (character) nodes. + \stopitem + \startitem + Then ligatures are constructed, if the font has such combinations. When + this built|-|in mechanism is used, in \CONTEXT\ we speak of base mode. + \stopitem + \startitem + After that inter|-|character kerns are applied, if the font provides + them. Again this is a base mode action. + \stopitem + \startitem + Finally the box gets packaged: + \startitemize + \startitem + In the case of a horizontal box, the list is packaged in a + hlist node, basically one liner, and its dimensions are calculated + and set. + \stopitem + \startitem + In the case of a vertical box, the paragraph is broken into one + or more lines, without hyphenation, with optimal hyphenation or + in the worst case with so called emergency stretch applied, and + the result becomes a vlist node with its dimensions set. + \stopitem + \stopitemize + \stopitem +\stopitemize + +In traditional \TEX\ the first four steps are interwoven but in \LUATEX\ we need +them split because the step~5 can be overloaded by a callback. In that case steps +3 and 4 (and maybe 2) are probably also overloaded, especially when you bring +handling of fonts under \LUA\ control. + +New in \LUATEX\ are three packers: \type {\hpack}, \type {\vpack} and \type +{\tpack}, which are companions to \type {\hbox}, \type {\vbox} and \type {\vtop} +but without the callbacks applied. Using them is a bit tricky as you never know +if a callback should be applied, which, because users can often add their own +\LUA\ code, is not something predictable. + +Another box related extension is direction. There are four possible directions +but because in \LUAMETATEX\ there are only two. Because this model has been upgraded, +it will be discusses in the next section. A \CONTEXT\ user is supposed to use the +official \CONTEXT\ interfaces in order to be downward compatible. + +\stopsection + +\startsection[title={\LUAMETATEX\ primitives}] + +There are two possible directions: left to right (the default) and right to left +for Hebrew and Arabic. Here is an example that shows how it'd done with low level +directives: + +\startbuffer +\hbox direction 0 {from left to right} +\hbox direction 1 {from right to left} +\stopbuffer + +\typebuffer[option=TEX] + +\startlinecorrection +\getbuffer +\stoplinecorrection + +A low level direction switch is done with: + +\startbuffer +\hbox direction 0 + {from left to right \textdirection 1 from right to left} +\hbox direction 1 + {from right to left \textdirection 1 from left to right} +\stopbuffer + +\typebuffer[option=TEX] + +\startlinecorrection +\getbuffer +\stoplinecorrection + +but actually this is kind of {\em not done} in \CONTEXT, because there you are +supposed to use the proper direction switches: + +\startbuffer +\naturalhbox {from left to right} +\reversehbox {from right to left} +\naturalhbox {from left to right \righttoleft from right to left} +\reversehbox {from right to left \lefttoright from left to right} +\stopbuffer + +\typebuffer[option=TEX] + +\startlinecorrection +\getbuffer +\stoplinecorrection + +Often more is needed to properly support right to left typesetting so using the +\CONTEXT\ commands is more robust. + +In \LUAMETATEX\ the box model has been extended a bit, this as a consequence of +dropping the vertical directional typesetting, which never worked well. In +previous sections we discussed the properties width, height and depth and the +shift resulting from a \type {\raise}, \type {\lower}, \type {\moveleft} and +\type {\moveright}. Actually, the shift is also used in for instance positioning +math elements. + +The way shifting influences dimensions can be somewhat puzzling. Internally, when +\TEX\ packages content in a box there are two cases: + +\startitemize + \startitem + When a horizontal box is made, and \typ {height - shift} is larger than the + maximum height so far, that delta is taken. When \typ {depth + shift} is + larger than the current depth, then that depth is adapted. So, a shift up + influences the height and a shift down influences the depth. + \stopitem + \startitem + In the case of vertical packaging, when \typ {width + shift} is larger + than the maximum box (line) width so far, that maximum gets bumped. So, a + shift to the right can contribute, but a shift to the left cannot result + in a negative width. This is also why vertical typesetting, where height + and depth are swapped with width, goes wrong: we somehow need to map two + properties onto one and conceptually \TEX\ is really set up for + horizontal typesetting. (And it's why I decided to just remove it from the + engine.) + \stopitem +\stopitemize + +This is one of these cases where \TEX\ behaves as expected but it also means that +there is some limitation to what can be manipulated. Setting the shift using one +of the four commands has a direct consequence when a box gets packaged which +happens immediately because the box is an argument to the foursome. + +There is in traditional \TEX, probably for good reason, no way to set the shift +of a box, if only because the effect would normally be none. But in \LUATEX\ we +can cheat, and therefore, for educational purposed \CONTEXT\ has implements +some cheats. + +We use this sample box: + +\startbuffer[demo] +\setbox\scratchbox\hbox\bgroup + \middlegray\vrule width 20mm depth -.5mm height 10mm + \hskip-20mm + \darkgray \vrule width 20mm height -.5mm depth 5mm +\egroup +\stopbuffer + +\typebuffer[demo][option=TEX] + +When we mess with the shift using the \CONTEXT\ \type {\shiftbox} helper, we see +no immediate effect. We only get the shift applied when we use another helper, +\type {\hpackbox}. + +\startbuffer +\hbox\bgroup + \showstruts \strut + \quad \copy\scratchbox + \quad \shiftbox\scratchbox -20mm \copy\scratchbox + \quad \hpackbox\scratchbox \box \scratchbox + \quad \strut +\egroup +\stopbuffer + +\typebuffer[option=TEX] + +\startlinecorrection +\getbuffer[demo]\getbuffer +\stoplinecorrection + +When instead we use \type {\vpackbox} we get a different result. This time we +move left. + +\startbuffer +\hbox\bgroup + \showstruts \strut + \quad \copy\scratchbox + \quad \shiftbox\scratchbox -10mm \copy\scratchbox + \quad \vpackbox\scratchbox \copy\scratchbox + \quad \strut +\egroup +\stopbuffer + +\typebuffer[option=TEX] + +\startlinecorrection +\getbuffer[demo]\getbuffer +\stoplinecorrection + +The shift is set via \LUA\ and the repackaging is also done in \LUA, using the +low level \type {hpack} and \type {vpack} helpers and these just happen to look +at the shift when doing their job. At the \TEX\ end this never happens. + +This long exploration of shifting serves a purpose: it demonstrates that there is +not that much direct control over boxes apart from their three dimensions. +However this was never a real problem as one can just wrap a box in another one +and use kerns to move the embedded box around. But nevertheless I decided to see +if the engine can be a bit more helpful, if only because all that extra wrapping +gives some overhead and complications when we want to manipulate boxes. And of +course it is also a nice playground. + +We start with changing the direction. Changing this property doesn't require +repackaging because directions are not really dealt with in the frontend. When +a box is converted to (for instance \PDF) the reversion happens. + +\startbuffer +\setbox\scratchbox\hbox{whatever} +\the\boxdirection\scratchbox: \copy\scratchbox \crlf +\boxdirection\scratchbox 1 +\the\boxdirection\scratchbox: \copy\scratchbox +\stopbuffer + +\typebuffer[option=TEX] + +\startlinecorrection +\getbuffer +\stoplinecorrection + +Another property that can be queried and set is an attribute. In order to get +a private attribute we define one. + +\startbuffer +\newattribute\MyAt +\setbox\scratchbox\hbox attr \MyAt 123 {whatever} +[\the\boxattr\scratchbox\MyAt] +\boxattr\scratchbox\MyAt 456 +[\the\boxattr\scratchbox\MyAt] +[\ifnum\boxattr\scratchbox\MyAt>400 okay\fi] +\stopbuffer + +\typebuffer[option=TEX] + +\startlinecorrection +\getbuffer +\stoplinecorrection + +The sum of the height and depth is available too. Because for practical reasons +setting that property is also needed then, the choice was made to distribute the +value equally over height and depth. + +\startbuffer +\setbox\scratchbox\hbox {height and depth} +[\the\ht\scratchbox] +[\the\dp\scratchbox] +[\the\boxtotal\scratchbox] +\boxtotal\scratchbox=20pt +[\the\ht\scratchbox] +[\the\dp\scratchbox] +[\the\boxtotal\scratchbox] +\stopbuffer + +\typebuffer[option=TEX] + +\startlinecorrection +\getbuffer +\stoplinecorrection + +We've now arrived to a set of properties that relate to each other. They are +a bit complex and given the number of possibilities one might need to revert +to some trial and error: orientations and offsets. As with the dimensions, +directions and attributes, they are passed as box specification. We start +with the orientation. + +\startbuffer +\hbox \bgroup \showboxes + \hbox orientation 0 {right} + \quad \hbox orientation 1 {up} + \quad \hbox orientation 2 {left} + \quad \hbox orientation 3 {down} +\egroup +\stopbuffer + +\typebuffer[option=TEX] + +\startlinecorrection +\getbuffer +\stoplinecorrection + +When the orientation is set, you can also set an offset. Where shifting around a box +can have consequences for the dimensions, an offset is virtual. It gets effective +in the backend, when the contents is converted to some output format. + +\startbuffer +\hbox \bgroup \showboxes + \hbox orientation 0 yoffset 10pt {right} + \quad \hbox orientation 1 xoffset 10pt {up} + \quad \hbox orientation 2 yoffset -10pt {left} + \quad \hbox orientation 3 xoffset -10pt {down} +\egroup +\stopbuffer + +\typebuffer[option=TEX] + +\startlinecorrection +\getbuffer +\stoplinecorrection + +The reason that offsets are related to orientation is that we need to know in +what direction the offsets have to be applied and this binding forces the user to +think about it. You can also set the offsets using commands. + +\startbuffer +\setbox\scratchbox\hbox{whatever}% +1 \copy\scratchbox +2 \boxorientation\scratchbox 2 \copy\scratchbox +3 \boxxoffset \scratchbox -15pt \copy\scratchbox +4 \boxyoffset \scratchbox -15pt \copy\scratchbox +5 +\stopbuffer + +\typebuffer[option=TEX] + +\startlinecorrection +\ruledhbox{\getbuffer} +\stoplinecorrection + +\startbuffer +\setbox\scratchboxone\hbox{whatever}% +\setbox\scratchboxtwo\hbox{whatever}% +1 \boxxoffset \scratchboxone -15pt \copy\scratchboxone +2 \boxyoffset \scratchboxone -15pt \copy\scratchboxone +3 \boxxoffset \scratchboxone -15pt \copy\scratchboxone +4 \boxyoffset \scratchboxone -15pt \copy\scratchboxone +5 \boxxmove \scratchboxtwo -15pt \copy\scratchboxtwo +6 \boxymove \scratchboxtwo -15pt \copy\scratchboxtwo +7 \boxxmove \scratchboxtwo -15pt \copy\scratchboxtwo +8 \boxymove \scratchboxtwo -15pt \copy\scratchboxtwo +\stopbuffer + +\typebuffer[option=TEX] + +\startlinecorrection +\ruledhbox{\getbuffer} +\stoplinecorrection + +The move commands are provides as convenience and contrary to the offsets they do +adapt the dimensions. Internally, with the box, we register the orientation and +the offsets and when you apply these commands multiple times the current values +get overwritten. But \unknown\ because an orientation can be more complex you +might not get the effects you expect when the options we discuss next are used. +The reason is that we store the original dimensions too and these come into play +when these other options are used: anchoring. So, normally you will apply an +orientation and offsets once only. + +% the next bit is derived from the followingup document + +The orientation specifier is actually a three byte number that best can be seen +hexadecimal (although we stay within the decimal domain). There are three +components: x|-|anchoring, y|-|anchoring and orientation: + +\starttyping +0x<X><Y><O> +\stoptyping + +or in \TEX\ speak: + +\starttyping +"<X><Y><O> +\stoptyping + +The landscape and seascape variants both sit on top of the baseline while the +flipped variant has its depth swapped with the height. Although this would be +enough a bit more control is possible. + +The vertical options of the horizontal variants anchor on the baseline, lower +corner, upper corner or center. + +\startbuffer +\ruledhbox orientation "002 {\TEX} and +\ruledhbox orientation "012 {\TEX} and +\ruledhbox orientation "022 {\TEX} and +\ruledhbox orientation "032 {\TEX} +\stopbuffer + +\typebuffer[option=TEX] + +\startlinecorrection +\ruledhbox{\getbuffer} +\stoplinecorrection + +The horizontal options of the horizontal variants anchor in the center, left, +right, halfway left and halfway right. + +\startbuffer +\ruledhbox orientation "002 {\TEX} and +\ruledhbox orientation "102 {\TEX} and +\ruledhbox orientation "202 {\TEX} and +\ruledhbox orientation "302 {\TEX} and +\ruledhbox orientation "402 {\TEX} +\stopbuffer + +\typebuffer[option=TEX] + +\startlinecorrection +\ruledhbox{\getbuffer} +\stoplinecorrection + +The orientation has consequences for the dimensions so they are dealt with in the +expected way in constructing lines, paragraphs and pages, but the anchoring is +virtual, like the offsets. There are two extra variants for orientation zero: on +top of baseline or below, with dimensions taken into account. + +\startbuffer +\ruledhbox orientation "000 {\TEX} and +\ruledhbox orientation "004 {\TEX} and +\ruledhbox orientation "005 {\TEX} +\stopbuffer + +\typebuffer[option=TEX] + +\startlinecorrection +\ruledhbox{\getbuffer} +\stoplinecorrection + +The anchoring can look somewhat confusing but you need to keep in mind that it is +normally only used in very controlled circumstances and not in running text. +Wrapped in macros users don't see the details. We're talking boxes here, so for +instance: + +\startbuffer +test\quad +\hbox orientation 3 \bgroup + \strut test\hbox orientation "002 \bgroup\strut test\egroup test% +\egroup \quad +\hbox orientation 3 \bgroup + \strut test\hbox orientation "002 \bgroup\strut test\egroup test% +\egroup \quad +\hbox orientation 3 \bgroup + \strut test\hbox orientation "012 \bgroup\strut test\egroup test% +\egroup \quad +\hbox orientation 3 \bgroup + \strut test\hbox orientation "022 \bgroup\strut test\egroup test% +\egroup \quad +\hbox orientation 3 \bgroup + \strut test\hbox orientation "032 \bgroup\strut test\egroup test% +\egroup \quad +\hbox orientation 3 \bgroup + \strut test\hbox orientation "042 \bgroup\strut test\egroup test% +\egroup +\quad test +\stopbuffer + +\typebuffer[option=TEX] + +\startlinecorrection +\ruledhbox{\getbuffer} +\stoplinecorrection + +\stopsection + +\stopdocument diff --git a/doc/context/sources/general/manuals/lowlevel/lowlevel-conditionals.tex b/doc/context/sources/general/manuals/lowlevel/lowlevel-conditionals.tex new file mode 100644 index 000000000..ea3c9e1a2 --- /dev/null +++ b/doc/context/sources/general/manuals/lowlevel/lowlevel-conditionals.tex @@ -0,0 +1,1409 @@ +% language=us + +\environment lowlevel-style + +\startdocument + [title=conditionals, + color=middleblue] + +\startsection[title=Preamble] + +\startsubsection[title=Introduction] + +You seldom need the low level conditionals because there are quite some so called +support macros available in \CONTEXT . For instance, when you want to compare two +values (or more accurate: sequences of tokens), you can do this: + +\starttyping[option=TEX] +\doifelse {foo} {bar} { + the same +} { + different +} +\stoptyping + +But if you look in the \CONTEXT\ code, you will see that often we use primitives +that start with \type {\if} in low level macros. There are good reasons for this. +First of all, it looks familiar when you also code in other languages. Another +reason is performance but that is only true in cases where the snippet of code is +expanded very often, because \TEX\ is already pretty fast. Using low level \TEX\ +can also be more verbose, which is not always nice in a document source. But, the +most important reason (for me) is the layout of the code. I often let the look +and feel of code determine the kind of coding. This also relates to the syntax +highlighting that I am using, which is consistent for \TEX, \METAPOST, \LUA, +etc.\ and evolved over decades. If code looks bad, it probably is bad. Of course +this doesn't mean all my code looks good; you're warned. In general we can say +that I often use \type {\if...} when coding core macros, and \type {\doifelse...} +macros in (document) styles and modules. + +In the sections below I will discuss the low level conditions in \TEX. For the +often more convenient \CONTEXT\ wrappers you can consult the source of the system +and support modules, the wiki and|/|or manuals. + +Some of the primitives shown here are only available in \LUATEX, and some only in +\LUAMETATEX . We could do without them for decades but they were added to these +engines because of convenience and, more important, because then made for nicer +code. Of course there's also the fun aspect. This manual is not an invitation to +use these very low level primitives in your document source. The ones that +probably make most sense are \type {\ifnum}, \type {\ifdim} and \type {\ifcase}. +The others are often wrapped into support macros that are more convenient. + +In due time I might add more examples and explanations. Also, maybe some more +tests will show up as part of the \LUAMETATEX\ project. + +\stopsubsection + +\startsubsection[title={Number and dimensions}] + +Numbers and dimensions are basic data types in \TEX. When you enter one, a number +is just that but a dimension gets a unit. Compare: + +\starttyping[option=TEX] +1234 +1234pt +\stoptyping + +If you also use \METAPOST, you need to be aware of the fact that in that language +there are not really dimensions. The \type {post} part of the name implies that +eventually a number becomes a \POSTSCRIPT\ unit which represents a base point (\type +{bp}) in \TEX. When in \METAPOST\ you entry \type {1234pt} you actually multiply +\type {1234} by the variable \type {pt}. In \TEX\ on the other hand, a unit like +\type {pt} is one of the keywords that gets parsed. Internally dimensions are +also numbers and the unit (keyword) tells the scanner what multiplier to use. +When that multiplier is one, we're talking of scaled points, with the unit \type +{sp}. + +\startbuffer +\the\dimexpr 12.34pt \relax +\the\dimexpr 12.34sp \relax +\the\dimexpr 12.99sp \relax +\the\dimexpr 1234sp \relax +\the\numexpr 1234 \relax +\stopbuffer + +\typebuffer[option=TEX] + +\startlines \getbuffer \stoplines + +When we serialize a dimension it always shows the dimension in points, unless we +serialize it as number. + +\startbuffer +\scratchdimen1234sp +\number\scratchdimen +\the\scratchdimen +\stopbuffer + +\typebuffer[option=TEX] + +\startlines \getbuffer \stoplines + +When a number is scanned, the first thing that is taken care of is the sign. In many +cases, when \TEX\ scans for something specific it will ignore spaces. It will +happily accept multiple signs: + +\startbuffer +\number +123 +\number +++123 +\number + + + 123 +\number +-+-+123 +\number --123 +\number ---123 +\stopbuffer + +\typebuffer[option=TEX] + +\startlines \getbuffer \stoplines + +Watch how the negation accumulates. The scanner can handle decimal, hexadecimal +and octal numbers: + +\startbuffer +\number -123 +\number -"123 +\number -'123 +\stopbuffer + +\typebuffer[option=TEX] + +\startlines \getbuffer \stoplines + +A dimension is scanned like a number but this time the scanner checks for upto +three parts: an either or not signed number, a period and a fraction. Here no +number means zero, so the next is valid: + +\startbuffer +\the\dimexpr . pt \relax +\the\dimexpr 1. pt \relax +\the\dimexpr .1pt \relax +\the\dimexpr 1.1pt \relax +\stopbuffer + +\typebuffer[option=TEX] + +\startlines \getbuffer \stoplines + +Again we can use hexadecimal and octal numbers but when these are entered, there +can be no fractional part. + +\startbuffer +\the\dimexpr 16 pt \relax +\the\dimexpr "10 pt \relax +\the\dimexpr '20 pt \relax +\stopbuffer + +\typebuffer[option=TEX] + +\startlines \getbuffer \stoplines + +The reason for discussing numbers and dimensions here is that there are cases where +when \TEX\ expects a number it will also accept a dimension. It is good to know that +for instance a macro defined with \type {\chardef} or \type {\mathchardef} also is +treated as a number. Even normal characters can be numbers, when prefixed by a \type +{`} (backtick). + +The maximum number in \TEX\ is 2147483647 so we can do this: + +\starttyping[option=TEX] +\scratchcounter2147483647 +\stoptyping + +but not this + +\starttyping[option=TEX] +\scratchcounter2147483648 +\stoptyping + +as it will trigger an error. A dimension can be positive and negative so there we +can do at most: + +\starttyping[option=TEX] +\scratchdimen 1073741823sp +\stoptyping + +\startbuffer +\scratchdimen1073741823sp +\number\scratchdimen +\the\scratchdimen +\scratchdimen16383.99998pt +\number\scratchdimen +\the\scratchdimen +\stopbuffer + +\typebuffer[option=TEX] + +\startlines +\getbuffer +\stoplines + +We can also do this: + +\startbuffer +\scratchdimen16383.99999pt +\number\scratchdimen +\the\scratchdimen +\stopbuffer + +\typebuffer[option=TEX] + +\startlines +\getbuffer +\stoplines + +but the next one will fail: + +\starttyping[option=TEX] +\scratchdimen16383.9999999pt +\stoptyping + +Just keep in mind that \TEX\ scans both parts as number so the error comes from +checking if those numbers combine well. + +\startbuffer +\ifdim 16383.99999 pt = 16383.99998 pt the same \else different \fi +\ifdim 16383.999979 pt = 16383.999980 pt the same \else different \fi +\ifdim 16383.999987 pt = 16383.999991 pt the same \else different \fi +\stopbuffer + +\typebuffer[option=TEX] + +Watch the difference in dividing, the \type {/} rounds, while the \type {:} +truncates. + +\startlines +\getbuffer +\stoplines + +You need to be aware of border cases, although in practice they never really +are a problem: + +\startbuffer +\ifdim \dimexpr16383.99997 pt/2\relax = \dimexpr 16383.99998 pt/2\relax + the same \else different +\fi +\ifdim \dimexpr16383.99997 pt:2\relax = \dimexpr 16383.99998 pt:2\relax + the same \else different +\fi +\stopbuffer + +\typebuffer[option=TEX] + +\startlines +\getbuffer +\stoplines + +\startbuffer +\ifdim \dimexpr1.99997 pt/2\relax = \dimexpr 1.99998 pt/2\relax + the same \else different +\fi +\ifdim \dimexpr1.99997 pt:2\relax = \dimexpr 1.99998 pt:2\relax + the same \else different +\fi +\stopbuffer + +\typebuffer[option=TEX] + +\startlines +\getbuffer +\stoplines + +\startbuffer +\ifdim \dimexpr1.999999 pt/2\relax = \dimexpr 1.9999995 pt/2\relax + the same \else different +\fi +\ifdim \dimexpr1.999999 pt:2\relax = \dimexpr 1.9999995 pt:2\relax + the same \else different +\fi +\stopbuffer + +\typebuffer[option=TEX] + +\startlines +\getbuffer +\stoplines + +This last case demonstrates that at some point the digits get dropped (still +assuming that the fraction is within the maximum permitted) so these numbers then +are the same. Anyway, this is not different in other programming languages and +just something you need to be aware of. + +\stopsubsection + +\stopsection + +\startsection[title={\TEX\ primitives}] + +\startsubsection[title={\tex{if}}] + +I seldom use this one. Internally \TEX\ stores (and thinks) in terms of tokens. +If you see for instance \type {\def} or \type {\dimen} or \type {\hbox} these all +become tokens. But characters like \type {A} or {@} also become tokens. In this +test primitive all non|-|characters are considered to be the same. In the next +examples this is demonstrated. + +\startbuffer +[\if AB yes\else nop\fi] +[\if AA yes\else nop\fi] +[\if CDyes\else nop\fi] +[\if CCyes\else nop\fi] +[\if\dimen\font yes\else nop\fi] +[\if\dimen\font yes\else nop\fi] +\stopbuffer + +\typebuffer[option=TEX] + +Watch how spaces after the two characters are kept: \inlinebuffer . This primitive looks +at the next two tokens but when doing so it expands. Just look at the following: + +\startbuffer +\def\AA{AA}% +\def\AB{AB}% +[\if\AA yes\else nop\fi] +[\if\AB yes\else nop\fi] +\stopbuffer + +\typebuffer[option=TEX] + +We get: \inlinebuffer . + +% protected macros + +\stopsubsection + +\startsubsection[title={\tex{ifcat}}] + +In \TEX\ characters (in the input) get interpreted according to their so called +catcodes. The most common are letters (alphabetic) and and other (symbols) but +for instance the backslash has the property that it starts a command, the dollar +signs trigger math mode, while the curly braced deal with grouping. If for +instance either or not the ampersand is special (for instance as column separator +in tables) depends on the macro package. + +\startbuffer +[\ifcat AB yes\else nop\fi] +[\ifcat AA yes\else nop\fi] +[\ifcat CDyes\else nop\fi] +[\ifcat CCyes\else nop\fi] +[\ifcat C1yes\else nop\fi] +[\ifcat\dimen\font yes\else nop\fi] +[\ifcat\dimen\font yes\else nop\fi] +\stopbuffer + +\typebuffer[option=TEX] + +This time we also compare a letter with a number: \inlinebuffer . In that case +the category codes differ (letter vs other) but in this test comparing the +letters result in a match. This is a test that is used only once in \CONTEXT\ and +even that occasion is dubious and will go away. + +You can use \type {\noexpand} to prevent expansion: + +\startbuffer +\def\A{A}% +\let\B B% +\def\C{D}% +\let\D D% +[\ifcat\noexpand\A Ayes\else nop\fi] +[\ifcat\noexpand\B Byes\else nop\fi] +[\ifcat\noexpand\C Cyes\else nop\fi] +[\ifcat\noexpand\C Dyes\else nop\fi] +[\ifcat\noexpand\D Dyes\else nop\fi] +\stopbuffer + +\typebuffer[option=TEX] + +We get: \inlinebuffer, so who still thinks that \TEX\ is easy to understand for a +novice user? + +\stopsubsection + +\startsubsection[title={\tex{ifnum}}] + +This condition compares its argument with another one, separated by an \type {<}, +\type {=} or \type {>} character. + +\starttyping[option=TEX] +\ifnum\scratchcounter<0 + less than +\else\ifnum\scratchcounter>0 + more than +\else + equal to +\fi zero +\stoptyping + +This is one of these situations where a dimension can be used instead. In that +case the dimension is in scaled points. + +\starttyping[option=TEX] +\ifnum\scratchdimen<0 + less than +\else\ifnum\scratchdimen>0 + more than +\else + equal to +\fi zero +\stoptyping + +Of course this equal treatment of a dimension and number is only true when the +dimension is a register or box property. + +\stopsubsection + +\startsection[title={\tex{ifdim}}] + +This condition compares one dimension with another one, separated by an \type {<}, +\type {=} or \type {>} sign. + +\starttyping[option=TEX] +\ifdim\scratchdimen<0pt + less than +\else\ifdim\scratchdimen>0pt + more than +\else + equal to +\fi zero +\stoptyping + +While when comparing numbers a dimension is a valid quantity but here you cannot +mix them: something with a unit is expected. + +\stopsubsection + +\startsubsection[title={\tex{ifodd}}] + +This one can come in handy, although in \CONTEXT\ it is only used in checking for +an odd of even page number. + +\startbuffer +\scratchdimen 3sp +\scratchcounter4 + +\ifodd\scratchdimen very \else not so \fi odd +\ifodd\scratchcounter very \else not so \fi odd +\stopbuffer + +\typebuffer[option=TEX] + +As with the previously discussed \type {\ifnum} you can use a dimension variable +too, which is then interpreted as representing scaled points. Here we get: + +\startlines +\getbuffer +\stoplines + +\stopsubsection + +\startsubsection[title={\tex{ifvmode}}] + +This is a rather trivial check. It takes no arguments and just is true when we're +in vertical mode. Here is an example: + +\startbuffer +\hbox{\ifvmode\else\par\fi\ifvmode v\else h\fi mode} +\stopbuffer + +\typebuffer[option=TEX] + +We're always in horizontal mode and issuing a \type {\par} inside a horizontal +box doesn't change that, so we get: \ruledhbox{\inlinebuffer}. + +\stopsubsection + +\startsubsection[title={\tex{ifhmode}}] + +As with \type {\ifvmode} this one has no argument and just tells if we're in +vertical mode. + +\startbuffer +\vbox { + \noindent \ifhmode h\else v\fi mode + \par + \ifhmode h\else \noindent v\fi mode +} +\stopbuffer + +\typebuffer[option=TEX] + +You can use it for instance to trigger injection of code, or prevent that some +content (or command) is done more than once: + +\startlinecorrection +\ruledhbox{\inlinebuffer} +\stoplinecorrection + +\stopsubsection + +\startsubsection[title={\tex{ifmmode}}] + +Math is something very \TEX\ so naturally you can check if you're in math mode. +here is an example of using this test: + +\starttyping[option=TEX] +\def\enforcemath#1{\ifmmode#1\else$ #1 $\fi} +\stoptyping + +Of course in reality macros that do such things are more advanced than this one. + +\stopsubsection + +\startsubsection[title={\tex{ifinner}}] + +\startbuffer +\def\ShowMode + {\ifhmode \ifinner inner \fi hmode + \else\ifvmode \ifinner inner \fi vmode + \else\ifmmode \ifinner inner \fi mmode + \else \ifinner inner \fi unset + \fi\fi\fi} +\stopbuffer + +\typebuffer[option=TEX] \getbuffer + +\startbuffer +\ShowMode \ShowMode + +\vbox{\ShowMode} + +\hbox{\ShowMode} + +$\ShowMode$ + +$$\ShowMode$$ +\stopbuffer + +\typebuffer[option=TEX] + +The first line has two tests, where the first one changes the mode to horizontal +simply because a text has been typeset. Watch how display math is not inner. + +\startpacked +\startlines +\getbuffer +\stoplines +\stoppacked + +By the way, moving the \type {\ifinner} test outside the branches (to the top of +the macro) won't work because once the word \type {inner} is typeset we're no +longer in vertical mode, if we were at all. + +\stopsubsection + +\startsubsection[title={\tex{ifvoid}}] + +A box is one of the basic concepts in \TEX. In order to understand this primitive +we present four cases: + +\startbuffer +\setbox0\hbox{} \ifvoid0 void \else content \fi +\setbox0\hbox{123} \ifvoid0 void \else content \fi +\setbox0\hbox{} \box0 \ifvoid0 void \else content \fi +\setbox0\hbox to 10pt{} \ifvoid0 void \else content \fi +\stopbuffer + +\typebuffer[option=TEX] + +In the first case, we have a box which is empty but it's not void. It helps to +know that internally an hbox is actually an object with a pointer to a linked +list of nodes. So, the first two can be seen as: + +\starttyping +hlist -> [nothing] +hlist -> 1 -> 2 -> 3 -> [nothing] +\stoptyping + +but in any case there is a hlist. The third case puts something in a hlist but +then flushes it. Now we have not even the hlist any more; the box register has +become void. The last case is a variant on the first. It is an empty box with a +given width. The outcome of the four lines (with a box flushed in between) is: + +\startlines +\getbuffer +\stoplines + +So, when you want to test if a box is really empty, you need to test also its +dimensions, which can be up to three tests, depending on your needs. + +\startbuffer +\setbox0\emptybox \ifvoid0 void\else content\fi +\setbox0\emptybox \wd0=10pt \ifvoid0 void\else content\fi +\setbox0\hbox to 10pt {} \ifvoid0 void\else content\fi +\setbox0\hbox {} \wd0=10pt \ifvoid0 void\else content\fi +\stopbuffer + +\typebuffer[option=TEX] + +Setting a dimension of a void voix (empty) box doesn't make it less void: + +\startlines +\getbuffer +\stoplines + +\stopsubsection + +\startsubsection[title={\tex{ifhbox}}] + +This test takes a box number and gives true when it is an hbox. + +\stopsubsection + +\startsubsection[title={\tex{ifvbox}}] + +This test takes a box number and gives true when it is an vbox. Both a \type +{\vbox} and \type {\vtop} are vboxes, the difference is in the height and depth +and the baseline. In a \type {\vbox} the last line determines the baseline + +\startlinecorrection +\ruledvbox{vbox or vtop\par vtop or vbox} +\stoplinecorrection + +And in a \type {\vtop} the first line takes control: + +\startlinecorrection +\ruledvtop{vbox or vtop\par vtop or vbox} +\stoplinecorrection + +but, once wrapped, both internally are just vlists. + +\stopsubsection + +\startsubsection[title={\tex{ifx}}] + +This test is actually used a lot in \CONTEXT: it compares two token(list)s: + +\startbuffer + \ifx a b Y\else N\fi + \ifx ab Y\else N\fi +\def\A {a}\def\B{b}\ifx \A\B Y\else N\fi +\def\A{aa}\def\B{a}\ifx \A\B Y\else N\fi +\def\A {a}\def\B{a}\ifx \A\B Y\else N\fi +\stopbuffer + +\typebuffer[option=TEX] + +Here the result is: \quotation{\inlinebuffer}. It does not expand the content, if +you want that you need to use an \type {\edef} to create two (temporary) macros +that get compared, like in: + +\starttyping[option=TEX] +\edef\TempA{...}\edef\TempB{...}\ifx\TempA\TempB ...\else ...\fi +\stoptyping + +\stopsubsection + +\startsubsection[title={\tex{ifeof}}] + +This test checks if a the pointer in a given input channel has reached its end. +It is also true when the file is not present. The argument is a number which +relates to the \type {\openin} primitive that is used to open files for reading. + +\stopsubsection + +\startsubsection[title={\tex{iftrue}}] + +It does what it says: always true. + +\stopsubsection + +\startsubsection[title={\tex{iffalse}}] + +It does what it says: always false. + +\stopsubsection + +\startsubsection[title={\tex{ifcase}}] + +The general layout of an \type {\ifcase} tests is as follows: + +\starttyping[option=TEX] +\ifcase<number> + when zero +\or + when one +\or + when two +\or + ... +\else + when something else +\fi +\stoptyping + +As in other places a number is a sequence of signs followed by one of more digits + +\stopsubsection + +\stopsection + +\startsection[title={\ETEX\ primitives}] + +\startsubsection[title={\tex{ifdefined}}] + +This primitive was introduced for checking the existence of a macro (or primitive) +and with good reason. Say that you want to know if \type {\MyMacro} is defined? One +way to do that is: + +\startbuffer +\ifx\MyMacro\undefined + {\bf undefined indeed} +\fi +\stopbuffer + +\typebuffer[option=TEX] + +This results in: \inlinebuffer , but is this macro really undefined? When \TEX\ +scans your source and sees a the escape character (the forward slash) it will +grab the next characters and construct a control sequence from it. Then it finds +out that there is nothing with that name and it will create a hash entry for a +macro with that name but with no meaning. Because \type {\undefined} is also not +defined, these two macros have the same meaning and therefore the \type {\ifx} is +true. Imagine that you do this many times, with different macro names, then your +hash can fill up. Also, when a user defined \type {\undefined} you're suddenly +get a different outcome. + +In order to catch the last problem there is the option to test directly: + +\startbuffer +\ifdefined\MyOtherMacro \else + {\bf also undefined} +\fi +\stopbuffer + +\typebuffer[option=TEX] + +This (or course) results in: \inlinebuffer, but the macro is still sort of +defined (with no meaning). The next section shows how to get around this. + +\stopsubsection + +\startsubsection[title={\tex{ifcsname}}] + +A macro is often defined using a ready made name, as in: + +\starttyping[option=TEX] +\def\OhYes{yes} +\stoptyping + +The name is made from characters with catcode letter which means that you cannot +use for instance digits or underscores unless you also give these characters that +catcode, which is not that handy in a document. You can however use \type +{\csname} to define a control sequence with any character in the name, like: + +\starttyping[option=TEX] +\expandafter\def\csname Oh Yes : 1\endcsname{yes} +\stoptyping + +Later on you can get this one with \type {\csname}: + +\starttyping[option=TEX] +\csname Oh Yes : 1\endcsname +\stoptyping + +However, if you say: + +\starttyping[option=TEX] +\csname Oh Yes : 2\endcsname +\stoptyping + +you won't get some result, nor a message about an undefined control sequence, but +the name triggers a define anyway, this time not with no meaning (undefined) but +as equivalent to \type {\relax}, which is why + +\starttyping[option=TEX] +\expandafter\ifx\csname Oh Yes : 2\endcsname\relax + {\bf relaxed indeed} +\fi +\stoptyping + +is the way to test its existence. As with the test in the previous section, +this can deplete the hash when you do lots of such tests. The way out of this +is: + +\starttyping[option=TEX] +\ifcsname Oh Yes : 2\endcsname \else + {\bf unknown indeed} +\fi +\stoptyping + +This time there is no hash entry created and therefore there is not even an +undefined control sequence. + +In \LUATEX\ there is an option to return false in case of a messy expansion +during this test, and in \LUAMETATEX\ that is default. This means that tests can +be made quite robust as it is pretty safe to assume that names that make sense +are constructed from regular characters and not boxes, font switches, etc. + +\stopsubsection + +\startsubsection[title={\tex{iffontchar}}] + +This test was also part of the \ETEX\ extensions and it can be used to see if +a font has a character. + +\startbuffer +\iffontchar\font`A + {\em This font has an A!} +\fi +\stopbuffer + +\typebuffer[option=TEX] + +And, as expected, the outcome is: \quotation {\inlinebuffer}. The test takes two +arguments, the first being a font identifier and the second a character number, +so the next checks are all valid: + +\starttyping[option=TEX] +\iffontchar\font `A yes\else nop\fi\par +\iffontchar\nullfont `A yes\else nop\fi\par +\iffontchar\textfont0`A yes\else nop\fi\par +\stoptyping + +In the perspective of \LUAMETATEX\ I considered also supporting \type {\fontid} +but it got a bit messy due to the fact that this primitive expands in a different +way so this extension was rejected. + +\stopsubsection + +\startsubsection[title={\tex{unless}}] + +You can negate the results of a test by using the \type {\unless} prefix, so for +instance you can replace: + +\starttyping[option=TEX] +\ifdim\scratchdimen=10pt + \dosomething +\else\ifdim\scratchdimen<10pt + \dosomething +\fi\fi +\stoptyping + +by: + +\starttyping[option=TEX] +\unless\ifdim\scratchdimen>10pt + \dosomething +\fi +\stoptyping + +\stopsubsection + +\stopsection + +\startsection[title={\LUATEX\ primitives}] + +\startsubsection[title={\tex{ifincsname}}] + +As it had no real practical usage uit might get dropped in \LUAMETATEX, so it +will not be discussed here. + +\stopsubsection + +\startsubsection[title={\tex{ifprimitive}}] + +As it had no real practical usage due to limitations, this one is not available +in \LUAMETATEX\ so it will not be discussed here. + +\stopsubsection + +\startsubsection[title={\tex{ifabsnum}}] + +This test is inherited from \PDFTEX\ and behaves like \type {\ifnum} but first +turns a negative number into a positive one. + +\stopsubsection + +\startsubsection[title={\tex{ifabsdim}}] + +This test is inherited from \PDFTEX\ and behaves like \type {\ifdim} but first +turns a negative dimension into a positive one. + +\stopsubsection + +\startsubsection[title={\tex{ifcondition}}] + +This is not really a test but in order to unstand that you need to know how +\TEX\ internally deals with tests. + +\starttyping[option=TEX] +\ifdimen\scratchdimen>10pt + \ifdim\scratchdimen<20pt + result a + \else + result b + \fi +\else + result c +\fi +\stoptyping + +When we end up in the branch of \quotation {result a} we need to skip two \type +{\else} branches after we're done. The \type {\if..} commands increment a level +while the \type {\fi} decrements a level. The \type {\else} needs to be skipped +here. In other cases the true branch needs to be skipped till we end up a the +right \type {\else}. When doing this skipping, \TEX\ is not interested in what it +encounters beyond these tokens and this skipping (therefore) goes real fast but +it does see nested conditions and doesn't interpret grouping related tokens. + +A side effect of this is that the next is not working as expected: + +\starttyping[option=TEX] +\def\ifmorethan{\ifdim\scratchdimen>} +\def\iflessthan{\ifdim\scratchdimen<} + +\ifmorethan10pt + \iflessthan20pt + result a + \else + result b + \fi +\else + result c +\fi +\stoptyping + +The \type{\iflessthan} macro is not seen as an \type {\if...} so the nesting gets +messed up. The solution is to fool the scanner in thinking that it is. Say we have: + +\startbuffer +\scratchdimen=25pt + +\def\ifmorethan{\ifdim\scratchdimen>} +\def\iflessthan{\ifdim\scratchdimen<} +\stopbuffer + +\typebuffer[option=TEX] \getbuffer + +and: + +\startbuffer +\ifcondition\ifmorethan10pt + \ifcondition\iflessthan20pt + result a + \else + result b + \fi +\else + result c +\fi +\stopbuffer + +\typebuffer[option=TEX] + +When we expand this snippet we get: \quotation {\inlinebuffer} and no error +concerning a failure in locating the right \type {\fi's}. So, when scanning the +\type {\ifcondition} is seen as a valid \type {\if...} but when the condition is +really expanded it gets ignored and the \type {\ifmorethan} has better come up +with a match or not. + +In this perspective it is also worth mentioning that nesting problems can be +avoided this way: + +\starttyping[option=TEX] +\def\WhenTrue {something \iftrue ...} +\def\WhenFalse{something \iffalse ...} + +\ifnum\scratchcounter>123 + \let\next\WhenTrue +\else + \let\next\WhenFalse +\fi +\next +\stoptyping + +This trick is mentioned in The \TeX book and can also be found in the plain \TEX\ +format. A variant is this: + +\starttyping[option=TEX] +\ifnum\scratchcounter>123 + \expandafter\WhenTrue +\else + \expandafter\WhenFalse +\fi +\stoptyping + +but using \type {\expandafter} can be quite intimidating especially when there +are multiple in a row. It can also be confusing. Take this: an \type +{\ifcondition} expects the code that follows to produce a test. So: + +\starttyping[option=TEX] +\def\ifwhatever#1% + {\ifdim#1>10pt + \expandafter\iftrue + \else + \expandafter\iffalse + \fi} + +\ifcondition\ifwhatever{10pt} + result a +\else + result b +\fi +\stoptyping + +This will not work! The reason is in the already mentioned fact that when we end +up in the greater than \type {10pt} case, the scanner will happily push the \type +{\iftrue} after the \type {\fi}, which is okay, but when skipping over the \type +{\else} it sees a nested condition without matching \type {\fi}, which makes ity +fail. I will spare you a solution with lots of nasty tricks, so here is the clean +solution using \type {\ifcondition}: + +\starttyping[option=TEX] +\def\truecondition {\iftrue} +\def\falsecondition{\iffalse} + +\def\ifwhatever#1% + {\ifdim#1>10pt + \expandafter\truecondition + \else + \expandafter\falsecondition + \fi} + +\ifcondition\ifwhatever{10pt} + result a +\else + result b +\fi +\stoptyping + +It will be no surprise that the two macros at the top are predefined in \CONTEXT. +It might be more of a surprise that at the time of this writing the usage in +\CONTEXT\ of this \type {\ifcondition} primitive is rather minimal. But that +might change. + +As a further teaser I'll show another simple one, + +\startbuffer +\def\HowOdd#1{\unless\ifnum\numexpr ((#1):2)*2\relax=\numexpr#1\relax} + +\ifcondition\HowOdd{1}very \else not so \fi odd +\ifcondition\HowOdd{2}very \else not so \fi odd +\ifcondition\HowOdd{3}very \else not so \fi odd +\stopbuffer + +\typebuffer[option=TEX] + +This renders: + +\startlines +\getbuffer +\stoplines + +The code demonstrates several tricks. First of all we use \type {\numexpr} which +permits more complex arguments, like: + +\starttyping[option=TEX] +\ifcondition\HowOdd{4+1}very \else not so \fi odd +\ifcondition\HowOdd{2\scratchcounter+9}very \else not so \fi odd +\stoptyping + +Another trick is that we use an integer division (the \type {:}) which is an +operator supported by \LUAMETATEX . + +\stopsubsection + +\stopsection + +\startsection[title={\LUAMETATEX\ primitives}] + +\startsubsection[title={\tex{ifcmpnum}}] + +This one is part of s set of three tests that all are a variant of a \type +{\ifcase} test. A simple example of the first test is this: + +\starttyping[option=TEX] +\ifcmpnum 123 345 less \or equal \else more \fi +\stoptyping + +The test scans for two numbers, which of course can be registers or expressions, +and sets the case value to 0, 1 or 2, which means that you then use the normal +\type {\or} and \type {\else} primitives for follow up on the test. + +\stopsubsection + +\startsubsection[title={\tex{ifchknum}}] + +This test scans a number and when it's okay sets the case value to 1, and otherwise +to 2. So you can do the next: + +\starttyping[option=TEX] +\ifchknum 123\or good \else bad \fi +\ifchknum bad\or good \else bad \fi +\stoptyping + +An error message is suppressed and the first \type {\or} can be seen as a sort of +recovery token, although in fact we just use the fast scanner mode that comes +with the \type {\ifcase}: because the result is 1 or 2, we never see invalid +tokens. + +\stopsubsection + +\startsubsection[title={\tex{ifnumval}}] + +A sort of combination of the previous two is \type {\ifnumval} which checks a +number but also if it's less, equal or more than zero: + +\starttyping[option=TEX] +\ifnumval 123\or less \or equal \or more \else error \fi +\ifnumval bad\or less \or equal \or more \else error \fi +\stoptyping + +You can decide to ignore the bad number or do something that makes more sense. +Often the to be checked value will be the content of a macro or an argument like +\type {#1}. + +\stopsubsection + +\startsubsection[title={\tex{ifcmpdim}}] + +This test is like \type {\ifcmpnum} but for dimensions. + +\stopsubsection + +\startsubsection[title={\tex{ifchkdim}}] + +This test is like \type {\ifchknum} but for dimensions. + +\stopsubsection + +\startsubsection[title={\tex{ifdimval}}] + +This test is like \type {\ifnumval} but for dimensions. + +\stopsubsection + +\startsubsection[title={\tex{iftok}}] + +Although this test is still experimental it can be used. What happens is that +two to be compared \quote {things} get scanned for. For each we first gobble +spaces and \type {\relax} tokens. Then we can have several cases: + +\startitemize[n,packed] + \startitem + When we see a left brace, a list of tokens is scanned upto the + matching right brace. + \stopitem + \startitem + When a reference to a token register is seen, that register is taken as + value. + \stopitem + \startitem + When a reference to an internal token register is seen, that register is + taken as value. + \stopitem + \startitem + When a macro is seen, its definition becomes the to be compared value. + \stopitem + \startitem + When a number is seen, the value of the corresponding register is taken + \stopitem +\stopitemize + +An example of the first case is: + +\starttyping[option=TEX] +\iftok {abc} {def}% + ... +\else + ... +\fi +\stoptyping + +The second case goes like this: + +\starttyping[option=TEX] +\iftok\scratchtoksone\scratchtokstwo + ... +\else + ... +\fi +\stoptyping + +Case one and four mixed: + +\starttyping[option=TEX] +\iftok{123}\TempX + ... +\else + ... +\fi +\stoptyping + +The last case is more a catch: it will issue an error when no number is given. +Eventually that might become a bit more clever (depending on our needs.) + +\stopsubsection + +\startsubsection[title={\tex{ifcstok}}] + +There is a subtle difference between this one and \type {iftok}: spaces +and \type {\relax} tokens are skipped but nothing gets expanded. So, when +we arrive at the to be compared \quote {things} we look at what is there, +as|-|is. + +\stopsubsection + +\startsubsection[title={\tex{iffrozen}}] + +{\em This is an experimental test.} Commands can be defined with the \type +{\frozen} prefix and this test can be used to check if that has been the case. + +\stopsubsection + +\startsubsection[title={\tex{ifprotected}}] + +Commands can be defined with the \type {\protected} prefix (or in \CONTEXT, for +historic reasons, with \type {\unexpanded}) and this test can be used to check if +that has been the case. + +\stopsubsection + +\startsubsection[title={\tex{ifusercmd}}] + +{\em This is an experimental test.} It can be used to see if the command is +defined at the user level or is a build in one. This one might evolve. + +\stopsubsection + +\startsubsection[title={\tex{orelse}}] + +This it not really a test primitive but it does act that way. Say that we have this: + +\starttyping[option=TEX] +\ifdim\scratchdimen>10pt + case 1 +\else\ifdim\scratchdimen<20pt + case 2 +\else\ifcount\scratchcounter>10 + case 3 +\else\ifcount\scratchcounter<20 + case 4 +\fi\fi\fi\fi +\stoptyping + +A bit nicer looks this: + +\starttyping[option=TEX] +\ifdim\scratchdimen>10pt + case 1 +\orelse\ifdim\scratchdimen<20pt + case 2 +\orelse\ifcount\scratchcounter>10 + case 3 +\orelse\ifcount\scratchcounter<20 + case 4 +\fi +\stoptyping + +We stay at the same level and the only test that cannot be used this way is \type +{\ifcondition} but that is no real problem. Sometimes a more flat test tree had +advantages but if you think that it gives better performance then you will be +disappointed. The fact that we stay at the same level is compensated by a bit +more parsing, so unless you have millions such cases (or expansions) it might +make a bit of a difference. As mentioned, I'm a bit sensitive for how code looks so +that was the main motivation for introducing it. + +A rather neat trick is the definition of \type {\quitcondition}: + +\starttyping[option=TEX] +\def\quitcondition{\orelse\iffalse} +\stoptyping + +This permits: + +\starttyping[option=TEX] +\ifdim\scratchdimen>10pt + case 1a + \quitcondition + case 4b +\fi +\stoptyping + +where, of course, the quitting normally is the result of some intermediate extra +test. But let me play safe here: beware of side effects. + +\stopsubsection + +\stopsection + +\startsection[title={For the brave}] + +\startsubsection[title={Full expansion}] + +If you don't understand the following code, don't worry. There is seldom much +reason to go this complex but obscure \TEX\ code attracts some users so \unknown + +When you have a macro that has for instance assignments, and when you expand that +macro inside an \type {\edef}, these assignments are not actually expanded but +tokenized. In \LUATEX\ there is a way to immediately apply these assignments and +that feature can be used to write a fully expandable user test. For instance: + +\startbuffer +\def\truecondition {\iftrue} +\def\falsecondition{\iffalse} + +\def\fontwithidhaschar#1#2% + {\immediateassignment\scratchcounter\numexpr\fontid\font\relax + \immediateassignment\setfontid\numexpr#1\relax + \iffontchar\font\numexpr#2\relax + \immediateassignment\setfontid\scratchcounter + \expandafter\truecondition + \else + \expandafter\falsecondition + \fi} +\stopbuffer + +\typebuffer[option=TEX] \getbuffer + +The \type {\iffontchar} test doesn't handle numeric font id, simply because +at the time it was added to \ETEX, there was no access to these id's. Now we +can do: + +\startbuffer +\edef\foo{\fontwithidhaschar{1} {75}yes\else nop\fi} \meaning\foo +\edef\foo{\fontwithidhaschar{1}{999}yes\else nop\fi} \meaning\foo + +[\ifcondition\fontwithidhaschar{1} {75}yes\else nop\fi] +[\ifcondition\fontwithidhaschar{1}{999}yes\else nop\fi] +\stopbuffer + +\typebuffer[option=TEX] + +These result in: + +\startlines +\getbuffer +\stoplines + +If you remove the \type {\immediateassignment} in the definition above then the +typeset results are still the same but the meanings of \type {\foo} look +different: they contain the assignments and the test for the character is +actually done when constructing the content of the \type {\edef}, but for the +current font. So, basically that test is now useless. + +\stopsubsection + +\startsubsection[title={User defined if's}] + +There is a \type {\newif} macro that defines three other macros: + +\starttyping[option=TEX] +\newif\ifOnMyOwnTerms +\stoptyping + +After this, not only \type {\ifOnMyOwnTerms} is defined, but also: + +\starttyping[option=TEX] +\OnMyOwnTermstrue +\OnMyOwnTermsfalse +\stoptyping + +These two actually are macros that redefine \type {\ifOnMyOwnTerms} to be either +equivalent to \type {\iftrue} and \type {\iffalse}. The (often derived from plain +\TEX) definition of \type {\newif} is a bit if a challenge as it has to deal with +removing the \type {if} in order to create the two extra macros and also make +sure that it doesn't get mixed up in a catcode jungle. + +In \CONTEXT\ we have a variant: + +\starttyping[option=TEX] +\newconditional\MyConditional +\stoptyping + +that can be used with: + +\starttyping[option=TEX] +\settrue\MyConditional +\setfalse\MyConditional +\stoptyping + +and tested like: + +\starttyping[option=TEX] +\ifconditional\MyConditional + ... +\else + ... +\fi +\stoptyping + +This one is cheaper on the hash and doesn't need the two extra macros per test. +The price is the use of \type {\ifconditional}, which is {\em not} to confused +with \type {\ifcondition} (it has bitten me already a few times). + +\stopsubsection + +\stopsection + +\startsubject[title=Colofon] + +\starttabulate +\NC Author \NC Hans Hagen \NC \NR +\NC \CONTEXT \NC \contextversion \NC \NR +\NC \LUAMETATEX \NC \texengineversion \NC \NR +\NC Support \NC www.pragma-ade.com \NC \NR +\NC \NC contextgarden.net \NC \NR +\stoptabulate + +\stopsubject + +\stopdocument diff --git a/doc/context/sources/general/manuals/lowlevel/lowlevel-expansion.tex b/doc/context/sources/general/manuals/lowlevel/lowlevel-expansion.tex new file mode 100644 index 000000000..1e2e00a35 --- /dev/null +++ b/doc/context/sources/general/manuals/lowlevel/lowlevel-expansion.tex @@ -0,0 +1,442 @@ +% language=us + +\environment lowlevel-style + +\startdocument + [title=expansion, + color=middleyellow] + +\startsection[title=Preamble] + +% \startsubsection[title=Introduction] +% \stopsubsection + +This short manual demonstrates a couple of properties of the macro language. It +is not the in|-|depth philosophical expose about macro languages, tokens, +expansion and such that some \TEX ies like. I prefer to stick to the practical +aspects. + +\stopsection + +\startsection[title={\TEX\ primitives}] + +The \TEX\ language provides quite some commands and those built in are called +primitives. User defined commands are called macros. A macro is a shortcut to a +list of primitives or macro calls. All can be mixed with characters that are to +be typeset somehow. + +\starttyping[option=TEX] +\def\MyMacro{b} + +a\MyMacro c +\stoptyping + +When \TEX\ reads this input the \type {a} gets turned into a glyph node with a +reference to the current font set and the character \type {a}. Then the parser +sees a macro call, and it will enter another input level where it expands this +macro. In this case it sees just an \type {b} and it will give this the same +treatment as the \type {a}. The macro ends, the input level decrements and the +\type {c} gets its treatment. + +A macro can contain references to macros so in practice the input can go several +levels down. + +\starttyping[option=TEX] +\def\MyMacroA{ and } +\def\MyMacroB{1\MyMacroA 2} + +a\MyMacroA b +\stoptyping + +When \type {\MyMacroB} is defined, its body gets three so called tokens: the +character token \type {a} with property \quote {other}, a token that is a +reference to the macro \type {\MyMacroB}, and a character token \type {2}, also +with property \quote {other} The meaning of \type {\MyMacroA} became five tokens: +a reference to a space token, then three character tokens with property \quote +{letter}, and finally again a space token. + +\starttyping[option=TEX] +\def \MyMacroA{ and } +\edef\MyMacroB{1\MyMacroA 2} + +a\MyMacroA b +\stoptyping + +In the previous example an \type {\edef} is used, where the \type {e} indicates +expansion. This time the meaning gets expanded. So we get effectively the same +as + +\starttyping[option=TEX] +\def\MyMacroB{1 and 2} +\stoptyping + +Characters are easy: they just expand, but not all primitives expand to their +meaning or effect. + +\startbuffer +\def\MyMacroA{\scratchcounter = 1 } +\def\MyMacroB{\advance\scratchcounter by 1} +\def\MyMacroC{\the\scratchcounter} + +\MyMacroA a +\MyMacroB b +\MyMacroB c +\MyMacroB d +\MyMacroC +\stopbuffer + +\typebuffer[option=TEX] + +\scratchcounter0 \getbuffer + +\startlines \tt +\meaning\MyMacroA +\meaning\MyMacroB +\meaning\MyMacroC +\stoplines + +Let's assume that \type {\scratchcounter} is zero to start with and use \type +{\edef's}: + +\startbuffer +\edef\MyMacroA{\scratchcounter = 1 } +\edef\MyMacroB{\advance\scratchcounter by 1} +\edef\MyMacroC{\the\scratchcounter} + +\MyMacroA a +\MyMacroB b +\MyMacroB c +\MyMacroB d +\MyMacroC +\stopbuffer + +\typebuffer[option=TEX] + +\scratchcounter0 \getbuffer + +\startlines \tt +\meaning\MyMacroA +\meaning\MyMacroB +\meaning\MyMacroC +\stoplines + +So, this time the third macro has basically its meaning frozen, but we can +prevent this by applying a \type {\noexpand} when we do this: + +\startbuffer +\edef\MyMacroA{\scratchcounter = 1 } +\edef\MyMacroB{\advance\scratchcounter by 1} +\edef\MyMacroC{\noexpand\the\scratchcounter} + +\MyMacroA a +\MyMacroB b +\MyMacroB c +\MyMacroB d +\MyMacroC +\stopbuffer + +\typebuffer[option=TEX] + +\scratchcounter0 \getbuffer + +\startlines \tt +\meaning\MyMacroA +\meaning\MyMacroB +\meaning\MyMacroC +\stoplines + +Of course this is a rather useless example but it serves its purpose: you'd better +be aware what gets expanded immediately in an \type {\edef}. In most cases you +only need to worry about \type {\the} and embedded macros (and then of course +their meanings). + +\def\MyShow{\quotation {\strut \inlinebuffer \expandafter \typ \expandafter +{\the\scratchtoks}\strut}} + +You can also store tokens in a so called token register. Here we use a predefined +scratch register: + +\startbuffer +\def\MyMacroA{ and } +\def\MyMacroB{1\MyMacroA 2} +\scratchtoks {\MyMacroA} +\stopbuffer + +\typebuffer[option=TEX] + +The content of \type {\scratchtoks} is: \MyShow, so no expansion has happened +here. + +\startbuffer +\def\MyMacroA{ and } +\def\MyMacroB{1\MyMacroA 2} +\scratchtoks \expandafter {\MyMacroA} +\stopbuffer + +\typebuffer[option=TEX] + +Now the content of \type {\scratchtoks} is: \MyShow, so this time expansion has +happened. + +\startbuffer +\def\MyMacroA{ and } +\def\MyMacroB{1\MyMacroA 2} +\scratchtoks \expandafter {\MyMacroB} +\stopbuffer + +\typebuffer[option=TEX] + +Indeed the macro gets expanded but only one level: \MyShow. Compare this with: + +\startbuffer +\def\MyMacroA{ and } +\edef\MyMacroB{1\MyMacroA 2} +\scratchtoks \expandafter {\MyMacroB} +\stopbuffer + +\typebuffer[option=TEX] + +The trick is to expand in two steps: \MyShow. Later we will see that other +engines provide some more expansion tricks. The only way to get a grip on +expansion is to just play with it. + +The \type {\expandafter} primitive expands the token (which can be a macro) after +the next next one and injects its meaning into the stream. So: + +\starttyping[option=TEX] +\expandafter \MyMacroA \MyMacroB +\stoptyping + +works okay. In a normal document you will never need this kind of hackery: it +only happens in a bit more complex macros. Here is an example: + +\startbuffer[a] +\scratchcounter 1 +\bgroup +\advance\scratchcounter 1 +\egroup +\the\scratchcounter +\stopbuffer + +\typebuffer[a][option=TEX] + +\startbuffer[b] +\scratchcounter 1 +\bgroup +\advance\scratchcounter 1 +\expandafter +\egroup +\the\scratchcounter +\stopbuffer + +\typebuffer[b][option=TEX] + +The first one gives \inlinebuffer[a], while the second gives \inlinebuffer[b]. + +% \let +% \futurelet +% \afterassignment +% \aftergroup + +\stopsection + +\startsection[title={\ETEX\ primitives}] + +In this engine a couple of extensions were added and later on \PDFTEX\ added some +more. We only discuss a few that relate to expansion. There is however a pitfall +here. Before \ETEX\ showed up, \CONTEXT\ already had a few mechanism that also +related to expansion and it used some names for macros that clash with those in +\ETEX. This is why we will use the \type {\normal} prefix here to indicate the +primitive. + +\startbuffer +\def\MyMacroA{a} +\def\MyMacroB{b} +\normalprotected\def\MyMacroC{c} +\edef\MyMacroABC{\MyMacroA\MyMacroB\MyMacroC} +\stopbuffer + +\typebuffer[option=TEX] \getbuffer + +These macros have the following meanings: + +\startlines \tt +\meaning\MyMacroA +\meaning\MyMacroB +\meaning\MyMacroC +\meaning\MyMacroABC +\stoplines + +In \CONTEXT\ you will use the \type {\unexpanded} prefix instead because that one +did something similar in older versions of \CONTEXT. As we were early adopters of +\ETEX, this later became a synonym to the \ETEX\ primitive. + +\startbuffer +\def\MyMacroA{a} +\def\MyMacroB{b} +\normalprotected\def\MyMacroC{c} +\normalexpanded{\scratchtoks{\MyMacroA\MyMacroB\MyMacroC}} +\stopbuffer + +\typebuffer[option=TEX] \getbuffer + +Here the wrapper around the token register assignment will expand the three +macros, unless they are protected, so its content becomes \MyShow. This saves +either a lot of more complex \type {\expandafter} usage or using an intermediate +\type {\edef}. In \CONTEXT\ the \type {\expanded} macro does something simpler +but it doesn't expand the first token as it is meant as a wrapper around a command, +like: + +\starttyping[option=TEX] +\expanded{\chapter{....}} % a ConTeXt command +\stoptyping + +where we do want to expand the title but not the \type {\chapter} command, not +that this would happen actually because \type {\chapter} is a protected command. + +The counterpart of \type {\normalexpanded} is \type {\normalunexpanded}, as in: + +\startbuffer +\def\MyMacroA{a} +\def\MyMacroB{b} +\normalprotected\def\MyMacroC{c} +\normalexpanded {\scratchtoks + {\MyMacroA\normalunexpanded {\MyMacroB}\MyMacroC}} +\stopbuffer + +\typebuffer[option=TEX] \getbuffer + +The register now holds \MyShow: three tokens, one character token and two +macro references. + +Tokens can represent characters, primitives, macros or be special entities like +starting math mode, beginning a group, assigning a dimension to a register, etc. +Although you can never really get back to the original input, you can come pretty +close, with: + +\startbuffer +\normaldetokenize{this can $ be anything \bgroup} +\stopbuffer + +\typebuffer[option=TEX] + +This (when typeset monospaced) is: {\tt \inlinebuffer}. The detokenizer is like +\type {\string} applied to each token in its argument. Compare this: + +\startbuffer +\normalexpanded { + \normaldetokenize{10pt} +} +\stopbuffer + +\typebuffer[option=TEX] + +We get four tokens: {\tt\inlinebuffer}. + +\startbuffer +\normalexpanded { + \string 1\string 0\string p\string t +} +\stopbuffer + +\typebuffer[option=TEX] + +So that was the same operation: {\tt\inlinebuffer}, but in both cases there is a +subtle thing going on: characters have a catcode which distinguishes them. The +parser needs to know what makes up a command name and normally that's only +letters. The next snippet shows these catcodes: + +\startbuffer +\normalexpanded { + \noexpand\the\catcode`\string 1 \noexpand\enspace + \noexpand\the\catcode`\string 0 \noexpand\enspace + \noexpand\the\catcode`\string p \noexpand\enspace + \noexpand\the\catcode`\string t \noexpand +} +\stopbuffer + +\typebuffer[option=TEX] + +The result is \quotation {\tt\inlinebuffer}: two characters are marked as \quote +{letter} and two fall in the \quote {other} category. + +\stopsection + +\startsection[title={\LUATEX\ primitives}] + +This engine adds a little in the expansion arena. First of all it offers a way to +extend token lists registers + +\startbuffer +\def\MyMacroA{a} +\def\MyMacroB{b} +\normalprotected\def\MyMacroC{b} +\scratchtoks{\MyMacroA\MyMacroB} +\stopbuffer + +\typebuffer[option=TEX] \getbuffer + +The result is: \MyShow. + +\startbuffer +\toksapp\scratchtoks{\MyMacroA\MyMacroB} +\stopbuffer + +\typebuffer[option=TEX] \getbuffer + +We're now at: \MyShow. + +\startbuffer +\etoksapp\scratchtoks{\MyMacroA\space\MyMacroB\space\MyMacroC} +\stopbuffer + +\typebuffer[option=TEX] \getbuffer + +The register has this content: \MyShow, so the additional context got expanded in +the process, except of course the protected macro \type {\MyMacroC}. + +There is a bunch of these combiners: \type {\toksapp} and \type {\tokspre} for +local appending and prepending, with global companions: \type {\gtoksapp} and +\type {\gtokspre}, as well as expanding variant: \type {\etoksapp}, \type +{\etokspre}, \type {\xtoksapp} and \type {\xtokspre}. + +There are not beforehand more efficient that using intermediate expanded macros +or token lists, simply because in the process \TEX\ has to create tokens lists +too, but sometimes they're just more convenient to use. + +A second extension is \type {\immediateassignment} which instead in tokenizing +the assignment directive applies it right now. + +\startbuffer +\edef\MyMacroA + {\scratchcounter 123 + \noexpand\the\scratchcounter} + +\edef\MyMacroB + {\immediateassignment\scratchcounter 123 + \noexpand\the\scratchcounter} +\stopbuffer + +\typebuffer[option=TEX] + +\getbuffer + +These two macros now have the meaning: + +\startlines \tt +\meaning\MyMacroA +\meaning\MyMacroB +\stoplines + +\stopsection + +\startsection[title={\LUAMETATEX\ primitives}] + +{\em todo} + +% \aftergroups + +\stopsection + +\stopdocument + diff --git a/doc/context/sources/general/manuals/lowlevel/lowlevel-registers.tex b/doc/context/sources/general/manuals/lowlevel/lowlevel-registers.tex new file mode 100644 index 000000000..8ccb0cd3a --- /dev/null +++ b/doc/context/sources/general/manuals/lowlevel/lowlevel-registers.tex @@ -0,0 +1,251 @@ +% language=us + +\environment lowlevel-style + +\startdocument + [title=registers, + color=darkmagenta] + +\startsection[title=Preamble] + +Registers are sets of variables that are accessed by index and a such resemble +registers in a processing unit. You can store a quantity in a register, retrieve +it, and also manipulate it. + +There is hardly any need to use them in \CONTEXT\ so we keep it simple. + +\stopsection + +\startsection[title={\TEX\ primitives}] + +There are several categories: + +\startitemize +\startitem + Integers (int): in order to be portable (at the time it surfaced) there are only + integers and no floats. The only place where \TEX\ uses floats internally is + when glue gets effective which happens in the backend. +\stopitem +\startitem + Dimensions (dimen): internally these are just integers but when they are entered they + are sliced into two parts so that we have a fractional part. The internal + representation is called a scaled point. +\stopitem +\startitem + Glue (skip): these are dimensions with a few additional properties: stretch and + shrink. Being a compound entity they are stored differently and thereby a bit + less efficient than numbers and dimensions. +\stopitem +\startitem + Math glue (muskip): this is the same as glue but with a unit that adapts to + the current math style properties. It's best to think about them as being + relative measures. +\stopitem +\startitem + Token lists (toks): these contain a list of tokens coming from the input + or coming from a place where they already have been converted. +\stopitem +\stopitemize + +The original \TEX\ engine had 256 entries per set. The first ten of each set are +normally reserved for scratch purposes: the even ones for local use, and the odd +ones for global usage. On top of that macro packages can reserve some for its own +use. It was quite easy to reach the maximum but there were tricks around that. +This limitation is no longer present in the variants in use today. + +Let's set a few dimension registers: + +\startbuffer[1] +\dimen 0 = 10 pt +\dimen2=10pt +\dimen4 10pt +\scratchdimen 10pt +\stopbuffer + +\typebuffer[1][option=TEX] + +We can serialize them with: + +\startbuffer[2] +\the \dimen0 +\number \dimen2 +\meaning\dimen4 +\meaning\scratchdimen +\stopbuffer + +\typebuffer[2][option=TEX] + +The results of these operations are: + +\startlines\tt +\getbuffer[1,2] +\stoplines + +The last two is not really useful but it is what you see when tracing options are +set. Here \type {\scratchdimen} is a shortcut for a register. This is {\em not} a +macro but a defined register. The low level \type {\dimendef} is used for this +but in a macro package you should not use that one but the higher level \type +{\newdimen} macro that uses it. + +\startbuffer[1] +\newdimen\MyDimenA +\def \MyDimenB{\dimen999} +\dimendef\MyDimenC998 +\stopbuffer + +\typebuffer[1][option=TEX] + +\startbuffer[2] +\meaning\MyDimenA +\meaning\MyDimenB +\meaning\MyDimenC +\stopbuffer + +\typebuffer[2][option=TEX] + +Watch the difference: + +\startlines\tt +\getbuffer[1,2] +\stoplines + +The first definition uses a yet free register so you won't get a clash. The +second one is just a shortcut using a macro and the third one too but again +direct shortcut. Try to imagine how the second line gets interpreted: + +\starttyping[option=TEX] +\MyDimenA10pt \MyDimenA10.5pt +\MyDimenB10pt \MyDimenB10.5pt +\MyDimenC10pt \MyDimenC10.5pt +\stoptyping + +Also try to imagine what messing around with \type {\MyDimenC} will do when we +also have defined a few hundred extra dimensions with \type {\newdimen}. + +In the case of dimensions the \type {\number} primitive will make the register +serialize as scaled points without unit \type {sp}. + +Next we see some of the other registers being assigned: + +\starttyping[option=TEX] +\count 0 = 100 +\skip 0 = 10pt plus 3pt minus 2pt +\skip 0 = 10pt plus 1fill +\muskip 0 = 10mu plus 3mu minus 2mu +\muskip 0 = 10mu minus 1 fil +\toks 0 = {hundred} +\stoptyping + +When a number is expected, you can use for instance this: + +\starttyping[option=TEX] +\scratchcounter\scratchcounterone +\stoptyping + +Here we use a few predefined scratch registers. You can also do this: + +\starttyping[option=TEX] +\scratchcounter\numexpr\scratchcounterone+\scratchcountertwo\relax +\stoptyping + +There are some quantities that also qualify as number: + +\starttyping[option=TEX] +\chardef\MyChar=123 % refers to character 123 (if present) +\scratchcounter\MyChar +\stoptyping + +In the past using \type {\chardef} was a way to get around the limited number of +registers, but it still had (in traditional \TEX) a limitation: you could not go +beyond 255. The \type {\mathchardef} could fo higher as it also encodes a family +number and class. This limitation has been lifted in \LUATEX. + +A character itself can also be interpreted as number, in which case it has to be +prefixed with a reverse quote: \type {`}, so: + +\startbuffer +\scratchcounter\numexpr`0+5\relax +\char\scratchcounter +\stopbuffer + +\typebuffer[option=TEX] + +produces \quotation {\inlinebuffer} because the \type {`0} expands into the +(\ASCII\ and \UTF8) slot {\tt \number`0} which represents the character zero. In +this case the next makes more sense: + +\starttyping[option=TEX] +\char\numexpr`0+5\relax +\stoptyping + +If you want to know more about all these quantities, \quotation {\TEX\ By Topic} +provides a good summary of what \TEX\ has to offer, and there is no need to repeat +it here. + +\stopsection + +\startsection[title={\ETEX\ primitives}] + +Apart from the ability to use expressions, the contribution to registers that +\ETEX\ brought was that suddenly we could use upto 65K of them, which is more +than enough. The extra registers were not as efficient as the first 256 because +they were stored in the hash table, but that was not really a problem. In \OMEGA\ +and later \LUATEX\ regular arrays were used, at the cost of more memory which in +the meantime has become cheap. As \CONTEXT\ moved to \ETEX\ rather early its +users never had to worry about it. + +\stopsection + +\startsection[title={\LUATEX\ primitives}] + +The \LUATEX\ engine introduced attributes. These are numeric properties that are +bound to the nodes that are the result of typesetting operations. They are +basically like integer registers but when set their values get bound and when +unset they are kind of invisible. + +\startitemize +\startitem + Attribute (attribute): a numeric property that when set becomes part of the + current attribute list that gets assigned to nodes. +\stopitem +\stopitemize + +Attributes can be used to communicate properties to \LUA\ callbacks. There are +several functions available for setting them and querying them. + +\starttyping[option=TEX] +\attribute999 = 123 +\stoptyping + +Using attributes this way is dangerous (of course I can only speak for \CONTEXT) +because an attribute value might trigger some action in a callback that gives +unwanted side effects. For convenience \CONTEXT\ provides: + +\startbuffer +\newattribute\MyAttribute +\stopbuffer + +\typebuffer[option=TEX] \getbuffer + +Which currently defines \type {\MyAttribute} as {\tt \meaning\MyAttribute} and is +meant to be used as: \footnote {The low level \type {\attributedef} command is +rather useless in the perspective of \CONTEXT.} + +\starttyping[option=TEX] +\attribute\MyAttribute = 123 +\stoptyping + +Just be aware that defining attributes can have an impact on performance. As you +cannot access them at the \TEX\ end you seldom need them. If you do you can +better use the proper more high level definers (not discussed here). + +\stopsection + +\startsection[title={\LUAMETATEX\ primitives}] + +{\em todo} + +\stopsection + +\stopdocument + diff --git a/doc/context/sources/general/manuals/lowlevel/lowlevel-style.tex b/doc/context/sources/general/manuals/lowlevel/lowlevel-style.tex new file mode 100644 index 000000000..ddd9df747 --- /dev/null +++ b/doc/context/sources/general/manuals/lowlevel/lowlevel-style.tex @@ -0,0 +1,104 @@ +% language=us + +% I started this series in June 2019 and I bet that it will never be complete or +% extensive enough. But I'll do my best to make it as useful as possible ConTeXt +% users out there who like to know about such details. Feel free to ask for more +% explanations. + +\startenvironment lowlevel-style + +\usemodule[abbreviations-logos] +\usemodule[scite] + +\setvariables + [document] + [title=No Title, + author=No Author, + color=NoColor] + +\setupbodyfont + [dejavu,11pt] + +\setuplayout + [width=middle, + height=middle, + backspace=2cm, + topspace=15mm] + +\setupwhitespace + [big] + +\setuphead + [chapter] + [style=\bfc, + color=darkgray] + +\setuphead + [section] + [style=\bfb, + %page=right, + color=darkgray] + +\setuphead + [subsection] + [style=\bfa, + color=darkgray] + +\setupfootertexts + [section] % [\documentvariable{title}] + +\setupfooter + [style=bold, + color=darkgray] + +\startuseMPgraphic{titlepage} + fill Page + withcolor "\documentvariable{color}" ; + + numeric d ; d := 2mm ; + + picture p ; p := textext.llft("\tex{}") + xysized (.1PaperWidth-2d,.1PaperHeight-2d) + shifted (.1PaperWidth- d,.1PaperHeight -d) + ; + + draw image ( + for i = 0 step .1 PaperWidth until PaperWidth : + for j = 0 step .1 PaperHeight until PaperHeight : + draw p shifted (i,j) ; + endfor ; + endfor ; + ) withcolor .5resolvedcolor("middlegray") ; + + draw textext.d("\strut low level") + xsized (.8PaperWidth) + shifted center topboundary Page + shifted -(0,.2PaperHeight) + withcolor "white" ; + draw textext.d("\strut \TeX") + xsized (.4PaperWidth) + shifted center topboundary Page + shifted -(0,.4PaperHeight) + withcolor "white" ; + draw textext.d("\strut\documentvariable{title}") + ysized 3cm + shifted center bottomboundary Page + shifted (0,.1PaperHeight) + withcolor "white" ; +\stopuseMPgraphic + +\startsetups document:start + + \startMPpage + \includeMPgraphic{titlepage} ; + \stopMPpage + + \page + + \startsubject[title=Contents] + \placelist[section][criterium=previous] + \stopsubject + +\stopsetups + +\stopenvironment |