diff options
| author | ruki <[email protected]> | 2025-11-15 00:56:02 +0800 |
|---|---|---|
| committer | ruki <[email protected]> | 2025-11-15 00:56:02 +0800 |
| commit | d1dc6ffca3a29e6e071fa7a16860a579f3ad552c (patch) | |
| tree | 79b04b156a64195814f6439c4fabbb8dccb46af4 /xmake/core/base/xml.lua | |
| parent | d6b3bc59cad34b6279f3df8a2066505ca82a2733 (diff) | |
add more comments
Diffstat (limited to 'xmake/core/base/xml.lua')
| -rw-r--r-- | xmake/core/base/xml.lua | 104 |
1 files changed, 72 insertions, 32 deletions
diff --git a/xmake/core/base/xml.lua b/xmake/core/base/xml.lua index a3f2c96f0..1e644777f 100644 --- a/xmake/core/base/xml.lua +++ b/xmake/core/base/xml.lua @@ -26,6 +26,25 @@ local io = require("base/io") local os = require("base/os") local table = require("base/table") +-- XML node structure: +-- { +-- name = "element-name" | nil (for non-element nodes) +-- kind = "element" | "text" | "comment" | "cdata" | "doctype" +-- 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) +-- } +-- +-- Example: +-- local doc = xml.decode("<root id='1'><item>foo</item><!--note--></root>") +-- doc.kind == "element" +-- doc.attrs.id == "1" +-- xml.find(doc, "root/item").kind == "element" +-- xml.text_of(doc) == "" -- since root has no direct text nodes +-- doc.children[2].kind == "comment" and doc.children[2].text == "note" +-- + +-- decode entities function xml._decode_entities(str) return (str:gsub("<", "<") :gsub(">", ">") @@ -34,16 +53,19 @@ function xml._decode_entities(str) :gsub("&", "&")) end +-- encode raw text for xml element content function xml._encode_text(str) return (str:gsub("&", "&") :gsub("<", "<") :gsub(">", ">")) end +-- encode attribute value for xml output function xml._encode_attr(str) return xml._encode_text(str):gsub("\"", """) end +-- parse attribute string to table (or nil if empty) function xml._parse_attrs(attrstr) local attrs attrstr:gsub("([%w_:%-%.]+)%s*=%s*([\"'])(.-)%2", function(key, quote, value) @@ -53,6 +75,29 @@ function xml._parse_attrs(attrstr) return attrs end +-- append normalized text node to top element on stack +function xml._append_text(stack, text, opt) + opt = opt or {} + if opt.trim_text ~= false then + text = text:gsub("^%s+", ""):gsub("%s+$", "") + end + if text ~= "" then + local top = stack[#stack] + top.children = top.children or {} + table.insert(top.children, xml.text(xml._decode_entities(text))) + end +end + +-- ensure closing tag matches stack and pop it +function xml._handle_closing(stack, tagname) + local top = stack[#stack] + if not top or top.name ~= tagname then + return nil, string.format("malformed xml: unexpected closing </%s>", tagname) + end + table.remove(stack) + return true +end + -- create an xml element node -- e.g. `local node = xml.new({name = "item", attrs = {id = "1"}, children = {xml.text("value")}})` function xml.new(opt) @@ -62,7 +107,7 @@ function xml.new(opt) attrs = opt.attrs, kind = opt.kind or "element", text = opt.text, - children = opt.children or {} + children = opt.children } end @@ -96,27 +141,6 @@ function xml.doctype(value) return xml.new({kind = "doctype", text = value or ""}) end -function xml._append_text(stack, text, opt) - opt = opt or {} - if opt.trim_text ~= false then - text = text:gsub("^%s+", ""):gsub("%s+$", "") - end - if text ~= "" then - local top = stack[#stack] - top.children = top.children or {} - table.insert(top.children, xml.text(xml._decode_entities(text))) - end -end - -function xml._handle_closing(stack, tagname) - local top = stack[#stack] - if not top or top.name ~= tagname then - return nil, string.format("malformed xml: unexpected closing </%s>", tagname) - end - table.remove(stack) - return true -end - -- decode xml string to tree node(s) -- e.g. `local doc, err = xml.decode("<root><item>foo</item></root>")` function xml.decode(data, opt) @@ -220,6 +244,7 @@ function xml.decode(data, opt) return root.children end +-- compute indent string for pretty output function xml._indent(opt, level) if not opt.pretty then return "" @@ -268,8 +293,10 @@ function xml._encode_node(node, opt, level) end local result = {} table.insert(result, xml._indent(opt, level) .. open .. ">") - for _, child in ipairs(node.children) do - table.insert(result, xml._encode_node(child, opt, level + 1)) + if node.children then + for _, child in ipairs(node.children) do + table.insert(result, xml._encode_node(child, opt, level + 1)) + end end table.insert(result, xml._indent(opt, level) .. "</" .. node.name .. ">") return table.concat(result, newline ~= "" and newline or "") @@ -302,18 +329,31 @@ function xml.save(filepath, node, opt) return io.writefile(filepath, data, opt) end --- find the first child node with the given name --- e.g. `local doc = xml.decode("<root><item id='1'/></root>")` --- `local item = xml.find(doc, "item")` -function xml.find(node, name) - if not node or not node.children then +-- find the first matching node by name or path (e.g. "root/item/subitem") +-- returns nil if not found +function xml.find(node, path) + if not node or not path or path == "" then return nil end - for _, child in ipairs(node.children) do - if child.name == name then - return child + local segments = path:split("/", {strict = true}) + local current = {node} + for _, segment in ipairs(segments) do + local next_level = {} + for _, parent in ipairs(current) do + if parent.children then + for _, child in ipairs(parent.children) do + if child.name == segment then + table.insert(next_level, child) + end + end + end + end + if #next_level == 0 then + return nil end + current = next_level end + return current[1] end -- get concatenated text from child nodes |
