1
2
3\startcomponent cldctxscanners
4
5\environment cldenvironment
6
7\startchapter[title={Scanners}]
8
9\startsection[title={Introduction}]
10
11\index {implementors}
12\index {scanners}
13
14Here we discuss methods to define macros that directly interface with the \LUA\
15side. It involves all kind of scanners. There are actually more than we discuss
16here but some are meant for low level usage. What is describe here has been used
17for ages and works quite well.
18
19{\em We dont discuss some of the more obscure options here. Some are there just
20because we need them as part of bootstrapping or initializing code and are of no
21real use to users.}
22
23\stopsection
24
25\startsection[title={A teaser first}]
26
27Most of this chapter is examples and you learn \TEX\ (and \LUA) best by just
28playing around. The nice thing about \TEX\ is that its all about visual output,
29so thats why in the next examples we just typeset some of what we just scanned.
30Of course in practice the \type {actions} will be more complex.
31
32\unexpanded\def\showmeaning#1
33 {\begingroup
34 \dontleavehmode
35 \ttbf\string #1\space
36 \tttf\meaning#1
37 \endgroup}
38
39\startbuffer[definition]
40\startluacode
41 interfaces.implement {
42 name = "MyMacroA",
43 public = true,
44 permanent = false,
45 arguments = "string",
46 actions = function(s)
47 context("(%s)",s)
48 end,
49 }
50\stopluacode
51\stopbuffer
52
53\startbuffer[usage]
54\MyMacroA{123}
55\MyMacroA{abc}
56\edef\temp{\MyMacroA{abc}}
57\stopbuffer
58
59\typebuffer[definition] \getbuffer[definition]
60
61By default a macro gets defined in the \type {\clf_} namespace but the \type
62{public} option makes it visible. This default indicates that it is actually a
63low level mechanism in origin. More often than not these interfaces are used like
64this:
65
66\starttyping
67\def\MyMacro#1{... \clf_MyMacroA{#1} ...}
68\stoptyping
69
70When we look at the meaning of \type {\MyMacroA} we get:
71
72\blank \showmeaning\MyMacroA \blank
73
74And when we apply this macro as:
75
76\typebuffer[usage]
77
78We get
79
80\blank \getbuffer[usage] \blank
81
82The meaning of \type {\temp} is:
83
84\blank \showmeaning\temp \blank
85
86We can also define the macro to be protected (\type {\unexpanded}) in \CONTEXT\
87speak). We can overload existing scanners but unless we specify the \type
88{overload} option, we get a warning on the console. However, in \LMTX\ there is
89catch. Implementers by default define macros as permanent unless one explicitly
90disables this so this is why in the previous definition we have done so. The
91overload flag below only makes sense in special cases, when for instance format
92file is made. (Of course overload protection only kicks in when it has been
93enabled.)
94
95\startbuffer[definition]
96\startluacode
97 interfaces.implement {
98 name = "MyMacroA",
99 public = true,
100
101 protected = true,
102 arguments = "string",
103 actions = function(s)
104 context("[%s]",s)
105 end,
106 }
107\stopluacode
108\stopbuffer
109
110\typebuffer[definition] \getbuffer[definition]
111
112This time we get:
113
114\getbuffer[usage]
115
116The meaning of \type {\temp} is:
117
118\blank \showmeaning\temp \blank
119
120\stopsection
121
122\startsection[title={Basic data types}]
123
124\index {implementorsarguments}
125
126It is actually possible to write very advanced scanners but unless youre in for
127obscurity the limited subset discussed here is normally enough. The \CONTEXT\
128user interface is rather predictable, unless you want to show off with weird
129additional interfaces, for instance by using delimiters other than curly braces
130and brackets, or by using separators other than commas.
131
132\startbuffer[definition]
133\startluacode
134 interfaces.implement {
135 name = "MyMacroB",
136 public = true,
137 arguments = { "string", "integer", "boolean", "dimen" },
138 actions = function(s,i,b,d)
139 context("<%s> <%i> <%l> <%p>",s,i,b,d)
140 end,
141 }
142\stopluacode
143\stopbuffer
144
145\typebuffer[definition] \getbuffer[definition]
146
147This time we grab four arguments, each of a different type:
148
149\startbuffer[usage]
150\MyMacroB{foo} 123 true 45.67pt
151
152\def\temp {oof}
153\scratchcounter 321
154\scratchdimen 76.54pt
155
156\MyMacroB\temp \scratchcounter false \scratchdimen
157\stopbuffer
158
159\typebuffer[usage]
160
161The above usage gives:
162
163\getbuffer[usage]
164
165As you can see, registers can be used as well, and the \type {\temp} macro is
166also accepted as argument. The integer and dimen arguments scan standard \TEX\
167values. If you want a \LUA\ number you can specify that as well. As our first
168example showed, when there is one argument you dont need an array to specify
169it.
170
171\startbuffer[definition]
172\startluacode
173 interfaces.implement {
174 name = "MyMacroC",
175 public = true,
176 arguments = "number",
177 actions = function(f)
178 context("<%.2f>",f)
179 end,
180 }
181\stopluacode
182\stopbuffer
183
184\typebuffer[definition] \getbuffer[definition]
185
186\startbuffer[usage]
187\MyMacroC 1.23
188\MyMacroC 1.23E4
189\MyMacroC 1.23E4
190\MyMacroC 0x1234
191\stopbuffer
192
193As you can see, hexadecimal numbers are also accepted:
194
195\typebuffer[usage]
196
197The above usage gives:
198
199\getbuffer[usage]
200
201\stopsection
202
203\startsection[title={Tables}]
204
205\index {implementorstables}
206
207A list can be grabbed too. The individual items are separated by spaces and
208items can be bound by braces.
209
210\startbuffer[definition]
211\startluacode
212 interfaces.implement {
213 name = "MyMacroD",
214 public = true,
215 arguments = "list",
216 actions = function(t)
217 context("< % + t >",t)
218 end,
219 }
220\stopluacode
221\stopbuffer
222
223\typebuffer[definition] \getbuffer[definition]
224
225\startbuffer[usage]
226\MyMacroD { 1 2 3 4 {5 6} }
227\stopbuffer
228
229The macro call:
230
231\typebuffer[usage]
232
233results in:
234
235\getbuffer[usage]
236
237Often in \LUA\ scripts tables are uses all over the place. Picking up a table is
238also supported by the implementer.
239
240\startbuffer[definition]
241\startluacode
242 interfaces.implement {
243 name = "MyMacroE",
244 public = true,
245 arguments = {
246 {
247 { "bar", "integer" },
248 { "foo", "dimen" },
249 }
250 },
251 actions = function(t)
252 context("<foo : %p> <bar : %i>",t.foo,t.bar)
253 end,
254 }
255\stopluacode
256\stopbuffer
257
258\typebuffer[definition] \getbuffer[definition]
259
260\startbuffer[usage]
261\MyMacroE {
262 foo 12pt
263 bar 34
264}
265\stopbuffer
266
267Watch out, we dont use equal signs and commas here:
268
269\typebuffer[usage]
270
271We get:
272
273\getbuffer[usage]
274
275All the above can be combined:
276
277\startbuffer[definition]
278\startluacode
279 interfaces.implement {
280 name = "MyMacroF",
281 public = true,
282 arguments = {
283 "string",
284 {
285 { "bar", "integer" },
286 { "foo", "dimen" },
287 },
288 {
289 { "one", "string" },
290 { "two", "string" },
291 },
292 },
293 actions = function(s,t1,t2)
294 context("<%s> <%p> <%i> <%s> <%s>",s,t1.foo,t1.bar,t2.one,t2.two)
295 end,
296 }
297\stopluacode
298\stopbuffer
299
300\typebuffer[definition] \getbuffer[definition]
301
302\startbuffer[usage]
303\MyMacroF
304 {oeps}
305 { foo 12pt bar 34 }
306 { one {x} two {y} }
307\stopbuffer
308
309The following call:
310
311\typebuffer[usage]
312
313Results in one string and two table arguments.
314
315\getbuffer[usage]
316
317You can nest tables, as in:
318
319\startbuffer[definition]
320\startluacode
321 interfaces.implement {
322 name = "MyMacroG",
323 public = true,
324 arguments = {
325 "string",
326 "string",
327 {
328 { "data", "string" },
329 { "tab", "string" },
330 { "method", "string" },
331 { "foo", {
332 { "method", "integer" },
333 { "compact", "number" },
334 { "nature" },
335 { "*" },
336 } },
337 { "compact", "string", "tonumber" },
338 { "nature", "boolean" },
339 { "escape" },
340 },
341 "boolean",
342 },
343 actions = function(s1, s2, t, b)
344 context("<%s> <%s>",s1,s2)
345 context("<%s> <%s> <%s>",t.data,t.tab,t.compact)
346 context("<%i> <%s> <%s>",t.foo.method,t.foo.nature,t.foo.whatever)
347 context("<%l>",b)
348 end,
349 }
350\stopluacode
351\stopbuffer
352
353\typebuffer[definition] \getbuffer[definition]
354
355\startbuffer[usage]
356\MyMacroG
357 {s1}
358 {s2}
359 {
360 data {d}
361 tab {t}
362 compact {12.34}
363 foo { method 1 nature {n} whatever {w} }
364 }
365 true
366\relax
367\stopbuffer
368
369Although the \type {\relax} is not really needed in the next calls, I often use
370it to indicate that were done:
371
372\typebuffer[usage]
373
374This typesets:
375
376\getbuffer[usage]
377
378\stopsection
379
380\startsection[title=Expansion]
381
382\index {implementorsexpansion}
383
384When working with scanners it is important to realize that we have to do with an
385expansion engine. When \TEX\ picks up a token, it can be done asis, that is the
386raw token, but it can also expand that token first (which can be recursive) and
387then pick up the first token that results from that. Sometimes you want that
388expansion, for instance when you pick up keywords, sometimes you dont.
389
390Expansion effects are most noticeable when we pickup a \quote {string} kind of
391value. In the implementor we have two methods for that: \type {string} and \type
392{argument}. The argument method has an expandable form (the default) and one
393that doesnt expand. Take this:
394
395\startbuffer[definition]
396\startluacode
397 interfaces.implement {
398 name = "MyMacroH",
399 public = true,
400 arguments = {
401 "string",
402 "argument",
403 "argumentasis",
404 },
405 actions = function(a,b,c)
406 context.type(a or "-") context.quad()
407 context.type(b or "-") context.quad()
408 context.type(c or "-") context.crlf()
409 end,
410 }
411\stopluacode
412\stopbuffer
413
414\typebuffer[definition] \getbuffer[definition]
415
416Now take this input:
417
418\startbuffer[usage]
419\def\Ca{A} \def\Cb{B} \def\Cc{C}
420\MyMacroH{a}{b}{c}
421\MyMacroH{a\Ca}{b\Cb}{c\Cc}
422\MyMacroH\Ca\Cb\Cc\relax
423\MyMacroH\Ca xx\relax
424\stopbuffer
425
426\typebuffer[usage]
427
428We we use the string method we need a \type {\relax} (or some spacer) to end
429scanning of the string when we dont use curly braces. The last line is
430actually kind of tricky because the macro expects two arguments after
431scanning the first string.
432
433\blank {\getbuffer[usage]} \blank
434
435\startbuffer[definition]
436\startluacode
437 interfaces.implement {
438 name = "MyMacroI",
439 public = true,
440 arguments = {
441 "argument",
442 "argumentasis",
443 },
444 actions = function(a,b,c)
445 context.type(a or "-") context.quad()
446 context.type(b or "-") context.quad()
447 context.type(c or "-") context.crlf()
448 end,
449 }
450\stopluacode
451\stopbuffer
452
453Here is a variant:
454
455\typebuffer[definition] \getbuffer[definition]
456
457\startbuffer[usage]
458\def\a{A} \def\b{B}
459\MyMacroI{a}{b}
460\MyMacroI{a\a}{b\b}
461\MyMacroI\a\b\relax
462\stopbuffer
463
464With:
465
466\typebuffer[usage]
467
468we get:
469
470\blank {\getbuffer[usage]} \blank
471
472\stopsection
473
474\startsection[title=Boxes]
475
476\index {implementorsboxes}
477
478You can pick up a box too. The value returned is a list node:
479
480\startbuffer[definition]
481\startluacode
482 interfaces.implement {
483 name = "MyMacroJ",
484 public = true,
485 arguments = "box",
486 actions = function(b)
487 context(b)
488 end,
489 }
490\stopluacode
491\stopbuffer
492
493\typebuffer[definition] \getbuffer[definition]
494
495The usual box specifiers are supported:
496
497\startbuffer[usage]
498\MyMacroJ \hbox {\strut Test 1}
499\MyMacroJ \hbox to 4cm {\strut Test 2}
500\stopbuffer
501
502So, with:
503
504\typebuffer[usage]
505
506we get:
507
508\blank {\forgetall\dontcomplain\getbuffer[usage]} \blank
509
510There are three variants that dont need the box operator \type {hbox}, \type
511{vbox} and \type {vtop}:
512
513\startbuffer[definition]
514\startluacode
515 interfaces.implement {
516 name = "MyMacroL",
517 public = true,
518 arguments = {
519 "hbox",
520 "vbox",
521 },
522 actions = function(h,v)
523 context(h)
524 context(v)
525 end,
526 }
527\stopluacode
528\stopbuffer
529
530\typebuffer[definition] \getbuffer[definition]
531
532Again, the usual box specifiers are supported:
533
534\startbuffer[usage]
535\MyMacroL {\strut Test 1h} to 10mm {\vfill Test 1v\vfill}
536\MyMacroL spread 1cm {\strut Test 2h} to 15mm {\vfill Test 2v\vfill}
537\stopbuffer
538
539This:
540
541\typebuffer[usage]
542
543gives:
544
545\blank {\forgetall\dontcomplain\showboxes \getbuffer[usage]} \blank
546
547\stopsection
548
549\startsection[title=Like \CONTEXT]
550
551\index {implementorshashes}
552\index {implementorsarrays}
553
554The previously discussed scanners dont use equal signs and commas as separators,
555but you can enforce that regime in the following way:
556
557
558\startbuffer[definition]
559\startluacode
560 interfaces.implement {
561 name = "MyMacroN",
562 public = true,
563 arguments = {
564 "hash",
565 "array",
566 },
567 actions = function(h, a)
568 context.totable(h)
569 context.quad()
570 context.totable(a)
571 end,
572 }
573\stopluacode
574\stopbuffer
575
576\typebuffer[definition] \getbuffer[definition]
577
578\startbuffer[usage]
579\MyMacroN
580 [ a = 1, b = 2 ]
581 [ 3, 4, 5, {6 7} ]
582\stopbuffer
583
584This:
585
586\typebuffer[usage]
587
588gives:
589
590\blank {\getbuffer[usage]} \blank
591
592\stopsection
593
594\startsection[title=Verbatim]
595
596\index {implementorsverbatim}
597
598There are a couple of rarely used scanners (there are more of course but these
599are pretty low level and not really used directly using implementors).
600
601\startbuffer[definition]
602\startluacode
603 interfaces.implement {
604 name = "MyMacroO",
605 public = true,
606 arguments = "verbatim",
607 actions = function(v)
608 context.type(v)
609 end,
610 }
611\stopluacode
612\stopbuffer
613
614\typebuffer[definition] \getbuffer[definition]
615
616\startbuffer[usage]
617\MyMacroO{this is \something verbatim}
618\stopbuffer
619
620There is no expansion applied in:
621
622\typebuffer[usage]
623
624so we get what we input:
625
626\blank {\getbuffer[usage]} \blank
627
628\stopsection
629
630\startsection[title=Macros]
631
632\index {implementorsmacros}
633
634We can pick up a control sequence without bothering what it actually
635represents:
636
637\startbuffer[definition]
638\startluacode
639 interfaces.implement {
640 name = "MyMacroP",
641 public = true,
642 arguments = "csname",
643 actions = function(c)
644 context("{\\ttbf name:} {\\tttf %s}",c)
645 end,
646 }
647\stopluacode
648\stopbuffer
649
650\typebuffer[definition] \getbuffer[definition]
651
652\startbuffer[usage]
653\MyMacroP\framed
654\stopbuffer
655
656The next control sequence is picked up and its name without the leading
657escape character is returned:
658
659\typebuffer[usage]
660
661So here we get:
662
663\blank {\getbuffer[usage]} \blank
664
665\stopsection
666
667\startsection[title={Token lists}]
668
669\index {implementorstoken lists}
670
671If you have no clue what tokens are in the perspective of \TEX, you can skip this
672section. We can grab a token list in two ways. The most \LUA ish way is to grab
673it as a table:
674
675\startbuffer[definition]
676\startluacode
677 interfaces.implement {
678 name = "MyMacroQ",
679 public = true,
680 arguments = "toks",
681 actions = function(t)
682 context("%S : ",t)
683 context.sprint(t)
684 context.crlf()
685 end,
686 }
687\stopluacode
688\stopbuffer
689
690\typebuffer[definition] \getbuffer[definition]
691
692\startbuffer[usage]
693\MyMacroQ{this is a {\bf token} list}
694\MyMacroQ{this is a \inframed{token} list}
695\stopbuffer
696
697\typebuffer[usage]
698
699The above sample code gives us:
700
701\blank {\getbuffer[usage]} \blank
702
703An alternative is to keep the list a user data object:
704
705\startbuffer[definition]
706\startluacode
707 interfaces.implement {
708 name = "MyMacroR",
709 public = true,
710 arguments = "tokenlist",
711 actions = function(t)
712 context("%S : ",t)
713 context.sprint(t)
714 context.crlf()
715 end,
716 }
717\stopluacode
718\stopbuffer
719
720\typebuffer[definition] \getbuffer[definition]
721
722\startbuffer[usage]
723\MyMacroR{this is a {\bf token} list}
724\MyMacroR{this is a \inframed{token} list}
725\stopbuffer
726
727\typebuffer[usage]
728
729Now we get:
730
731\blank {\getbuffer[usage]} \blank
732
733\stopsection
734
735\startsection[title={Actions}]
736
737\index {implementorsactions}
738
739The plural \type {actions} suggests that there can be more than one and indeed
740that is the case. The next example shows a sequence of actions that are applied.
741The first one gets the arguments passes.
742
743\startbuffer[definition]
744\startluacode
745 interfaces.implement {
746 name = "MyMacroS",
747 public = true,
748 arguments = "string",
749 actions = { characters.upper, context },
750 }
751\stopluacode
752\stopbuffer
753
754\typebuffer[definition] \getbuffer[definition]
755
756\startbuffer[usage]
757\MyMacroS{uppercase}
758\stopbuffer
759
760\typebuffer[usage]
761
762Gives: \inlinebuffer[usage]
763
764You can pass default arguments too. That way you can have multiple macros using
765the same action. Heres how to do that:
766
767\startbuffer[definition]
768\startluacode
769 local function MyMacro(a,b,sign)
770 if sign then
771 context("$%i + %i = %i$",a,b,a+b)
772 else
773 context("$%i - %i = %i$",a,b,a-b)
774 end
775 end
776
777 interfaces.implement {
778 name = "MyMacroPlus",
779 public = true,
780 arguments = { "integer", "integer", true },
781 actions = MyMacro,
782 }
783
784 interfaces.implement {
785 name = "MyMacroMinus",
786 public = true,
787 arguments = { "integer", "integer", false },
788 actions = MyMacro,
789 }
790\stopluacode
791\stopbuffer
792
793\typebuffer[definition] \getbuffer[definition]
794
795So,
796
797\startbuffer[usage]
798\MyMacroPlus 654 321 \crlf
799\MyMacroMinus 654 321 \crlf
800\stopbuffer
801
802\typebuffer[usage]
803
804Gives:
805
806\getbuffer[usage]
807
808If you need to pass a string, you pass it as \type {"preset"}, so single quotes
809inside the double ones. Otherwise strings are interpreted as scanner types.
810
811\stopsection
812
813\startsection[title={Embedded \LUA\ code}]
814
815When you mix \TEX\ and \LUA, you can put the \LUA\ code in a \TEX\ file, for
816instance a style. In the previous sections we used this approach:
817
818\starttyping
819\startluacode
820
821\stopluacode
822\stoptyping
823
824This method is both reliable and efficient but you need to keep into mind that
825macros get expanded. In the next code, the second line will give an error when
826you have not defined \type {\foo} as expandable macro. When it is unexpandable it
827will get passed as it is and \LUA\ will see a \type {\f} as an escaped character.
828So, when you want a macro be passes as macro, you need to do it as in the third
829line. The fact that there is a comment trigger (\type {}) doesnt help here.
830
831\starttyping
832\startluacode
833 context("foo")
834
835 context("\\bar")
836\stopluacode
837\stoptyping
838
839When you use \type {\ctxlua} the same is true but there you also need to keep an
840eye on special characters. For instance a percent sign is then interpreted in the
841\TEX\ way and because all becomes one line, a \type {} somewhere in the middle
842will make the rest of the line comment:
843
844\starttyping
845\ctxlua {
846 context("foo")
847
848
849}
850\stoptyping
851
852Here, the second line goes away (\TEX\ comment) and the third line obscures all
853that follows. You can use \type {\letterpercent} to smuggle a percent sign in a
854\type {\ctxlua} call. Special characters like a hash symbol also need to be
855passed by name. Normally curly braces are no problem because \LUA\ also likes
856them properly nested.
857
858When things become too messy and complex you can always put the code in an
859external file and load that one (e.g. with \type {require}.
860
861In the examples in this chapter we put the function in the table, but for long
862ones you might want to do this:
863
864\starttyping
865\startluacode
866 local function MyMacro(s)
867
868 end
869
870 interfaces.implement {
871 name = "MyMacro",
872 public = true,
873 arguments = "string",
874 actions = MyMacro,
875 }
876\stopluacode
877\stoptyping
878
879It is a common mistake not to define variables and functions as local. If you
880define them global for sure there will become a time when this bites you.
881
882\stopsection
883
884\stopchapter
885
886\stopcomponent
887
888
889
890
891
892
893\startluacode
894
895 local function Grabbed(t)
896 context(t)
897 end
898
899 interfaces.implement {
900 name = "GrabC",
901 public = true,
902 actions = Grabbed,
903 arguments = "bracketed",
904 }
905 interfaces.implement {
906 name = "GrabD",
907 public = true,
908 actions = Grabbed,
909 arguments = "bracketedasis",
910 }
911 interfaces.implement {
912 name = "GrabE",
913 public = true,
914 actions = Grabbed,
915 arguments = "optional",
916 }
917\stopluacode
918
919\GrabC [foo {\red okay C} bar] \par
920\GrabC \par
921\GrabD [foo {\red okay D} bar] \par
922\GrabE [foo {\red okay E} bar] \par
923\GrabE \par
924
925
926
927\startluacode
928 local random = math.random
929 local randomseed = math.randomseed
930
931 local scan_word = tokens.scanners.word
932 local scan_integer = tokens.scanners.integer
933 local scan_dimen = tokens.scanners.dimen
934 local scan_number = tokens.scanners.float
935
936 local scan_integer = tokens.scanners.luainteger
937 local scan_cardinal = tokens.scanners.luacardinal
938 local scan_number = tokens.scanners.luanumber
939
940 local I = 0
941 local D = 0
942 local F = 0
943
944 interfaces.implement {
945 name = "TestInteger",
946 public = true,
947 actions = function(b) if b then return I else I = scan_integer() end end,
948 valuetype = "count",
949 }
950
951 interfaces.implement {
952 name = "TestDimension",
953 public = true,
954 actions = function(b) if b then return D else D = scan_dimen() end end,
955 valuetype = "dimen",
956 }
957
958 interfaces.implement {
959 name = "TestFloat",
960 public = true,
961 actions = function(b)
962 if b then
963 context("%q",F)
964 else
965 F = scan_number()
966 end
967 end,
968 valuetype = "none",
969 }
970
971 interfaces.implement {
972 name = "TestThis",
973 public = true,
974 actions = function(b)
975 if b then
976 return random(scan_integer(),scan_integer())
977 else
978 randomseed(scan_integer(true))
979 end
980 end,
981 valuetype = "count",
982 }
983
984\stopluacode
985
986{\tttf [set:\TestInteger 808714][get: \the\TestInteger ]}\par
987{\tttf [set:\TestDimension 12.34pt][get: \the\TestDimension]}\par
988{\tttf [set:\TestFloat 12.34567890e99][get: \the\TestFloat ]}\par
989
990{\tttf \TestThis 123 \dorecurse{10}{\the \TestThis 1 10 \space}}\par
991{\tttf \TestThis 123 \dorecurse{10}{\the \TestThis 1 10 \space}}\par
992{\tttf \TestThis 456 \dorecurse{10}{\the \TestThis 1 10 \space}}\par
993{\tttf \dorecurse{10}{\the \TestThis 1 10 \space}}\par
994
995\TestFloat .1e20\relax
996\TestFloat 0x1.693d8e8943f17p332\relax
997\TestFloat 0x1.693d8e8943f17p332\relax
998
999\TestFloat 123.345E67\relax
1000
1001{\tttf [\the\TestFloat]}
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019\startluacode
1020 local t_bf = whatever and whatever.bf or token.create("bf")
1021 local put = token.put_next
1022
1023 interfaces.implement {
1024 name = "whateverbfone",
1025 public = true,
1026 actions = function()
1027 put(t_bf)
1028 end
1029 }
1030
1031 local ctx_whateverbfxxx = context.whateverbfxxx
1032
1033 interfaces.implement {
1034 name = "whateverbftwo",
1035 public = true,
1036 actions = function()
1037 ctx_whateverbfxxx(false)
1038 end
1039 }
1040
1041 interfaces.implement {
1042 name = "whateverbfthree",
1043 public = true,
1044 actions = context.core.cs.whateverbfxxx
1045 }
1046
1047\stopluacode
1048
1049\let\whateverbfxxx\bf
1050
1051\dontleavehmode{xxx: \whateverbfxxx xxx}\quad
1052\dontleavehmode{one: \whateverbfone one}\quad
1053\dontleavehmode{two: \whateverbftwo two}\quad
1054\dontleavehmode{two: \whateverbfthree three}\par
1055 |