about summary refs log tree commit diff
path: root/cmd/xmltree/main.ha
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/xmltree/main.ha')
-rw-r--r--cmd/xmltree/main.ha104
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);
+};