summaryrefslogtreecommitdiff
path: root/xmake/core/base/xml.lua
diff options
context:
space:
mode:
authorruki <[email protected]>2025-11-15 00:14:29 +0800
committerruki <[email protected]>2025-11-15 00:14:29 +0800
commit3d6be9e48846d619bf389ee9a16157281bafa87b (patch)
treee6e9071fa08b60f82864cc4e97eadaed7c209ba0 /xmake/core/base/xml.lua
parentd1dc6ffca3a29e6e071fa7a16860a579f3ad552c (diff)
fix test
Diffstat (limited to 'xmake/core/base/xml.lua')
-rw-r--r--xmake/core/base/xml.lua88
1 files changed, 68 insertions, 20 deletions
diff --git a/xmake/core/base/xml.lua b/xmake/core/base/xml.lua
index 1e644777f..2aa4036b8 100644
--- a/xmake/core/base/xml.lua
+++ b/xmake/core/base/xml.lua
@@ -29,10 +29,11 @@ local table = require("base/table")
-- XML node structure:
-- {
-- name = "element-name" | nil (for non-element nodes)
--- kind = "element" | "text" | "comment" | "cdata" | "doctype"
+-- kind = "element" | "text" | "comment" | "cdata" | "doctype" | "document"
-- attrs = { key = value, ... } or nil (only for elements)
-- text = "raw text" (for text/comment/cdata/doctype nodes)
--- children = { child1, child2, ... } or nil (only for elements with children)
+-- children = { child1, child2, ... } or nil (for elements or document nodes)
+-- prolog = { comment/doctypes before root } or nil (only on root element)
-- }
--
-- Example:
@@ -41,6 +42,7 @@ local table = require("base/table")
-- doc.attrs.id == "1"
-- xml.find(doc, "root/item").kind == "element"
-- xml.text_of(doc) == "" -- since root has no direct text nodes
+-- doc.prolog[1].kind == "doctype" -- e.g. when document had <!DOCTYPE ...>
-- doc.children[2].kind == "comment" and doc.children[2].text == "note"
--
@@ -145,8 +147,19 @@ end
-- e.g. `local doc, err = xml.decode("<root><item>foo</item></root>")`
function xml.decode(data, opt)
opt = opt or {}
- local root = {name = "__root__", attrs = {}, children = {}}
- local stack = {root}
+ local root_children = {}
+ local doc_node = {kind = "document", children = root_children}
+ local stack = {doc_node}
+ local function ensure_children(node)
+ if not node.children then
+ if node == doc_node then
+ node.children = root_children
+ else
+ node.children = {}
+ end
+ end
+ return node.children
+ end
local i = 1
local len = #data
while i <= len do
@@ -167,8 +180,8 @@ function xml.decode(data, opt)
end
local value = data:sub(lt + 4, close - 1)
local top = stack[#stack]
- top.children = top.children or {}
- table.insert(top.children, xml.comment(value))
+ local children = ensure_children(top)
+ table.insert(children, xml.comment(value))
i = close + 3
elseif data:sub(lt + 1, lt + 8) == "![CDATA[" then
local close = data:find("]]>", lt + 9, true)
@@ -177,18 +190,18 @@ function xml.decode(data, opt)
end
local value = data:sub(lt + 9, close - 1)
local top = stack[#stack]
- top.children = top.children or {}
- table.insert(top.children, xml.cdata(value))
+ local children = ensure_children(top)
+ table.insert(children, xml.cdata(value))
i = close + 3
- elseif data:sub(lt + 1, lt + 9):upper() == "!DOCTYPE" then
- local close = data:find(">", lt + 9)
+ elseif data:sub(lt + 1, lt + 8):upper() == "!DOCTYPE" then
+ local close = data:find(">", lt + 8)
if not close then
return nil, "unterminated doctype declaration"
end
- local value = data:sub(lt + 10, close - 1)
+ local value = data:sub(lt + 9, close - 1)
local top = stack[#stack]
- top.children = top.children or {}
- table.insert(top.children, xml.doctype(value))
+ local children = ensure_children(top)
+ table.insert(children, xml.doctype(value))
i = close + 1
elseif data:sub(lt + 1, lt + 1) == "?" then
local close = data:find("?>", lt + 2, true)
@@ -227,8 +240,8 @@ function xml.decode(data, opt)
local attrs = xml._parse_attrs(attrstr or "")
local node = xml.empty(tagname, attrs)
local top = stack[#stack]
- top.children = top.children or {}
- table.insert(top.children, node)
+ local children = ensure_children(top)
+ table.insert(children, node)
if not selfclose then
table.insert(stack, node)
end
@@ -238,10 +251,26 @@ function xml.decode(data, opt)
if #stack ~= 1 then
return nil, "malformed xml: unclosed tags"
end
- if #root.children == 1 then
- return root.children[1]
+ local element_nodes = {}
+ for _, child in ipairs(root_children) do
+ if child.kind == "element" then
+ table.insert(element_nodes, child)
+ end
+ end
+ if #element_nodes == 1 then
+ local rootnode = element_nodes[1]
+ local prolog = {}
+ for _, child in ipairs(root_children) do
+ if child ~= rootnode then
+ table.insert(prolog, child)
+ end
+ end
+ if #prolog > 0 then
+ rootnode.prolog = prolog
+ end
+ return rootnode
end
- return root.children
+ return root_children
end
-- compute indent string for pretty output
@@ -260,7 +289,15 @@ end
function xml._encode_node(node, opt, level)
opt = opt or {}
level = level or 0
- if node.kind == "text" then
+ if node.kind == "document" then
+ local parts = {}
+ if node.children then
+ for _, child in ipairs(node.children) do
+ table.insert(parts, xml._encode_node(child, opt, level))
+ end
+ end
+ return table.concat(parts, opt.pretty and "\n" or "")
+ elseif node.kind == "text" then
local indent = xml._indent(opt, level)
local text = xml._encode_text(tostring(node.text or ""))
if opt.pretty then
@@ -306,7 +343,18 @@ end
-- e.g. `local xmlstr = xml.encode(node, {pretty = true, indent = 2})`
function xml.encode(node, opt)
opt = opt or {}
- return xml._encode_node(node, opt, 0)
+ local fragments = {}
+ local prolog = node.prolog
+ if prolog then
+ for _, child in ipairs(prolog) do
+ table.insert(fragments, xml._encode_node(child, opt, 0))
+ if opt.pretty then
+ table.insert(fragments, "\n")
+ end
+ end
+ end
+ table.insert(fragments, xml._encode_node(node, opt, 0))
+ return table.concat(fragments)
end
-- load xml file