use a bipartite graph
This commit is contained in:
		
							
								
								
									
										88
									
								
								filter.lua
									
									
									
									
									
								
							
							
						
						
									
										88
									
								
								filter.lua
									
									
									
									
									
								
							@@ -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
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user