node-nut.lmt /size: 22 Kb    last modification: 2024-01-16 09:02
1if not modules then modules = { } end modules ['node-nut'] = {
2    version   = 1.001,
3    comment   = "companion to node-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-- Some comments anbout going nuts can be found in the mkiv file with the
10-- suffix "lua", including some timing and musings. We copy the direct table
11-- (1) because that's what we did while things evolved and (2) because we
12-- also add some more helpers.
13
14local type, rawget = type, rawget
15
16local nodes           = nodes
17local direct          = node.direct
18
19local fastcopy        = table.fastcopy
20
21local nodecodes       = nodes.nodecodes
22local hlist_code      = nodecodes.hlist
23local vlist_code      = nodecodes.vlist
24local glyph_code      = nodecodes.glyph
25
26local tonode          = direct.tonode
27local tonut           = direct.todirect
28
29local isnode          = direct.isnode
30local isnut           = direct.isdirect
31local isdirect        = direct.isdirect
32
33local d_remove_node   = direct.remove
34local d_flushnode     = direct.flushnode
35local d_getnext       = direct.getnext
36local d_getprev       = direct.getprev
37local d_getid         = direct.getid
38local d_getlist       = direct.getlist
39local d_find_tail     = direct.tail
40local d_insertafter   = direct.insertafter
41local d_insertbefore  = direct.insertbefore
42local d_slide         = direct.slide
43local d_traverse      = direct.traverse
44local d_setlink       = direct.setlink
45local d_getboth       = direct.getboth
46
47local nuts = {
48    addmargins              = direct.addmargins,
49    addxoffset              = direct.addxoffset,
50    addxymargins            = direct.addxymargins,
51    addyoffset              = direct.addyoffset,
52    append                  = direct.append,
53    appendaftertail         = direct.appendaftertail,
54    checkdiscretionaries    = direct.checkdiscretionaries,
55    collapsing              = direct.collapsing,
56    copy                    = direct.copy,
57    copylist                = direct.copylist,
58    copynode                = direct.copy,
59    copyonly                = direct.copyonly,
60    count                   = direct.count,
61    currentattributes       = direct.currentattributes,
62    delete                  = direct.delete,
63    dimensions              = direct.dimensions,
64    effectiveglue           = direct.effectiveglue,
65    endofmath               = direct.endofmath,
66    exchange                = direct.exchange,
67    findattribute           = direct.findattribute,
68    findnode                = direct.findnode,
69    firstglyphnode          = direct.firstglyphnode,
70    firstglyph              = direct.firstglyph,
71    firstchar               = direct.firstchar,
72    flattendiscretionaries  = direct.flattendiscretionaries,
73    flattenleaders          = direct.flattenleaders,
74    flush                   = d_flushnode,
75    flushlist               = direct.flushlist,
76    flushnode               = d_flushnode,
77    free                    = direct.free,
78    freeze                  = direct.freeze,
79    getanchors              = direct.getanchors,
80    getattr                 = direct.getattribute,
81    getattribute            = direct.getattribute,
82    getattributelist        = direct.getattributelist,
83    getattributes           = direct.getattributes,
84    getattrlist             = direct.getattributelist,
85    getattrs                = direct.getattributes,
86    getboth                 = d_getboth,
87    getbottom               = direct.getbottom,
88    getbottomdelimiter      = direct.getbottomdelimiter,
89    getbox                  = direct.getbox,
90    getboxglue              = direct.getglue,
91    getchar                 = direct.getchar,
92    getchardict             = direct.getchardict,
93    getcharspec             = direct.getcharspec,
94    getchoice               = direct.getchoice,
95    getclass                = direct.getclass,
96    getcornerkerns          = direct.getcornerkerns,
97    getdata                 = direct.getdata,
98    getdegree               = direct.getdegree,
99    getdelimiter            = direct.getdelimiter,
100    getmiddle               = direct.getdelimiter,
101    getdenominator          = direct.getdenominator,
102    getdepth                = direct.getdepth,
103    getdir                  = direct.getdir,
104    getdirection            = direct.getdirection,
105    getdisc                 = direct.getdisc,
106    getdiscpart             = direct.getdiscpart,
107    getdiscretionary        = direct.getdisc,
108    getexpansion            = direct.getexpansion,
109    getfam                  = direct.getfam,
110    getfield                = direct.getfield,
111    getfont                 = direct.getfont,
112    getgeometry             = direct.getgeometry,
113    getglue                 = direct.getglue,
114    getglyphdata            = direct.getglyphdata,
115    getglyphdimensions      = direct.getglyphdimensions,
116    getheight               = direct.getheight,
117    getid                   = d_getid,
118    getindex                = direct.getindex,
119    getinputfields          = direct.getinputfields,
120    getkern                 = direct.getkern,
121    getkerndimension        = direct.getkerndimension,
122    getlang                 = direct.getlanguage,-- will become obsolete
123    getlanguage             = direct.getlanguage,
124    getleader               = direct.getleader,
125    getleftdelimiter        = direct.getleftdelimiter,
126    getlist                 = d_getlist,
127    getlistdimensions       = direct.getlistdimensions,
128    getmiddledelimiter      = direct.getdelimiter,
129    getnext                 = d_getnext,
130    getnormalizedline       = direct.getnormalizedline,
131    getnucleus              = direct.getnucleus,
132    getnumerator            = direct.getnumerator,
133    getoffsets              = direct.getoffsets,
134    getoptions              = direct.getoptions,
135    getorientation          = direct.getorientation,
136    getparstate             = direct.getparstate,
137    getpenalty              = direct.getpenalty,
138    getpost                 = direct.getpost,
139    getpre                  = direct.getpre,
140    getprev                 = d_getprev,
141    getprime                = direct.getprime,
142    getreplace              = direct.getreplace,
143    getrightdelimiter       = direct.getrightdelimiter,
144    getruledata             = direct.getdata, -- obsolete when we have the split
145    getruledimensions       = direct.getruledimensions,
146    setruledimensions       = direct.setruledimensions,
147    getscale                = direct.getscale,
148    getscales               = direct.getscales,
149    getscript               = direct.getscript,
150    getshift                = direct.getshift,
151    getslant                = direct.getslant,
152    getspeciallist          = direct.getspeciallist,
153    getstate                = direct.getstate,
154    getsub                  = direct.getsub,
155    getsubpre               = direct.getsubpre,
156    getsubtype              = direct.getsubtype,
157    getsup                  = direct.getsup,
158    getsuppre               = direct.getsuppre,
159    getsurround             = direct.getkern,
160    gettop                  = direct.gettop,
161    gettopdelimiter         = direct.gettopdelimiter,
162    gettotal                = direct.gettotal,
163    getusedattributes       = direct.getusedattributes,
164    getvalue                = direct.getdata, -- obsolete
165    getwhd                  = direct.getwhd,
166    getwidth                = direct.getwidth,
167    getweight               = direct.getweight,
168    getwordrange            = direct.getwordrange,
169    getxscale               = direct.getxscale,
170    getxyscales             = direct.getxyscales,
171    getyscale               = direct.getyscale,
172    gluetostring            = direct.gluetostring,
173    hasattribute            = direct.hasattribute,
174    hasdimensions           = direct.hasdimensions,
175    hasfield                = direct.hasfield,
176    hasgeometry             = direct.hasgeometry,
177    hasglyph                = direct.hasglyph,
178    hasglyphoption          = direct.hasglyphoption,
179    hasdiscoption           = direct.hasdiscoption,
180    hpack                   = direct.hpack,
181    hyphenating             = direct.hyphenating,
182    ignoremathskip          = direct.ignoremathskip,
183    insertafter             = d_insertafter,
184    insertbefore            = d_insertbefore,
185    ischar                  = direct.ischar,
186    isdirect                = isdirect,
187    isglyph                 = direct.isglyph,
188    isnextchar              = direct.isnextchar,
189    isnextglyph             = direct.isnextglyph,
190    isnode                  = isnode,
191    isnut                   = isdirect,
192    isprevchar              = direct.isprevchar,
193    isprevglyph             = direct.isprevglyph,
194    iszeroglue              = direct.iszeroglue,
195    isnext                  = direct.isnext,
196    isprev                  = direct.isprev,
197    isboth                  = direct.isboth,
198    issimilarglyph          = direct.issimilarglyph,
199    isitalicglyph           = direct.isitalicglyph,
200    firstitalicglyph        = direct.firstitalicglyph,
201    kerning                 = direct.kerning,
202    lastnode                = direct.lastnode,
203    length                  = direct.length,
204    ligaturing              = direct.ligaturing,
205    makextensible           = direct.makextensible,
206    migrate                 = direct.migrate,
207    mlisttohlist            = direct.mlisttohlist,
208    naturalhsize            = direct.naturalhsize,
209    naturalwidth            = direct.naturalwidth,
210    new                     = direct.new,
211    newmathglyph            = direct.newmathglyph,
212    patchattributes         = direct.patchattributes,
213    prependbeforehead       = direct.prependbeforehead,
214    protectglyph            = direct.protectglyph,
215    protectglyphs           = direct.protectglyphs,
216    protectglyphsnone       = direct.protectglyphsnone,
217    protrusionskippable     = direct.protrusionskippable,
218    rangedimensions         = direct.rangedimensions,
219    remove                  = d_remove_node,
220    removefromlist          = direct.removefromlist,
221    repack                  = direct.repack,
222    reverse                 = direct.reverse,
223    serialized              = direct.serialized,
224    setanchors              = direct.setanchors,
225    setattr                 = direct.setattribute,
226    setattribute            = direct.setattribute,
227    setattributelist        = direct.setattributelist,
228    setattributes           = direct.setattributes,
229    setattrlist             = direct.setattributelist,
230    setattrs                = direct.setattributes,
231    setboth                 = direct.setboth,
232    setbottom               = direct.setbottom,
233    setbox                  = direct.setbox,
234    setboxglue              = direct.setglue,
235    setchar                 = direct.setchar,
236    setchardict             = direct.setchardict,
237    setchoice               = direct.setchoice,
238    setclass                = direct.setclass,
239    setdata                 = direct.setdata,
240    setdegree               = direct.setdegree,
241    setdelimiter            = direct.setdelimiter,
242    setdenominator          = direct.setdenominator,
243    setdepth                = direct.setdepth,
244    setdir                  = direct.setdir,
245    setdirection            = direct.setdirection,
246    setdisc                 = direct.setdisc,
247    setdiscpart             = direct.setdiscpart,
248    setdiscretionary        = direct.setdisc,
249    setexpansion            = direct.setexpansion,
250    setfam                  = direct.setfam,
251    setfield                = direct.setfield,
252    setfont                 = direct.setfont,
253    setgeometry             = direct.setgeometry,
254    setglue                 = direct.setglue,
255    setglyphdata            = direct.setglyphdata,
256    setheight               = direct.setheight,
257    setinputfields          = direct.setinputfields,
258    setkern                 = direct.setkern,
259    setlang                 = direct.setlanguage,
260    setlanguage             = direct.setlanguage,
261    setleader               = direct.setleader,
262    setleftdelimiter        = direct.setleftdelimiter,
263    setlink                 = d_setlink,
264    setlist                 = direct.setlist,
265    setmiddledelimiter      = direct.setdelimiter,
266    setnext                 = direct.setnext,
267    setnucleus              = direct.setnucleus,
268    setnumerator            = direct.setnumerator,
269    setoffsets              = direct.setoffsets,
270    setoptions              = direct.setoptions,
271    setorientation          = direct.setorientation,
272    setpenalty              = direct.setpenalty,
273    setpost                 = direct.setpost,
274    setpre                  = direct.setpre,
275    setprev                 = direct.setprev,
276    setprime                = direct.setprime,
277    setreplace              = direct.setreplace,
278    setrightdelimiter       = direct.setrightdelimiter,
279    setruledata             = direct.setdata, -- obsolete when we have the split
280    setscale                = direct.setscale or direct.setscales,
281    setscales               = direct.setscales,
282    setscript               = direct.setscript,
283    setshift                = direct.setshift,
284    setslant                = direct.setslant,
285    setspeciallist          = direct.setspeciallist,
286    setsplit                = direct.setsplit,
287    setstate                = direct.setstate,
288    setsub                  = direct.setsub,
289    setsubpre               = direct.setsubpre,
290    setsubtype              = direct.setsubtype,
291    setsup                  = direct.setsup,
292    setsuppre               = direct.setsuppre,
293    setsurround             = direct.setkern,
294    setvalue                = direct.setdata, -- obsolete
295    settop                  = direct.settop,
296    setwhd                  = direct.setwhd,
297    setwidth                = direct.setwidth,
298    setweight               = direct.setweight,
299    show                    = direct.show,
300    slide                   = d_slide,
301    softenhyphens           = direct.softenhyphens,
302    startofpar              = direct.startofpar,
303    tail                    = d_find_tail,
304    takeattr                = direct.unsetattribute, -- ?
305    tonode                  = tonode,
306    tonut                   = tonut,
307    tostring                = direct.tostring,
308    traverse                = d_traverse,
309    traversechar            = direct.traversechar,
310    traversecontent         = direct.traversecontent,
311    traverseglyph           = direct.traverseglyph,
312    traverseid              = direct.traverseid,
313    traverseitalic          = direct.traverseitalic,
314    traverseleader          = direct.traverseleader,
315    traverselist            = direct.traverselist,
316    unprotectglyph          = direct.unprotectglyph,
317    unprotectglyphs         = direct.unprotectglyphs,
318    unsetattribute          = direct.unsetattribute,
319    unsetattributes         = direct.unsetattributes,
320    usedlist                = direct.usedlist,
321    usesfont                = direct.usesfont,
322    verticalbreak           = direct.verticalbreak,
323    vpack                   = direct.vpack,
324    write                   = direct.write,
325    xscaled                 = direct.xscaled,
326    yscaled                 = direct.yscaled,
327}
328
329nodes.nuts     = nuts
330
331nodes.isnode   = isnode
332nodes.isdirect = isnut
333nodes.isnut    = isnut
334
335nodes.tonode   = tonode
336nodes.tonut    = tonut
337
338function nuts.delete(head,current)
339    return d_remove_node(head,current,true)
340end
341
342function nuts.replace(head,current,new) -- no head returned if false
343    if not new then
344        head, current, new = false, head, current
345    end
346    local prev, next = d_getboth(current)
347    if prev or next then
348        d_setlink(prev,new,next)
349    end
350    if head then
351        if head == current then
352            head = new
353        end
354        d_flushnode(current)
355        return head, new
356    else
357        d_flushnode(current)
358        return new
359    end
360end
361
362local function countall(stack,flat)
363    local n = 0
364    while stack do
365        local id = d_getid(stack)
366        if not flat and id == hlist_code or id == vlist_code then
367            local list = d_getlist(stack)
368            if list then
369                n = n + 1 + countall(list) -- self counts too
370            else
371                n = n + 1
372            end
373        else
374            n = n + 1
375        end
376        stack = d_getnext(stack)
377    end
378    return n
379end
380
381nuts.countall = countall
382
383function nodes.countall(stack,flat)
384    return countall(tonut(stack),flat)
385end
386
387function nuts.append(head,current,...)
388    for i=1,select("#",...) do
389        head, current = d_insertafter(head,current,(select(i,...)))
390    end
391    return head, current
392end
393
394function nuts.prepend(head,current,...)
395    for i=1,select("#",...) do
396        head, current = d_insertbefore(head,current,(select(i,...)))
397    end
398    return head, current
399end
400
401function nuts.linked(...) -- slides !
402    local head, last
403    for i=1,select("#",...) do
404        local next = select(i,...)
405        if next then
406            if head then
407                d_setlink(last,next)
408            else
409                head = next
410            end
411            last = d_find_tail(next) -- we could skip the last one
412        end
413    end
414    return head
415end
416
417function nuts.concat(list) -- consider tail instead of slide
418    local head, tail
419    for i=1,#list do
420        local li = list[i]
421        if li then
422            if head then
423                d_setlink(tail,li)
424            else
425                head = li
426            end
427            tail = d_slide(li)
428        end
429    end
430    return head, tail
431end
432
433function nuts.reference(n)
434    return n or "<none>"
435end
436
437function nuts.vianuts (f) return function(n,...) return tonode(f(tonut (n),...)) end end
438function nuts.vianodes(f) return function(n,...) return tonut (f(tonode(n),...)) end end
439
440nodes.vianuts  = nuts.vianuts
441nodes.vianodes = nuts.vianodes
442
443function nodes.insertlistafter(h,c,n)
444    local t = n_tail(n)
445    if c then
446        local cn = n_getnext(c)
447        if cn then
448            -- no setboth here yet
449            n_setfield(t,"next",cn)
450            n_setfield(cn,"prev",t)
451        else
452            n_setfield(t,"next",nil)
453        end
454        n_setfield(c,"next",n)
455        n_setfield(n,"prev",c)
456        return h, n
457    end
458    return n, t
459end
460
461function nuts.insertlistafter(h,c,n)
462    local t = d_tail(n)
463    if c then
464        local cn = d_getnext(c)
465        if cn then
466            d_setlink(t,cn)
467        else
468            d_setnext(t)
469        end
470        d_setlink(c,n)
471        return h, n
472    end
473    return n, t
474end
475
476-- test code only
477
478-- collectranges and mix
479
480local report = logs.reporter("sliding")
481
482local function message(detail,head,current,previous)
483    report("error: %s, current: %s:%s, previous: %s:%s, list: %s, text: %s",
484        detail,
485        nodecodes[d_getid(current)],
486        current,
487        nodecodes[d_getid(previous)],
488        previous,
489        nodes.idstostring(head),
490        nodes.listtoutf(head)
491    )
492    utilities.debugger.showtraceback(report)
493end
494
495local function warn()
496    report()
497    report("warning: the slide tracer is enabled")
498    report()
499    warn = false
500end
501
502local function tracedslide(head)
503    if head then
504        if warn then
505            warn()
506        end
507        local next = d_getnext(head)
508        if next then
509            local prev = head
510            for n in d_traverse(next) do
511                local p = d_getprev(n)
512                if not p then
513                    message("unset",head,n,prev)
514                 -- break
515                elseif p ~= prev then
516                    message("wrong",head,n,prev)
517                 -- break
518                end
519                prev = n
520            end
521        end
522        return d_slide(head)
523    end
524end
525
526local function nestedtracedslide(head,level) -- no sliding !
527    if head then
528        if warn then
529            warn()
530        end
531        local id = d_getid(head)
532        local next = d_getnext(head)
533        if next then
534            report("%whead:%s",level or 0,nodecodes[id])
535            local prev = head
536            for n in d_traverse(next) do
537                local p = d_getprev(n)
538                if not p then
539                    message("unset",head,n,prev)
540                 -- break
541                elseif p ~= prev then
542                    message("wrong",head,n,prev)
543                 -- break
544                end
545                prev = n
546                local id = d_getid(n)
547                if id == hlist_code or id == vlist_code then
548                    nestedtracedslide(d_getlist(n),(level or 0) + 1)
549                end
550            end
551        elseif id == hlist_code or id == vlist_code then
552            report("%wlist:%s",level or 0,nodecodes[id])
553            nestedtracedslide(d_getlist(head),(level or 0) + 1)
554        end
555     -- return d_slide(head)
556    end
557end
558
559local function untracedslide(head)
560    if head then
561        if warn then
562            warn()
563        end
564        local next = d_getnext(head)
565        if next then
566            local prev = head
567            for n in d_traverse(next) do
568                local p = d_getprev(n)
569                if not p then
570                    return "unset", d_getid(n)
571                elseif p ~= prev then
572                    return "wrong", d_getid(n)
573                end
574                prev = n
575            end
576        end
577        return d_slide(head)
578    end
579end
580
581nuts.tracedslide       = tracedslide
582nuts.untracedslide     = untracedslide
583nuts.nestedtracedslide = nestedtracedslide
584
585-- this might move
586
587local propertydata = direct.getpropertiestable(true)
588
589local getattr = nuts.getattr
590local setattr = nuts.setattr
591
592nodes.properties = {
593    data = propertydata,
594}
595
596-- experimental code with respect to copying attributes has been removed
597-- as it doesn't pay of (most attributes are only accessed once anyway)
598
599function nuts.getprop(n,k)
600    local p = propertydata[n]
601    if p then
602        if k then
603            return p[k]
604        else
605            return p
606        end
607    end
608end
609
610function nuts.rawprop(n,k)
611    local p = rawget(propertydata,n)
612    if p then
613        if k then
614            return p[k]
615        else
616            return p
617        end
618    end
619end
620
621function nuts.setprop(n,k,v)
622    local p = propertydata[n]
623    if p then
624        p[k] = v
625    else
626        propertydata[n] = { [k] = v }
627    end
628end
629
630function nuts.theprop(n)
631    local p = propertydata[n]
632    if not p then
633        p = { }
634        propertydata[n] = p
635    end
636    return p
637end
638
639
640function nuts.isdone(n,k)
641    local p = propertydata[n]
642    if not p then
643        propertydata[n] = { [k] = true }
644        return false
645    end
646    local v = p[k]
647    if v == nil then
648        propertydata[n] = { [k] = true }
649        return false
650    end
651    return v
652end
653
654function nuts.copy_properties(source,target,what)
655    local newprops = propertydata[source]
656    if not newprops then
657        -- nothing to copy
658        return
659    end
660    if what then
661        -- copy one category
662        newprops = rawget(source,what)
663        if newprops then
664            newprops = fastcopy(newprops)
665            local p = rawget(propertydata,target)
666            if p then
667                p[what] = newprops
668            else
669                propertydata[target] = {
670                    [what] = newprops,
671                }
672            end
673        end
674    else
675        -- copy all properties
676        newprops = fastcopy(newprops)
677        propertydata[target] = newprops
678    end
679    return newprops -- for checking
680end
681