diff options
Diffstat (limited to 'doc/context/sources/general/manuals/texit/texit-lookahead.tex')
-rw-r--r-- | doc/context/sources/general/manuals/texit/texit-lookahead.tex | 387 |
1 files changed, 387 insertions, 0 deletions
diff --git a/doc/context/sources/general/manuals/texit/texit-lookahead.tex b/doc/context/sources/general/manuals/texit/texit-lookahead.tex new file mode 100644 index 000000000..d3652e744 --- /dev/null +++ b/doc/context/sources/general/manuals/texit/texit-lookahead.tex @@ -0,0 +1,387 @@ +\environment texit-style + +\startcomponent texit-lookahead + +\startchapter[title={Lookahead}] + +When you look at the \TEX\ source of a macro package, your can often see +constructs like this: + +\startTEX +\def\foo#1% + {We do something with "#1".} +\stopTEX + +or maybe: + +\startTEX +\def\foo#1{% + We do something with "#1".% +} +\stopTEX + +Normally the percentage symbol is used to indicate a comment, but here +are no comments. In these cases it makes the definition effectively + +\startTEX +\def\foo#1{do something with "#1"!} +\stopTEX + +which is different from when we would not have that percent sign there: + +\startTEX +\def\foo#1 {We do something with "#1"!} +\stopTEX + +That variant is valid \TEX\ code but expects a space as delimiter of the +argument to \type {\foo}. This means that you can say: + +\startTEX +\foo{1} \foo 2 \foo {34} and \foo 56 . +\stopTEX + +while this can trigger an error message (when no space is seen at some point) or +at least give unexpected results. + +\startTEX +\foo{1}\foo 2\foo {34}and\foo 56. +\stopTEX + +A different use of the percent is seen in cases like this: + +\startTEX +\def\foo#1% + {We do something % + with "#1".} +\stopTEX + +This time we want to preserve the space after \type {something} because an +end|-|of|-|line would either or not collapse it with \type {with} depending on +how the endofline character is set up. Normally: + +\startTEX +\def\foo#1% + {We do something + with "#1".} +\stopTEX + +Will also add a space after something but when \TEX\ is set up to ignore lines +you get a collapse. So the explicit space is a robust way out. Both cases of +using or omitting the comment symbol are easy to spot as they trigger an error +or result in weird typeset results. + +\startbuffer[defs] +\def\fooA#1% + {\ifnum#1>100 + yes\else nop% + \fi} + +\def\fooB#1{\ifnum#1>100 yes\else nop \fi} + +\def\fooC#1% + {\ifnum#1>100% + yes\else nop% + \fi} +\stopbuffer + +\typebuffer[defs][option=TEX] \getbuffer[defs] + +We test this with: + +\startbuffer[demo] +\fooA{100} \fooB{100} \fooC{100} +\fooA{101} \fooB{101} \fooC{101} +\stopbuffer + +\typebuffer[demo][option=TEX] + +And the result is probably what you expect: + +\startlines +\getbuffer[demo] +\stoplines + +\startbuffer[defs] +\def\fooA#1% + {\ifnum#1>100 + 1\else 0% + \fi} + +\def\fooB#1{\ifnum#1>100 1\else 0\fi} + +\def\fooC#1% + {\ifnum#1>100% + 1\else 0% + \fi} +\stopbuffer + +However, when we have the following macro body: + +\typebuffer[defs][option=TEX] \getbuffer[defs] + +We get this output. Do you see the issue? + +\startlines +\getbuffer[demo] +\stoplines + +A preferred way to catch this is the following as a \type {\relax} ends scanning +for a number: + +\startbuffer[defs] +\def\foo#1% + {\ifnum#1>100\relax + 1\else 0% + \fi} +\stopbuffer + +\typebuffer[defs][option=TEX] \getbuffer[defs] + +However, watch what happens here: + +\startbuffer[demo] +\edef\result{\foo{123}} +\stopbuffer + +\typebuffer[demo][option=TEX] \getbuffer[demo] + +The \type {\result} macro has the following body: + +\expanded{\setbuffer[result]\meaning\result\endbuffer} + +\typebuffer[result][option=TEX] + +A neat trick out of this is the following: + +\startbuffer[defs] +\def\foo#1% + {\ifnum#1>\numexpr100\relax + 1\else 0% + \fi} +\stopbuffer + +\typebuffer[defs][option=TEX] \getbuffer[defs] + +\getbuffer[demo] + +Now the body of \type {\result} looks like this: + +\expanded{\setbuffer[result]\meaning\result\endbuffer} + +\typebuffer[result][option=TEX] + +Of course this also works: + +\startTEX +\def\foo#1% + {\ifnum#1>100 % + 1\else 0% + \fi} +\stopTEX + +as a space also delimits scanning the number. But that method can actually introduce +that space in the output. Think of this definition: + +\startbuffer[defs] +\def\foo#1#2% + {\ifnum#1>#2 % + 1\else 0% + \fi} +\stopbuffer + +\typebuffer[defs][option=TEX] \getbuffer[defs] + +What if \type {#2} has a trailing space? What if it is a verbose number? What if +it is a counter variable? + +\startbuffer[demo] +\scratchcounter=100 + [\foo{101}{100}] [\foo{101}{100 }] [\foo{101}\scratchcounter] +\scratchcounter=101 + [\foo{100}{101}] [\foo{100}{101 }] [\foo{100}\scratchcounter] +\stopbuffer + +\typebuffer[demo][option=TEX] + +\startlines +\getbuffer[demo] +\stoplines + +If you really want to introduce an unpredictable situation, use a coding style like +this: + +\startTEX +\def\foo#1#2#3#4{\if#1=#2#3\else#4\fi} +\stopTEX + +This is not that imaginary as you often see users play safe and do things like this: + +\startTEX +\ifnum\scratchcounterone=\scratchcountertwo% + ... +\else + ... +\fi +\stopTEX + +Here the percent sign is useless as the number scanner already got the number, +just try: + +\startTEX +\scratchcounterone=1 +\scratchcountertwo=1 + +\ifnum\scratchcounterone=\scratchcountertwo + yes +\else + nop +\fi +\stopTEX + +A previous one liner formatted like this really is not better! + +\startTEX +\def\foo#1#2#3#4% + {\ifnum#1=#2% + #3% + \else + #4% + \fi} +\stopTEX + +When you define macros more often than not you don't want unexpected spaces (aka spurious spaces) +which is why in \CONTEXT\ for instance setups ignores lines: + +\startbuffer[defs] +\startsetups foo + here + we ignore + spaces at the end + of a line +\stopsetups +\stopbuffer + +\typebuffer[defs][option=TEX] \getbuffer[defs] + +so we get: \quotation {\directsetup{foo}} which means that the normally few times +that we {\em do} want spaces we need to be explicit: + +\startbuffer[defs] +\startsetups foo + here\space + we ignore\space + spaces at the end\space + of a line\space +\stopsetups +\stopbuffer + +\typebuffer[defs][option=TEX] \getbuffer[defs] + +Now we're okay: \quotation {\directsetup{foo}}. The same is true for: + +\startTEX +\starttexdefinition foo + here\space + we ignore\space + spaces at the end\space + of a line\space +\stoptexdefinition +\stopTEX + +There are more cases where \TEX\ will look further. Take for example skip (glue) +scanning. A glue specification can have \type {plus} and \type {minus} fields. + +\startbuffer[defs] +\scratchdimenone=10pt +\scratchskipone =10pt plus 10pt minus 10pt +\scratchskiptwo =0pt +\stopbuffer + +\typebuffer[defs][option=TEX] + +Now take the following test: + +\startbuffer[demo] +{1 \scratchskiptwo 10pt plus 10pt \relax\the\scratchskiptwo} +{2 \scratchskiptwo \scratchdimenone plus 10pt \relax\the\scratchskiptwo} +{3 \scratchskiptwo 1\scratchdimenone plus 10pt \relax\the\scratchskiptwo} +{4 \scratchskiptwo \scratchskipone plus 10pt \relax\the\scratchskiptwo} +{5 \scratchskiptwo 1\scratchskipone plus 10pt \relax\the\scratchskiptwo} +\stopbuffer + +\typebuffer[demo][option=TEX] + +\startlines +\inlinebuffer[defs]\getbuffer[demo] +\stoplines + +If you wonder what the second \type {\relax} does, here is a variant: + +\startlines +{1 \scratchskiptwo 10pt plus 10pt \the\scratchskiptwo} +{2 \scratchskiptwo \scratchdimenone plus 10pt \the\scratchskiptwo} +{3 \scratchskiptwo 1\scratchdimenone plus 10pt \the\scratchskiptwo} +{4 \scratchskiptwo \scratchskipone plus 10pt \the\scratchskiptwo} +{5 \scratchskiptwo 1\scratchskipone plus 10pt \the\scratchskiptwo} +\stoplines + +\typebuffer[demo][option=TEX] + +\startlines +\inlinebuffer[defs]\getbuffer[demo] +\stoplines + +In this second variant \TEX\ happily keep looking for a glue specification when +it sees the \type {\the} so it serializes \type {\scratchskiptwo}. But as it sees +\type {0pt} then, it stops scanning the glue spec. What we get typeset is the old +value, not the new one! If you want to prevent this you need to \type {\relax}. + +Another case where \TEX\ keeps scanning is the following: + +\startbuffer[demo] +\vrule width 40pt height 2pt depth 5pt \quad +\vrule width 40pt height 20pt depth 5pt height 10pt \quad +\vrule width 40pt height 10pt height 20pt \quad +\vrule width 40pt height 20pt depth 5pt height 10pt width 80pt +\stopbuffer + +\typebuffer[demo][option=TEX] + +This gives the rules: + +\startlinecorrection \darkgray +\getbuffer[demo] +\stoplinecorrection + +So you can overload dimensions. The space before the \type {quad} is gobbled as +part of the look ahead for more keywords. + +Often rules (just like glue assignments) are wrapped in macro definitions where the +macro writer used \type {\relax} to look ahead. That way you prevent an error message +in cases like: + +\startTEX +\def\foo{\vrule width 40pt height 2pt} + +The \foo depth of this thought is amazing. +\stopTEX + +because \type {of} definitely is not a valid dimension. Even more subtle is: + +\startTEX +\def\foo{\hskip 10pt plus 1fil} + +The \foo fine points of typesetting can actually become a nightmare. +\stopTEX + +As \TEX\ will now see the \type {f} of \type {fine} as further specification and +think that you want \type {1fill}. + +So, the most important lesson of this chapter is that you need to be aware of the way +\TEX\ scans for quantities and specifications. In most cases the users can safely use +a \type {\relax} to prevent a lookahead. And try to avoid adding percent signs all +over the place. + +\stopchapter + +\stopcomponent |