diff options
Diffstat (limited to 'tex/context/base/node-ppt.lua')
-rw-r--r-- | tex/context/base/node-ppt.lua | 476 |
1 files changed, 476 insertions, 0 deletions
diff --git a/tex/context/base/node-ppt.lua b/tex/context/base/node-ppt.lua new file mode 100644 index 000000000..c8cba8566 --- /dev/null +++ b/tex/context/base/node-ppt.lua @@ -0,0 +1,476 @@ +if not modules then modules = { } end modules ['node-ppt'] = { + version = 1.001, + comment = "companion to node-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- This is all very exeperimental and likely to change. + +local next, type, unpack, load = next, type, table.unpack, load + +local serialize = table.serialize +local formatters = string.formatters + +local report = logs.reporter("properties") +local report_setting = logs.reporter("properties","setting") +local trace_setting = false trackers.register("properties.setting", function(v) trace_setting = v end) + +-- report("using experimental properties") + +local nuts = nodes.nuts +local tonut = nuts.tonut +local tonode = nuts.tonode +local getid = nuts.getid +local getnext = nuts.getnext +local getprev = nuts.getprev +local getsubtype = nuts.getsubtype +local getfield = nuts.getfield +local setfield = nuts.setfield +local getlist = nuts.getlist +local flushnode = nuts.flush +local removenode = nuts.remove +local traverse = nuts.traverse +local traverse_id = nuts.traverse_id + +local nodecodes = nodes.nodecodes +local whatsitcodes = nodes.whatsitcodes + +local whatsit_code = nodecodes.whatsit +local hlist_code = nodecodes.hlist +local vlist_code = nodecodes.vlist +local userdefined_code = whatsitcodes.userdefined +local localpar_code = whatsitcodes.localpar + +local nodepool = nodes.pool +local new_usernumber = nodepool.usernumber + +local nutpool = nuts.pool +local nut_usernumber = nutpool.usernumber + +local variables = interfaces.variables +local v_before = variables.before +local v_after = variables.after +local v_here = variables.here + +local cache = { } +local nofslots = 0 +local property_id = nodepool.userids["property"] + +local properties = nodes.properties if not properties then return end -- temp +local propertydata = properties.data + +local starttiming = statistics.starttiming +local stoptiming = statistics.stoptiming + +if not propertydata then + return +end + +-- management + +local function register(where,data,...) + if not data then + data = where + where = v_after + end + if data then + local data = { where, data, ... } + nofslots = nofslots + 1 + if nofslots > 1 then + cache[nofslots] = data + else + -- report("restarting attacher") + cache = { data } -- also forces collection + end + return new_usernumber(property_id,nofslots) + end +end + +local writenode = node.write +local flushnode = context.flushnode + +function commands.deferredproperty(...) +-- context(register(...)) + flushnode(register(...)) +end + + +function commands.immediateproperty(...) + writenode(register(...)) +end + +commands.attachproperty = commands.deferredproperty + +local actions = { } properties.actions = actions + +table.setmetatableindex(actions,function(t,k) + report("unknown property action %a",k) + local v = function() end + return v +end) + +local f_delayed = formatters["return function(target,head,where,propdata,parent) %s end"] +local f_immediate = formatters["return function(target,head,where,propdata) %s end"] + +local nofdelayed = 0 -- better is to keep track of it per page ... we can have deleted nodes with properties + +function actions.delayed(target,head,where,propdata,code,...) -- this one is used at the tex end +-- local kind = type(code) +-- if kind == "string" then +-- code, err = load(f_delayed(code)) +-- if code then +-- code = code() +-- end +-- elseif kind ~= "function" then +-- code = nil +-- end + if code then + local delayed = propdata.delayed + if delayed then + delayed[#delayed+1] = { where, code, ... } + else + propdata.delayed = { { where, code, ... } } + nofdelayed = nofdelayed + 1 + end + end +end + +function actions.fdelayed(target,head,where,propdata,code,...) -- this one is used at the tex end +-- local kind = type(code) +-- if kind == "string" then +-- code, err = load(f_delayed(code)) +-- if code then +-- code = code() +-- end +-- elseif kind ~= "function" then +-- code = nil +-- end + if code then + local delayed = propdata.delayed + if delayed then + delayed[#delayed+1] = { false, code, ... } + else + propdata.delayed = { { false, code, ... } } + nofdelayed = nofdelayed + 1 + end + end +end + +function actions.immediate(target,head,where,propdata,code,...) -- this one is used at the tex end + local kind = type(code) + if kind == "string" then + local f = f_immediate(code) + local okay, err = load(f) + if okay then + local h = okay()(target,head,where,propdata,...) + if h and h ~= head then + return h + end + end + elseif kind == "function" then + local h = code()(target,head,where,propdata,...) + if h and h ~= head then + return h + end + end +end + +-- another experiment (a table or function closure are equally efficient); a function +-- is easier when we want to experiment with different (compatible) implementations + +-- function nodes.nuts.pool.deferredfunction(...) +-- nofdelayed = nofdelayed + 1 +-- local n = nut_usernumber(property_id,0) +-- propertydata[n] = { deferred = { ... } } +-- return n +-- end + +-- function nodes.nuts.pool.deferredfunction(f) +-- nofdelayed = nofdelayed + 1 +-- local n = nut_usernumber(property_id,0) +-- propertydata[n] = { deferred = f } +-- return n +-- end + +-- maybe actions will get parent too + +local function delayed(head,parent) -- direct based + for target in traverse(head) do + local p = propertydata[target] + if p then + -- local deferred = p.deferred -- kind of late lua (but too soon as we have no access to pdf.h/v) + -- if deferred then + -- -- if #deferred > 0 then + -- -- deferred[1](unpack(deferred,2)) + -- -- else + -- -- deferred[1]() + -- -- end + -- deferred() + -- p.deferred = false + -- if nofdelayed == 1 then + -- nofdelayed = 0 + -- return head + -- else + -- nofdelayed = nofdelayed - 1 + -- end + -- else + local delayed = p.delayed + if delayed then + for i=1,#delayed do + local d = delayed[i] + local code = d[2] + local kind = type(code) + if kind == "string" then + code, err = load(f_delayed(code)) + if code then + code = code() + end + end + local where = d[1] + if where then + local h = code(target,where,head,p,parent,unpack(d,3)) -- target where propdata head parent + if h and h ~= head then + head = h + end + else + code(unpack(d,3)) + end + end + p.delayed = nil + if nofdelayed == 1 then + nofdelayed = 0 + return head + else + nofdelayed = nofdelayed - 1 + end + end + -- end + end + local id = getid(target) + if id == hlist_code or id == vlist_code then + local list = getlist(target) + if list then + local done = delayed(list,parent) + if done then + setfield(target,"list",done) + end + if nofdelayed == 0 then + return head + end + end + else + -- maybe also some more lists? but we will only use this for some + -- special cases .. who knows + end + end + return head +end + +function properties.delayed(head) -- + if nofdelayed > 0 then + -- if next(propertydata) then + starttiming(properties) + head = delayed(tonut(head)) + stoptiming(properties) + return tonode(head), true -- done in shipout anyway + -- else + -- delayed = 0 + -- end + end + return head, false +end + +-- more explicit ones too + +local anchored = { + [v_before] = function(n) + while n do + n = getprev(n) + if getid(n) == whatsit_code and getsubtype(n) == user_code and getfield(n,"user_id") == property_id then + -- continue + else + return n + end + end + end, + [v_after] = function(n) + while n do + n = getnext(n) + if getid(n) == whatsit_code then + local subtype = getsubtype(n) + if (subtype == userdefined_code and getfield(n,"user_id") == property_id) then + -- continue + elseif subtype == localpar_code then + -- continue .. can't happen anyway as we cannot write + else + return n + end + else + return n + end + end + end, + [v_here] = function(n) + -- todo + end, +} + +table.setmetatableindex(anchored,function(t,k) + v = anchored[v_after] + t[k] = v + return v +end) + +function properties.attach(head) + + if nofslots <= 0 then + return head, false + end + + local done = false + local last = nil + local head = tonut(head) + + starttiming(properties) + + for source in traverse_id(whatsit_code,head) do + if getsubtype(source) == userdefined_code then + if last then + removenode(head,last,true) + last = nil + end + if getfield(source,"user_id") == property_id then + local slot = getfield(source,"value") + local data = cache[slot] + if data then + cache[slot] = nil + local where = data[1] + local target = anchored[where](source) + if target then + local first = data[2] + local method = type(first) + local p_target = propertydata[target] + local p_source = propertydata[source] + if p_target then + if p_source then + for k, v in next, p_source do + p_target[k] = v + end + end + if method == "table" then + for k, v in next, first do + p_target[k] = v + end + elseif method == "function" then + first(target,head,where,p_target,unpack(data,3)) + elseif method == "string" then + actions[first](target,head,where,p_target,unpack(data,3)) + end + elseif p_source then + if method == "table" then + propertydata[target] = p_source + for k, v in next, first do + p_source[k] = v + end + elseif method == "function" then + propertydata[target] = p_source + first(target,head,where,p_source,unpack(data,3)) + elseif method == "string" then + propertydata[target] = p_source + actions[first](target,head,where,p_source,unpack(data,3)) + end + else + if method == "table" then + propertydata[target] = first + elseif method == "function" then + local t = { } + propertydata[target] = t + first(target,head,where,t,unpack(data,3)) + elseif method == "string" then + local t = { } + propertydata[target] = t + actions[first](target,head,where,t,unpack(data,3)) + end + end + if trace_setting then + report_setting("node %i, id %s, data %s", + target,nodecodes[getid(target)],serialize(propertydata[target],false)) + end + end + if nofslots == 1 then + nofslots = 0 + last = source + break + else + nofslots = nofslots - 1 + end + end + last = source + end + end + end + + if last then + removenode(head,last,true) + end + + stoptiming(properties) + + return head, done + +end + +local tasks = nodes.tasks + +-- maybe better hard coded in-place + +-- tasks.prependaction("processors","before","nodes.properties.attach") +-- tasks.appendaction("shipouts","normalizers","nodes.properties.delayed") + +statistics.register("properties processing time", function() + return statistics.elapsedseconds(properties) +end) + +-- only for development + +-- local function show(head,level,report) +-- for target in traverse(head) do +-- local p = propertydata[target] +-- if p then +-- report("level %i, node %i, id %s, data %s", +-- level,target,nodecodes[getid(target)],serialize(propertydata[target],false)) +-- end +-- local id = getid(target) +-- if id == hlist_code or id == vlist_code then +-- local list = getlist(target) +-- if list then +-- show(list,level+1,report) +-- end +-- else +-- -- maybe more lists +-- end +-- end +-- return head, false +-- end +-- +-- local report_shipout = logs.reporter("properties","shipout") +-- local report_processors = logs.reporter("properties","processors") +-- +-- function properties.showshipout (head) return tonode(show(tonut(head),1,report_shipout )), true end +-- function properties.showprocessors(head) return tonode(show(tonut(head),1,report_processors)), true end +-- +-- tasks.prependaction("shipouts","before","nodes.properties.showshipout") +-- tasks.disableaction("shipouts","nodes.properties.showshipout") +-- +-- trackers.register("properties.shipout",function(v) +-- tasks.setaction("shipouts","nodes.properties.showshipout",v) +-- end) +-- +-- tasks.appendaction ("processors","after","nodes.properties.showprocessors") +-- tasks.disableaction("processors","nodes.properties.showprocessors") +-- +-- trackers.register("properties.processors",function(v) +-- tasks.setaction("processors","nodes.properties.showprocessors",v) +-- end) |