From e604b131604179aaa7ddc9398c5a4def35a3b944 Mon Sep 17 00:00:00 2001 From: Yu Cong Date: Mon, 13 Oct 2025 00:23:29 +0800 Subject: [PATCH] yay --- filter.lua | 82 +++++++++++++++++++++++++---------------------------- input.md | 4 +-- output.html | 29 ++++++++++++++++--- 3 files changed, 65 insertions(+), 50 deletions(-) diff --git a/filter.lua b/filter.lua index 4abbaa7..ea255a0 100644 --- a/filter.lua +++ b/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 } diff --git a/input.md b/input.md index def3579..c329951 100644 --- a/input.md +++ b/input.md @@ -1,6 +1,6 @@ ::: {#structure} -inc (need thm2 and a fake label) +inc (need thm2, thm1 and a fake label) thm1 - inthm1 @@ -8,7 +8,7 @@ thm2 - inthm2 (need thm1) :::::::::::::::: -:::{include="thm2 , fakelabel" #inc} +:::{include="thm2, thm1 , fakelabel" #inc} This line will be ignored ::::::::::::::::::::::::::::::::::::::::::::::::::::::: diff --git a/output.html b/output.html index 308a0a0..102d986 100644 --- a/output.html +++ b/output.html @@ -1,10 +1,26 @@
-

inc (need thm2 and a fake label)

+

inc (need thm2, thm1 and a fake label)

thm1 - inthm1

thm2 - inthm2 (need thm1)

-
-

This line will be ignored

+
+
+

test thm2

+
+
+

test thm1

+
+

something

+
+
+
+
+
+

test thm1

+
+

something

+
+

test thm1

@@ -15,7 +31,12 @@

test thm2

-

i need theorem 1

+
+

test thm1

+
+

something

+
+