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: *opaque) (void | error) = { const repo = repo: *repository; return parse_element(parser, "repository", ("xmlns", null), ("xmlns:c", null), ("xmlns:glib", null), ("xmlns:doc", null), ("doc:format", null, 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: *opaque) (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: *opaque) (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), ("function-inline", null, null), ("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: *opaque) (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: *opaque) (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), ("method-inline", null, null), ("function", &parse_function, &new.functions), ("function-inline", null, null), ("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: *opaque) (void | error) = { const implements = implements: *[]str; let name = ""; parse_element(parser, "implements", ("name", &name), )?; append(implements, name)!; }; fn parse_interface(parser: *xml::parser, interfaces: *opaque) (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), ("function-inline", null, null), ("constructor", &parse_constructor, &new.constructors), ("method", &parse_method, &new.methods), ("method-inline", null, null), ("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: *opaque) (void | error) = { const prerequisites = prerequisites: *[]str; let name = ""; parse_element(parser, "prerequisite", ("name", &name), )?; append(prerequisites, name)!; }; fn parse_record(parser: *xml::parser, records: *opaque) (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), ("function-inline", null, null), ("union", &parse_union_entry, &new.entries), ("method", &parse_method, &new.methods), ("method-inline", null, null), ("constructor", &parse_constructor, &new.constructors), )?; append(records, new)!; }; fn parse_record_entry(parser: *xml::parser, entries: *opaque) (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: *opaque) (void | error) = { const constructors = constructors: *[]constructor; let new = constructor { return_value = void, ... }; 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: *opaque) (void | error) = { const methods = methods: *[]method; let new = method { return_value = void, ... }; 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: *opaque) (void | error) = { const vmethods = vmethods: *[]virtual_method; let new = virtual_method { return_value = void, ... }; 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: *opaque) (void | error) = { const entries = entries: *[]entry; let new = field { type_ = simple_type { ... }, ... }; let type_: any_type = simple_type { ... }; let cb = callback { return_value = void, ... }; 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: *opaque) (void | error) = { const properties = properties: *[]property; let new = property { type_ = simple_type { ... }, ... }; 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: *opaque) (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), ("function-inline", null, null), )?; append(enums, new)!; }; fn parse_function(parser: *xml::parser, functions: *opaque) (void | error) = { const functions = functions: *[]function; let new = function { return_value = void, ... }; 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: *opaque) (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), ("method-inline", null, null), ("function", &parse_function, &new.functions), ("function-inline", null, null), ("record", &parse_record_entry, &new.entries), )?; append(unions, new)!; }; fn parse_union_entry(parser: *xml::parser, entries: *opaque) (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: *opaque) (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), ("function-inline", null, null), )?; append(bitfields, new)!; }; fn parse_callback(parser: *xml::parser, callbacks: *opaque) (void | error) = { const callbacks = callbacks: *[]callback; let new = callback { return_value = void, ... }; parse_one_callback(parser, &new)?; append(callbacks, new)!; }; fn parse_one_callback(parser: *xml::parser, cb: *opaque) (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: *opaque) (void | error) = { const constants = constants: *[]constant; let new = constant { type_ = void, ... }; 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: *opaque) (void | error) = { const signals = signals: *[]signal; let new = signal { return_value = void, ... }; 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: *opaque) (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) [20]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), ("glib:async-func", null), ("glib:sync-func", null), ("glib:finish-func", null), ]; fn parse_parameters(parser: *xml::parser, parameters: *opaque) (void | error) = { return parse_element(parser, "parameters", ("parameter", &parse_parameter, parameters), ("varargs", &parse_varargs, parameters), ); }; fn parse_method_parameters(parser: *xml::parser, parameters: *opaque) (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: *opaque) (void | error) = { const param = param: *instance_parameter; return parse_element(parser, "instance-parameter", parse_info(param), ("name", ¶m.name), ("nullable", ¶m.is_nullable), ("allow-none", ¶m.allow_none), ("direction", ¶m.direction), ("caller-allocates", ¶m.caller_allocates), ("transfer-ownership", ¶m.transfer_ownership), ("type", &parse_simple_type, ¶m.type_), ); }; fn parse_parameter(parser: *xml::parser, parameters: *opaque) (void | error) = { const parameters = parameters: *[]callable_param; let new = callable_param { parameter = simple_type { ... }, ... }; 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: *opaque) (void | error) = { *(has_varargs: *bool) = true; return parse_element(parser, "varargs"); }; fn parse_return_value(parser: *xml::parser, return_value: *opaque) (void | error) = { const return_value = return_value: *(callable_return | void); let new = callable_return { type_ = simple_type { ... }, ... }; 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: *opaque) (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: *opaque) (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: *opaque) (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: *opaque) (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: *opaque) (void | error) = { const out = out: *any_type; let array = array_type { inner = null: *any_type, ... }; parse_array_type(parser, &array)?; *out = array; }; type parse_func = nullable *fn(parser: *xml::parser, data: *opaque) (void | error); type scan_element = ( str, parse_func, nullable *opaque, ); 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: *fn(_p: *xml::parser, _d: *opaque) (void | error) => func(parser, data: *opaque)?; 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; }; };