diff options
Diffstat (limited to 'cmd/xmltree/main.ha')
-rw-r--r-- | cmd/xmltree/main.ha | 104 |
1 files changed, 104 insertions, 0 deletions
diff --git a/cmd/xmltree/main.ha b/cmd/xmltree/main.ha new file mode 100644 index 0000000..5f15eaa --- /dev/null +++ b/cmd/xmltree/main.ha @@ -0,0 +1,104 @@ +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 <file.xml>"); + }; + + 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); +}; |