yay
This commit is contained in:
		
							
								
								
									
										82
									
								
								filter.lua
									
									
									
									
									
								
							
							
						
						
									
										82
									
								
								filter.lua
									
									
									
									
									
								
							@@ -4,8 +4,8 @@ local function show(s)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
------------------------------------------------
 | 
			
		||||
local label_map = {} -- label has to be unique!
 | 
			
		||||
local adj = {}       -- graph
 | 
			
		||||
local adj = {} -- graph
 | 
			
		||||
local include_nodes = {}
 | 
			
		||||
local function add_edge(u, v)
 | 
			
		||||
    if not adj[u] then
 | 
			
		||||
        adj[u] = {}
 | 
			
		||||
@@ -14,7 +14,7 @@ local function add_edge(u, v)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
---------------------------------------------------------
 | 
			
		||||
-- Topological sort (DFS-based, with cycle detection)
 | 
			
		||||
-- Topological sort (DFS-based, with cycle detection) -- chatgpt
 | 
			
		||||
---------------------------------------------------------
 | 
			
		||||
local visited = {}  -- "perm" mark: node completely processed
 | 
			
		||||
local in_stack = {} -- "temp" mark: node currently in recursion stack
 | 
			
		||||
@@ -49,28 +49,21 @@ local function _dfs(u, path)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function topo_sort()
 | 
			
		||||
    local rank = {}
 | 
			
		||||
    for u, _ in pairs(adj) do
 | 
			
		||||
        if not visited[u] then
 | 
			
		||||
            _dfs(u, {})
 | 
			
		||||
            if cycle then return nil, cycle end
 | 
			
		||||
            if cycle then return rank, cycle end
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    -- build rank map
 | 
			
		||||
    local rank = {}
 | 
			
		||||
    for i, u in ipairs(order) do
 | 
			
		||||
        rank[u] = i
 | 
			
		||||
    end
 | 
			
		||||
    return rank
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function collect_labels(blk)
 | 
			
		||||
    if blk.identifier and blk.identifier ~= "" then
 | 
			
		||||
        label_map[blk.identifier] = blk:clone()
 | 
			
		||||
    end
 | 
			
		||||
    return nil
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function words(s)
 | 
			
		||||
    local res = {}
 | 
			
		||||
    for part in s:gmatch("[^,]+") do               -- split by commas
 | 
			
		||||
@@ -83,12 +76,12 @@ local function words(s)
 | 
			
		||||
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
 | 
			
		||||
        table.insert(include_nodes, blk.identifier)
 | 
			
		||||
        -- 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.
 | 
			
		||||
@@ -107,7 +100,6 @@ local function dfs(blk, stack) -- depth first search on a top level blk
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
@@ -125,22 +117,31 @@ local function dfs(blk, stack) -- depth first search on a top level blk
 | 
			
		||||
    end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function replace(e)
 | 
			
		||||
    local include = e.attributes["include"]
 | 
			
		||||
    if include then
 | 
			
		||||
        local blocks = {}
 | 
			
		||||
        -- Replace the Div contents
 | 
			
		||||
        for _, l in ipairs(words(include)) do
 | 
			
		||||
            if label_map[l] then
 | 
			
		||||
                table.insert(blocks, label_map[l]:clone())
 | 
			
		||||
                show("insert [" .. label_map[l].identifier .. ']\n')
 | 
			
		||||
            else
 | 
			
		||||
                io.stderr:write("Cannot find AST node with label " .. l .. "\n")
 | 
			
		||||
-- read doc and return the concatenation of target nodes for an include-node
 | 
			
		||||
-- Things become messy here since the include list is ordered.
 | 
			
		||||
-- one has to again transverse the AST to collect labelled nodes and then
 | 
			
		||||
-- do the concatenation...
 | 
			
		||||
local function collect_node(doc, l)
 | 
			
		||||
    local blocks = {}
 | 
			
		||||
    local targets = words(l.attributes["include"])
 | 
			
		||||
    local nodes = {}
 | 
			
		||||
    doc:walk {
 | 
			
		||||
        Div = function(div)
 | 
			
		||||
            if div.identifier and div.identifier ~= "" then
 | 
			
		||||
                nodes[div.identifier] = div:clone()
 | 
			
		||||
            end
 | 
			
		||||
            return nil
 | 
			
		||||
        end
 | 
			
		||||
    }
 | 
			
		||||
    for _, t in ipairs(targets) do
 | 
			
		||||
        if nodes[t] then
 | 
			
		||||
            table.insert(blocks, nodes[t]:clone())
 | 
			
		||||
            -- show("insert [" .. nodes[t].identifier .. ']\n')
 | 
			
		||||
        else
 | 
			
		||||
            io.stderr:write("Cannot find AST node with label " .. t .. "\n")
 | 
			
		||||
        end
 | 
			
		||||
        return pandoc.Div(blocks, e.attr)
 | 
			
		||||
    end
 | 
			
		||||
    return e -- no change
 | 
			
		||||
    return pandoc.Div(blocks, l.attr)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return {
 | 
			
		||||
@@ -151,30 +152,23 @@ return {
 | 
			
		||||
            dfs(blk, {})
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        -- sanity check
 | 
			
		||||
        show("edges:")
 | 
			
		||||
        for u, vs in pairs(adj) do
 | 
			
		||||
            for _, v in ipairs(vs) do
 | 
			
		||||
                show(u .. "->" .. v)
 | 
			
		||||
            end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        -- topological sort
 | 
			
		||||
        local rank, cycle = topo_sort()
 | 
			
		||||
        if cycle then
 | 
			
		||||
            error("Cycle detected:" .. table.concat(cycle, " -> ") .. "\n")
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        -- sanity check
 | 
			
		||||
        show("ranks:")
 | 
			
		||||
        for k, v in pairs(rank) do
 | 
			
		||||
            show(k .. "--" .. v)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        -- replace
 | 
			
		||||
        -- for i, blk in ipairs(doc.blocks) do
 | 
			
		||||
        --     doc.blocks[i] = replace(blk)
 | 
			
		||||
        -- end
 | 
			
		||||
        table.sort(include_nodes, function(x, y)
 | 
			
		||||
            return rank[x] > rank[y]
 | 
			
		||||
        end)
 | 
			
		||||
        for _, v in ipairs(include_nodes) do
 | 
			
		||||
            doc = doc:walk { Div = function(div)
 | 
			
		||||
                if div.identifier and div.identifier == v then
 | 
			
		||||
                    return collect_node(doc, div)
 | 
			
		||||
                end
 | 
			
		||||
            end }
 | 
			
		||||
        end
 | 
			
		||||
        return doc
 | 
			
		||||
    end
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user