node-ini.lua /size: 11 Kb    last modification: 2023-12-21 09:44
1if not modules then modules = { } end modules ['node-ini'] = {
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-- Most of the code that had accumulated here is now separated in modules.
10
11local next, type, tostring = next, type, tostring
12local gsub = string.gsub
13local concat, remove = table.concat, table.remove
14local sortedhash, sortedkeys, swapped = table.sortedhash, table.sortedkeys, table.swapped
15
16-- Access to nodes is what gives LuaTeX its power. Here we implement a few helper
17-- functions. These functions are rather optimized.
18--
19-- When manipulating node lists in ConTeXt, we will remove nodes and insert new
20-- ones. While node access was implemented, we did quite some experiments in order
21-- to find out if manipulating nodes in Lua was feasible from the perspective of
22-- performance.
23--
24-- First of all, we noticed that the bottleneck is more with excessive callbacks
25-- (some gets called very often) and the conversion from and to TeX's
26-- datastructures. However, at the Lua end, we found that inserting and deleting
27-- nodes in a table could become a bottleneck.
28--
29-- This resulted in two special situations in passing nodes back to TeX: a table
30-- entry with value 'false' is ignored, and when instead of a table 'true' is
31-- returned, the original table is used.
32--
33-- Insertion is handled (at least in ConTeXt as follows. When we need to insert a
34-- node at a certain position, we change the node at that position by a dummy node,
35-- tagged 'inline' which itself has_attribute the original node and one or more new
36-- nodes. Before we pass back the list we collapse the list. Of course collapsing
37-- could be built into the TeX engine, but this is a not so natural extension.
38
39-- When we collapse (something that we only do when really needed), we also ignore
40-- the empty nodes. [This is obsolete!]
41
42-- local gf = node.direct.getfield
43-- local n = table.setmetatableindex("number")
44-- function node.direct.getfield(a,b) n[b] = n[b] + 1  print(b,n[b]) return gf(a,b) end
45
46nodes               = nodes or { }
47local nodes         = nodes
48nodes.handlers      = nodes.handlers or { }
49
50local mark          = utilities.storage.mark
51local allocate      = utilities.storage.allocate
52local formatcolumns = utilities.formatters.formatcolumns
53
54local getsubtypes   = node.subtypes
55local getvalues     = node.values
56
57tex.magicconstants = { -- we use tex.constants for something else
58    running  = -1073741824,
59    maxdimen =  1073741823, -- 0x3FFFFFFF or 2^30-1
60    trueinch =     4736286,
61}
62
63local listcodes     = mark(getsubtypes("list"))
64local rulecodes     = mark(getsubtypes("rule"))
65local dircodes      = mark(getsubtypes("dir"))
66local glyphcodes    = mark(getsubtypes("glyph"))
67local disccodes     = mark(getsubtypes("disc"))
68local gluecodes     = mark(getsubtypes("glue"))
69local fillcodes     = mark(getsubtypes("fill"))
70local boundarycodes = mark(getsubtypes("boundary"))
71local penaltycodes  = mark(getsubtypes("penalty"))
72local kerncodes     = mark(getsubtypes("kern"))
73local margincodes   = mark(getsubtypes("marginkern"))
74local mathcodes     = mark(getsubtypes("math"))
75local noadcodes     = mark(getsubtypes("noad"))
76local radicalcodes  = mark(getsubtypes("radical"))
77local accentcodes   = mark(getsubtypes("accent"))
78local fencecodes    = mark(getsubtypes("fence"))
79----- fractioncodes = mark(getsubtypes("fraction"))
80local parcodes      = allocate { [0] = "vmode_par", "local_box", "hmode_par", "penalty", "math" }
81
82local function simplified(t)
83    local r = { }
84    for k, v in next, t do
85        r[k] = gsub(v,"_","")
86    end
87    return r
88end
89
90local nodecodes = simplified(node.types())
91local whatcodes = simplified(node.whatsits and node.whatsits() or { })
92
93local usercodes = allocate {
94    [ 97] = "attribute", -- a
95    [100] = "number",    -- d
96    [102] = "float",     -- f
97    [108] = "lua",       -- l
98    [110] = "node",      -- n
99    [115] = "string",    -- s
100    [116] = "token"      -- t
101}
102
103local noadoptions = allocate {
104    set      =        0x08,
105    unused_1 = 0x00 + 0x08,
106    unused_2 = 0x01 + 0x08,
107    axis     = 0x02 + 0x08,
108    no_axis  = 0x04 + 0x08,
109    exact    = 0x10 + 0x08,
110    left     = 0x11 + 0x08,
111    middle   = 0x12 + 0x08,
112    right    = 0x14 + 0x08,
113}
114
115local dirvalues = allocate {
116    [0] = "lefttoright",
117    [1] = "righttoleft",
118}
119
120local literalvalues = allocate {
121    [0] = "origin",
122    [1] = "page",
123    [2] = "always",
124    [3] = "raw",
125    [4] = "text",
126    [5] = "font",
127    [6] = "special",
128}
129
130local gluevalues = mark(getvalues("glue"))
131
132gluecodes        = allocate(swapped(gluecodes,gluecodes))
133dircodes         = allocate(swapped(dircodes,dircodes))
134boundarycodes    = allocate(swapped(boundarycodes,boundarycodes))
135noadcodes        = allocate(swapped(noadcodes,noadcodes))
136radicalcodes     = allocate(swapped(radicalcodes,radicalcodes))
137nodecodes        = allocate(swapped(nodecodes,nodecodes))
138whatcodes        = allocate(swapped(whatcodes,whatcodes))
139listcodes        = allocate(swapped(listcodes,listcodes))
140glyphcodes       = allocate(swapped(glyphcodes,glyphcodes))
141kerncodes        = allocate(swapped(kerncodes,kerncodes))
142penaltycodes     = allocate(swapped(penaltycodes,penaltycodes))
143mathcodes        = allocate(swapped(mathcodes,mathcodes))
144fillcodes        = allocate(swapped(fillcodes,fillcodes))
145margincodes      = allocate(swapped(margincodes,margincodes))
146disccodes        = allocate(swapped(disccodes,disccodes))
147accentcodes      = allocate(swapped(accentcodes,accentcodes))
148fencecodes       = allocate(swapped(fencecodes,fencecodes))
149parcodes         = allocate(swapped(parcodes,parcodes))
150rulecodes        = allocate(swapped(rulecodes,rulecodes))
151usercodes        = allocate(swapped(usercodes,usercodes))
152noadoptions      = allocate(swapped(noadoptions,noadoptions))
153dirvalues        = allocate(swapped(dirvalues,dirvalues))
154gluevalues       = allocate(swapped(gluevalues,gluevalues))
155literalvalues    = allocate(swapped(literalvalues,literalvalues))
156
157if not nodecodes.delimiter then
158    -- as in luametatex / lmtx
159    local d = nodecodes.delim
160    nodecodes.delimiter = d
161    nodecodes[d]        = "delimiter"
162    nodecodes.delim     = nil
163end
164
165if not nodecodes.par then
166    -- as in luametatex / lmtx
167    local p = nodecodes.localpar
168    nodecodes.par = p
169    nodecodes[p]  = "par"
170end
171
172if not nodecodes.insert then
173    -- as in luametatex / lmtx
174    local i = nodecodes.ins
175    nodecodes.insert = i
176    nodecodes[i]     = "insert"
177    nodecodes.ins    = nil
178end
179
180if not gluecodes.indentskip then
181    gluecodes.indentskip       = gluecodes.userskip
182    gluecodes.lefthangskip     = gluecodes.userskip
183    gluecodes.righthangskip    = gluecodes.userskip
184    gluecodes.correctionskip   = gluecodes.userskip
185    gluecodes.intermathskip    = gluecodes.userskip
186    gluecodes.parfillleftskip  = gluecodes.parfillskip
187    gluecodes.parfillrightskip = gluecodes.parfillskip
188end
189
190if not whatcodes.literal then
191    whatcodes.literal     = whatcodes.pdfliteral
192    whatcodes.lateliteral = whatcodes.pdflateliteral
193    whatcodes.save        = whatcodes.pdfsave
194    whatcodes.restore     = whatcodes.pdfrestore
195    whatcodes.setmatrix   = whatcodes.pdfsetmatrix
196end
197
198nodes.gluecodes            = gluecodes
199nodes.dircodes             = dircodes
200nodes.boundarycodes        = boundarycodes
201nodes.noadcodes            = noadcodes
202nodes.nodecodes            = nodecodes
203nodes.whatcodes            = whatcodes
204nodes.listcodes            = listcodes
205nodes.glyphcodes           = glyphcodes
206nodes.kerncodes            = kerncodes
207nodes.penaltycodes         = penaltycodes
208nodes.mathcodes            = mathcodes
209nodes.fillcodes            = fillcodes
210nodes.margincodes          = margincodes
211nodes.disccodes            = disccodes
212nodes.accentcodes          = accentcodes
213nodes.radicalcodes         = radicalcodes
214nodes.fencecodes           = fencecodes
215nodes.parcodes             = parcodes
216nodes.rulecodes            = rulecodes
217nodes.usercodes            = usercodes
218nodes.noadoptions          = noadoptions
219nodes.dirvalues            = dirvalues
220nodes.gluevalues           = gluevalues
221nodes.literalvalues        = literalvalues
222
223nodes.subtypes = allocate {
224    [nodecodes.accent]     = accentcodes,
225    [nodecodes.boundary]   = boundarycodes,
226    [nodecodes.dir]        = dircodes,
227    [nodecodes.disc]       = disccodes,
228    [nodecodes.fence]      = fencecodes,
229    [nodecodes.glue]       = gluecodes,
230    [nodecodes.glyph]      = glyphcodes,
231    [nodecodes.hlist]      = listcodes,
232    [nodecodes.kern]       = kerncodes,
233    [nodecodes.par]        = parcodes,
234 -- [nodecodes.marginkern] = margincodes,
235    [nodecodes.math]       = mathcodes,
236    [nodecodes.noad]       = noadcodes,
237    [nodecodes.penalty]    = penaltycodes,
238    [nodecodes.radical]    = radicalcodes,
239    [nodecodes.rule]       = rulecodes,
240 -- [nodecodes.user]       = usercodes,
241    [nodecodes.vlist]      = listcodes,
242    [nodecodes.whatsit]    = whatcodes,
243    [nodecodes.marginkern] = margincodes
244}
245
246table.setmetatableindex(nodes.subtypes,function(t,k)
247    local v = { }
248    t[k] = v
249    return v
250end)
251
252-- a few more friendly aliases:
253
254nodes.skipcodes            = gluecodes
255nodes.directioncodes       = dircodes
256nodes.whatsitcodes         = whatcodes
257nodes.marginkerncodes      = margincodes
258nodes.discretionarycodes   = disccodes
259nodes.directionvalues      = dirvalues
260nodes.skipvalues           = gluevalues
261nodes.literalvalues        = literalvalues
262
263glyphcodes.glyph           = glyphcodes.character
264
265listcodes.row              = listcodes.alignment
266listcodes.column           = listcodes.alignment
267
268kerncodes.kerning          = kerncodes.fontkern
269
270kerncodes.italiccorrection = kerncodes.italiccorrection or 1 -- new
271
272literalvalues.direct       = literalvalues.always
273
274nodes.codes = allocate { -- mostly for listing
275    glue        = skipcodes,
276    boundary    = boundarycodes,
277    noad        = noadcodes,
278    node        = nodecodes,
279    hlist       = listcodes,
280    vlist       = listcodes,
281    glyph       = glyphcodes,
282    kern        = kerncodes,
283    penalty     = penaltycodes,
284    math        = mathnodes,
285    fill        = fillcodes,
286    margin      = margincodes,
287    disc        = disccodes,
288    whatsit     = whatcodes,
289    accent      = accentcodes,
290    fence       = fencecodes,
291    rule        = rulecodes,
292    user        = usercodes,
293    noadoptions = noadoptions,
294}
295
296nodes.noadoptions = {
297    set      =        0x08,
298    unused_1 = 0x00 + 0x08,
299    unused_2 = 0x01 + 0x08,
300    axis     = 0x02 + 0x08,
301    no_axis  = 0x04 + 0x08,
302    exact    = 0x10 + 0x08,
303    left     = 0x11 + 0x08,
304    middle   = 0x12 + 0x08,
305    right    = 0x14 + 0x08,
306}
307
308local report_codes = logs.reporter("nodes","codes")
309
310function nodes.showcodes()
311    local t = { }
312    for name, codes in sortedhash(nodes.codes) do
313        local sorted = sortedkeys(codes)
314        for i=1,#sorted do
315            local s = sorted[i]
316            if type(s) ~= "number" then
317                t[#t+1] = { name, s, codes[s] }
318            end
319        end
320    end
321    formatcolumns(t)
322    for k=1,#t do
323        report_codes (t[k])
324    end
325end
326
327trackers.register("system.showcodes", nodes.showcodes)
328
329-- We don't need this sanitize-after-callback in ConTeXt and by disabling it we
330-- also have a way to check if LuaTeX itself does the right thing.
331
332if node.fix_node_lists then
333    node.fix_node_lists(false)
334end
335