// XMLParser.swift // GIR XML → IR. The delegate maintains an explicit element stack so that every // element attaches to its true parent, and nested constructs (container element // types, callback-typed parameters, virtual-method bodies) nest correctly // instead of leaking into whatever `current*` slot happened to be set. import Foundation #if canImport(FoundationXML) import FoundationXML #endif /// Errors that can occur during GIR XML parsing. public enum GIRParserError: Error { /// The XML document is malformed or could not be parsed. /// - Parameter description: A human-readable error description. case invalidXML(String) /// An unexpected XML element was encountered. /// - Parameter description: Details about the unexpected element. case unexpectedElement(String) /// A required attribute is missing from an XML element. /// - Parameter description: Description of the missing attribute. case missingAttribute(String) } /// Parses GIR XML files into the intermediate representation model. /// /// Uses Foundation's `XMLParser` in a SAX-style delegate pattern, driving an /// explicit element stack. All GIR element types are handled: classes, /// interfaces, records, enumerations, bitfields, callbacks, methods, /// properties, signals, functions, constants, and aliases. /// /// The parser is deliberately non-judgemental: it records what the GIR says, /// including symbols that cannot be bound. Deciding what to *bind* is the /// planner's job, which reports skips with reasons. /// /// ### Example /// ```swift /// let parser = GIRParser() /// let repo = try parser.parse(fileURL: URL(fileURLWithPath: "/usr/share/gir-1.0/GLib-2.0.gir")) /// print(repo.namespaces.first?.classes.count ?? 0) /// ``` public struct GIRParser { /// Creates a new GIR parser. public init() {} /// Parses a GIR XML document from a string. /// - Parameter xmlString: The XML content to parse. /// - Returns: A `Repository` containing all parsed namespaces and types. /// - Throws: `GIRParserError` if the XML cannot be encoded as UTF-8 or parsing fails. public func parse(xmlString: String) throws -> Repository { guard let data = xmlString.data(using: .utf8) else { throw GIRParserError.invalidXML("Failed to encode XML string as UTF-8") } return try parse(data: data) } /// Parses a GIR XML document from a file URL. /// - Parameter fileURL: The URL of the .gir file to parse. /// - Returns: A `Repository` containing all parsed namespaces and types. /// - Throws: `GIRParserError` if the file cannot be read or parsing fails. public func parse(fileURL: URL) throws -> Repository { let data = try Data(contentsOf: fileURL) return try parse(data: data) } /// Parses raw XML data using a SAX-style delegate. /// - Parameter data: The XML data to parse. /// - Returns: A `Repository` with the parsed content. /// - Throws: `GIRParserError` if parsing fails. private func parse(data: Data) throws -> Repository { let delegate = GIRXMLDelegate() let parser = XMLParser(data: data) parser.delegate = delegate if parser.parse(), delegate.parseError == nil { return delegate.repository } throw delegate.parseError ?? GIRParserError.invalidXML(parser.parserError?.localizedDescription ?? "unknown error") } } // MARK: - Element Stack /// A partially-built `` element. /// /// Nested `` children supply container element types, e.g. /// ``. struct TypeBuilder { /// The GIR type name attribute, e.g. `"utf8"` or `"GLib.List"`. var name: String /// The `c:type` attribute, e.g. `"gchar*"`. var cType: String /// Element types collected from nested `` children. var children: [GIRType] = [] } /// A partially-built `` element. struct ArrayBuilder { /// The array's `name` attribute; set for GLib container arrays. var name: String? /// Length metadata gathered from the element's attributes. var info: ArrayInfo /// The element type, from the nested `` or `` child. var children: [GIRType] = [] } /// One open XML element during parsing. /// /// The delegate pushes a frame on every element it tracks and pops it on close, /// attaching the completed value to the frame beneath. Untracked elements push /// ``ignored`` so that their children attach to nothing rather than leaking into /// an ancestor. indirect enum Frame { case namespace(Namespace) case klass(Class) case interface(Interface) case record(Record) case enumeration(Enumeration) case bitfield(Bitfield) case callback(Callback) case constructor(Constructor) case method(Method) case function(GlobalFunction) case signal(Signal) case property(Property) case parameterList([Parameter]) case parameter(Parameter) case returnValue(ReturnValue) case field(Field) case constant(Constant) case alias(Alias) case type(TypeBuilder) case array(ArrayBuilder) case doc /// An element whose content is deliberately discarded (virtual methods, /// unions, source positions, and anything else not modelled). case ignored(String) } // MARK: - XMLParser Delegate /// SAX-style delegate for `XMLParser` that builds a `Repository` from GIR XML. /// /// Maintains an explicit ``Frame`` stack: `didStartElement` pushes a frame, /// `didEndElement` pops it and attaches the finished value to its parent. This /// makes parent-child relationships exact, which flat `current*` slots could not /// express for nested constructs. final class GIRXMLDelegate: NSObject, XMLParserDelegate { /// The repository being populated during parsing. var repository = Repository() /// Set to the first error encountered, or `nil` if parsing succeeds. var parseError: GIRParserError? /// The stack of currently-open elements, outermost first. private var stack: [Frame] = [] /// Accumulated character data for the innermost `` element. private var currentText: String = "" // MARK: Element start func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName: String?, attributes attributeDict: [String: String] = [:]) { switch elementName { case "c:include": if let name = attributeDict["name"] { repository.cHeaderPath = name } stack.append(.ignored(elementName)) case "package": if let name = attributeDict["name"] { repository.packageName = name } stack.append(.ignored(elementName)) case "include": // GIR dependency includes appear before the element. // Derive the link name as lowercase(library) + "-" + major version, // e.g. "Gdk-4.0" → "gdk-4". if let name = attributeDict["name"], let version = attributeDict["version"] { let majorVersion = version.split(separator: ".").first.map(String.init) ?? version repository.includedLibraryLinks.append("\(name.lowercased())-\(majorVersion)") repository.includedPackages.append(IncludeEntry(name: name, version: version)) } stack.append(.ignored(elementName)) case "namespace": guard let name = requireAttribute("name", from: attributeDict, for: elementName, parser: parser), let version = requireAttribute("version", from: attributeDict, for: elementName, parser: parser) else { return } stack.append(.namespace(Namespace( name: name, version: version, cSharedLibrary: attributeDict["shared-library"] ?? "", cIdentifierPrefix: attributeDict["c:identifier-prefixes"] ?? "" ))) case "class": guard let name = requireAttribute("name", from: attributeDict, for: elementName, parser: parser) else { return } stack.append(.klass(Class( name: name, cType: attributeDict["c:type"] ?? "", parent: attributeDict["parent"], isAbstract: attributeDict["abstract"] == "1", isFinal: attributeDict["final"] == "1", getTypeFunction: attributeDict["glib:get-type"], typeName: attributeDict["glib:type-name"], symbolInfo: Self.symbolInfo(from: attributeDict), refFunc: attributeDict["glib:ref-func"], unrefFunc: attributeDict["glib:unref-func"] ))) case "interface": guard let name = requireAttribute("name", from: attributeDict, for: elementName, parser: parser) else { return } stack.append(.interface(Interface( name: name, cType: attributeDict["c:type"] ?? "", getTypeFunction: attributeDict["glib:get-type"], typeName: attributeDict["glib:type-name"], symbolInfo: Self.symbolInfo(from: attributeDict) ))) case "prerequisite": // GIR spells prerequisites as child elements, not an attribute. if let name = attributeDict["name"] { mutateTop { if case .interface(var iface) = $0 { iface.prereqs.append(name); $0 = .interface(iface) } } } stack.append(.ignored(elementName)) case "record": guard let name = attributeDict["name"] else { // Anonymous nested (a C struct inside a , e.g. // GskPathPoint). Not a namespace-level type; discard it and its // fields — record fields are deferred and the parent union is // already .ignored. stack.append(.ignored(elementName)) return } stack.append(.record(Record( name: name, cType: attributeDict["c:type"] ?? "", isOpaque: attributeDict["opaque"] == "1", isDisguised: attributeDict["disguised"] == "1", isGTypeStructFor: attributeDict["glib:is-gtype-struct-for"], getTypeFunction: attributeDict["glib:get-type"], typeName: attributeDict["glib:type-name"], copyFunction: attributeDict["copy-function"], freeFunction: attributeDict["free-function"], symbolInfo: Self.symbolInfo(from: attributeDict) ))) case "enumeration": guard let name = requireAttribute("name", from: attributeDict, for: elementName, parser: parser) else { return } stack.append(.enumeration(Enumeration( name: name, cType: attributeDict["c:type"] ?? "", getTypeFunction: attributeDict["glib:get-type"], typeName: attributeDict["glib:type-name"], errorDomain: attributeDict["glib:error-domain"], symbolInfo: Self.symbolInfo(from: attributeDict) ))) case "bitfield": guard let name = requireAttribute("name", from: attributeDict, for: elementName, parser: parser) else { return } stack.append(.bitfield(Bitfield( name: name, cType: attributeDict["c:type"] ?? "", getTypeFunction: attributeDict["glib:get-type"], typeName: attributeDict["glib:type-name"], symbolInfo: Self.symbolInfo(from: attributeDict) ))) case "member": guard let name = requireAttribute("name", from: attributeDict, for: elementName, parser: parser), let value = requireAttribute("value", from: attributeDict, for: elementName, parser: parser), let cid = requireAttribute("c:identifier", from: attributeDict, for: elementName, parser: parser) else { return } let member = EnumMember(name: name, value: value, cIdentifier: cid) mutateTop { switch $0 { case .enumeration(var e): e.members.append(member); $0 = .enumeration(e) case .bitfield(var b): b.members.append(member); $0 = .bitfield(b) default: break } } stack.append(.ignored(elementName)) case "callback": guard let name = requireAttribute("name", from: attributeDict, for: elementName, parser: parser) else { return } stack.append(.callback(Callback( name: name, cType: attributeDict["c:type"] ?? "", throwsGError: attributeDict["throws"] == "1", symbolInfo: Self.symbolInfo(from: attributeDict) ))) case "constructor": guard let name = requireAttribute("name", from: attributeDict, for: elementName, parser: parser), let cid = requireAttribute("c:identifier", from: attributeDict, for: elementName, parser: parser) else { return } stack.append(.constructor(Constructor( name: name, cIdentifier: cid, throwsGError: attributeDict["throws"] == "1", symbolInfo: Self.symbolInfo(from: attributeDict) ))) case "method": guard let name = requireAttribute("name", from: attributeDict, for: elementName, parser: parser), let cid = requireAttribute("c:identifier", from: attributeDict, for: elementName, parser: parser) else { return } stack.append(.method(Method( name: name, cIdentifier: cid, throwsGError: attributeDict["throws"] == "1", symbolInfo: Self.symbolInfo(from: attributeDict) ))) case "function": guard let name = requireAttribute("name", from: attributeDict, for: elementName, parser: parser), let cid = requireAttribute("c:identifier", from: attributeDict, for: elementName, parser: parser) else { return } stack.append(.function(GlobalFunction( name: name, cIdentifier: cid, throwsGError: attributeDict["throws"] == "1", symbolInfo: Self.symbolInfo(from: attributeDict) ))) case "signal", "glib:signal": guard let name = attributeDict["name"] ?? attributeDict["glib:name"] else { parseError = .missingAttribute("Missing 'name' on <\(elementName)>") parser.abortParsing() return } stack.append(.signal(Signal( name: name, isDetailed: attributeDict["detailed"] == "1", symbolInfo: Self.symbolInfo(from: attributeDict) ))) case "property": guard let name = requireAttribute("name", from: attributeDict, for: elementName, parser: parser) else { return } stack.append(.property(Property( name: name, type: .void, isReadable: attributeDict["readable"] != "0", isWritable: attributeDict["writable"] == "1", isConstructOnly: attributeDict["construct-only"] == "1", isNullable: attributeDict["nullable"] == "1", transferOwnership: TransferOwnership(rawValue: attributeDict["transfer-ownership"] ?? "none") ?? .none, getter: attributeDict["getter"], setter: attributeDict["setter"], symbolInfo: Self.symbolInfo(from: attributeDict) ))) case "parameters": stack.append(.parameterList([])) case "parameter", "instance-parameter": guard let name = requireAttribute("name", from: attributeDict, for: elementName, parser: parser) else { return } stack.append(.parameter(Parameter( name: name, type: .void, cType: attributeDict["c:type"] ?? "", isNullable: attributeDict["nullable"] == "1", isOptional: attributeDict["optional"] == "1", transferOwnership: TransferOwnership(rawValue: attributeDict["transfer-ownership"] ?? "none") ?? .none, isInstanceParameter: elementName == "instance-parameter", direction: ParameterDirection(rawValue: attributeDict["direction"] ?? "in") ?? .in, callerAllocates: attributeDict["caller-allocates"] == "1", scope: attributeDict["scope"].flatMap(CallbackScope.init(rawValue:)), closureIndex: attributeDict["closure"].flatMap(Int.init), destroyIndex: attributeDict["destroy"].flatMap(Int.init) ))) case "return-value": stack.append(.returnValue(ReturnValue( type: .void, isNullable: attributeDict["nullable"] == "1", transferOwnership: TransferOwnership(rawValue: attributeDict["transfer-ownership"] ?? "none") ?? .none ))) case "type": stack.append(.type(TypeBuilder( name: attributeDict["name"] ?? "none", cType: attributeDict["c:type"] ?? "" ))) case "array": stack.append(.array(ArrayBuilder( name: attributeDict["name"], info: ArrayInfo( lengthParameterIndex: attributeDict["length"].flatMap(Int.init), fixedSize: attributeDict["fixed-size"].flatMap(Int.init), isZeroTerminated: attributeDict["zero-terminated"].map { $0 == "1" } ?? (attributeDict["length"] == nil && attributeDict["fixed-size"] == nil), cType: attributeDict["c:type"] ?? "" ) ))) case "constant": guard let name = requireAttribute("name", from: attributeDict, for: elementName, parser: parser), let value = requireAttribute("value", from: attributeDict, for: elementName, parser: parser) else { return } stack.append(.constant(Constant(name: name, value: value, type: .void))) case "alias": guard let name = requireAttribute("name", from: attributeDict, for: elementName, parser: parser), let cType = requireAttribute("c:type", from: attributeDict, for: elementName, parser: parser) else { return } stack.append(.alias(Alias(name: name, cType: cType, target: .void))) case "implements": if let name = attributeDict["name"] { mutateTop { if case .klass(var cls) = $0 { cls.implements.append(name); $0 = .klass(cls) } } } stack.append(.ignored(elementName)) case "field": guard let name = requireAttribute("name", from: attributeDict, for: elementName, parser: parser) else { return } stack.append(.field(Field( name: name, type: .void, isReadable: attributeDict["readable"] != "0", isWritable: attributeDict["writable"] == "1" ))) case "doc": currentText = "" stack.append(.doc) default: stack.append(.ignored(elementName)) } } // MARK: Element end func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName: String?) { guard let frame = stack.popLast() else { return } switch frame { case .namespace(let ns): repository.namespaces.append(ns) case .klass(let cls): mutateTop { if case .namespace(var ns) = $0 { ns.classes.append(cls); $0 = .namespace(ns) } } case .interface(let iface): mutateTop { if case .namespace(var ns) = $0 { ns.interfaces.append(iface); $0 = .namespace(ns) } } case .record(let record): mutateTop { if case .namespace(var ns) = $0 { ns.records.append(record); $0 = .namespace(ns) } } case .enumeration(let e): mutateTop { if case .namespace(var ns) = $0 { ns.enumerations.append(e); $0 = .namespace(ns) } } case .bitfield(let b): mutateTop { if case .namespace(var ns) = $0 { ns.bitfields.append(b); $0 = .namespace(ns) } } case .callback(let cb): // Namespace-level callbacks are bindable types in their own right. // A callback nested in a is a function-pointer member of a // C struct (vtables such as GIOFuncs): at the ABI level that field // is exactly an opaque pointer, which is what it is recorded as. // Callbacks nested anywhere else describe a signature in place and // are not separate declarations. mutateTop { switch $0 { case .namespace(var ns): ns.callbacks.append(cb); $0 = .namespace(ns) case .field(var f): f = Field(name: f.name, type: .pointer, isReadable: f.isReadable, isWritable: f.isWritable, doc: f.doc) $0 = .field(f) default: break } } case .constructor(let ctor): mutateTop { switch $0 { case .klass(var cls): cls.constructors.append(ctor); $0 = .klass(cls) case .record(var rec): rec.constructors.append(ctor); $0 = .record(rec) default: break } } case .method(let method): mutateTop { switch $0 { case .klass(var cls): cls.methods.append(method); $0 = .klass(cls) case .interface(var iface): iface.methods.append(method); $0 = .interface(iface) case .record(var rec): rec.methods.append(method); $0 = .record(rec) default: break } } case .function(let fn): mutateTop { switch $0 { case .klass(var cls): cls.functions.append(fn); $0 = .klass(cls) case .interface(var iface): iface.functions.append(fn); $0 = .interface(iface) case .record(var rec): rec.functions.append(fn); $0 = .record(rec) case .namespace(var ns): ns.functions.append(fn); $0 = .namespace(ns) default: break } } case .signal(let sig): mutateTop { switch $0 { case .klass(var cls): cls.signals.append(sig); $0 = .klass(cls) case .interface(var iface): iface.signals.append(sig); $0 = .interface(iface) default: break } } case .property(let prop): mutateTop { switch $0 { case .klass(var cls): cls.properties.append(prop); $0 = .klass(cls) case .interface(var iface): iface.properties.append(prop); $0 = .interface(iface) default: break } } case .parameterList(let params): mutateTop { switch $0 { case .method(var m): m.parameters = params; $0 = .method(m) case .constructor(var c): c.parameters = params; $0 = .constructor(c) case .function(var f): f.parameters = params; $0 = .function(f) case .signal(var s): s.parameters = params; $0 = .signal(s) case .callback(var cb): cb.parameters = params; $0 = .callback(cb) default: break } } case .parameter(let param): mutateTop { if case .parameterList(var list) = $0 { list.append(param); $0 = .parameterList(list) } } case .returnValue(let rv): mutateTop { switch $0 { case .method(var m): m.returnValue = rv; $0 = .method(m) case .constructor(var c): c.returnValue = rv; $0 = .constructor(c) case .function(var f): f.returnValue = rv; $0 = .function(f) case .signal(var s): s.returnValue = rv; $0 = .signal(s) case .callback(var cb): cb.returnValue = rv; $0 = .callback(cb) default: break } } case .field(let field): mutateTop { if case .record(var rec) = $0 { rec.fields.append(field); $0 = .record(rec) } } case .constant(let c): mutateTop { if case .namespace(var ns) = $0 { ns.constants.append(c); $0 = .namespace(ns) } } case .alias(let a): mutateTop { if case .namespace(var ns) = $0 { ns.aliases.append(a); $0 = .namespace(ns) } } case .type(let builder): assignType(Self.buildType(from: builder), cType: builder.cType) case .array(let builder): let element = builder.children.first ?? .pointer if let name = builder.name, let kind = Self.containerKind(forTypeName: name) { assignType(.container(kind, elements: builder.children), cType: builder.info.cType) } else { assignType(.cArray(element, builder.info), cType: builder.info.cType) } case .doc: assignDoc(currentText.trimmingCharacters(in: .whitespacesAndNewlines)) currentText = "" case .ignored: break } } func parser(_ parser: XMLParser, foundCharacters string: String) { currentText += string } // MARK: - Attachment helpers /// Mutates the innermost open frame in place. /// /// - Parameter body: A closure receiving the top frame for mutation. Not /// called when the stack is empty. private func mutateTop(_ body: (inout Frame) -> Void) { guard !stack.isEmpty else { return } body(&stack[stack.count - 1]) } /// Assigns a resolved type to whichever construct encloses it. /// /// Handles every element that has a `` or `` child: parameters, /// return values, properties, constants, aliases, record fields, and nested /// container/array element types. /// /// - Parameters: /// - type: The resolved GIR type. /// - cType: The `c:type` spelling, applied to parameters when non-empty. private func assignType(_ type: GIRType, cType: String) { mutateTop { switch $0 { case .parameter(var p): p.type = type if !cType.isEmpty { p.cType = cType } $0 = .parameter(p) case .returnValue(var rv): rv.type = type if !cType.isEmpty { rv.cType = cType } $0 = .returnValue(rv) case .property(var prop): prop.type = type $0 = .property(prop) case .constant(var c): c.type = type $0 = .constant(c) case .alias(var a): a.target = type $0 = .alias(a) case .field(var f): // Field is immutable in the IR, so rebuild it with the type // that only becomes known when the child closes. f = Field(name: f.name, type: type, isReadable: f.isReadable, isWritable: f.isWritable, doc: f.doc) $0 = .field(f) case .type(var builder): builder.children.append(type) $0 = .type(builder) case .array(var builder): if builder.children.isEmpty { builder.info.elementCType = cType } builder.children.append(type) $0 = .array(builder) default: break } } } /// Attaches documentation text to whichever construct encloses the ``. /// /// - Parameter text: The trimmed documentation text; ignored when empty. private func assignDoc(_ text: String) { guard !text.isEmpty else { return } mutateTop { switch $0 { case .klass(var x): x.doc = text; $0 = .klass(x) case .interface(var x): x.doc = text; $0 = .interface(x) case .record(var x): x.doc = text; $0 = .record(x) case .enumeration(var x): x.doc = text; $0 = .enumeration(x) case .bitfield(var x): x.doc = text; $0 = .bitfield(x) case .callback(var x): x.doc = text; $0 = .callback(x) case .constructor(var x): x.doc = text; $0 = .constructor(x) case .method(var x): x.doc = text; $0 = .method(x) case .function(var x): x.doc = text; $0 = .function(x) case .signal(var x): x.doc = text; $0 = .signal(x) case .property(var x): x.doc = text; $0 = .property(x) case .parameter(var x): x.doc = text; $0 = .parameter(x) case .returnValue(var x): x.doc = text; $0 = .returnValue(x) case .constant(var x): x.doc = text; $0 = .constant(x) case .alias(var x): x.doc = text; $0 = .alias(x) case .field(var x): x = Field(name: x.name, type: x.type, isReadable: x.isReadable, isWritable: x.isWritable, doc: text) $0 = .field(x) default: break } } } /// Requires that an attribute exists, or aborts parsing with an error. /// - Parameters: /// - key: The attribute name to look up. /// - dict: The attribute dictionary from the current XML element. /// - element: The name of the XML element (for error messages). /// - parser: The XML parser to abort on failure. /// - Returns: The attribute value, or `nil` if the attribute is missing. private func requireAttribute(_ key: String, from dict: [String: String], for element: String, parser: XMLParser) -> String? { guard let value = dict[key] else { parseError = .missingAttribute("Missing '\(key)' on <\(element)>") parser.abortParsing() return nil } return value } // MARK: - Type mapping /// Extracts the binding-relevant GIR metadata common to all symbols. /// /// - Parameter dict: The element's attribute dictionary. /// - Returns: The parsed ``SymbolInfo``. static func symbolInfo(from dict: [String: String]) -> SymbolInfo { SymbolInfo( isIntrospectable: dict["introspectable"] != "0", isDeprecated: dict["deprecated"] == "1", deprecatedVersion: dict["deprecated-version"], shadowedBy: dict["shadowed-by"], shadows: dict["shadows"], movedTo: dict["moved-to"] ) } /// Builds a `GIRType` from a completed `` element. /// /// Container type names (`GLib.List`, `GLib.HashTable`, …) become /// ``GIRType/container(_:elements:)`` carrying their nested element types; /// everything else maps through ``girType(forName:)``. /// /// - Parameter builder: The accumulated `` element state. /// - Returns: The resolved GIR type. static func buildType(from builder: TypeBuilder) -> GIRType { if let kind = containerKind(forTypeName: builder.name) { return .container(kind, elements: builder.children) } return girType(forName: builder.name) } /// Maps a GIR container type name to its ``ContainerKind``. /// /// - Parameter name: A GIR type name, e.g. `"GLib.List"`. /// - Returns: The container kind, or `nil` if the name is not a container. static func containerKind(forTypeName name: String) -> ContainerKind? { switch name { case "GLib.List": return .list case "GLib.SList": return .slist case "GLib.HashTable": return .hashTable case "GLib.Array": return .array case "GLib.PtrArray": return .ptrArray case "GLib.ByteArray": return .byteArray default: return nil } } /// Maps a GIR type name string to the corresponding `GIRType` case. /// /// Recognizes the full set of GLib primitive names and dotted /// namespace-qualified references (e.g. `Gtk.Widget`). Unknown names become /// an unqualified `.typeRef`, which the ``TypeRegistry`` later resolves — /// or reports as unresolvable, rather than fabricating a Swift type. /// /// - Parameter name: The GIR type name (e.g. `"gint32"`, `"utf8"`, `"Gtk.Widget"`). /// - Returns: The corresponding `GIRType` value. static func girType(forName name: String) -> GIRType { switch name { case "none": return .void case "gboolean": return .boolean case "gint8": return .int8 case "gint16": return .int16 case "gint", "gint32": return .int32 case "gint64": return .int64 case "guint8": return .uint8 case "guint16": return .uint16 case "guint", "guint32": return .uint32 case "guint64": return .uint64 case "glong", "gintptr", "time_t": return .long case "gulong", "guintptr": return .ulong case "gsize": return .size case "gssize", "goffset": return .ssize case "gchar": return .char case "gshort": return .int16 case "guchar": return .uchar case "gushort": return .uint16 case "gunichar": return .unichar case "gunichar2": return .uint16 case "GType": return .gtype case "gfloat": return .float case "gdouble": return .double case "utf8": return .string case "filename": return .filename case "gpointer", "gconstpointer": return .pointer case "va_list": return .vaList default: if let dotIndex = name.firstIndex(of: ".") { let ns = String(name[..