1if not modules then modules = { } end modules ['cldf-ini'] = {
2 version = 1.001,
3 comment = "companion to cldf-ini.mkiv",
4 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5 copyright = "PRAGMA ADE / ConTeXt Development Team",
6 license = "see context related readme files"
7}
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68local format, stripstring = string.format, string.strip
69local next, type, tostring, tonumber, unpack, select, rawset = next, type, tostring, tonumber, unpack, select, rawset
70local insert, remove, concat = table.insert, table.remove, table.concat
71local lpegmatch, lpegC, lpegS, lpegP, lpegV, lpegCc, lpegCs, patterns = lpeg.match, lpeg.C, lpeg.S, lpeg.P, lpeg.V, lpeg.Cc, lpeg.Cs, lpeg.patterns
72
73local formatters = string.formatters
74local setmetatableindex = table.setmetatableindex
75local setmetatablecall = table.setmetatablecall
76local setmetatablenewindex = table.setmetatablenewindex
77
78context = context or { }
79commands = commands or { }
80interfaces = interfaces or { }
81
82local context = context
83local commands = commands
84local interfaces = interfaces
85
86local loaddata = io.loaddata
87
88local tex = tex
89local texsprint = tex.sprint
90local texprint = tex.print
91
92
93
94
95local isnode = node.isnode or node.is_node
96local copynodelist = node.copylist or node.copy_list
97local writenode = node.write
98local tonut = node.direct.todirect
99local tonode = node.direct.tonode
100
101local newtoken = token.new
102
103
104local istoken = token.istoken or token.is_token
105local setluatoken = token.setlua or token.set_lua
106
107
108
109local isprintable = tex.isprintable or function(n)
110 return n and (type(n) == "string" or isnode(n) or istoken(n))
111end
112
113if tex.runlocal then
114
115 tex.runtoks = tex.runlocal
116 tex.quittoks = tex.quitlocal
117else
118 tex.runlocal = tex.runtoks
119 tex.quitlocal = tex.quittoks
120end
121
122local catcodenumbers = catcodes.numbers
123
124local ctxcatcodes = catcodenumbers.ctxcatcodes
125local prtcatcodes = catcodenumbers.prtcatcodes
126local texcatcodes = catcodenumbers.texcatcodes
127local txtcatcodes = catcodenumbers.txtcatcodes
128local vrbcatcodes = catcodenumbers.vrbcatcodes
129local xmlcatcodes = catcodenumbers.xmlcatcodes
130
131local flush = texsprint
132local flushdirect = texprint
133
134
135local report_context = logs.reporter("cld","tex")
136local report_cld = logs.reporter("cld","stack")
137
138
139local processlines = true
140
141local trialtypesetting = function() return false end
142
143function context.settrialtypesettingmethod(f)
144 trialtypesetting = f
145 context.trialtypesetting = f
146end
147
148context.trialtypesetting = function() return trialtypesetting() end
149
150local knownfunctions = (lua.getfunctionstable or lua.get_functions_table)(true)
151local showstackusage = false
152
153trackers.register("context.stack",function(v) showstackusage = v end)
154
155local freed = { }
156local nofused = 0
157local noffreed = 0
158
159local usedstack = function()
160 return nofused, noffreed
161end
162
163local flushfunction = function(slot,arg)
164 if arg() or trialtypesetting() then
165
166 else
167 noffreed = noffreed + 1
168 freed[noffreed] = slot
169 knownfunctions[slot] = false
170 end
171end
172
173local storefunction = function(arg)
174 local f = function(slot) flushfunction(slot,arg) end
175 if noffreed > 0 then
176 local n = freed[noffreed]
177 freed[noffreed] = nil
178 noffreed = noffreed - 1
179 knownfunctions[n] = f
180 return n
181 else
182 nofused = nofused + 1
183 knownfunctions[nofused] = f
184 return nofused
185 end
186end
187
188local flushnode = function(slot,arg)
189 if trialtypesetting() then
190 writenode(copynodelist(arg))
191 else
192 writenode(arg)
193 noffreed = noffreed + 1
194 freed[noffreed] = slot
195 knownfunctions[slot] = false
196 end
197end
198
199local storenode = function(arg)
200 local f = function(slot) flushnode(slot,arg) end
201 if noffreed > 0 then
202 local n = freed[noffreed]
203 freed[noffreed] = nil
204 noffreed = noffreed - 1
205 knownfunctions[n] = f
206 return n
207 else
208 nofused = nofused + 1
209 knownfunctions[nofused] = f
210 return nofused
211 end
212end
213
214storage.storedfunctions = storage.storedfunctions or { }
215local storedfunctions = storage.storedfunctions
216local initex = environment.initex
217
218storage.register("storage/storedfunctions", storedfunctions, "storage.storedfunctions")
219
220local f_resolve = nil
221local p_resolve = ((1-lpegP("."))^1 / function(s) f_resolve = f_resolve[s] end * lpegP(".")^0)^1
222
223local function resolvestoredfunction(str)
224 if type(str) == "string" then
225 f_resolve = global
226 lpegmatch(p_resolve,str)
227 return f_resolve
228 else
229 return str
230 end
231end
232
233local function expose(slot,f,...)
234 local func = resolvestoredfunction(f)
235 if not func then
236 func = function() report_cld("beware: unknown function %i called: %s",slot,f) end
237 end
238 knownfunctions[slot] = func
239 return func(...)
240end
241
242if initex then
243
244else
245 local slots = table.sortedkeys(storedfunctions)
246 local last = #slots
247 if last > 0 then
248
249 for i=1,last do
250 local slot = slots[i]
251 local data = storedfunctions[slot]
252 knownfunctions[slot] = function(...)
253
254 return expose(slot,data,...)
255 end
256 end
257
258 nofused = slots[last]
259
260 for i=1,nofused do
261 if not knownfunctions[i] then
262 noffreed = noffreed + 1
263 freed[noffreed] = i
264 end
265 end
266
267 end
268end
269
270local registerfunction = function(f,direct,slot)
271 local func
272 if slot then
273
274 elseif noffreed > 0 then
275 slot = freed[noffreed]
276 freed[noffreed] = nil
277 noffreed = noffreed - 1
278 else
279 nofused = nofused + 1
280 slot = nofused
281 end
282 if direct then
283 if initex then
284 func = function(...) expose(slot,f,...) end
285 storedfunctions[slot] = f
286 else
287 func = resolvestoredfunction(f)
288 end
289 if type(func) ~= "function" then
290 func = function() report_cld("invalid resolve %A, case %s",f,1) end
291 end
292 elseif type(f) == "string" then
293 func = loadstring(f)
294 if type(func) ~= "function" then
295 func = function() report_cld("invalid code %A, case %s",f,2) end
296 end
297 elseif type(f) == "function" then
298 func = f
299 else
300 func = function() report_cld("invalid function %A, case %s",f,3) end
301 end
302 knownfunctions[slot] = func
303 return slot
304end
305
306local unregisterfunction = function(slot)
307 if knownfunctions[slot] then
308 noffreed = noffreed + 1
309 freed[noffreed] = slot
310 knownfunctions[slot] = false
311 else
312 report_cld("invalid function slot %A",slot)
313 end
314end
315
316local reservefunction = function()
317 if noffreed > 0 then
318 local n = freed[noffreed]
319 freed[noffreed] = nil
320 noffreed = noffreed - 1
321 return n
322 else
323 nofused = nofused + 1
324 return nofused
325 end
326end
327
328local callfunctiononce = function(slot)
329 knownfunctions[slot](slot)
330 noffreed = noffreed + 1
331 freed[noffreed] = slot
332 knownfunctions[slot] = false
333end
334
335setmetatablecall(knownfunctions,function(t,n) return knownfunctions[n](n) end)
336
337
338
339do
340
341 local stub = { }
342 local done = false
343 local message = function()
344
345 if not done then
346 report_cld("")
347 report_cld("use : slot = context.functions.register(f)")
348 report_cld("and : context.functions.unregister(slot)")
349 report_cld("")
350 done = true
351 end
352 end
353
354 setmetatable(stub, {
355 __index = message,
356 __newindex = message,
357 })
358
359 function lua.getfunctionstable()
360 message()
361 return stub
362 end
363
364 lua.get_functions_table = lua.getfunctionstable
365
366end
367
368
369
370
371
372
373
374
375
376local storedscanners = interfaces.storedscanners or { }
377local namesofscanners = interfaces.namesofscanners or { }
378local interfacescanners = { }
379local privatenamespace = "clf_"
380
381interfaces.storedscanners = storedscanners
382interfaces.namesofscanners = namesofscanners
383
384storage.register("interfaces/storedscanners", storedscanners, "interfaces.storedscanners")
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399local registerscanner if CONTEXTLMTXMODE > 0 then
400
401
402
403
404
405 local function toflags(specification)
406 local protected = (specification.protected and "protected") or (specification.semiprotected and "semiprotected")
407 local untraced = specification.untraced and "untraced"
408 local usage = specification.usage
409 if usage == "value" then
410 return "global", "value", "permanent", "untraced", protected
411 elseif usage == "condition" then
412 return "global", "conditional", "permanent", "untraced", protected
413 elseif specification.frozen then
414 return "global", "frozen", untraced, protected
415 elseif specification.permanent == false or specification.onlyonce then
416 return "global", untraced, protected, semiprotected
417 else
418 return "global", "permanent", untraced, protected
419 end
420 end
421
422 registerscanner = function(name,action,specification)
423 rawset(interfacescanners,name,action)
424 local n = registerfunction("interfaces.scanners."..name,true,storedscanners[name])
425 storedscanners[name] = n
426 namesofscanners[n] = name
427 name = specification.public and name or (privatenamespace .. name)
428
429 setluatoken(name,n,toflags(specification))
430 end
431
432else
433
434 registerscanner = function(name,action,specification)
435 rawset(interfacescanners,name,action)
436 local n = storedscanners[name]
437 n = registerfunction("interfaces.scanners."..name,true,n)
438 storedscanners[name] = n
439 namesofscanners[n] = name
440 name = specification.public and name or (privatenamespace .. name)
441 setluatoken(name,n,"global",specification.protected and "protected" or "")
442 end
443
444end
445
446interfaces.registerscanner = registerscanner
447
448function interfaces.knownscanner(name)
449 return interfacescanners[name]
450end
451
452function interfaces.nameofscanner(slot)
453 return namesofscanners[slot] or slot
454end
455
456if CONTEXTLMTXMODE > 0 then
457
458 callbacks.register("show_lua_call", function(what, slot)
459 local name = namesofscanners[slot]
460
461 return name and formatters["%s \\%s"](what,name) or ""
462 end, "provide lua call details")
463
464end
465
466setmetatablenewindex(interfacescanners, function(t,k,v)
467 report_cld("don't register scanner %a directly",k)
468
469end)
470
471interfaces.scanners = storage.mark(interfacescanners)
472
473context.functions = {
474 register = function(qualifiedname) return registerfunction(qualifiedname) end,
475 unregister = unregisterfunction,
476 reserve = reservefunction,
477 known = knownfunctions,
478 callonce = callfunctiononce,
479}
480
481function commands.ctxfunction(code,namespace)
482 context(registerfunction(code,namespace))
483end
484
485function commands.ctxscanner(name,code,namespace)
486 local n = registerfunction(code,namespace)
487 if storedscanners[name] then
488 storedscanners[name] = n
489 end
490 context(n)
491end
492
493local function dummy() end
494
495local texsetmacro = token.setmacro or token.set_macro
496
497function commands.ctxresetter(name)
498 return function()
499 if storedscanners[name] then
500 rawset(interfacescanners,name,dummy)
501
502 texsetmacro(privatenamespace .. name,"","global")
503 end
504 end
505end
506
507
508
509local catcodestack = { }
510local currentcatcodes = ctxcatcodes
511local contentcatcodes = ctxcatcodes
512
513local catcodes = {
514 ctx = ctxcatcodes, ctxcatcodes = ctxcatcodes, context = ctxcatcodes,
515 prt = prtcatcodes, prtcatcodes = prtcatcodes, protect = prtcatcodes,
516 tex = texcatcodes, texcatcodes = texcatcodes, plain = texcatcodes,
517 txt = txtcatcodes, txtcatcodes = txtcatcodes, text = txtcatcodes,
518 vrb = vrbcatcodes, vrbcatcodes = vrbcatcodes, verbatim = vrbcatcodes,
519 xml = xmlcatcodes, xmlcatcodes = xmlcatcodes,
520}
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535local catcodelevel = 0
536
537local function pushcatcodes(c)
538 catcodelevel = catcodelevel + 1
539 catcodestack[catcodelevel] = currentcatcodes
540 currentcatcodes = (c and catcodes[c] or tonumber(c)) or currentcatcodes
541 contentcatcodes = currentcatcodes
542end
543
544local function popcatcodes()
545 if catcodelevel > 0 then
546 currentcatcodes = catcodestack[catcodelevel] or currentcatcodes
547 catcodelevel = catcodelevel - 1
548 end
549 contentcatcodes = currentcatcodes
550end
551
552function context.unprotect()
553
554 catcodelevel = catcodelevel + 1
555 catcodestack[catcodelevel] = currentcatcodes
556 currentcatcodes = prtcatcodes
557 contentcatcodes = prtcatcodes
558
559 flush("\\unprotect")
560end
561
562function context.protect()
563
564 flush("\\protect")
565
566 if catcodelevel > 0 then
567 currentcatcodes = catcodestack[catcodelevel] or currentcatcodes
568 catcodelevel = catcodelevel - 1
569 end
570 contentcatcodes = currentcatcodes
571end
572
573context.catcodes = catcodes
574context.pushcatcodes = pushcatcodes
575context.popcatcodes = popcatcodes
576
577
578
579local newline = patterns.newline
580local space = patterns.spacer
581local spacing = newline * space^0
582local content = lpegC((1-spacing)^1)
583local emptyline = space^0 * newline^2
584 + newline * space^1 * newline^1
585local endofline = space^0 * newline * space^0
586local simpleline = endofline * lpegP(-1)
587
588local verbose = lpegC((1-space-newline)^1)
589local beginstripper = (lpegS(" \t")^1 * newline^1) / ""
590local endstripper = beginstripper * lpegP(-1)
591
592local justaspace = space * lpegCc("")
593local justanewline = newline * lpegCc("")
594
595local function n_content (s) flush (contentcatcodes,s ) end
596local function n_verbose (s) flush (vrbcatcodes, s ) end
597local function n_endofline () flush (currentcatcodes," \r") end
598local function n_emptyline () flushdirect(currentcatcodes,"\r" ) end
599local function n_simpleline() flush (currentcatcodes," \r") end
600
601local n_exception = ""
602
603
604
605function context.newtexthandler(specification)
606 specification = specification or { }
607
608 local s_catcodes = specification.catcodes
609
610 local f_before = specification.before
611 local f_after = specification.after
612
613 local f_endofline = specification.endofline or n_endofline
614 local f_emptyline = specification.emptyline or n_emptyline
615 local f_simpleline = specification.simpleline or n_simpleline
616 local f_content = specification.content or n_content
617 local f_space = specification.space
618
619 local p_exception = specification.exception
620
621 if s_catcodes then
622 f_content = function(s)
623 flush(s_catcodes,s)
624 end
625 end
626
627 local pattern
628 if f_space then
629 if p_exception then
630 local content = lpegC((1-spacing-p_exception)^1)
631 pattern =
632 (
633 justaspace / f_space
634 + justanewline / f_endofline
635 + p_exception
636 + content / f_content
637 )^0
638 else
639 local content = lpegC((1-space-endofline)^1)
640 pattern =
641 (
642 justaspace / f_space
643 + justanewline / f_endofline
644 + content / f_content
645 )^0
646 end
647 else
648 if p_exception then
649 local content = lpegC((1-spacing-p_exception)^1)
650 pattern =
651 simpleline / f_simpleline
652 +
653 (
654 emptyline / f_emptyline
655 + endofline / f_endofline
656 + p_exception
657 + content / f_content
658 )^0
659 else
660 local content = lpegC((1-spacing)^1)
661 pattern =
662 simpleline / f_simpleline
663 +
664 (
665 emptyline / f_emptyline
666 + endofline / f_endofline
667 + content / f_content
668 )^0
669 end
670 end
671
672 if f_before then
673 pattern = (P(true) / f_before) * pattern
674 end
675
676 if f_after then
677 pattern = pattern * (P(true) / f_after)
678 end
679
680 return function(str) return lpegmatch(pattern,str) end, pattern
681end
682
683function context.newverbosehandler(specification)
684 specification = specification or { }
685
686 local f_line = specification.line or function() flushdirect("\r") end
687 local f_space = specification.space or function() flush (" ") end
688 local f_content = specification.content or n_verbose
689 local f_before = specification.before
690 local f_after = specification.after
691
692 local pattern =
693 justanewline / f_line
694 + verbose / f_content
695 + justaspace / f_space
696
697 if specification.strip then
698 pattern = beginstripper^0 * (endstripper + pattern)^0
699 else
700 pattern = pattern^0
701 end
702
703 if f_before then
704 pattern = (lpegP(true) / f_before) * pattern
705 end
706
707 if f_after then
708 pattern = pattern * (lpegP(true) / f_after)
709 end
710
711 return function(str) return lpegmatch(pattern,str) end, pattern
712end
713
714local flushlines = context.newtexthandler {
715 content = n_content,
716 endofline = n_endofline,
717 emptyline = n_emptyline,
718 simpleline = n_simpleline,
719}
720
721
722
723local printlines_ctx = (
724 (newline) / function() texprint("") end +
725 (1-newline)^1 / function(s) texprint(ctxcatcodes,s) end * newline^-1
726)^0
727
728local printlines_raw = (
729 (newline) / function() texprint("") end +
730 (1-newline)^1 / function(s) texprint(s) end * newline^-1
731)^0
732
733function context.printlines(str,raw)
734 if raw then
735 lpegmatch(printlines_raw,str)
736 else
737 lpegmatch(printlines_ctx,str)
738 end
739end
740
741function context.printtable(t,separator)
742 if separator == nil or separator == true then
743 separator = "\r"
744 elseif separator == "" or separator == false then
745 separator = ""
746 end
747 local s = concat(t,separator)
748 if s ~= "" then
749 context(s)
750 end
751end
752
753
754
755local containseol = patterns.containseol
756
757local lua_call_code = tokens.commands.lua_expandable_call or tokens.commands.lua_call
758
759local sortedhashindeed = false
760
761directives.register("context.sorthash",function(v)
762 sortedhashindeed = v and table.sortedhash or nil
763end)
764
765local function writer(parent,command,...)
766 if type(command) == "string" then
767 flush(currentcatcodes,command)
768 else
769 flush(command)
770 end
771 local direct = false
772 for i=1,select("#",...) do
773 local ti = select(i,...)
774 if direct then
775 local typ = type(ti)
776 if typ == "string" or typ == "number" then
777 flush(currentcatcodes,ti)
778 else
779 report_context("error: invalid use of direct in %a, only strings and numbers can be flushed directly, not %a",command,typ)
780 end
781 direct = false
782 elseif ti == nil then
783
784 elseif ti == "" then
785 flush(currentcatcodes,"{}")
786
787
788 else
789 local typ = type(ti)
790 if typ == "string" then
791
792 if processlines and lpegmatch(containseol,ti) then
793 flush(currentcatcodes,"{")
794 flushlines(ti)
795 flush(currentcatcodes,"}")
796 elseif currentcatcodes == contentcatcodes then
797 flush(currentcatcodes,"{",ti,"}")
798 else
799 flush(currentcatcodes,"{")
800 flush(contentcatcodes,ti)
801 flush(currentcatcodes,"}")
802 end
803 elseif typ == "number" then
804
805 flush(currentcatcodes,"{",ti,"}")
806 elseif typ == "table" then
807 local tn = #ti
808 if tn == 0 then
809 local done = false
810 if sortedhashindeed then
811 for k, v in sortedhashindeed(ti) do
812 if done then
813 if v == "" then
814 flush(currentcatcodes,",",k,'=')
815 else
816 flush(currentcatcodes,",",k,"={",v,"}")
817 end
818 else
819 if v == "" then
820 flush(currentcatcodes,"[",k,"=")
821 else
822 flush(currentcatcodes,"[",k,"={",v,"}")
823 end
824 done = true
825 end
826 end
827 else
828 for k, v in next, ti do
829 if done then
830 if v == "" then
831 flush(currentcatcodes,",",k,'=')
832 else
833 flush(currentcatcodes,",",k,"={",v,"}")
834 end
835 else
836 if v == "" then
837 flush(currentcatcodes,"[",k,"=")
838 else
839 flush(currentcatcodes,"[",k,"={",v,"}")
840 end
841 done = true
842 end
843 end
844 end
845 if done then
846 flush(currentcatcodes,"]")
847 else
848 flush(currentcatcodes,"[]")
849 end
850 elseif tn == 1 then
851 local tj = ti[1]
852 if type(tj) == "function" then
853 tj = storefunction(tj)
854 flush(currentcatcodes,"[",newtoken(tj,lua_call_code),"]")
855 else
856 flush(currentcatcodes,"[",tj,"]")
857 end
858 else
859 flush(currentcatcodes,"[")
860 for j=1,tn do
861 local tj = ti[j]
862 if type(tj) == "function" then
863 tj = storefunction(tj)
864 flush(currentcatcodes,"[",newtoken(tj,lua_call_code),j == tn and "]" or ",")
865 else
866 if j == tn then
867 flush(currentcatcodes,tj,"]")
868 else
869 flush(currentcatcodes,tj,",")
870 end
871 end
872 end
873 end
874 elseif typ == "function" then
875
876 ti = storefunction(ti)
877 flush(currentcatcodes,"{",newtoken(ti,lua_call_code),"}")
878 elseif typ == "boolean" then
879 if ti then
880 flushdirect(currentcatcodes,"\r")
881 else
882 direct = true
883 end
884 elseif typ == "thread" then
885 report_context("coroutines not supported as we cannot yield across boundaries")
886
887 elseif isprintable(ti) then
888 flush(currentcatcodes,"{",ti,"}")
889 else
890 local s = tostring(ti)
891 if s then
892 flushdirect(currentcatcodes,s)
893 else
894 report_context("error: %a gets a weird argument %a",command,ti)
895 end
896 end
897
898
899
900
901
902
903
904 end
905 end
906end
907
908local toks = tokens.cache
909context.tokenizedcs = toks
910
911local core = setmetatableindex(function(parent,k)
912 local t
913 local f = function(first,...)
914 if not t then
915 t = toks[k]
916 end
917 if first == nil then
918 flush(t)
919 else
920 return writer(context,t,first,...)
921 end
922 end
923 parent[k] = f
924 return f
925end)
926
927core.cs = setmetatableindex(function(parent,k)
928 local t
929 local f = function()
930 if not t then
931 t = toks[k]
932 end
933 flush(t)
934 end
935 parent[k] = f
936 return f
937end)
938
939local indexer = function(parent,k)
940 if type(k) == "string" then
941 return core[k]
942 else
943 return context
944 end
945end
946
947context.core = core
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005local caller = function(parent,f,a,...)
1006 if not parent then
1007
1008 elseif f ~= nil then
1009 local typ = type(f)
1010 if typ == "string" then
1011 if f == "" then
1012
1013
1014
1015
1016 elseif a then
1017 flush(contentcatcodes,formatters[f](a,...))
1018
1019 elseif processlines and lpegmatch(containseol,f) then
1020 flushlines(f)
1021 else
1022 flush(contentcatcodes,f)
1023 end
1024 elseif typ == "number" then
1025 if a then
1026 flush(currentcatcodes,f,a,...)
1027 else
1028 flush(currentcatcodes,f)
1029 end
1030 elseif typ == "function" then
1031
1032 f = storefunction(f)
1033 flush(currentcatcodes,"{",newtoken(f,lua_call_code),"}")
1034 elseif typ == "boolean" then
1035 if f then
1036 if a ~= nil then
1037 flushlines(a)
1038 else
1039 flushdirect(currentcatcodes,"\n")
1040 end
1041 else
1042 if a ~= nil then
1043
1044 writer(parent,"",a,...)
1045 else
1046
1047 end
1048 end
1049 elseif typ == "thread" then
1050 report_context("coroutines not supported as we cannot yield across boundaries")
1051
1052 elseif isprintable(f) then
1053 flush(f)
1054 else
1055 local s = tostring(f)
1056 if s then
1057 flushdirect(currentcatcodes,s)
1058 else
1059 report_context("error: %a gets a weird argument %a","context",f)
1060 end
1061 end
1062
1063
1064
1065
1066
1067
1068
1069 end
1070end
1071
1072context.nodes = {
1073 store = storenode,
1074 flush = function(n)
1075 flush(n)
1076 end,
1077}
1078
1079context.nuts = {
1080 store = function(n)
1081 return storenode(tonut(n))
1082 end,
1083 flush = function(n,d)
1084 flush(tonode(n))
1085 end,
1086}
1087
1088local defaultcaller = caller
1089
1090setmetatableindex(context,indexer)
1091setmetatablecall (context,caller)
1092
1093function context.sprint(...)
1094 flush(...)
1095end
1096
1097function context.fprint(first,second,third,...)
1098 if type(first) == "number" then
1099 if third then
1100 flush(first,formatters[second](third,...))
1101 else
1102 flush(first,second)
1103 end
1104 else
1105 if second then
1106 flush(formatters[first](second,third,...))
1107 else
1108 flush(first)
1109 end
1110 end
1111end
1112
1113tex.fprint = context.fprint
1114
1115
1116
1117local trace_stack = { report_context }
1118
1119local normalflush = flush
1120local normalflushdirect = flushdirect
1121
1122local normalwriter = writer
1123local currenttrace = report_context
1124local nofwriters = 0
1125local nofflushes = 0
1126
1127local tracingpermitted = true
1128
1129local visualizer = lpeg.replacer {
1130 { "\n", "<<newline>>" },
1131 { "\r", "<<par>>" },
1132}
1133
1134statistics.register("traced context", function()
1135 local used, freed = usedstack()
1136 local unreachable = used - freed
1137 if nofwriters > 0 or nofflushes > 0 then
1138 return format("writers: %s, flushes: %s, maxstack: %s",nofwriters,nofflushes,used,freed,unreachable)
1139 elseif showstackusage or unreachable > 0 then
1140 return format("maxstack: %s, freed: %s, unreachable: %s",used,freed,unreachable)
1141 end
1142end)
1143
1144
1145
1146local luacalls = {
1147 lua_function_call = true,
1148 lua_protected_call = true,
1149 lua_value = true,
1150 lua_local_call = true,
1151 lua_call = true,
1152}
1153
1154local function userdata(argument)
1155 if isnode(argument) then
1156 return formatters["<< %s node %i>>"](nodes.nodecodes[argument.id],tonut(argument))
1157 elseif istoken(argument) then
1158 local csname = argument.csname
1159 if csname then
1160
1161 return formatters["\\%s"](csname)
1162 end
1163 if luacalls[argument.cmdname] then
1164 return "<<function>>"
1165 end
1166 return "<<token>>"
1167 else
1168 return "<<userdata>>"
1169 end
1170end
1171
1172
1173local tracedwriter = function(parent,...)
1174 nofwriters = nofwriters + 1
1175 local savedflush = flush
1176 local savedflushdirect = flushdirect
1177 local t = { "w : - : " }
1178 local n = 1
1179 local traced = function(catcodes,...)
1180 local s = type(catcodes) == "number" and { ... } or { catcodes, ... }
1181 for i=1,#s do
1182 local argument = s[i]
1183 local argtype = type(argument)
1184 if argtype == "string" then
1185 s[i] = lpegmatch(visualizer,argument)
1186 elseif argtype == "number" then
1187 s[i] = argument
1188 elseif argtype == "userdata" then
1189 s[i] = userdata(argument)
1190 else
1191 s[i] = formatters["<<%S>>"](argument)
1192 end
1193 end
1194 s = concat(s)
1195 s = lpegmatch(visualizer,s)
1196 n = n + 1
1197 t[n] = s
1198 end
1199 flush = function(...)
1200 normalflush(...)
1201 if tracingpermitted then
1202 traced(...)
1203 end
1204 end
1205 flushdirect = function(...)
1206 normalflushdirect(...)
1207 if tracingpermitted then
1208 traced(...)
1209 end
1210 end
1211 normalwriter(parent,...)
1212 flush = savedflush
1213 flushdirect = savedflushdirect
1214 currenttrace(concat(t))
1215end
1216
1217
1218
1219local traced = function(one,two,...)
1220 if two ~= nil then
1221
1222 local catcodes = type(one) == "number" and one
1223 local arguments = catcodes and { two, ... } or { one, two, ... }
1224 local collapsed = { formatters["f : %s : "](catcodes or '-') }
1225 local c = 1
1226 for i=1,#arguments do
1227 local argument = arguments[i]
1228 local argtype = type(argument)
1229 c = c + 1
1230 if argtype == "string" then
1231 collapsed[c] = lpegmatch(visualizer,argument)
1232 elseif argtype == "number" then
1233 collapsed[c] = argument
1234 elseif argtype == "userdata" then
1235 collapsed[c] = userdata(argument)
1236 else
1237 collapsed[c] = formatters["<<%S>>"](argument)
1238 end
1239 end
1240 currenttrace(concat(collapsed))
1241 elseif one ~= nil then
1242
1243 local argtype = type(one)
1244 if argtype == "string" then
1245 currenttrace(formatters["f : - : %s"](lpegmatch(visualizer,one)))
1246 elseif argtype == "number" then
1247 currenttrace(formatters["f : - : %s"](one))
1248 elseif argtype == "userdata" then
1249 currenttrace(formatters["F : - : %s"](userdata(one)))
1250 else
1251 currenttrace(formatters["f : - : <<%S>>"](one))
1252 end
1253 end
1254end
1255
1256local tracedflush = function(one,two,...)
1257 nofflushes = nofflushes + 1
1258 if two ~= nil then
1259 normalflush(one,two,...)
1260 else
1261 normalflush(one)
1262 end
1263 if tracingpermitted then
1264 traced(one,two,...)
1265 end
1266end
1267
1268local tracedflushdirect = function(one,two,...)
1269 nofflushes = nofflushes + 1
1270 if two ~= nil then
1271 normalflushdirect(one,two,...)
1272 else
1273 normalflushdirect(one)
1274 end
1275 if tracingpermitted then
1276 traced(one,two,...)
1277 end
1278end
1279
1280function context.pushlogger(trace)
1281 trace = trace or report_context
1282 insert(trace_stack,currenttrace)
1283 currenttrace = trace
1284end
1285
1286function context.poplogger()
1287 if #trace_stack > 1 then
1288 currenttrace = remove(trace_stack) or report_context
1289 else
1290 currenttrace = report_context
1291 end
1292end
1293
1294function context.settracing(v)
1295 if v then
1296 flush = tracedflush
1297 flushdirect = tracedflushdirect
1298 writer = tracedwriter
1299 else
1300 flush = normalflush
1301 flushdirect = normalflushdirect
1302 writer = normalwriter
1303 end
1304 return flush, writer, flushdirect
1305end
1306
1307function context.getlogger()
1308 return flush, writer, flushdirect
1309end
1310
1311trackers.register("context.trace",context.settracing)
1312
1313local trace_cld = false trackers.register("context.files", function(v) trace_cld = v end)
1314
1315do
1316
1317
1318
1319
1320 local resolve = resolvers.savers.byscheme
1321 local validstring = string.valid
1322 local input = context.input
1323
1324 local function viafile(data,tag)
1325 if data and data ~= "" then
1326 local filename = resolve("virtual",validstring(tag,"viafile"),data)
1327
1328 input(filename)
1329
1330 end
1331 end
1332
1333 context.viafile = viafile
1334
1335
1336
1337 local collected = nil
1338 local nofcollected = 0
1339 local sentinel = string.char(26)
1340 local level = 0
1341
1342 local function collect(c,a,...)
1343 if type(c) == "userdata" then
1344 nofcollected = nofcollected + 1
1345
1346 collected[nofcollected] = "\\" .. c.csname
1347 end
1348 if a then
1349 for i=1,select("#",a,...) do
1350 local c = select(i,a,...)
1351 nofcollected = nofcollected + 1
1352 collected[nofcollected] = type(c) == "userdata" and userdata(c) or c
1353 end
1354 end
1355 end
1356
1357 local collectdirect = collect
1358 local permitted = true
1359
1360
1361
1362
1363 function context.startcollecting()
1364 if level == 0 then
1365 collected = { }
1366 nofcollected = 0
1367 flush = collect
1368 flushdirect = collectdirect
1369 permitted = tracingpermitted
1370 end
1371 level = level + 1
1372 end
1373
1374 function context.stopcollecting()
1375 level = level - 1
1376 if level < 1 then
1377 local result = concat(collected,sentinel)
1378 flush = normalflush
1379 flushdirect = normalflushdirect
1380 tracingpermitted = permitted
1381 collected = nil
1382 nofcollected = 0
1383 level = 0
1384 viafile(result)
1385 end
1386 end
1387
1388 local findtexfile = resolvers.findtexfile
1389 local findfile = resolvers.findfile
1390
1391 function context.runfile(filename)
1392 local foundname = findtexfile(file.addsuffix(filename,"cld")) or ""
1393 if foundname ~= "" then
1394 local ok = dofile(foundname)
1395 if type(ok) == "function" then
1396 if trace_cld then
1397 report_context("begin of file %a (function call)",foundname)
1398 end
1399 ok()
1400 if trace_cld then
1401 report_context("end of file %a (function call)",foundname)
1402 end
1403 elseif ok then
1404 report_context("file %a is processed and returns true",foundname)
1405 else
1406 report_context("file %a is processed and returns nothing",foundname)
1407 end
1408 else
1409 report_context("unknown file %a",filename)
1410 end
1411 end
1412
1413 function context.loadfile(filename)
1414 context(stripstring(loaddata(findfile(filename))))
1415 end
1416
1417 function context.loadviafile(filename)
1418 viafile(stripstring(loaddata(findfile(filename))))
1419 end
1420
1421end
1422
1423
1424
1425function context.direct(first,...)
1426 if first ~= nil then
1427 return writer(context,"",first,...)
1428 end
1429end
1430
1431
1432
1433do
1434
1435 local delayed = { }
1436
1437 local function indexer(parent,k)
1438 local f = function(...)
1439 local a = { ... }
1440 return function()
1441
1442 return core[k](unpack(a))
1443 end
1444 end
1445 parent[k] = f
1446 return f
1447 end
1448
1449 local function caller(parent,...)
1450 local a = { ... }
1451 return function()
1452
1453 return defaultcaller(context,unpack(a))
1454 end
1455 end
1456
1457 setmetatableindex(delayed,indexer)
1458 setmetatablecall (delayed,caller)
1459
1460 context.delayed = delayed
1461
1462end
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505context.nested = context.delayed
1506
1507
1508
1509function context.newindexer(catcodes,cmdcodes)
1510 local handler = { }
1511
1512 local function indexer(parent,k)
1513 local command = core[k]
1514 local f = function(...)
1515 local savedcatcodes = contentcatcodes
1516 contentcatcodes = catcodes
1517 command(...)
1518 contentcatcodes = savedcatcodes
1519 end
1520 parent[k] = f
1521 return f
1522 end
1523
1524 local function caller(parent,...)
1525 local savedcatcodes = contentcatcodes
1526 contentcatcodes = catcodes
1527 defaultcaller(parent,...)
1528 contentcatcodes = savedcatcodes
1529 end
1530
1531 handler.cs = core.cs
1532
1533 setmetatableindex(handler,indexer)
1534 setmetatablecall (handler,caller)
1535
1536 return handler
1537end
1538
1539context.verbatim = context.newindexer(vrbcatcodes,ctxcatcodes)
1540context.puretext = context.newindexer(txtcatcodes,ctxcatcodes)
1541context.protected = context.newindexer(prtcatcodes,prtcatcodes)
1542
1543
1544
1545do
1546
1547 local formatted = { }
1548
1549
1550
1551 local function formattedflush(parent,c,catcodes,fmt,...)
1552 if not catcodes then
1553 return writer(parent,c)
1554 elseif not fmt then
1555 return writer(parent,c,catcodes)
1556 elseif type(catcodes) == "number" then
1557 local result
1558 pushcatcodes(catcodes)
1559 result = writer(parent,c,formatters[fmt](...))
1560 popcatcodes()
1561 return result
1562 else
1563 return writer(parent,c,formatters[catcodes](fmt,...))
1564 end
1565 end
1566
1567 local toks = tokens.cache
1568
1569 local indexer = function(parent,k)
1570 if type(k) == "string" then
1571 local t
1572 local f = function(first,...)
1573 if not t then
1574 t = toks[k]
1575 end
1576 if first == nil then
1577 flush(t)
1578 else
1579 return formattedflush(parent,t,first,...)
1580 end
1581 end
1582 parent[k] = f
1583 return f
1584 else
1585 return context
1586 end
1587 end
1588
1589
1590
1591 local function caller(parent,catcodes,fmt,...)
1592 if not catcodes then
1593
1594 elseif not fmt then
1595 flush(catcodes)
1596 elseif type(catcodes) == "number" then
1597 flush(catcodes,formatters[fmt](...))
1598 else
1599 flush(formatters[catcodes](fmt,...))
1600 end
1601 end
1602
1603 setmetatableindex(formatted,indexer)
1604 setmetatablecall (formatted,caller)
1605
1606 context.formatted = formatted
1607
1608end
1609
1610do
1611
1612
1613
1614 local metafun = { }
1615
1616 function metafun.start()
1617 context.startMPcode()
1618 end
1619
1620 function metafun.stop()
1621 context.stopMPcode()
1622 end
1623
1624 setmetatablecall(metafun,defaultcaller)
1625
1626 function metafun.color(name)
1627 return name
1628 end
1629
1630
1631
1632 local delayed = { }
1633
1634 local function indexer(parent,k)
1635 local f = function(...)
1636 local a = { ... }
1637 return function()
1638 return metafun[k](unpack(a))
1639 end
1640 end
1641 parent[k] = f
1642 return f
1643 end
1644
1645
1646 local function caller(parent,...)
1647 local a = { ... }
1648 return function()
1649 return metafun(unpack(a))
1650 end
1651 end
1652
1653 setmetatableindex(delayed,indexer)
1654 setmetatablecall (delayed,caller)
1655
1656 context.metafun = metafun
1657 metafun.delayed = delayed
1658
1659end
1660
1661
1662
1663do
1664
1665 function context.concat(...)
1666 context(concat(...))
1667 end
1668
1669 local p_texescape = patterns.texescape
1670
1671 function context.escaped(s)
1672 if s then
1673 context(lpegmatch(p_texescape,s) or s)
1674 else
1675
1676 end
1677 end
1678
1679 function context.escape(s)
1680 if s then
1681 return lpegmatch(p_texescape,s) or s
1682 else
1683 return ""
1684 end
1685 end
1686
1687end
1688
1689
1690
1691do
1692
1693 local single = lpegP("%")
1694 local double = lpegP("%%")
1695 local lquoted = lpegP("%[")
1696 local rquoted = lpegP("]%")
1697 local space = lpegP(" ")
1698
1699 local start = [[
1700 local texescape = lpeg.patterns.texescape
1701 local lpegmatch = lpeg.match
1702 return function(variables) return
1703 ]]
1704
1705 local stop = [[
1706 end
1707 ]]
1708
1709 local replacer = lpegP { "parser",
1710 parser = lpegCs(lpegCc(start) * lpegV("step") * (lpegCc("..") * lpegV("step"))^0 * lpegCc(stop)),
1711 unquoted = (lquoted*space/'')
1712 * ((lpegC((1-space*rquoted)^1)) / "lpegmatch(texescape,variables%0 or '')" )
1713 * (space*rquoted/'')
1714 + (lquoted/'')
1715 * ((lpegC((1-rquoted)^1)) / "lpegmatch(texescape,variables['%0'] or '')" )
1716 * (rquoted/''),
1717 key = (single*space/'')
1718 * ((lpegC((1-space*single)^1)) / "(variables%0 or '')" )
1719 * (space*single/'')
1720 + (single/'')
1721 * ((lpegC((1-single)^1)) / "(variables['%0'] or '')" )
1722 * (single/''),
1723 escape = double/'%%',
1724 step = lpegV("unquoted")
1725 + lpegV("escape")
1726 + lpegV("key")
1727 + lpegCc("\n[===[") * (1 - lpegV("unquoted") - lpegV("escape") - lpegV("key"))^1 * lpegCc("]===]\n"),
1728 }
1729
1730 local templates = { }
1731
1732 local function indexer(parent,k)
1733 local v = lpegmatch(replacer,k)
1734 if not v then
1735
1736 v = "error: no valid template (1)"
1737 else
1738 local f = loadstring(v)
1739 if type(f) ~= "function" then
1740
1741 v = "error: no valid template (2)"
1742 else
1743 f = f()
1744 if not f then
1745
1746 v = "error: no valid template (3)"
1747 else
1748 v = f
1749 end
1750 end
1751 end
1752 if type(v) == "function" then
1753 local f = function(first,second)
1754 if second then
1755 pushcatcodes(first)
1756 flushlines(v(second))
1757 popcatcodes()
1758 else
1759 flushlines(v(first))
1760 end
1761 end
1762 parent[k] = f
1763 return f
1764 else
1765 return function()
1766 flush(v)
1767 end
1768 end
1769
1770 end
1771
1772 local function caller(parent,k,...)
1773 return parent[k](...)
1774 end
1775
1776 setmetatableindex(templates,indexer)
1777 setmetatablecall (templates,caller)
1778
1779 context.templates = templates
1780
1781end
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792do
1793
1794
1795
1796
1797 local modelevels = tex.getmodevalues()
1798 local t = table.keys(modelevels)
1799 tex.modelevels = table.swapped(modelevels,modelevels)
1800
1801 for i=1,#t do local k = t[i] modelevels[-k] = modelevels[k] end
1802
1803 if CONTEXTLMTXMODE > 0 then
1804
1805
1806
1807 local flagcodes = tex.getflagvalues()
1808 tex.flagcodes = table.swapped(flagcodes,flagcodes)
1809
1810 end
1811
1812end
1813 |