use a bipartite graph

This commit is contained in:
2025-10-12 23:09:00 +08:00
parent e133a11d61
commit 1eca7a902c
3 changed files with 96 additions and 47 deletions

View File

@@ -1,11 +1,18 @@
local label_map = {} -- label has to be unique!
-- helper functions for debugging
local function show(s)
io.stderr:write("[Debug] " .. s .. "\n")
end
------------------------------------------------
local label_map = {} -- label has to be unique!
local adj = {} -- graph
local function add_edge(u, v)
if not adj[u] then
adj[u] = {}
end
table.insert(adj[u], v)
end
local function collect_labels(blk)
if blk.identifier and blk.identifier ~= "" then
label_map[blk.identifier] = blk:clone()
@@ -13,27 +20,6 @@ local function collect_labels(blk)
return nil
end
local function dfs(blk, stack) -- depth first search on a top level blk
-- check for identifier and build label_map
if blk.identifier and blk.identifier ~= "" then
label_map[blk.identifier] = blk:clone()
table.insert(stack, blk.identifier)
end
-- recurse into child blocks
-- type matters. see https://hackage-content.haskell.org/package/pandoc-types-1.23.1/docs/Text-Pandoc-Definition.html#t:Block
-- fortunately, we only need to recurse on divs.
if blk.t == 'Div' then
for _, inner in ipairs(blk.content) do
dfs(inner, stack)
end
end
-- pop
if blk.identifier and blk.identifier ~= "" then
table.remove(stack) -- pop
end
end
local function words(s)
local res = {}
for part in s:gmatch("[^,]+") do -- split by commas
@@ -45,6 +31,49 @@ local function words(s)
return res
end
local function dfs(blk, stack) -- depth first search on a top level blk
-- check for identifier and build label_map
-- look for 2 types of AST node: divs with include attr and divs with labels
local labelled = false
local include = false
if blk.attributes and blk.attributes["include"] then -- this must be a leaf node
include = true
-- labels in include may appears later in the dfs than this include-node
-- but we assume every label will be there and build the graph now
-- This is a directed bipartite grpah.
-- one side for labeled nodes and one side for include-nodes
for _, l in ipairs(words(blk.attributes["include"])) do
-- insert edges
-- what's the identifier of this include-node?...
-- well... you must write a label for each include-node...
-- this can be done using another filter
add_edge(blk.identifier, l)
end
-- insert more edges
for _, l in ipairs(stack) do
add_edge(l, blk.identifier)
end
elseif blk.identifier and blk.identifier ~= "" then
-- collect labelled nodes & maintain the stack
labelled = true
label_map[blk.identifier] = blk:clone()
table.insert(stack, blk.identifier)
end
-- recurse into child blocks
-- type matters. see https://hackage-content.haskell.org/package/pandoc-types-1.23.1/docs/Text-Pandoc-Definition.html#t:Block
-- fortunately, we only need to recurse on divs.
if not include and blk.t == 'Div' then
for _, inner in ipairs(blk.content) do
dfs(inner, stack)
end
end
-- pop
if labelled then
table.remove(stack)
end
end
local function replace(e)
local include = e.attributes["include"]
if include then
@@ -66,9 +95,16 @@ end
return {
-- traverse = 'topdown',
Pandoc = function(doc)
-- collect labels
-- collect labels & build the graph
for _, blk in ipairs(doc.blocks) do
dfs(blk,{})
dfs(blk, {})
end
show("edges:")
for u, vs in pairs(adj) do
for _, v in ipairs(vs) do
show(u .. "->" .. v)
end
end
-- replace