use xml = format::fastxml; use sort; use os; use fmt; use strings; type node = struct { name: str, attributes: []str, has_text: bool, children: []*node, }; fn add_attr(n: *node, attr: xml::attribute) void = { for (let i = 0z; i < len(n.attributes); i += 1) { if (n.attributes[i] == attr.0) return; }; append(n.attributes, strings::dup(attr.0)); }; fn add_text(n: *node, text: str) void = { const clean = strings::trim(text); n.has_text ||= len(clean) > 0; }; fn get_node(n: *node, name: str) *node = { for (let i = 0z; i < len(n.children); i += 1) { if (n.children[i].name == name) return n.children[i]; }; let new = alloc(node { name = strings::dup(name), ... }); append(n.children, new); return new; }; fn print(n: *node, indent: size) void = { for (let i = 0z; i < indent; i += 1) fmt::print("> ")!; fmt::print(n.name)!; if (n.has_text) fmt::print("+")!; fmt::print(" [")!; for (let i = 0z; i < len(n.attributes); i += 1) { fmt::printf("{}{}", if (i == 0) "" else " ", n.attributes[i])!; }; fmt::println("]")!; for (let i = 0z; i < len(n.children); i += 1) { print(n.children[i], indent + 1); }; }; export fn main() void = { if (len(os::args) - 1 < 1) { fmt::fatal("Usage: xmltree "); }; const file = os::open(os::args[1])!; const parser = xml::parse(file)!; defer xml::parser_free(parser); let tree = null: *node; defer free(tree); // memory leaks! let stack: []*node = []; defer free(stack); for (true) { const token = match (xml::scan(parser)!) { case let t: xml::token => yield t; case void => break; }; match (token) { case let start: xml::elementstart => if (len(stack) == 0) { tree = alloc(node { name = strings::dup(start), ... }); append(stack, tree); } else { let current = stack[len(stack) - 1]; append(stack, get_node(current, start)); }; case let end: xml::elementend => //fmt::printfln("stack => {}", stack[len(stack) - 1].name)!; //fmt::printfln("file => {}", end)!; assert(end == stack[len(stack) - 1].name); delete(stack[len(stack) - 1]); if (len(stack) == 0) break; case let attr: xml::attribute => let current = stack[len(stack) - 1]; add_attr(current, attr); case let text: xml::text => let current = stack[len(stack) - 1]; add_text(current, text); }; }; print(tree, 0); };