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