summaryrefslogtreecommitdiff
path: root/tex/context/base/node-ini.lua
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/node-ini.lua')
-rw-r--r--tex/context/base/node-ini.lua1254
1 files changed, 135 insertions, 1119 deletions
diff --git a/tex/context/base/node-ini.lua b/tex/context/base/node-ini.lua
index 8b451124e..8185e3033 100644
--- a/tex/context/base/node-ini.lua
+++ b/tex/context/base/node-ini.lua
@@ -7,225 +7,68 @@ if not modules then modules = { } end modules ['node-ini'] = {
}
--[[ldx--
-<p>Access to nodes is what gives <l n='luatex'/> its power. Here we
-implement a few helper functions. These functions are rather optimized.</p>
+<p>Most of the code that had accumulated here is now separated in
+modules.</p>
--ldx]]--
-local format = string.format
-
-nodes = nodes or { }
-nodes.trace = false
-nodes.ignore = nodes.ignore or false
-
-local hlist = node.id('vlist')
-local vlist = node.id('hlist')
-local glyph = node.id('glyph')
-local disc = node.id('disc')
-local mark = node.id('mark')
-local glue = node.id('glue')
-local whatsit = node.id('whatsit')
-
--- handy helpers
-
-if node.protect_glyphs then
-
- nodes.protect_glyphs = node.protect_glyphs
- nodes.unprotect_glyphs = node.unprotect_glyphs
+-- this module is being reconstructed
-else do
+local utf = unicode.utf8
+local next, type = next, type
+local format, concat, match, utfchar = string.format, table.concat, string.match, utf.char
- -- initial value subtype : X000 0001 = 1 = 0x01 = char
- --
- -- expected before linebreak : X000 0000 = 0 = 0x00 = glyph
- -- X000 0010 = 2 = 0x02 = ligature
- -- X000 0100 = 4 = 0x04 = ghost
- -- X000 1010 = 10 = 0x0A = leftboundary lig
- -- X001 0010 = 18 = 0x12 = rightboundary lig
- -- X001 1010 = 26 = 0x1A = both boundaries lig
- -- X000 1100 = 12 = 0x1C = leftghost
- -- X001 0100 = 20 = 0x14 = rightghost
+local chardata = characters and characters.data
+--[[ldx--
+<p>We start with a registration system for atributes so that we can use the
+symbolic names later on.</p>
+--ldx]]--
- local traverse_id = node.traverse_id
-
- function nodes.protect_glyphs(head)
- local done = false
- for g in traverse_id(glyph,head) do
- local s = g.subtype
- if s == 1 then
- done, g.subtype = true, 256
- elseif s <= 256 then
- done, g.subtype = true, 256 + s
- end
- end
- return done
- end
-
- function nodes.unprotect_glyphs(head)
- local done = false
- for g in traverse_id(glyph,head) do
- local s = g.subtype
- if s > 256 then
- done, g.subtype = true, s - 256
- end
- end
- return done
- end
-
-end end
-
-do
-
- local remove, free = node.remove, node.free
-
- function nodes.remove(head, current, free_too)
- local t = current
- head, current = remove(head,current)
- if t then
- if free_too then
- free(t)
- t = nil
- else
- t.next, t.prev = nil, nil
- end
- end
- return head, current, t
- end
-
---~ function nodes.remove(head, current, delete)
---~ local t = current
---~ if current == head then
---~ current = current.next
---~ if current then
---~ current.prev = nil
---~ end
---~ head = current
---~ else
---~ local prev, next = current.prev, current.next
---~ if prev then
---~ prev.next = next
---~ end
---~ if next then
---~ next.prev = prev
---~ end
---~ current = next -- not: or next
---~ end
---~ if t then
---~ if free_too then
---~ free(t)
---~ t = nil
---~ else
---~ t.next, t.prev = nil, nil
---~ end
---~ end
---~ return head, current, t
---~ end
-
+attributes = attributes or { }
- function nodes.delete(head,current)
- return nodes.remove(head,current,true)
- end
+attributes.names = attributes.names or { }
+attributes.numbers = attributes.numbers or { }
+attributes.list = attributes.list or { }
+attributes.unsetvalue = -0x7FFFFFFF
- nodes.before = node.insert_before -- broken
- nodes.after = node.insert_after
+storage.register("attributes/names", attributes.names, "attributes.names")
+storage.register("attributes/numbers", attributes.numbers, "attributes.numbers")
+storage.register("attributes/list", attributes.list, "attributes.list")
- function nodes.before(h,c,n)
- if c then
- if c == h then
- n.next = h
- n.prev = nil
- h.prev = n
- else
- local cp = c.prev
- n.next = c
- n.prev = cp
- if cp then
- cp.next = n
- end
- c.prev = n
- return h, n
- end
- end
- return n, n
- end
+local names, numbers, list = attributes.names, attributes.numbers, attributes.list
- function nodes.after(h,c,n)
- if c then
- local cn = c.next
- if cn then
- n.next = cn
- cn.prev = n
- else
- n.next = nil
- end
- c.next = n
- n.prev = c
---~ if c ~= h then
- return h, n
---~ end
- end
- return n, n
+function attributes.define(name,number) -- at the tex end
+ if not numbers[name] then
+ numbers[name], names[number], list[number] = number, name, { }
end
-
- function nodes.show_list(head, message)
- if message then
- texio.write_nl(message)
- end
- for n in node.traverse(head) do
- texio.write_nl(tostring(n))
- end
- end
-
end
--- will move
+--[[ldx--
+<p>We can use the attributes in the range 127-255 (outside user space). These
+are only used when no attribute is set at the \TEX\ end which normally
+happens in <l n='context'/>.</p>
+--ldx]]--
-nodes.processors = { }
-nodes.processors.char = { }
-nodes.processors.char.proc = { }
+storage.shared.attributes_last_private = storage.shared.attributes_last_private or 127
-function nodes.report(t,done)
- if nodes.trace then -- best also test this before calling
- if done then
- if status.output_active then
- texio.write(format("<++ %s>",nodes.count(t)))
- else
- texio.write(format("<+ %s>",nodes.count(t)))
- end
- else
- if status.output_active then
- texio.write(format("<-- %s>",nodes.count(t)))
- else
- texio.write(format("<- %s>",nodes.count(t)))
- end
+function attributes.private(name) -- at the lua end (hidden from user)
+ local number = numbers[name]
+ if not number then
+ local last = storage.shared.attributes_last_private or 127
+ if last < 255 then
+ last = last + 1
+ storage.shared.attributes_last_private = last
end
+ number = last
+ numbers[name], names[number], list[number] = number, name, { }
end
+ return number
end
-do
-
- local function count(stack,flat)
- local n = 0
- while stack do
- local id = stack.id
- if not flat and id == hlist or id == vlist then
- local list = stack.list
- if list then
- n = n + 1 + count(list) -- self counts too
- else
- n = n + 1
- end
- else
- n = n + 1
- end
- stack = stack.next
- end
- return n
- end
-
- nodes.count = count
-
-end
+--[[ldx--
+<p>Access to nodes is what gives <l n='luatex'/> its power. Here we
+implement a few helper functions. These functions are rather optimized.</p>
+--ldx]]--
--[[ldx--
<p>When manipulating node lists in <l n='context'/>, we will remove
@@ -255,963 +98,136 @@ into the <l n='tex'/> engine, but this is a not so natural extension.</p>
also ignore the empty nodes. [This is obsolete!]</p>
--ldx]]--
+nodes = nodes or { }
---[[ldx--
-<p>Serializing nodes can be handy for tracing. Also, saving and
-loading node lists can come in handy as soon we are going to
-use external applications to process node lists.</p>
---ldx]]--
-
-function nodes.show(stack)
---~ texio.write_nl(table.serialize(stack))
-end
-
-function nodes.save(stack,name) -- *.ltn : luatex node file
---~ if name then
---~ file.savedata(name,table.serialize(stack))
---~ else
---~ texio.write_nl('log',table.serialize(stack))
---~ end
-end
-
-function nodes.load(name)
---~ return file.loaddata(name)
-end
-
--- node-cap.lua
-
---~ nodes.capture = { } -- somehow fails
-
---~ function nodes.capture.start(cbk)
---~ local head, tail = nil, nil
---~ callbacks.push(cbk, function(t)
---~ if tail then
---~ tail.next = t
---~ else
---~ head, tail = t, t
---~ end
---~ while tail.next do
---~ tail = tail.next
---~ end
---~ return false
---~ end)
---~ function nodes.capture.stop()
---~ function nodes.capture.stop() end
---~ function nodes.capture.get()
---~ function nodes.capture.get() end
---~ return head
---~ end
---~ callbacks.pop(cbk)
---~ end
---~ function nodes.capture.get() end -- error
---~ end
-
---~ nodes.capture.stop = function() end
---~ nodes.capture.get = function() end
-
--- node-gly.lua
-
-fonts = fonts or { }
-fonts.otf = fonts.otf or { }
-fonts.tfm = fonts.tfm or { }
-fonts.tfm.id = fonts.tfm.id or { }
-
-local tfm = fonts.tfm
-local otf = fonts.otf
-local tfmid = fonts.tfm.id
-
-do
-
- local has_attribute = node.has_attribute
- local traverse_id = node.traverse_id
-
- local pairs = pairs
-
- local starttiming, stoptiming = input.starttiming, input.stoptiming
+local hlist = node.id('hlist')
+local vlist = node.id('vlist')
+local glyph = node.id('glyph')
+local glue = node.id('glue')
+local penalty = node.id('penalty')
+local kern = node.id('kern')
+local whatsit = node.id('whatsit')
- function nodes.process_characters(head)
- -- not ok yet; we need a generic blocker
- -- if status.output_active then
- if false then -- status.output_active then
- return head, false -- true
+local traverse_id = node.traverse_id
+local traverse = node.traverse
+local slide_nodes = node.slide
+local free_node = node.free
+local remove_node = node.remove
+
+function nodes.remove(head, current, free_too)
+ local t = current
+ head, current = remove_node(head,current)
+ if t then
+ if free_too then
+ free_node(t)
+ t = nil
else
- -- either next or not, but definitely no already processed list
- starttiming(nodes)
- local usedfonts, attrfonts, done = { }, { }, false
- -- todo: should be independent of otf
- local set_dynamics = otf.set_dynamics -- todo: font-var.lua so that we can global this one
- local a, u, prevfont, prevattr = 0, 0, nil, 0
- for n in traverse_id(glyph,head) do
- local font, attr = n.font, has_attribute(n,0) -- zero attribute is reserved for fonts, preset to 0 is faster (first match)
- if attr and attr > 0 then
- if font ~= prevfont or attr ~= prevattr then
- local used = attrfonts[font]
- if not used then
- used = { }
- attrfonts[font] = used
- end
- if not used[attr] then
- local d = set_dynamics(tfmid[font],attr) -- todo, script, language -> n.language also axis
- if d then
- used[attr] = d
- a = a + 1
- end
- end
- prevfont, prevattr = font, attr
- end
- elseif font ~= prevfont then
- prevfont, prevattr = font, 0
- local used = usedfonts[font]
- if not used then
- local data = tfmid[font]
- if data then
- local shared = data.shared -- we need to check shared, only when same features
- if shared then
- local processors = shared.processors
- if processors and #processors > 0 then
- usedfonts[font] = processors
- u = u + 1
- end
- end
- else
- -- probably nullfont
- end
- end
- else
- prevattr = attr
- end
- end
- -- we could combine these and just make the attribute nil
- if u > 0 then
- for font, processors in pairs(usedfonts) do
- local n = #processors
- if n == 1 then
- local h, d = processors[1](head,font,false)
- head, done = h or head, done or d
- else
- for i=1,#processors do
- local h, d = processors[i](head,font,false)
- head, done = h or head, done or d
- end
- end
- end
- end
- if a > 0 then -- we need to get rid of a loop here
- for font, dynamics in pairs(attrfonts) do
- for attribute, processors in pairs(dynamics) do -- attr can switch in between
- local n = #processors
- if n == 1 then
- local h, d = processors[1](head,font,attribute)
- head, done = h or head, done or d
- else
- for i=1,n do
- local h, d = processors[i](head,font,attribute)
- head, done = h or head, done or d
- end
- end
- end
- end
- end
- stoptiming(nodes)
- if nodes.trace then
- nodes.report(head,done)
- end
- return head, true
- end
- end
-
-end
-
--- vbox: grouptype: vbox vtop output split_off split_keep | box_type: exactly|aditional
--- hbox: grouptype: hbox adjusted_hbox(=hbox_in_vmode) | box_type: exactly|aditional
-
-do
-
- local has_attribute, set, attribute = node.has_attribute, node.set_attribute, tex.attribute
-
- function nodes.inherit_attributes(n) -- still ok ?
- if n then
- local i = 1
- while true do
- local a = attribute[i]
- if a < 0 then
- break
- else
- local ai = has_attribute(n,i)
- if not ai then
- set(n,i,a)
- end
- i = i + 1
- end
- end
+ t.next, t.prev = nil, nil
end
- end
-
+ end
+ return head, current, t
end
-function nodes.length(head)
- if head then
- local m = 0
- for n in node.traverse(head) do
- m = m + 1
- end
- return m
- else
- return 0
- end
+function nodes.delete(head,current)
+ return nodes.remove(head,current,true)
end
-lists = lists or { }
-chars = chars or { }
-words = words or { } -- not used yet
-
-callbacks.trace = false
-
-do
+nodes.before = node.insert_before -- broken
+nodes.after = node.insert_after
- kernel = kernel or { }
+-- we need to test this, as it might be fixed
- local starttiming, stoptiming = input.starttiming, input.stoptiming
- local hyphenate, ligaturing, kerning = lang.hyphenate, node.ligaturing, node.kerning
-
- function kernel.hyphenation(head,tail) -- lang.hyphenate returns done
- if head == tail then
- return head, tail, false
- else
- starttiming(kernel)
- local done = head ~= tail and hyphenate(head,tail)
- stoptiming(kernel)
- return head, tail, done
- end
- end
- function kernel.ligaturing(head,tail) -- node.ligaturing returns head,tail,done
- if head == tail then
- return head, tail, false
+function nodes.before(h,c,n)
+ if c then
+ if c == h then
+ n.next = h
+ n.prev = nil
+ h.prev = n
else
- starttiming(kernel)
- local head, tail, done = ligaturing(head,tail)
- stoptiming(kernel)
- return head, tail, done
- end
- end
- function kernel.kerning(head,tail) -- node.kerning returns head,tail,done
- if head == tail then
- return head, tail, false
- else
- starttiming(kernel)
- local head, tail, done = kerning(head,tail)
- stoptiming(kernel)
- return head, tail, done
- end
- end
-
-end
-
-callback.register('hyphenate' , function(head,tail) return tail end)
-callback.register('ligaturing', function(head,tail) return tail end)
-callback.register('kerning' , function(head,tail) return tail end)
-
-nodes.tasks = nodes.tasks or { }
-nodes.tasks.data = nodes.tasks.data or { }
-
-function nodes.tasks.new(name,list)
- local tasklist = sequencer.reset()
- nodes.tasks.data[name] = { list = tasklist, runner = false }
- for _, task in ipairs(list) do
- sequencer.appendgroup(tasklist,task)
- end
-end
-
-function nodes.tasks.appendaction(name,group,action,where,kind)
- local data = nodes.tasks.data[name]
- sequencer.appendaction(data.list,group,action,where,kind)
- data.runner = false
-end
-
-function nodes.tasks.prependaction(name,group,action,where,kind)
- local data = nodes.tasks.data[name]
- sequencer.prependaction(data.list,group,action,where,kind)
- data.runner = false
-end
-
-function nodes.tasks.removeaction(name,group,action)
- local data = nodes.tasks.data[name]
- sequencer.removeaction(data.list,group,action)
- data.runner = false
-end
-
-function nodes.tasks.showactions(name,group,action,where,kind)
- local data = nodes.tasks.data[name]
- logs.report("nodes","task %s, list:\n%s",name,sequencer.nodeprocessor(data.list))
-end
-
-function nodes.tasks.actions(name)
- local data = nodes.tasks.data[name]
- return function(head,tail)
- local runner = data.runner
- if not runner then
- if nodes.trace_tasks then
- logs.report("nodes","creating task runner '%s'",name)
+ local cp = c.prev
+ n.next = c
+ n.prev = cp
+ if cp then
+ cp.next = n
end
- runner = sequencer.compile(data.list,sequencer.nodeprocessor)
- data.runner = runner
+ c.prev = n
+ return h, n
end
- return runner(head,tail)
end
+ return n, n
end
-nodes.tasks.new (
- "processors",
- {
- "before", -- for users
- "normalizers",
- "characters",
- "words",
- "fonts",
- "lists",
- "after", -- for users
- }
-)
-
--- these definitions will move
-
-nodes.tasks.appendaction("processors", "normalizers", "nodes.normalize_fonts", nil)
-nodes.tasks.appendaction("processors", "characters", "chars.handle_mirroring", nil, "notail")
-nodes.tasks.appendaction("processors", "characters", "chars.handle_casing", nil, "notail")
-nodes.tasks.appendaction("processors", "characters", "chars.handle_breakpoints", nil, "notail")
-nodes.tasks.appendaction("processors", "words", "kernel.hyphenation", nil)
-nodes.tasks.appendaction("processors", "words", "languages.words.check", nil, "notail")
-nodes.tasks.appendaction("processors", "fonts", "nodes.process_characters", nil, "notail")
-nodes.tasks.appendaction("processors", "fonts", "nodes.protect_glyphs", nil, "nohead")
-nodes.tasks.appendaction("processors", "fonts", "kernel.ligaturing", nil)
-nodes.tasks.appendaction("processors", "fonts", "kernel.kerning", nil)
-nodes.tasks.appendaction("processors", "lists", "lists.handle_spacing", nil, "notail")
-nodes.tasks.appendaction("processors", "lists", "lists.handle_kerning", nil, "notail")
-
-
-local free = node.free
-
-local function cleanup_page(head) -- rough
- local prev, start = nil, head
- while start do
- local id, nx = start.id, start.next
- if id == disc or id == mark then
- if prev then
- prev.next = nx
- end
- if start == head then
- head = nx
- end
- local tmp = start
- start = nx
- free(tmp)
- elseif id == hlist or id == vlist then
- local sl = start.list
- if sl then
- start.list = cleanup_page(sl)
- end
- prev, start = start, nx
+function nodes.after(h,c,n)
+ if c then
+ local cn = c.next
+ if cn then
+ n.next = cn
+ cn.prev = n
else
- prev, start = start, nx
+ n.next = nil
end
+ c.next = n
+ n.prev = c
+ return h, n
end
- return head
-end
-
-nodes.cleanup_page_first = false
-
-function nodes.cleanup_page(head)
- if nodes.cleanup_page_first then
- head = cleanup_page(head)
- end
- return head, false
-end
-
-nodes.tasks.new (
- "shipouts",
- {
- "before", -- for users
- "normalizers",
- "finishers",
- "after", -- for users
- }
-)
-
-nodes.tasks.appendaction("shipouts", "normalizers", "nodes.cleanup_page", nil, "notail")
-nodes.tasks.appendaction("shipouts", "finishers", "shipouts.handle_color", nil, "notail")
-nodes.tasks.appendaction("shipouts", "finishers", "shipouts.handle_transparency", nil, "notail")
-nodes.tasks.appendaction("shipouts", "finishers", "shipouts.handle_overprint", nil, "notail")
-nodes.tasks.appendaction("shipouts", "finishers", "shipouts.handle_negative", nil, "notail")
-nodes.tasks.appendaction("shipouts", "finishers", "shipouts.handle_effect", nil, "notail")
-nodes.tasks.appendaction("shipouts", "finishers", "shipouts.handle_viewerlayer", nil, "notail")
-
-local actions = nodes.tasks.actions("shipouts")
-
-function nodes.process_page(head) -- problem, attr loaded before node, todo ...
- return actions(head) -- no tail
+ return n, n
end
--- or just: nodes.process_page = nodes.tasks.actions("shipouts")
-
-
-do -- remove these
-
- local actions = nodes.tasks.actions("processors")
- local first_character = node.first_character
- local slide = node.slide
-
- local n = 0
-
- local function reconstruct(head)
- local t = { }
- local h = head
- while h do
- local id = h.id
- if id == glyph then
- t[#t+1] = utf.char(h.char)
- else
- t[#t+1] = "[]"
- end
- h = h.next
- end
- return table.concat(t)
- end
-
- local function tracer(what,state,head,groupcode,before,after,show)
- if not groupcode then
- groupcode = "unknown"
- elseif groupcode == "" then
- groupcode = "mvl"
- end
- n = n + 1
- if show then
- texio.write_nl(format("%s %s: %s, group: %s, nodes: %s -> %s, string: %s",what,n,state,groupcode,before,after,reconstruct(head)))
+function nodes.replace(head,current,new)
+ if current and next then
+ local p, n = current.prev, current.next
+ new.prev, new.next = p, n
+ if p then
+ p.next = new
else
- texio.write_nl(format("%s %s: %s, group: %s, nodes: %s -> %s",what,n,state,groupcode,before,after))
- end
- end
-
- function nodes.processors.pre_linebreak_filter(head,groupcode) -- todo: tail
- local first, found = first_character(head)
- if found then
- if callbacks.trace then
- local before = nodes.count(head,true)
- local head, tail, done = actions(head,slide(head))
- local after = nodes.count(head,true)
- if done then
- tracer("pre_linebreak","changed",head,groupcode,before,after,true)
- else
- tracer("pre_linebreak","unchanged",head,groupcode,before,after,true)
- end
- return (done and head) or true
- else
- local head, tail, done = actions(head,slide(head))
- return (done and head) or true
- end
- else
- if callbacks.trace then
- local n = nodes.count(head,false)
- tracer("pre_linebreak","no chars",head,groupcode,n,n)
- end
- return true
- end
- end
-
- function nodes.processors.hpack_filter(head,groupcode) -- todo: tail
- local first, found = first_character(head)
- if found then
- if callbacks.trace then
- local before = nodes.count(head,true)
- local head, tail, done = actions(head,slide(head))
- local after = nodes.count(head,true)
- if done then
- tracer("hpack","changed",head,groupcode,before,after,true)
- else
- tracer("hpack","unchanged",head,groupcode,before,after,true)
- end
- return (done and head) or true
- else
- local head, tail, done = actions(head,slide(head))
- return (done and head) or true
- end
- end
- if callbacks.trace then
- local n = nodes.count(head,false)
- tracer("hpack","no chars",head,groupcode,n,n)
- end
- return true
- end
-
-end
-
-callback.register('pre_linebreak_filter', nodes.processors.pre_linebreak_filter)
-callback.register('hpack_filter' , nodes.processors.hpack_filter)
-
-do
-
- -- beware, some field names will change in a next release of luatex
-
- local expand = table.tohash {
- "list", -- list_ptr & ins_ptr & adjust_ptr
- "pre", --
- "post", --
- "spec", -- glue_ptr
- "top_skip", --
- "attr", --
- "replace", -- nobreak
- "components", -- lig_ptr
- "box_left", --
- "box_right", --
- "glyph", -- margin_char
- "leader", -- leader_ptr
- "action", -- action_ptr
- "value", -- user_defined nodes with subtype 'a' en 'n'
- }
-
- -- page_insert: "height", "last_ins_ptr", "best_ins_ptr"
- -- split_insert: "height", "last_ins_ptr", "best_ins_ptr", "broken_ptr", "broken_ins"
-
- local ignore = table.tohash {
- "page_insert",
- "split_insert",
- "ref_count",
- }
-
- local dimension = table.tohash {
- "width", "height", "depth", "shift",
- "stretch", "shrink",
- "xoffset", "yoffset",
- "surround",
- "kern",
- "box_left_width", "box_right_width"
- }
-
- -- flat: don't use next, but indexes
- -- verbose: also add type
- -- can be sped up
-
- nodes.dimensionfields = dimension
- nodes.listablefields = expand
- nodes.ignorablefields = ignore
-
- -- not ok yet:
-
- function nodes.astable(n,sparse) -- not yet ok
- local f, t = node.fields(n.id,n.subtype), { }
- for i=1,#f do
- local v = f[i]
- local d = n[v]
- if d then
- if ignore[v] or v == "id" then
- -- skip
- elseif expand[v] then -- or: type(n[v]) ~= "string" or type(n[v]) ~= "number" or type(n[v]) ~= "table"
- t[v] = "pointer to list"
- elseif sparse then
- if (type(d) == "number" and d ~= 0) or (type(d) == "string" and d ~= "") then
- t[v] = d
- end
- else
- t[v] = d
- end
- end
- end
- t.type = node.type(n.id)
- return t
- end
-
- local nodefields = node.fields
- local nodetype = node.type
-
- -- under construction:
-
- local function totable(n,flat,verbose)
- local function to_table(n)
- local f = nodefields(n.id,n.subtype)
- local tt = { }
- for k=1,#f do
- local v = f[k]
- local nv = n[v]
- if nv then
- if ignore[v] then
- -- skip
- elseif expand[v] then
- if type(nv) == "number" or type(nv) == "string" then
- tt[v] = nv
- else
- tt[v] = totable(nv,flat,verbose)
- end
- elseif type(nv) == "table" then
- tt[v] = nv -- totable(nv,flat,verbose) -- data
- else
- tt[v] = nv
- end
- end
- end
- if verbose then
- tt.type = nodetype(tt.id)
- end
- return tt
+ head = new
end
if n then
- if flat then
- local t = { }
- while n do
- t[#t+1] = to_table(n)
- n = n.next
- end
- return t
- else
- local t = to_table(n)
- if n.next then
- t.next = totable(n.next,flat,verbose)
- end
- return t
- end
- else
- return { }
+ n.prev = new
end
+ free_node(current)
end
+ return head, current
+end
- nodes.totable = totable
-
- local function key(k)
- return ((type(k) == "number") and "["..k.."]") or k
- end
-
- -- not ok yet; this will become a module
+-- will move
- local function serialize(root,name,handle,depth,m)
- handle = handle or print
- if depth then
- depth = depth .. " "
- handle(("%s%s={"):format(depth,key(name)))
- else
- depth = ""
- local tname = type(name)
- if tname == "string" then
- if name == "return" then
- handle("return {")
- else
- handle(name .. "={")
- end
- elseif tname == "number"then
- handle("[" .. name .. "]={")
- else
- handle("t={")
- end
- end
- if root then
- local fld
- if root.id then
- fld = nodefields(root.id,root.subtype) -- we can cache these (todo)
+local function count(stack,flat)
+ local n = 0
+ while stack do
+ local id = stack.id
+ if not flat and id == hlist or id == vlist then
+ local list = stack.list
+ if list then
+ n = n + 1 + count(list) -- self counts too
else
- fld = table.sortedkeys(root)
- end
- if type(root) == 'table' and root['type'] then -- userdata or table
- handle(("%s %s=%q,"):format(depth,'type',root['type']))
- end
- for _,k in ipairs(fld) do
- if k == "ref_count" then
- -- skip
- elseif k then
- local v = root[k]
- local t = type(v)
- if t == "number" then
- if v == 0 then
- -- skip
- else
- handle(("%s %s=%s,"):format(depth,key(k),v))
- end
- elseif t == "string" then
- if v == "" then
- -- skip
- else
- handle(("%s %s=%q,"):format(depth,key(k),v))
- end
- elseif v then -- userdata or table
- serialize(v,k,handle,depth,m+1)
- end
- end
- end
- if root['next'] then -- userdata or table
- serialize(root['next'],'next',handle,depth,m+1)
+ n = n + 1
end
- end
- if m and m > 0 then
- handle(("%s},"):format(depth))
else
- handle(("%s}"):format(depth))
+ n = n + 1
end
+ stack = stack.next
end
-
- function nodes.serialize(root,name)
- local t = { }
- local function flush(s)
- t[#t+1] = s
- end
- serialize(root, name, flush, nil, 0)
- return table.concat(t,"\n")
- end
-
- function nodes.serializebox(n,flat,verbose)
- return nodes.serialize(nodes.totable(tex.box[n],flat,verbose))
- -- return nodes.serialize(tex.box[n])
- end
-
- function nodes.visualizebox(...)
- -- tex.sprint(tex.ctxcatcodes,"\\starttyping\n" .. nodes.serializebox(...) .. "\n\\stoptyping\n")
- tex.print(tex.ctxcatcodes,"\\starttyping")
- tex.print(nodes.serializebox(...))
- tex.print("\\stoptyping")
- end
-
- function nodes.list(head,n) -- name might change to nodes.type
- if not n then
- tex.print(tex.ctxcatcodes,"\\starttyping")
- end
- while head do
- local id = head.id
- tex.print(string.rep(" ",n or 0) .. tostring(head) .. "\n")
- if id == hlist or id == vlist then
- nodes.list(head.list,(n or 0)+1)
- end
- head = head.next
- end
- if not n then
- tex.print("\\stoptyping")
- end
- end
-
- function nodes.print(head,n)
- while head do
- local id = head.id
- texio.write_nl(string.rep(" ",n or 0) .. tostring(head))
- if id == hlist or id == vlist then
- nodes.print(head.list,(n or 0)+1)
- end
- head = head.next
- end
- end
-
- function nodes.check_for_leaks(sparse)
- local l = { }
- local q = node.usedlist()
- for p in node.traverse(q) do
- local s = table.serialize(nodes.astable(p,sparse),node.type(p.id))
- l[s] = (l[s] or 0) + 1
- end
- node.flush_list(q)
- for k, v in pairs(l) do
- texio.write_nl(format("%s * %s", v, k))
- end
- end
-
-end
-
-if not node.list_has_attribute then -- no longer needed
-
- function node.list_has_attribute(list,attribute)
- if list and attribute then
- for n in node.traverse(list) do
- local a = has_attribute(n,attribute)
- if a then return a end
- end
- end
- return false
- end
-
+ return n
end
-function nodes.pack_list(head)
- local t = { }
- for n in node.traverse(head) do
- t[#t+1] = tostring(n)
- end
- return t
-end
+nodes.count = count
-do
+-- new
- function nodes.leftskip(n)
- while n do
- local id = n.id
- if id == glue then
- if n.subtype == 8 then -- 7 in c/web source
- return (n.spec and n.spec.width) or 0
- else
- return 0
- end
- elseif id == whatsit then
- n = n.next
- elseif id == hlist then
- return n.width
- else
- break
- end
- end
- return 0
- end
- function nodes.rightskip(n)
- if n then
- n = node.slide(n)
- while n do
- local id = n.id
- if id == glue then
- if n.subtype == 9 then -- 8 in the c/web source
- return (n.spec and n.spec.width) or 0
- else
- return 0
- end
- elseif id == whatsit then
- n = n.prev
- else
- break
- end
- end
+function attributes.ofnode(n)
+ local a = n.attr
+ if a then
+ local names = attributes.names
+ a = a.next
+ while a do
+ local number, value = a.number, a.value
+ texio.write_nl(format("%s : attribute %3i, value %4i, name %s",tostring(n),number,value,names[number] or '?'))
+ a = a.next
end
- return false
- end
-
+ end
end
--- goodie
---
--- if node.valid(tex.box[0]) then print("valid node") end
-
---~ do
---~ local n = node.new(0,0)
---~ local m = getmetatable(n)
---~ m.__metatable = 'node'
---~ node.free(n)
-
---~ function node.valid(n)
---~ return n and getmetatable(n) == 'node'
---~ end
---~ end
+local left, space = lpeg.P("<"), lpeg.P(" ")
--- for the moment we put this here:
-
-do
-
- nodes.tracers = { }
- nodes.tracers.characters = { }
-
- local function collect(head,list,tag,n)
- n = n or 0
- local ok, fn = false, nil
- while head do
- local id = head.id
- if id == glyph then
- local f = head.font
- if f ~= fn then
- ok, fn = false, f
- end
- local c = head.char
- local d = tfmid[f].descriptions[c]
- local i = (d and d.index) or -1
- if not ok then
- ok = true
- n = n + 1
- list[n] = list[n] or { }
- list[n][tag] = { }
- end
- local l = list[n][tag]
- l[#l+1] = { c, f, i }
- elseif id == disc then
- -- skip
- else
- ok = false
- end
- head = head.next
- end
- end
-
- function nodes.tracers.characters.equal(ta, tb)
- if #ta ~= #tb then
- return false
- else
- for i=1,#ta do
- local a, b = ta[i], tb[i]
- if a[1] ~= b[1] or a[2] ~= b[2] or a[3] ~= b[3] then
- return false
- end
- end
- end
- return true
- end
- function nodes.tracers.characters.string(t)
- local tt = { }
- for i=1,#t do
- tt[i] = utf.char(t[i][1])
- end
- return table.concat(tt,"")
- end
- function nodes.tracers.characters.unicodes(t,decimal)
- local tt = { }
- for i=1,#t do
- if decimal then
- tt[i] = t[i][1]
- else
- tt[i] = format("%04X",t[i][1])
- end
- end
- return table.concat(tt," ")
- end
- function nodes.tracers.characters.indices(t,decimal)
- local tt = { }
- for i=1,#t do
- if decimal then
- tt[i] = t[i][3]
- else
- tt[i] = format("%04X",t[i][3])
- end
- end
- return table.concat(tt," ")
- end
- function nodes.tracers.characters.fonts(t)
- local f = t[1] and t[1][2]
- return (f and file.basename(tfmid[f].filename or "unknown")) or "unknown"
- end
-
- function nodes.tracers.characters.start()
- local npc = nodes.process_characters
- local list = { }
- function nodes.process_characters(head)
- local n = #list
- collect(head,list,'before',n)
- local h, d = npc(head)
- collect(head,list,'after',n)
- if #list > n then
- list[#list+1] = { }
- end
- return h, d
- end
- function nodes.tracers.characters.stop()
- tracers.list['characters'] = list
- lmx.set('title', 'ConTeXt Character Processing Information')
- lmx.set('color-background-one', lmx.get('color-background-yellow'))
- lmx.set('color-background-two', lmx.get('color-background-purple'))
- lmx.show('context-characters.lmx')
- lmx.restore()
- nodes.process_characters = npc
- end
- end
-
- local stack = { }
-
- function nodes.tracers.start(tag)
- stack[#stack+1] = tag
- local tracer = nodes.tracers[tag]
- if tracer and tracer.start then
- tracer.start()
- end
- end
- function nodes.tracers.stop()
- local tracer = stack[#stack]
- if tracer and tracer.stop then
- tracer.stop()
- end
- stack[#stack] = nil
- end
-
-end
+nodes.filterkey = left * (1-left)^0 * left * space^0 * lpeg.C((1-space)^0)