about summary refs log tree commit diff
path: root/gir
diff options
context:
space:
mode:
authorAlexey Yerin <yyp@disroot.org>2023-05-28 14:10:05 +0300
committerAlexey Yerin <yyp@disroot.org>2023-05-28 14:15:22 +0300
commit16e730f540acfe6a0015464c81831cdfacaf107b (patch)
treea2aa04bf788dc16977fc6cda2ed874938c3c13c5 /gir
Initial commit
Diffstat (limited to 'gir')
-rw-r--r--gir/parse.ha742
-rw-r--r--gir/types.ha363
2 files changed, 1105 insertions, 0 deletions
diff --git a/gir/parse.ha b/gir/parse.ha
new file mode 100644
index 0000000..f844cd9
--- /dev/null
+++ b/gir/parse.ha
@@ -0,0 +1,742 @@
+use fmt;
+use io;
+use strconv;
+use strings;
+use xml = format::fastxml;
+
+// The provided GIR file is invalid.
+export type invalid = !void;
+
+// Any error which can occur during GIR parsing.
+export type error = !(invalid | xml::error);
+
+// Returns a human-friendly representation of an [[error]].
+export fn strerror(err: error) const str = {
+	match (err) {
+	case invalid =>
+		return "File does not follow GIR structure";
+	case let err: xml::error =>
+		return xml::strerror(err);
+	};
+};
+
+// Parses a file into a [[repository]].
+export fn parse(in: io::file) (repository | error) = {
+	const parser = xml::parse(in)?;
+	defer xml::parser_free(parser);
+
+	let repo = repository { ... };
+	parse_element(parser, "",
+		("repository", &parse_repository, &repo),
+	)?;
+	return repo;
+};
+
+fn parse_repository(parser: *xml::parser, repo: *void) (void | error) = {
+	const repo = repo: *repository;
+	return parse_element(parser, "repository",
+		("xmlns", null),
+		("xmlns:c", null),
+		("xmlns:glib", null),
+		("version", &repo.version),
+		("c:identifier-prefixes", null),
+		("c:symbol-prefixes", null),
+		("include", &parse_include, &repo.includes),
+		("c:include", null, null),
+		("package", null, null),
+		("namespace", &parse_namespace, &repo.namespaces),
+	);
+};
+
+fn parse_include(parser: *xml::parser, includes: *void) (void | error) = {
+	const includes = includes: *[]include;
+	let new = include { ... };
+	parse_element(parser, "include",
+		("name", &new.name),
+		("version", &new.version),
+	)?;
+	append(includes, new);
+};
+
+fn parse_namespace(parser: *xml::parser, namespaces: *void) (void | error) = {
+	const namespaces = namespaces: *[]namespace;
+	let new = namespace { ... };
+	parse_element(parser, "namespace",
+		("name", &new.name),
+		("version", &new.version),
+		("c:identifier-prefixes", null),
+		("c:symbol-prefixes", null),
+		("shared-library", null),
+
+		("alias", &parse_alias, &new.aliases),
+		("class", &parse_class, &new.classes),
+		("interface", &parse_interface, &new.interfaces),
+		("record", &parse_record, &new.records),
+		("enumeration", &parse_enumeration, &new.enums),
+		("function", &parse_function, &new.functions),
+		("union", &parse_union, &new.unions),
+		("bitfield", &parse_bitfield, &new.bitfields),
+		("callback", &parse_callback, &new.callbacks),
+		("constant", &parse_constant, &new.constants),
+		("annotation", null, null),
+		("glib:boxed", null, null),
+
+		// Undocumented and unsupported
+		("docsection", null, null),
+		("function-macro", null, null),
+	)?;
+	append(namespaces, new);
+};
+
+fn parse_alias(parser: *xml::parser, aliases: *void) (void | error) = {
+	const aliases = aliases: *[]alias;
+	let new = alias { ... };
+	parse_element(parser, "alias",
+		parse_info(&new),
+		("name", &new.name),
+		("c:type", &new.c_type),
+		("type", &parse_simple_type, &new.inner),
+	)?;
+	append(aliases, new);
+};
+
+fn parse_class(parser: *xml::parser, classes: *void) (void | error) = {
+	const classes = classes: *[]class;
+	let new = class { ... };
+	parse_element(parser, "class",
+		parse_info(&new),
+		("name", &new.name),
+		("glib:type-name", &new.glib_type_name),
+		("glib:get-type", &new.glib_get_type),
+		("parent", &new.parent),
+		("glib:type-struct", null),
+		("glib:ref-func", &new.glib_ref_func),
+		("glib:unref-func", &new.glib_unref_func),
+		("glib:set-value-func", &new.glib_set_value_func),
+		("glib:get-value-func", &new.glib_get_value_func),
+		("c:type", &new.c_type),
+		("c:symbol-prefix", &new.symbol_prefix),
+		("abstract", &new.abstract),
+		("glib:fundamental", &new.fundamental),
+		("final", &new.final),
+
+		("implements", &parse_implements, &new.implements),
+		("constructor", &parse_constructor, &new.constructors),
+		("method", &parse_method, &new.methods),
+		("function", &parse_function, &new.functions),
+		("virtual-method", &parse_virtual_method, &new.virtual_methods),
+		("field", &parse_field, &new.entries),
+		("property", &parse_property, &new.properties),
+		("glib:signal", &parse_signal, &new.signals),
+		("union", &parse_union_entry, &new.entries),
+		("constant", &parse_constant, &new.constants),
+		("record", &parse_record_entry, &new.entries),
+		("callback", &parse_callback, &new.callbacks),
+	)?;
+	append(classes, new);
+};
+fn parse_implements(parser: *xml::parser, implements: *void) (void | error) = {
+	const implements = implements: *[]str;
+	let name = "";
+	parse_element(parser, "implements",
+		("name", &name),
+	)?;
+	append(implements, name);
+};
+fn parse_interface(parser: *xml::parser, interfaces: *void) (void | error) = {
+	const interfaces = interfaces: *[]interface;
+	let new = interface { ... };
+	parse_element(parser, "interface",
+		parse_info(&new),
+		("name", &new.name),
+		("glib:type-name", &new.glib_type_name),
+		("glib:get-type", &new.glib_get_type),
+		("c:symbol-prefix", null),
+		("c:type", &new.c_type),
+		("glib:type-struct", null),
+
+		("prerequisite", &parse_prerequisite, &new.prerequisites),
+		("implements", &parse_implements, &new.implements),
+		("function", &parse_function, &new.functions),
+		("constructor", &parse_constructor, &new.constructors),
+		("method", &parse_method, &new.methods),
+		("virtual-method", &parse_virtual_method, &new.virtual_methods),
+		("field", &parse_field, &new.entries),
+		("property", &parse_property, &new.properties),
+		("glib:signal", &parse_signal, &new.signals),
+		("callback", &parse_callback, &new.callbacks),
+		("constant", &parse_constant, &new.constants),
+	)?;
+	append(interfaces, new);
+};
+fn parse_prerequisite(parser: *xml::parser, prerequisites: *void) (void | error) = {
+	const prerequisites = prerequisites: *[]str;
+	let name = "";
+	parse_element(parser, "prerequisite",
+		("name", &name),
+	)?;
+	append(prerequisites, name);
+};
+
+fn parse_record(parser: *xml::parser, records: *void) (void | error) = {
+	const records = records: *[]record;
+	let new = record { ... };
+	parse_element(parser, "record",
+		parse_info(&new),
+		("name", &new.name),
+		("c:type", &new.c_type),
+		("disguised", &new.disguised),
+		("opaque", &new.opaque),
+		("pointer", &new.pointer),
+		("glib:type-name", &new.glib_type_name),
+		("glib:get-type", &new.glib_get_type),
+		("c:symbol-prefix", null),
+		("foreign", &new.foreign),
+		("glib:is-gtype-struct-for", null),
+		("copy-function", &new.copy_function),
+		("free-function", &new.free_function),
+
+		("field", &parse_field, &new.entries),
+		("function", &parse_function, &new.functions),
+		("union", &parse_union_entry, &new.entries),
+		("method", &parse_method, &new.methods),
+		("constructor", &parse_constructor, &new.constructors),
+	)?;
+	append(records, new);
+};
+
+fn parse_record_entry(parser: *xml::parser, entries: *void) (void | error) = {
+	const entries = entries: *[]entry;
+	let records: []record = [];
+	defer free(records);
+	parse_record(parser, &records)?;
+	append(entries, records[0]);
+};
+
+fn parse_constructor(parser: *xml::parser, constructors: *void) (void | error) = {
+	const constructors = constructors: *[]constructor;
+	let new = constructor { ... };
+	parse_element(parser, "constructor",
+		parse_callable(&new),
+		("parameters", &parse_parameters, &new.params),
+		("return-value", &parse_return_value, &new.return_value),
+	)?;
+	append(constructors, new);
+};
+fn parse_method(parser: *xml::parser, methods: *void) (void | error) = {
+	const methods = methods: *[]method;
+	let new = method { ... };
+	parse_element(parser, "method",
+		parse_callable(&new),
+		("glib:set-property", &new.glib_set_property),
+		("glib:get-property", &new.glib_get_property),
+		("parameters", &parse_method_parameters,
+			&(&new.instance, &new.params)),
+		("return-value", &parse_return_value, &new.return_value),
+	)?;
+	append(methods, new);
+};
+fn parse_virtual_method(parser: *xml::parser, vmethods: *void) (void | error) = {
+	const vmethods = vmethods: *[]virtual_method;
+	let new = virtual_method { ... };
+	parse_element(parser, "virtual-method",
+		parse_callable(&new),
+		("invoker", &new.invoker),
+		("parameters", &parse_method_parameters,
+			&(&new.instance, &new.params)),
+		("return-value", &parse_return_value, &new.return_value),
+	)?;
+	append(vmethods, new);
+};
+fn parse_field(parser: *xml::parser, entries: *void) (void | error) = {
+	const entries = entries: *[]entry;
+	let new = field { ... };
+	let type_: any_type = simple_type { ... };
+	let cb = callback { ... };
+	parse_element(parser, "field",
+		parse_info(&new),
+		("name", &new.name),
+		("writable", &new.writable),
+		("readable", &new.readable),
+		("private", &new.private),
+		("bits", &new.bits),
+
+		("callback", &parse_one_callback, &cb),
+		parse_any_type(&type_),
+	)?;
+	if (len(cb.name) > 0) {
+		new.type_ = cb;
+	} else {
+		new.type_ = type_;
+	};
+	append(entries, new);
+};
+fn parse_property(parser: *xml::parser, properties: *void) (void | error) = {
+	const properties = properties: *[]property;
+	let new = property { ... };
+	parse_element(parser, "property",
+		parse_info(&new),
+		("name", &new.name),
+		("writable", &new.writable),
+		("readable", &new.readable),
+		("construct", &new.construct),
+		("construct-only", &new.construct_only),
+		("setter", &new.setter),
+		("getter", &new.getter),
+		("default-value", &new.default_value),
+		("transfer-ownership", &new.transfer_ownership),
+
+		parse_any_type(&new.type_),
+	)?;
+	append(properties, new);
+};
+
+fn parse_enumeration(parser: *xml::parser, enums: *void) (void | error) = {
+	const enums = enums: *[]enumeration;
+	let new = enumeration { ... };
+	parse_element(parser, "enumeration",
+		parse_info(&new),
+		("name", &new.name),
+		("c:type", &new.c_type),
+		("glib:type-name", &new.glib_type_name),
+		("glib:get-type", &new.glib_get_type),
+		("glib:error-domain", &new.glib_error_domain),
+
+		("member", &parse_member, &new.members),
+		("function", &parse_function, &new.functions),
+	)?;
+	append(enums, new);
+};
+
+fn parse_function(parser: *xml::parser, functions: *void) (void | error) = {
+	const functions = functions: *[]function;
+	let new = function { ... };
+	parse_element(parser, "function",
+		parse_callable(&new),
+		("parameters", &parse_parameters, &new.params),
+		("return-value", &parse_return_value, &new.return_value),
+	)?;
+	append(functions, new);
+};
+
+fn parse_union(parser: *xml::parser, unions: *void) (void | error) = {
+	const unions = unions: *[]union_;
+	let new = union_ { ... };
+	parse_element(parser, "union",
+		parse_info(&new),
+		("name", &new.name),
+		("c:type", &new.c_type),
+		("c:symbol-prefix", null),
+		("glib:type-name", &new.glib_type_name),
+		("glib:get-type", &new.glib_get_type),
+		("copy-function", &new.copy_function),
+		("free-function", &new.free_function),
+
+		("field", &parse_field, &new.entries),
+		("constructor", &parse_constructor, &new.constructors),
+		("method", &parse_method, &new.methods),
+		("function", &parse_function, &new.functions),
+		("record", &parse_record_entry, &new.entries),
+	)?;
+	append(unions, new);
+};
+
+fn parse_union_entry(parser: *xml::parser, entries: *void) (void | error) = {
+	const entries = entries: *[]entry;
+	let unions: []union_ = [];
+	defer free(unions);
+	parse_union(parser, &unions)?;
+	append(entries, unions[0]);
+};
+
+fn parse_bitfield(parser: *xml::parser, bitfields: *void) (void | error) = {
+	const bitfields = bitfields: *[]bitfield;
+	let new = bitfield { ... };
+	parse_element(parser, "bitfield",
+		parse_info(&new),
+		("name", &new.name),
+		("c:type", &new.c_type),
+		("glib:type-name", &new.glib_type_name),
+		("glib:get-type", &new.glib_get_type),
+
+		("member", &parse_member, &new.members),
+		("function", &parse_function, &new.functions),
+	)?;
+	append(bitfields, new);
+};
+
+fn parse_callback(parser: *xml::parser, callbacks: *void) (void | error) = {
+	const callbacks = callbacks: *[]callback;
+	let new = callback { ... };
+	parse_one_callback(parser, &new)?;
+	append(callbacks, new);
+};
+fn parse_one_callback(parser: *xml::parser, cb: *void) (void | error) = {
+	const cb = cb: *callback;
+	return parse_element(parser, "callback",
+		parse_info(cb),
+		("name", &cb.name),
+		("c:type", &cb.c_type),
+		("throws", &cb.throws),
+
+		("parameters", &parse_parameters, &cb.params),
+		("return-value", &parse_return_value, &cb.return_value),
+	);
+};
+
+fn parse_constant(parser: *xml::parser, constants: *void) (void | error) = {
+	const constants = constants: *[]constant;
+	let new = constant { ... };
+	let type_: any_type = simple_type { ... };
+	parse_element(parser, "constant",
+		parse_info(&new),
+		("name", &new.name),
+		("value", &new.value),
+		("c:type", &new.c_type),
+		("c:identifier", &new.c_identifier),
+		parse_any_type(&type_),
+	)?;
+	if (type_ is array_type || len((type_ as simple_type).name) > 0) {
+		new.type_ = type_;
+	} else {
+		new.type_ = void;
+	};
+	append(constants, new);
+};
+
+fn parse_signal(parser: *xml::parser, signals: *void) (void | error) = {
+	const signals = signals: *[]signal;
+	let new = signal { ... };
+	parse_element(parser, "glib:signal",
+		parse_info(&new),
+		("name", &new.name),
+		("detailed", &new.detailed),
+		("when", &new.when),
+		("action", &new.action),
+		("no-hooks", &new.no_hooks),
+		("no-recurse", &new.no_recurse),
+		("emitter", &new.emitter),
+
+		("parameters", &parse_parameters, &new.params),
+		("return-value", &parse_return_value, &new.return_value),
+	)?;
+	append(signals, new);
+};
+
+// enum + bitfield
+fn parse_member(parser: *xml::parser, members: *void) (void | error) = {
+	const members = members: *[]member;
+	let new = member { ... };
+	parse_element(parser, "member",
+		parse_info(&new),
+		("name", &new.name),
+		("value", &new.value),
+		("c:identifier", &new.c_identifier),
+		("glib:nick", &new.glib_nick),
+		("glib:name", &new.glib_name),
+	)?;
+	append(members, new);
+};
+
+// callable
+fn parse_callable(callable: *callable) [17]scan = [
+	("introspectable", null),
+	("deprecated", null),
+	("deprecated-version", null),
+	("version", null),
+	("stability", null),
+	("doc", &parse_doc, &callable.doc),
+	("doc-version", null, null),
+	("doc-stability", null, null),
+	("doc-deprecated", null, null),
+	("source-position", null, null),
+	("attribute", null, null),
+	("name", &callable.name),
+	("c:identifier", &callable.c_identifier),
+	("shadowed-by", &callable.shadowed_by),
+	("shadows", &callable.shadows),
+	("throws", &callable.throws),
+	("moved-to", null),
+];
+fn parse_parameters(parser: *xml::parser, parameters: *void) (void | error) = {
+	return parse_element(parser, "parameters",
+		("parameter", &parse_parameter, parameters),
+		("varargs", &parse_varargs, parameters),
+	);
+};
+fn parse_method_parameters(parser: *xml::parser, parameters: *void) (void | error) = {
+	const parameters = parameters: *(*instance_parameter, *[]callable_param);
+	return parse_element(parser, "parameters",
+		("instance-parameter", &parse_instance_parameter, parameters.0),
+		("parameter", &parse_parameter, parameters.1),
+		("varargs", &parse_varargs, parameters.1),
+	);
+};
+fn parse_instance_parameter(parser: *xml::parser, param: *void) (void | error) = {
+	const param = param: *instance_parameter;
+	return parse_element(parser, "instance-parameter",
+		parse_info(param),
+		("name", &param.name),
+		("nullable", &param.is_nullable),
+		("allow-none", &param.allow_none),
+		("direction", &param.direction),
+		("caller-allocates", &param.caller_allocates),
+		("transfer-ownership", &param.transfer_ownership),
+
+		("type", &parse_simple_type, &param.type_),
+	);
+};
+fn parse_parameter(parser: *xml::parser, parameters: *void) (void | error) = {
+	const parameters = parameters: *[]callable_param;
+	let new = callable_param { ... };
+	let type_: any_type = simple_type { ... };
+	let has_varargs = false;
+	parse_element(parser, "parameter",
+		parse_info(&new),
+		("name", &new.name),
+		("nullable", &new.is_nullable),
+		("allow-none", &new.allow_none),
+		("introspectable", &new.introspectable),
+		("closure", &new.closure),
+		("destroy", &new.destroy),
+		("scope", &new.scope),
+		("direction", &new.direction),
+		("caller-allocates", &new.caller_allocates),
+		("optional", &new.optional),
+		("skip", &new.skip),
+		("transfer-ownership", &new.transfer_ownership),
+		("varargs", &parse_varargs, &has_varargs),
+		parse_any_type(&type_),
+	)?;
+	if (has_varargs) {
+		new.parameter = varargs;
+	} else {
+		new.parameter = type_;
+	};
+	append(parameters, new);
+};
+fn parse_varargs(parser: *xml::parser, has_varargs: *void) (void | error) = {
+	*(has_varargs: *bool) = true;
+	return parse_element(parser, "varargs");
+};
+fn parse_return_value(parser: *xml::parser, return_value: *void) (void | error) = {
+	const return_value = return_value: *(callable_return | void);
+	let new = callable_return { ... };
+	parse_element(parser, "return-value",
+		parse_info(&new),
+		("introspectable", &new.introspectable),
+		("nullable", &new.is_nullable),
+		("closure", &new.closure),
+		("destroy", &new.destroy),
+		("skip", &new.skip),
+		("allow-none", &new.allow_none),
+		("transfer-ownership", &new.transfer_ownership),
+		parse_any_type(&new.type_),
+	)?;
+	*return_value = new;
+};
+
+// documentation
+fn parse_info(doc: *documentation) [11]scan = [
+	("introspectable", null),
+	("deprecated", null),
+	("deprecated-version", null),
+	("version", null),
+	("stability", null),
+	("doc", &parse_doc, &doc.doc),
+	("doc-version", null, null),
+	("doc-stability", null, null),
+	("doc-deprecated", null, null),
+	("source-position", null, null),
+	("attribute", null, null),
+];
+fn parse_doc(parser: *xml::parser, text: *void) (void | error) = {
+	const text = text: *str;
+	return parse_element(parser, "doc",
+		("xml:space", null),
+		("xml:whitespace", null),
+		("filename", null),
+		("line", null),
+		("column", null),
+		text,
+	);
+};
+
+// types
+fn parse_simple_type(parser: *xml::parser, out: *void) (void | error) = {
+	const out = out: *simple_type;
+	return parse_element(parser, "type",
+		parse_info(out),
+		("name", &out.name),
+		("c:type", &out.c_type),
+		("introspectable", &out.introspectable),
+		("type", null, null),
+		("array", null, null),
+	);
+};
+
+fn parse_array_type(parser: *xml::parser, out: *void) (void | error) = {
+	const out = out: *array_type;
+	let inner: any_type = simple_type { ... };
+	parse_element(parser, "array",
+		parse_info(out),
+		("name", &out.name),
+		("zero-terminated", &out.zero_terminated),
+		("fixed-size", &out.fixed_size),
+		("introspectable", &out.introspectable),
+		("length", &out.length),
+		("c:type", &out.c_type),
+		parse_any_type(&inner),
+	)?;
+	out.inner = alloc(inner);
+};
+
+fn parse_any_type(out: *any_type) [2]scan = [
+	("type", &parse_simple_type_union, out),
+	("array", &parse_array_type_union, out),
+];
+fn parse_simple_type_union(parser: *xml::parser, out: *void) (void | error) = {
+	const out = out: *any_type;
+	let simple = simple_type { ... };
+	parse_simple_type(parser, &simple)?;
+	*out = simple;
+};
+fn parse_array_type_union(parser: *xml::parser, out: *void) (void | error) = {
+	const out = out: *any_type;
+	let array = array_type { ... };
+	parse_array_type(parser, &array)?;
+	*out = array;
+};
+
+type parse_func = fn(parser: *xml::parser, data: *void) (void | error);
+type scan_element = (
+	str,
+	nullable *parse_func,
+	nullable *void,
+);
+type scan_attribute = (
+	str,
+	(nullable *str | *uint | *bool),
+);
+type scan_text = *str;
+type scan = (scan_element | scan_attribute | scan_text | []scan);
+
+fn parse_element(
+	parser: *xml::parser,
+	current: str,
+	scans: scan...
+) (void | error) = {
+	let elements: []scan_element = [];
+	defer free(elements);
+	let attrs: []scan_attribute = [];
+	defer free(attrs);
+	let text_out: nullable *str = null;
+
+	for (let i = 0z; i < len(scans); i += 1) {
+		match (scans[i]) {
+		case let s: []scan =>
+			for (let j = 0z; j < len(s); j += 1) match (s[j]) {
+			case let s: scan_element =>
+				append(elements, s);
+			case let s: scan_attribute =>
+				append(attrs, s);
+			case let s: scan_text =>
+				assert(text_out is null, "only one scan_text is allowed");
+				text_out = s;
+			case => abort("too deep");
+			};
+		case let s: scan_element =>
+			append(elements, s);
+		case let s: scan_attribute =>
+			append(attrs, s);
+		case let s: scan_text =>
+			assert(text_out is null, "only one scan_text is allowed");
+			text_out = s;
+		};
+	};
+
+	for (true) match (xml::scan(parser)?) {
+	case let start: xml::elementstart =>
+		let found = false;
+		for (let i = 0z; i < len(elements); i += 1) {
+			const (name, func, data) = elements[i];
+			if (start == name) {
+				match (func) {
+				case let func: *parse_func =>
+					func(parser, data: *void)?;
+				case null =>
+					ignore(parser, name)?;
+				};
+				found = true;
+				break;
+			};
+		};
+		if (!found) {
+			fmt::fatalf("Unhandled element {}->{}", current, start);
+			//return invalid;
+		};
+	case let end: xml::elementend =>
+		assert(len(end) == 0 || end == current);
+		break;
+	case let attr: xml::attribute =>
+		let found = false;
+		for (let i = 0z; i < len(attrs); i += 1) {
+			const (name, out) = attrs[i];
+			if (attr.0 == name) {
+				match (out) {
+				case let s: nullable *str =>
+					match(s) {
+					case let s: *str =>
+						*s = strings::dup(attr.1);
+					case null => yield;
+					};
+				case let b: *bool =>
+					*b = (attr.1 == "1");
+				case let u: *uint =>
+					match (strconv::stou(attr.1)) {
+					case let parsed: uint =>
+						*u = parsed;
+					case =>
+						return invalid;
+					};
+				};
+				found = true;
+				break;
+			};
+		};
+		if (!found) {
+			fmt::fatalf("Unhandled attribute {}->{}", current, attr.0);
+			//return invalid;
+		};
+	case let text: xml::text =>
+		if (len(strings::trim(text)) > 0) {
+			match (text_out) {
+			case let s: *str =>
+				*s = strings::dup(text);
+			case null =>
+				yield;
+			};
+		};
+	case void =>
+		if (len(current) == 0) {
+			break;
+		};
+		return invalid;
+	};
+};
+
+fn ignore(parser: *xml::parser, name: str) (void | error) = {
+	const name = strings::dup(name);
+	defer free(name);
+
+	for (true) match (xml::scan(parser)?) {
+	case let start: xml::elementstart =>
+		ignore(parser, start)?;
+	case let end: xml::elementend =>
+		assert(end == name || end == "");
+		break;
+	case void =>
+		return invalid;
+	case => yield;
+	};
+};
diff --git a/gir/types.ha b/gir/types.ha
new file mode 100644
index 0000000..6616d69
--- /dev/null
+++ b/gir/types.ha
@@ -0,0 +1,363 @@
+export type repository = struct {
+	version: str,
+
+	includes: []include,
+	namespaces: []namespace,
+};
+export fn repository_finish(repo: *repository) void = {
+	free(repo.version);
+	for (let i = 0z; i < len(repo.includes); i += 1) {
+		include_finish(&repo.includes[i]);
+	};
+	for (let i = 0z; i < len(repo.namespaces); i += 1) {
+		namespace_finish(&repo.namespaces[i]);
+	};
+};
+
+export type include = struct {
+	name: str,
+	version: str,
+};
+export fn include_finish(incl: *include) void = {
+	free(incl.name);
+	free(incl.version);
+};
+
+export type namespace = struct {
+	name: str,
+	version: str,
+
+	aliases: []alias,
+	classes: []class,
+	interfaces: []interface,
+	records: []record,
+	enums: []enumeration,
+	functions: []function,
+	unions: []union_,
+	bitfields: []bitfield,
+	callbacks: []callback,
+	constants: []constant,
+};
+export fn namespace_finish(ns: *namespace) void = {
+	free(ns.name);
+	free(ns.version);
+	// TODO
+};
+
+export type alias = struct {
+	documentation,
+
+	name: str,
+	c_type: str,
+
+	inner: simple_type,
+};
+
+export type class = struct {
+	documentation,
+
+	name: str,
+	glib_type_name: str,
+	glib_get_type: str,
+	parent: str,
+	glib_ref_func: str,
+	glib_unref_func: str,
+	glib_set_value_func: str,
+	glib_get_value_func: str,
+	c_type: str,
+	symbol_prefix: str,
+	abstract: bool,
+	fundamental: bool,
+	final: bool,
+
+	entries: []entry,
+	implements: []str,
+	constructors: []constructor,
+	methods: []method,
+	functions: []function,
+	virtual_methods: []virtual_method,
+	properties: []property,
+	signals: []signal,
+	constants: []constant,
+	callbacks: []callback,
+};
+export type interface = struct {
+	documentation,
+
+	name: str,
+	glib_type_name: str,
+	glib_get_type: str,
+	symbol_prefix: str,
+	c_type: str,
+
+	entries: []entry,
+	prerequisites: []str,
+	implements: []str,
+	functions: []function,
+	constructors: []constructor,
+	methods: []method,
+	virtual_methods: []virtual_method,
+	properties: []property,
+	signals: []signal,
+	callbacks: []callback,
+	constants: []constant,
+};
+export type record = struct {
+	documentation,
+
+	name: str,
+	c_type: str,
+	disguised: bool,
+	opaque: bool,
+	pointer: bool,
+	glib_type_name: str,
+	glib_get_type: str,
+	symbol_prefix: str,
+	foreign: bool,
+	copy_function: str,
+	free_function: str,
+
+	entries: []entry,
+	functions: []function,
+	methods: []method,
+	constructors: []constructor,
+};
+
+// FIXME: find a better name for this
+export type entry = (field | union_ | record);
+
+export type constructor = struct {
+	callable,
+
+	params: []callable_param,
+	return_value: (callable_return | void),
+};
+export type method = struct {
+	callable,
+
+	glib_set_property: str,
+	glib_get_property: str,
+
+	instance: instance_parameter,
+	params: []callable_param,
+	return_value: (callable_return | void),
+};
+export type virtual_method = struct {
+	callable,
+
+	invoker: str,
+
+	instance: instance_parameter,
+	params: []callable_param,
+	return_value: (callable_return | void),
+};
+export type field = struct {
+	documentation,
+
+	name: str,
+	writable: bool,
+	readable: bool,
+	private: bool,
+	bits: uint,
+
+	type_: (callback | any_type),
+};
+export type property = struct {
+	documentation,
+
+	name: str,
+	writable: bool,
+	readable: bool,
+	construct: bool,
+	construct_only: bool,
+	setter: str,
+	getter: str,
+	default_value: str,
+	transfer_ownership: str,
+
+	type_: any_type,
+};
+
+export type enumeration = struct {
+	documentation,
+
+	name: str,
+	c_type: str,
+	glib_type_name: str,
+	glib_get_type: str,
+	glib_error_domain: str,
+
+	members: []member,
+	functions: []function,
+};
+
+export type function = struct {
+	callable,
+
+	params: []callable_param,
+	return_value: (callable_return | void),
+};
+
+export type union_ = struct {
+	documentation,
+
+	name: str,
+	c_type: str,
+	symbol_prefix: str,
+	glib_type_name: str,
+	glib_get_type: str,
+	copy_function: str,
+	free_function: str,
+
+	entries: []entry,
+	constructors: []constructor,
+	methods: []method,
+	functions: []function,
+};
+
+export type bitfield = struct {
+	documentation,
+
+	name: str,
+	c_type: str,
+	glib_type_name: str,
+	glib_get_type: str,
+
+	members: []member,
+	functions: []function,
+};
+
+export type callback = struct {
+	documentation,
+
+	name: str,
+	c_type: str,
+	throws: bool,
+
+	params: []callable_param,
+	return_value: (callable_return | void),
+};
+
+export type constant = struct {
+	documentation,
+
+	name: str,
+	value: str,
+	c_type: str,
+	c_identifier: str,
+
+	type_: (any_type | void),
+};
+
+export type signal = struct {
+	documentation,
+
+	name: str,
+	detailed: bool,
+	when: str,
+	action: bool,
+	no_hooks: bool,
+	no_recurse: bool,
+	emitter: str,
+
+	params: []callable_param,
+	return_value: (callable_return | void),
+};
+
+export type member = struct {
+	documentation,
+
+	name: str,
+	value: str,
+	c_identifier: str,
+	glib_nick: str,
+	glib_name: str,
+};
+
+export type callable = struct {
+	documentation,
+
+	name: str,
+	c_identifier: str,
+	shadowed_by: str,
+	shadows: str,
+	throws: bool,
+};
+
+export type callable_param = struct {
+	documentation,
+
+	name: str,
+	is_nullable: bool,
+	allow_none: bool,
+	introspectable: bool,
+	closure: uint,
+	destroy: uint,
+	scope: str,
+	direction: str,
+	caller_allocates: bool,
+	optional: bool,
+	skip: bool,
+	transfer_ownership: str,
+
+	parameter: (any_type | varargs),
+};
+
+export type instance_parameter = struct {
+	documentation,
+
+	name: str,
+	is_nullable: bool,
+	allow_none: bool,
+	direction: str,
+	caller_allocates: str,
+	transfer_ownership: str,
+
+	type_: simple_type,
+};
+
+export type varargs = void;
+
+export type callable_return = struct {
+	documentation,
+
+	introspectable: bool,
+	is_nullable: bool,
+	closure: uint,
+	scope: str,
+	destroy: uint,
+	skip: bool,
+	allow_none: bool,
+	transfer_ownership: str,
+
+	type_: any_type,
+};
+
+export type documentation = struct {
+	doc: str,
+};
+
+export type simple_type = struct {
+	documentation,
+
+	name: str,
+	c_type: str,
+	introspectable: bool,
+
+	inner: []any_type,
+};
+
+export type array_type = struct {
+	documentation,
+
+	name: str,
+	zero_terminated: bool,
+	fixed_size: uint,
+	introspectable: bool,
+	length: uint,
+	c_type: str,
+
+	inner: *any_type,
+};
+
+export type any_type = (simple_type | array_type);