Cogl bindings preview.
[girtod.git] / girtod.d
blob25d304a15ad7b4af9371e5e521a616b8919b264e
2 // Copyright Artur Skawina 2012.
3 // Distributed under the Boost Software License, Version 1.0.
4 // (See accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
8 private:
10 import std.array;
11 import std.string;
12 import std.stdio;
13 import std.xml;
14 import std.conv;
15 import std.algorithm;
16 import std.exception;
18 // Compile in tracing support? (also configurable at runtime)
19 debug = TRACE;
21 ////////////////////////////////////////////////////////////
23 enum PACKAGE_NAME = "gtk2";
24 // Module naming notes:
25 // - The filenames of modules needs to be lowercase, to work with
26 // broken filesystems.
27 // - D's module system is primitive, so this was designed to keep things
28 // as simple as possible (eg not using absolute module names can result
29 // in problems, either in the module files or apps that import them in
30 // various ways.
31 // - The original mixed-caps module names should be used in the modules.
34 string[] cprotos; // All seen functions and C "methods".
36 auto ew(S...)(S args) { return stderr.writeln(args); }
38 debug(TRACE) {
39 bool showtrace;
40 void trace(string cl, string c, string m) {
41 // Note: this intentionally writes to stdout - we want
42 // to see the trace in context of the generated output
43 if (showtrace)
44 writefln("//TRACE:%s(%s) %s", cl, c, m);
47 static if (!is(typeof(trace))) {
48 void trace(string cl, string c, string m) { }
51 // ["ab", "cd", "ef", "gh", "ij"] <= "ab,cd, ef, gh , ij"
52 auto csv2arr(scope string s) { return array( map!strip( split(s, ",") ) ); }
54 struct Indent {
55 enum by = 3;
56 private int level;
57 private static immutable spaces =
58 " ";
59 @property string s() { return spaces[0..level]; }
60 alias s this;
61 string opUnary(string op:"++")() { level += by; return s; }
62 string opUnary(string op:"--")() { level -= by; assert (level>=0); return s; }
64 Indent indent;
66 private string nodkeyword(scope string s) {
67 // Associative array literals are non-const expressions, so...
68 switch (s) {
69 case "double": return "double_";
70 case "string": return "string_";
71 case "union": return "union_";
72 case "module": return "module_";
73 case "int": return "int_";
74 case "in": return "in_";
75 case "out": return "out_";
76 case "ref": return "ref_";
77 case "function": return "function_";
78 case "foreach": return "foreach_";
79 case "delete": return "delete_";
80 case "continue": return "continue_";
81 case "new": return "new_";
82 case "export": return "export_";
83 case "body": return "body_";
84 case "version": return "version_";
85 case "scope": return "scope_";
86 case "break": return "break_";
87 case "false": return "false_";
88 case "true": return "true_";
89 case "alias": return "alias_";
90 case "main": return "main_";
91 case "default": return "default_";
92 case "interface": return "interface_";
93 default:
95 // "signals" ie gtk callback functions have '-' both
96 // in function names and args...
97 s = replace(s, "-", "_");
99 // Unfortunately shortening the names of some enums leaves
100 // them with a leading digit, so turn "2BIG" into "_2BIG":
101 import std.ascii;
102 if (s && s.length>0 && isDigit(s[0]))
103 return "_" ~ s;
104 return s;
107 private string escdquote(scope string s) {
108 import std.array;
109 return replace(s, "\\", "\\\\");
113 // From namespace tags:
114 string[] ident_prefixes; // eg. ["G"];
115 string[] symbol_prefixes; // eg. ["g", "glib"]
117 // Figure out the native D type from the gtktype and ctype.
118 // "signals" turns on heuristics, which isn't needed elsewhere,
119 // but the signal callback descriptions are lacking vital info...
120 string Dtype(scope string gt, scope string ct, bool signals=false) {
122 if (!gt && ct)
123 gt = ct;
125 // Some gtk signals have /no/ info; at least don't break the build.
126 if (signals && !gt)
127 return "void*";
129 // If we're given an array, strip the "[n]" part, do the conversion,
130 // then append the "[n]" back and return the result. Keeps things simple.
131 if (gt.length>=1 && gt[$-1]==']') {
132 size_t slen;
133 auto spos = std.string.indexOf(gt, '[');
134 if (spos>1)
135 slen = gt.length - spos;
136 if (slen>0 && gt[$-slen..$]==ct[$-slen..$]) {
137 string suf = gt[$-slen..$];
138 return Dtype(gt[0..$-slen], ct[0..$-slen]) ~ suf;
141 // Ditto for pointers; note - only the ctype has the right type
142 // (number of '*'). Keep in mind arrays of pointers exist.
143 if (ct.length>=1 && ct[$-1]=='*' && gt[$-1]!='*') {
144 size_t alen;
145 auto apos = std.string.indexOf(ct, '*');
146 if (apos>=0)
147 alen = ct.length - apos;
148 string suf = ct[$-alen..$];
149 return Dtype(gt, ct[0..$-alen]) ~ suf;
152 // Obvious conversions first:
153 if (gt=="gint8" && ct=="gint8") return "byte";
154 if (gt=="guint8" && ct=="guint8") return "ubyte";
155 if (gt=="guint8" && ct=="gchar") return "ubyte";
156 if (gt=="guint8" && ct=="guchar") return "ubyte";
157 if (gt=="guint8" && ct=="unsigned char") return "ubyte";
158 if (gt=="gint16" && ct=="gint16") return "short";
159 if (gt=="guint16" && ct=="guint16") return "ushort";
160 if (gt=="gint32" && ct=="gint32") return "int";
161 if (gt=="guint32" && ct=="guint32") return "uint";
162 if (gt=="gint64" && ct=="gint64") return "long";
163 if (gt=="guint64" && ct=="guint64") return "ulong";
166 if (gt=="gshort" && ct=="gshort") return "short";
167 if (gt=="gshort" && ct=="short") return "short";
168 if (gt=="gushort" && ct=="gushort") return "ushort";
170 version (None) {
171 if (gt=="gint" && ct=="signed") return "c_int";
172 if (gt=="gint" && ct=="int") return "c_int";
173 if (gt=="gint" && ct=="gint") return "c_int";
174 if (gt=="gint" && !ct) return "c_int"; // gtk2 needs it.
175 if (gt=="guint" && ct=="guint") return "c_uint";
176 if (gt=="guint" && ct=="unsigned int") return "c_uint";
177 if (gt=="guint" && ct=="unsigned") return "c_uint";
178 } else {
179 // Assumes 32-bit wide target ints, but is far more readable...
180 if (gt=="gint" && ct=="signed") return "int";
181 if (gt=="gint" && ct=="int") return "int";
182 if (gt=="gint" && ct=="gint") return "int";
183 if (gt=="gint" && !ct) return "int"; // gtk2 needs it.
184 if (gt=="guint" && ct=="guint") return "uint";
185 if (gt=="guint" && ct=="unsigned int") return "uint";
186 if (gt=="guint" && ct=="unsigned") return "uint";
188 if (gt=="gfloat" && ct=="gfloat") return "float";
189 if (gt=="gfloat" && ct=="float") return "float";
190 if (gt=="gdouble" && ct=="double") return "double";
191 if (gt=="gdouble" && ct=="gdouble") return "double";
192 if (gt=="long double" && ct=="long double") return "real";
194 if (gt=="none" && ct=="void") return "void";
196 if (gt=="gunichar" && ct=="gunichar") return "dchar";
197 if (gt=="guint16" && ct=="gunichar2") return "wchar";
199 if (gt=="glong" && ct=="long") return "c_long";
200 if (gt=="glong" && ct=="glong") return "c_long";
201 if (gt=="gulong" && ct=="gulong") return "c_ulong";
202 if (gt=="gulong" && ct=="unsigned long") return "c_ulong";
204 if (gt=="gsize" && ct=="gsize") return "size_t";
205 if (gt=="gssize" && ct=="gssize") return "ssize_t";
206 if (gt=="gint64" && ct=="goffset") return "long"; // signed 64bit int, C99: off64_t
208 if (gt=="gchar" && ct=="gchar") return "char";
209 if (gt=="gchar" && ct=="char") return "char";
210 if (gt=="gchar" && ct=="guchar") return "ubyte";
212 if (gt=="utf8" && ct=="char") return "char";
213 if (gt=="utf8" && ct=="gchar") return "char";
215 if (gt=="guint8" && ct=="char") return "ubyte";
217 if (gt=="gpointer" && ct=="void") return "void";
218 if (gt=="gpointer" && !ct) return "void*"; // GTK2
219 if (gt=="gpointer" && ct=="gpointer") return "void*";
221 if (gt=="guint8" && ct=="void") return "ubyte"; // used for buffer *'s.
222 if (gt=="guint8" && ct=="gpointer") return "void*";
223 if (gt=="gpointer" && ct=="gconstpointer") return "const(void)*";
225 if (gt=="guint8" && ct=="gconstpointer") return "const(ubyte)*";
226 if (gt=="Variant" && ct=="gconstpointer") return "const(Variant)*";
227 if (gt=="VariantType" && ct=="gconstpointer") return "const(VariantType)*";
228 // gobject:
229 if (gt=="Object" && ct=="gpointer") return "Object*";
230 if (gt=="TypeClass" && ct=="gpointer") return "TypeClass*";
231 if (gt=="TypeInterface" && ct=="gpointer") return "TypeInterface*";
233 if (gt=="DBusObjectManagerClient" && ct=="GDBusObjectManager") return "DBusObjectManagerClient";
236 if (gt=="gboolean" && ct=="gboolean") return "int";
237 if (gt=="glong" && ct=="time_t") return "time_t"; // well...
239 if (gt=="guint" && ct=="uid_t") return "uint"; // Good enough?
241 // GLib:
242 if (gt=="gpointer" && ct=="GArray") return "Array";
243 if (gt=="gpointer" && ct=="GByteArray") return "ByteArray";
244 if (gt=="gpointer" && ct=="GPtrArray") return "PtrArray";
245 if (gt=="gpointer" && ct=="FILE") return "FILE";
247 if (gt=="guint8" && ct=="GByteArray") return "ByteArray";
249 // Not even the module names are used consistently...
251 void fixupmodnames(scope string from, scope string to) {
252 auto orggt = gt;
253 auto fl = from.length+1; // '+1' cause of the trailing '.'.
254 if (gt.length>=fl && gt[0..fl]==from ~ ".") {
255 gt = to ~ "." ~ gt[fl..$];
257 // Don't change ct for "GObject.ObjectClass" "GObjectClass"
258 if (ct.length>1 && orggt[fl..$]==ct[1..$])
259 return;
261 fl--;
262 if (ct.length>fl && from == ct[0..fl])
263 ct = to ~ ct[fl..$];
267 fixupmodnames("GLib", "GLib2");
268 fixupmodnames("GdkPixbuf", "GdkPixbuf2");
269 fixupmodnames("GObject", "GObject2");
270 fixupmodnames("Gio", "Gio2");
271 fixupmodnames("Gdk", "Gdk2");
273 if (gt=="va_list" && ct=="va_list") return "va_list";
275 // gobject
276 if (gt=="GType" && ct=="GType") return "Type";
277 if (gt=="GObject2.Object" && ct=="gpointer") return "GObject2.Object*";
279 if (gt=="GdkPixbuf2.Pixbuf" && ct=="GdkPixbuf") return "GdkPixbuf2.Pixbuf"; // in gdk
281 if (gt=="cairo.Surface" && ct=="cairo_surface_t") return "cairo.Surface"; // in gdk
282 if (gt=="cairo.Context" && ct=="cairo_t") return "cairo.Context"; // in gdk
283 if (gt=="cairo.FontOptions" && ct=="cairo_font_options_t") return "cairo.FontOptions"; // in gdk
284 if (gt=="cairo.Content" && ct=="cairo_content_t") return "cairo.Content"; // in gdk
285 if (gt=="cairo.Pattern" && ct=="cairo_pattern_t") return "cairo.Pattern"; // in gdk
287 // Ugh:
288 // "Pango.Font" "PangoFont*" => "Pango.Font*"; // in gdk
289 // "Pango.GlyphString" "PangoGlyphString*" => "Pango.GlyphString*"; // in gdk
290 // "Pango.Direction" "PangoDirection" => "Pango.Direction"
292 auto mlen = countUntil(gt, ".");
293 if (mlen>0 && ct.length>mlen && gt[0..mlen]==ct[0..mlen]) {
294 auto ctapos = countUntil(ct, "*");
295 if (ctapos==-1)
296 ctapos = ct.length;
297 if (ctapos>0 && gt[mlen+1..$]==ct[mlen..ctapos] )
298 return gt ~ ct[ctapos..$];
302 // ATK needs this one, as the datatype is missing both name and definition; this lets it build.
303 if (gt=="AtkPropertyValues*" && ct=="AtkPropertyValues*") return "_PropertyValues*";
305 // GDK:
306 if (gt=="GdkPixbuf2.PixbufAlphaMode" && ct=="GdkPixbuf2AlphaMode") return "GdkPixbuf2.PixbufAlphaMode";
308 // GTK:
309 if (gt=="filename" && ct=="gchar") return "char"; // Probably const, but...
310 if (gt=="filename" && !ct) return "char*"; // Probably const, but...
312 if (gt=="GdkPixbuf2.PixbufAnimation" && ct=="GdkPixbuf2Animation") return "GdkPixbuf2.PixbufAnimation";
313 if (gt=="GdkPixbuf2.PixbufAnimationIter" && ct=="GdkPixbuf2AnimationIter") return "GdkPixbuf2.PixbufAnimationIter";
315 // Signals:
316 // In fact if this is for a signal and has no <ct> it probably should be pointer...
317 // We're gonna guess, and we'll be wrong sometimes. But we will be right in many
318 // cases and there isn't really much else that we could do...
319 if (signals && !ct && gt[$-1]!='*') {
320 if (gt=="GObject2.Error") return "GLib2.Error*";
322 if (std.string.indexOf(gt,'.')!=-1) return gt ~ "*";
323 if (gt[0]>='A' && gt[0]<='Z') return gt ~ "*";
325 switch (gt) {
326 case "gfloat": return "float";
327 case "gdouble": return "double";
328 case "none": return "void";
329 case "utf8": return "char*";
330 case "gint": return "c_int";
331 case "guint": return "c_uint";
332 case "gboolean": return "c_int";
333 default:
337 // Fix up some obviously broken types.
338 if (!ct) {
339 if (gt=="Object") return "Object*";
342 version(all) { // Disable this while debugging.
343 if (gt==ct)
344 return gt;
347 if (gt && (!ct || ct==""))
348 return gt; // what else?...
350 enforce(gt && ct, "Missing type: GTK: \"" ~ gt ~ "\" C: \"" ~ ct ~ "\"");
352 foreach (prefix; ident_prefixes) {
353 auto l = prefix.length;
354 if (ct.length>l && ct[0..l]==prefix) {
355 // Try Quark" "GQuark -> "Quark"
356 string stripped_ct = ct[l..$];
357 if (stripped_ct==gt)
358 return stripped_ct;
362 // Last try: if ctype starts with 'G' and last dot-delimited component
363 // of gtktype equals the post-G ctype, use that.
364 // Eg. "Gio.AppLaunchContext", "GAppLaunchContext" => "Gio.AppLaunchContext"
365 if (ct[0]=='G') {
366 auto tail = "." ~ ct[1..$];
367 if(gt.length>=tail.length && gt[$-tail.length..$] == tail)
368 return gt;
371 enforce(0, "Unknown type: GTK: \"" ~ gt ~ "\" C: \"" ~ ct ~ "\"");
372 assert(0); // @noreturn
375 // Set string <dst> to <aa[index]>, but only if <aa> contains such value.
376 // if <req> is given assert if the value is missing.
377 void aassign(ref string dst, in string[string] aa, in string index, string req=null) {
378 if (auto p = index in aa)
379 dst = *p;
380 else if (req)
381 enforce(0, "Missing " ~ req ~ "; Available: " ~ to!string(aa));
383 string get_name_attr(in Element e) {
384 auto tag = e.tag;
385 if (auto p = "name" in tag.attr) {
386 // Debugging checks, harmless, can be left enabled.
387 if (*p!="GLib.List" && *p!="GLib.SList"&& *p!="GLib.HashTable")
388 enforce(tag.type==TagType.EMPTY, tag.toString());
389 // <alias>es trigger this one.
390 //enforce(tag.attr.length==1, to!string(tag.attr.length));
391 return *p;
393 return null;
396 string simplify_version(scope string v) pure {
397 if (v=="1.0")
398 return "";
399 else if (v[1..$]==".0")
400 return v[0..1];
401 return v;
404 class x_elem {
405 string tagname, doc, dochead, ver;
407 this(string s) { tagname = s; }
409 final void parse_doc_text(ElementParser ep) {
410 ep.onText = (string s) {
411 trace("DOC", "", s);
412 if (dochead)
413 doc ~= "\n" ~ dochead ~ ": ";
414 doc ~= s;
416 SkipEndTag(ep, "doc");
417 ep.parse();
420 final void register_doc_parser(ElementParser ep, string dochead=null) {
421 ep.onStartTag["doc"] = &parse_doc_text;
423 auto tag = ep.tag();
425 if (auto p = "version" in tag.attr)
426 ver = " " ~ *p;
427 this.dochead = dochead;
429 final string comment_to_D() {
430 import std.array;
431 string r;
432 if (doc) {
433 while (doc[$-1]=='\n')
434 doc = doc[0..$-1];
435 while (doc[0]=='\n')
436 doc = doc[1..$];
437 r = "\n// " ~ replace(doc, "\n", "\n// ");
438 r = replace(r, "\n// ", "\n" ~ indent ~ "// ") ~ "\n";
439 // Remove the newline before one-line comments.
440 import std.algorithm;
441 if (count(r, '\n')<=2)
442 r = r[1..$];
444 return r;
447 final void parsearray(ElementParser p, ref string _type, ref string _ctype,
448 bool makepointer=0) {
449 p.onStartTag["array"] = (ElementParser ep) {
450 aassign(_ctype, ep.tag().attr, "c:type");
451 if (makepointer)
452 _ctype ~= "*";
453 ep.onStartTag["type"] = (ElementParser ep) { ep.parse(); };
454 ep.onEndTag["type"] = (in Element e) {
455 trace("ELEMARR", tagname, e.tag.toString());
456 aassign(_type, e.tag.attr, "name");
458 ep.parse();
463 final class x_include : x_elem {
464 string name, ver;
466 this(string s) { super(s); }
468 void parse(in Element e) {
469 auto tag = e.tag;
470 enforce(tag.attr.length==2);
471 name = tag.attr["name"];
472 ver = tag.attr["version"];
475 override string toString() {
476 string modulebasename = name ~ simplify_version(ver);
477 string modulebasenamel = toLower(modulebasename);
478 // If we use "import GLib2" the compiler will fail to find the module
479 // while building the other ones.
480 // If we use "import gtk2.GLib2" the compiler will complain about
481 // missing Glib2.* types.
482 // NOTE: Imports are "public" so that apps don't have to import Gdk
483 // just for types only used for callback args etc.
484 return indent ~ "public import " ~ PACKAGE_NAME ~ "." ~ modulebasenamel ~ ";\n"
485 ~ indent ~ "alias " ~ PACKAGE_NAME ~ "." ~ modulebasenamel ~ " "
486 ~ modulebasename ~ ";";
490 string thismodulename;
492 final class x_namespace : x_elem {
493 string name, ver;
495 this(string s) { super(s); }
497 void parse(ElementParser ep) {
498 auto attr = ep.tag().attr;
499 name = attr["name"];
500 ver = attr["version"];
502 writeln();
504 if (auto p = "c:symbol-prefixes" in attr) {
505 symbol_prefixes = csv2arr(*p);
506 writeln("// c:symbol-prefixes: ", symbol_prefixes);
508 if (auto p = "c:identifier-prefixes" in attr) {
509 ident_prefixes = csv2arr(*p);
510 write("// c:identifier-prefixes: ", ident_prefixes, "\n\n");
513 // We do not parse further, ie no ep.parse() here.
516 override string toString() {
517 thismodulename = name ~ simplify_version(ver);
518 string s = indent ~ "// module " ~ thismodulename ~ ";\n";
519 // Per module fixups:
520 s ~= load_mixin("_MODULE_HEAD");
521 load_delete();
523 return s;
527 // "ALNUM" <= shortenconst("alnum", "G_ASCII_ALNUM")
528 string shortenconst(scope string name, scope string cname) {
529 if (cname.length>=name.length) {
530 auto shortname = cname[($-name.length)..$];
531 if (shortname==toUpper(name))
532 return nodkeyword(shortname);
534 return cname;
537 final class x_bitfield : x_elem {
538 string name;
539 x_field[] members;
541 this(string s) { super(s); }
543 void parse(ElementParser ep) {
544 trace("BITFIELD", tagname, ep.tag().toString());
545 auto tag = ep.tag();
546 name = tag.attr["name"];
548 register_doc_parser(ep);
549 ep.onStartTag["member"] = (ElementParser ep) {};
550 ep.onEndTag["member"] = (in Element e) {
551 enforce(e.tag.type==TagType.EMPTY, e.tag.toString());
553 auto m = new x_field("member");
554 members ~= m;
556 auto mattr = e.tag.attr;
557 aassign(m.name, mattr, "name", "bitfield name");
558 aassign(m.value, mattr, "value", "bitfield value");
559 aassign(m.identifier, mattr, "c:identifier", "bitfield c:identifier");
562 ep.onStartTag["function"] = (ElementParser ep) {
563 ew("FIXME: Skipping enum func: ", name, ".", ep.tag().attr["name"]);
564 // a gobject-introspection update made functions appear inside
565 // <enumeration> tags. We could handle this by using struct
566 // namespaces, but let's ignore these "functions" for now.
567 ep.parse();
569 ep.onEndTag["function"] = (in Element e) {
572 ep.parse();
575 override string toString() {
576 // Do we care about the basetype (ie do 31bits+ values happen?)
577 string r = comment_to_D() ~ indent ~ "enum " ~ name;
578 if (ver)
579 r ~= " /* Version" ~ ver ~ " */";
580 r ~= " {\n";
581 indent++;
583 foreach (i, m; members) {
584 long v = to!long(m.value);
585 if (v==0x8000_0000L)
586 m.value = "cast(uint)" ~ m.value;
587 r ~= indent ~ shortenconst(m.name, m.identifier) ~ " = " ~ m.value ~
588 (i!=members.length-1 ? "," : "") ~ "\n";
591 return r ~ --indent ~ "}";
595 x_field gerrfield; // pseudo field; used by functions that "throw" in gtk-speak.
596 static this() {
597 gerrfield = new x_field("GError");
598 gerrfield.type = "GLib2.Error**";
599 gerrfield.name = "error=null";
600 gerrfield.cname = "error";
603 final class x_field : x_elem {
604 string type, name, cname;
605 string value; // for constants
606 string identifier; // for bitfields
607 string ctype;
608 bool varargs; // true if this parameter is "..."
609 bool private_;
610 string def_init;
611 string direction;
612 x_struct struct_; // embedded struct/union;
613 x_function callback; // a callback, ie embedded function pointer.
614 x_elem docparent; // parent element, where we place our doc.
615 int bitfield; // if >0 then we need to handle it specially...
617 this(string s, x_elem parent=null) { super(s); docparent=parent;}
619 override string toString() {
620 string r = comment_to_D();
622 if (auto p = name in delete_symbol)
623 return "// " ~ tagname ~ " \"" ~ name ~ "\" removed: " ~ *p;
625 if (struct_)
626 return r ~ struct_.toString();
628 switch (tagname) {
629 case "constant":
630 switch (type) {
631 case "utf8":
632 r ~= indent ~ "enum " ~ name ~ " = \"" ~ escdquote(value) ~ "\";";
633 break;
634 default:
635 string contype = Dtype(type, ctype);
637 // "int MUTEX_DEBUG_MAGIC = cast(int)4175530711;" needs the cast...
638 string concast;
639 if (contype=="int") {
640 long conval = to!long(value);
641 if (conval>int.max && conval<=uint.max)
642 concast = "cast(int)";
644 r ~= indent ~ "enum " ~ contype ~ " " ~ name ~ " = " ~ concast ~ value ~ ";";
646 break;
647 case "alias":
648 string dtype = Dtype(type, ctype);
649 // We're renaming types, and that can cause problems, as in Gtk2.Type case.
650 if (dtype==name)
651 r ~= "/* ";
652 r ~= indent ~ "alias " ~ dtype ~ " " ~ name ~ ";" /*~ "\t// " ~ ctype*/;
653 if (dtype==name)
654 r ~= " */";
655 break;
656 // case "field": /* Fields in classes/structs are handled in containers. */
657 default:
658 enforce(0, "Unknown field type: " ~ tagname);
660 return r;
663 void parse(ElementParser ep) {
664 trace("FIELD", tagname, ep.tag().toString());
665 SkipEndTag(ep, tagname);
666 auto tag = ep.tag();
668 auto field_attr = tag.attr;
669 aassign(name, field_attr, "name");
670 aassign(value, field_attr, "value"); // Constants.
672 if (docparent)
673 docparent.register_doc_parser(ep, name ? "<" ~ name ~ ">": "PARAM");
674 else
675 register_doc_parser(ep);
677 if (auto p = "private" in field_attr)
678 private_ = *p=="1";
680 if (auto p = "bits" in field_attr) // Bitfields need special treatment.
681 bitfield = to!int(*p);
683 ep.onStartTag["type"] = (ElementParser ep) {};
684 ep.onEndTag["type"] = (in Element e) {
685 auto a = e.tag.attr;
686 aassign(ctype, a, "c:type");
687 if (tagname=="alias" || tagname=="parameter") {
688 /* GLib.List etc have extra info in child, ignored for now */
689 auto p = "name" in a;
690 if ( !p || ( *p!="GLib.List" && *p!="GLib.SList" && *p!="GLib.HashTable" ) )
691 enforce(e.tag.type==TagType.EMPTY, e.tag.toString());
693 switch (tagname) {
694 default:
695 type = get_name_attr(e);
699 onEmptyTag(ep, "attribute", (in Element e) {
700 auto tag = e.tag;
701 enforce(tag.attr.length==2);
702 enforce(tag.attr["name"]=="c:identifier");
703 identifier = tag.attr["value"];
706 if (tagname=="parameter") {
707 if (auto p = "allow-none" in field_attr) {
708 if (*p=="1")
709 def_init = "=null";
711 if (auto p = "direction" in field_attr)
712 direction = "/*" ~ *p ~ "*/ ";
714 parsearray(ep, type, ctype);
715 ep.onStartTag["varargs"] = (ElementParser ep) {
716 name = "...";
717 varargs = 1;
718 SkipEndTag(ep, "varargs");
719 ep.parse();
723 if (tagname=="field") {
724 ep.onStartTag["array"] = (ElementParser ep) {};
725 ep.onEndTag["array"] = (in Element e) {
726 trace("FIELDARR", tagname, e.tag.toString());
727 string size;
728 aassign(size, e.tag.attr, "fixed-size");
729 if (auto p = "readable" in field_attr) {
730 if (!size && *p=="0")
731 // Generates zero sized array, but as long as all instances
732 // are private and not followed by public fields - harmless.
733 size = "0";
735 //enforce(size);
736 // Ugh, eg GTK.InputDialogClass contains embedded arrays w/o size.
737 // Not usuable anyway, so we only care about it being syntactically correct.
738 if (!size)
739 size = "666";
741 if (size=="-1")
742 size = "/*GIR: -1*/ 0"; // Hmm, does this really belong in GIR...
744 // Using D syntax for arrays.
745 type ~= "[" ~ size ~ "]";
746 ctype ~= "[" ~ size ~ "]";
750 ep.onStartTag["callback"] = (ElementParser ep) {
751 callback = new x_function("functionp");
752 callback.parse(ep);
755 ep.parse();
759 final class x_function : x_elem {
760 string name, cname;
761 x_struct obj; // object if this is a method.
762 string rettype, crettype, retanno;
763 string throws; // actually gerrors, oh well.
764 bool is_variadic;
765 x_field[] args;
767 this(string s, x_struct pobj=null) { super(s); obj = pobj; }
769 string c_func(bool proto, bool skipthis, scope const(x_field)[] args) {
770 string r;
772 if (skipthis)
773 args = args[1..$];
775 r ~= nodkeyword(cname) ~ "(";
777 foreach(i, a; args) {
778 if (a.varargs)
779 r ~= "..."; // Variadic - have to be aliased (avoids having to deal w/ varargs).
780 else {
781 if (proto)
782 r ~= a.direction ~ Dtype(a.type, a.ctype) ~ " ";
783 r ~= a.cname ? nodkeyword(a.cname) : nodkeyword(a.name);
784 if (proto)
785 r ~= a.def_init;
786 if (i!=args.length-1)
787 r ~= ", ";
791 r ~= ")";
792 return r;
795 override string toString() {
796 string Drettype = Dtype(rettype, crettype);
797 string r = comment_to_D() ~ indent;
798 bool hasthis = 0;
799 bool refret;
801 // Ugh. Turning off -ffunction-sections causes link failures
802 // because if this missing glib function.
803 if (cname=="glib_dummy_decl")
804 return "";
806 if (name=="") {
807 auto pos = std.string.indexOf(cname, '_');
808 name = "/*NAME MISSING IN GIR*/ ";
809 if (pos==-1)
810 name ~= "missing_" ~ cname;
811 else
812 name ~= cname[pos+1..$];
815 bool is_funcp = tagname=="callback" ||
816 tagname=="functionp" ||
817 tagname=="virtual-method";
819 // Fix up the argument list.
821 auto allargs = args;
823 if (obj) {
824 auto t = new x_field("this_");
825 t.type = obj.name ~ "*";
826 t.name = "this_";
828 if (tagname!="constructor") {
829 // Methods have a this pointer as the first arg.
830 allargs = t ~ allargs;
832 if (tagname!="function")
833 hasthis = 1;
835 else {
836 // Constructors return pointers to new objects, not some base type.
837 Drettype = obj.name ~ "*";
841 if (throws)
842 allargs ~= gerrfield;
844 foreach(i, a; allargs) {
845 // Fix up any missing arg names.
846 if (!a.name)
847 a.name = "arg_" ~ cast(char)('a'+i);
848 // Gtk2 has out params w/o a ctype and "gint" type - we'll do our best to fix it...
849 if (a.direction=="/*out*/ " && !a.ctype /*&& a.type[$-1]!='*'*/) {
850 a.ctype = a.type ~ "*";
851 a.direction ~= "/*POINTER*/ ";
853 // Fix up default params.
854 if (a.def_init=="=null")
855 switch (Dtype(a.type, a.ctype)) {
856 case "int": a.def_init = "=0"; break;
857 default: /**/; break;
861 // Remove default initializers from args that are followed by ones w/o defaults.
863 bool nondefarg_seen = 0;
864 foreach_reverse(a; allargs) {
865 if (!a.def_init && !a.varargs)
866 nondefarg_seen = 1;
867 if (nondefarg_seen)
868 a.def_init = null;
872 if (tagname=="glib:signal") {
873 Drettype = Dtype(rettype, crettype, 1);
875 // Signals have an extra 'user_data' pointer as the last arg.
876 auto udata = new x_field("user_data");
877 udata.type = "void*";
878 udata.name = "user_data=null"; // "=null" JIC previous args were optional.
879 allargs = allargs ~ udata;
881 r ~= "extern (C) ";
882 r ~= "alias ";
883 r ~= "static ";
884 r ~= Drettype ~ " " ~ retanno;
885 r ~= "function (";
887 foreach(i, a; allargs) {
888 r ~= a.direction ~ Dtype(a.type, a.ctype, 1) ~ " " ~ nodkeyword(a.name) ~ a.def_init;
889 if (i!=allargs.length-1)
890 r ~= ", ";
893 r ~= ")";
894 r ~= " signal_" ~ nodkeyword(name);
895 r ~= ";\n";
897 // Add some convenience templates:
899 if (!obj.emitted("signal_connect")) {
900 r ~= "\n";
901 r ~= indent ~ "ulong signal_connect(string name, CB)(CB cb, void* data=null, ConnectFlags cf=cast(ConnectFlags)0) {\n";
902 r ~= ++indent ~ "return super_.signal_connect!name(cb, data, cf);\n";
903 r ~= --indent ~ "}\n\n";
906 r ~= indent ~ `ulong signal_connect(string name:"` ~ name ~ `", `;
907 r ~= `CB:signal_` ~ nodkeyword(name) ~ ")(CB cb, void* data=null, ConnectFlags cf=cast(ConnectFlags)0) {\n";
908 r ~= ++indent ~ `return signal_connect_data(&this, cast(char*)"` ~ name ~ "\",\n";
909 r ~= indent ~ "cast(GObject2.Callback)cb, data, null, cf);\n";
910 r ~= --indent ~ "}\n";
912 return r;
915 if (is_variadic) {
916 bool unsupported = tagname!="function" &&
917 tagname!="constructor";
918 if (unsupported) {
919 r ~= "/+ Not available -- variadic " ~
920 ( hasthis ?
921 "methods unsupported - use the C function directly.\n"
923 "functions with non-D linkage must have >1 parameter.\n" );
924 r ~= ++indent;
927 r ~= "alias " ~ cname ~ " " ~ nodkeyword(name) ~ "; // Variadic\n";
928 if (unsupported)
929 r ~= --indent ~ "+/\n";
931 goto add_cproto;
934 bool hasUpperCase(string s) {
935 import std.uni;
936 foreach(c; s)
937 if (isUpper(c))
938 return 1;
939 return 0;
942 string refDrettype = Drettype;
943 if (reftypes && Drettype[$-1]=='*' && hasUpperCase(Drettype)) {
944 refDrettype = Drettype[0..$-1];
945 refret = 1;
948 if (is_funcp)
949 r ~= "extern (C) ";
950 if (tagname=="callback")
951 r ~= "alias ";
952 if (tagname=="constructor" || tagname=="function")
953 r ~= "static ";
955 if (refret)
956 r ~= "Ref!(" ~ refDrettype ~ ")";
957 else
958 r ~= Drettype;
960 r ~= " " ~ retanno;
962 if (is_funcp)
963 r ~= "function (";
964 else
965 r ~= nodkeyword(name) ~ "(";
967 auto args = allargs;
969 if (hasthis)
970 args = args[1..$];
972 foreach(i, a; args) {
973 if (a.varargs)
974 r ~= "...";
975 else {
976 r ~= a.direction ~ Dtype(a.type, a.ctype) ~ " " ~ nodkeyword(a.name) ~ a.def_init;
977 if (i!=args.length-1)
978 r ~= ", ";
982 r ~= ")";
984 if (is_funcp)
985 r ~= " " ~ nodkeyword(name);
987 if (tagname=="functionp")
988 return r;
990 if (tagname=="callback" || tagname=="virtual-method") {
991 r ~= ";\n";
992 return r;
995 r ~= " {\n" ~ ++indent;
997 if (Drettype!="void")
998 r ~= "return ";
1000 if (refret)
1001 r ~= "Ref!(" ~ refDrettype ~ ")(";
1003 string oname;
1004 if (hasthis) {
1005 oname = allargs[0].name;
1006 allargs[0].name = "&this";
1008 r ~= c_func(0, 0, allargs);
1009 if (refret)
1010 r ~= ")";
1011 r ~= ";\n" ~ --indent ~ "}\n";
1012 if (hasthis)
1013 allargs[0].name = oname;
1015 add_cproto:
1016 if (!is_funcp /*&& tagname=="function" || obj*/)
1017 cprotos ~= Drettype ~ " " ~ retanno ~ c_func(1, 0, allargs) ~ ";";
1019 return r;
1022 void parse(ElementParser ep) {
1023 trace("FUNC", tagname ~ (obj ? ": " ~ obj.name : ""), ep.tag().toString());
1024 // "callbacks" in structs are actually pointers.
1025 SkipEndTag(ep, tagname!="functionp" ? tagname : "callback");
1026 register_doc_parser(ep);
1027 auto tag = ep.tag();
1029 name = tag.attr["name"];
1030 aassign(cname, tag.attr, "c:identifier");
1031 aassign(throws, tag.attr, "throws");
1033 auto p = "introspectable" in tag.attr;
1034 if (p && *p=="0")
1035 doc ~= "Unintrospectable " ~ tagname ~ ": " ~ name ~ "() / " ~ cname ~ "()\n";
1037 ep.onStartTag["return-value"] = (ElementParser ep) {
1038 SkipEndTag(ep, "return-value");
1039 register_doc_parser(ep, "RETURNS");
1041 auto tag = ep.tag();
1043 if (auto p = "transfer-ownership" in tag.attr) {
1044 switch (*p) {
1045 case "none": retanno = ""; break;
1046 case "full": retanno = "/*new*/ "; break;
1047 case "container": retanno = "/*new container*/ "; break;
1048 default: enforce(0, "Unknown transfer-ownership: " ~ *p);
1052 parsearray(ep, rettype, crettype);
1054 ep.onStartTag["type"] = (ElementParser ep) {};
1055 ep.onEndTag["type"] = (in Element e) {
1056 auto tag = e.tag;
1057 aassign(crettype, tag.attr, "c:type");
1058 aassign(rettype, tag.attr, "name"/*, "func retval typename"*/);
1059 // If we have a ctype, but no type, try using the ctype.
1060 if (!rettype && crettype) {
1061 crettype = "/*CTYPE*/ " ~ crettype;
1062 rettype = crettype;
1064 enforce(rettype, "Missing func retval typename" );
1067 ep.parse();
1069 // Note the trick is there can be multiple nested <type>s;
1070 // once we get here we should have filled in the types properly.
1071 // Keep in mind we only care about the outermost type, that's
1072 // why parsing the _end_ </type> tag works for us.
1074 // Unfortunately c:type is often missing, so this check is too strict.
1075 //enforce(crettype, "Missing func retval c:type" ~ to!string(this));
1078 ep.onStartTag["parameters"] = (ElementParser pa) {
1079 trace("FUNCPARM", tagname, ep.tag().toString());
1080 SkipEndTag(pa, "parameters");
1081 pa.onStartTag["parameter"] = (ElementParser pa) {
1082 SkipEndTag(pa, "parameter");
1083 auto arg = new x_field("parameter", this); args ~= arg; arg.parse(pa);
1084 if (arg.varargs)
1085 is_variadic = 1;
1087 pa.parse();
1090 ep.parse();
1094 final class x_struct : x_elem {
1095 string name, parent;
1096 x_struct embedded_in;
1097 x_field[] fields;
1098 x_function[] methods;
1099 bool[string] emitted_;
1101 this(string tn, x_struct emb=null) {
1102 super(tn);
1103 embedded_in = emb;
1104 switch (tagname) {
1105 case "record": break;
1106 case "class": break;
1107 case "union": break;
1108 case "interface": break;
1109 default: enforce(0, "Unknown struct type: " ~ tagname);
1113 private bool emitted(string s) {
1114 if (auto p = s in emitted_)
1115 return 1;
1116 emitted_[s] = 1;
1117 return 0;
1120 private string basetypename(scope string type) {
1121 auto i = lastIndexOf(type, '.');
1122 return i==-1 ? type : type[i+1..$];
1125 override string toString() {
1126 // Everything looks like struct to us. Except unions.
1127 string r = comment_to_D() ~ indent ~
1128 ((tagname=="union") ? "union " : "struct ") ~ name;
1130 if (parent)
1131 r ~= " /* : " ~ parent ~ " */";
1132 if (ver)
1133 r ~= " /* Version" ~ ver~ " */";
1134 r ~= " {\n";
1136 indent++;
1138 // "alias super this".
1140 // First, if this struct has a parent, but we don't know any of its
1141 // fields then add the parent as the only (sub)struct -- harmless as
1142 // we couldn't access the opaque empty struct anyway and this lets
1143 // the pseudo-inheritance work.
1144 if (parent && fields.length==0) {
1145 auto pf = new x_field("record");
1146 pf.type = parent;
1147 pf.ctype = parent;
1148 pf.name = "method_parent";
1149 fields ~= pf;
1152 if (parent && fields.length>0 && (
1153 Dtype(fields[0].type, fields[0].ctype)==Dtype(parent, null) ||
1154 Dtype(fields[0].type, fields[0].ctype)==parent
1155 )) {
1156 auto fieldname = nodkeyword(fields[0].name);
1158 r ~= indent ~ "alias " ~ fieldname ~ " this;\n";
1159 r ~= indent ~ "alias " ~ fieldname ~ " super_;\n";
1161 // Add an extra alias for easier upcasting.
1162 // NOTE: The name we add could clash with existing symbols;
1163 // this does not happen in practice, will be caught at
1164 // compiletime if it does, and can be addressed then.
1165 // Also "object" is not the safest symbol to add, but
1166 // works so far, let's wait until it causes problems
1167 // before renaming it to something like "gobject".
1168 auto aname = basetypename(nodkeyword(fields[0].type));
1169 aname = toLower(aname);
1170 if (aname!=fieldname)
1171 r ~= indent ~ "alias " ~ fieldname ~ " " ~ aname ~ ";\n";
1173 // The parent cannot be private, so fix it:
1174 if (fields[0].private_)
1175 fields[0].private_ = 0;
1178 // No bitfields in D.
1179 string bmix;
1180 int bfwidth;
1181 char bfcount='A'; // More than one __dummyNN names? Must be different.
1183 void eofbf() {
1184 if (!bmix)
1185 return;
1187 // For now we'll assume all bitfields are at least 32bits wide (C int-sized)
1188 int rawwidth;
1189 if (bfwidth>0 && bfwidth<=32)
1190 rawwidth = 32;
1191 else if (bfwidth>32 && bfwidth<=64)
1192 rawwidth = 64;
1193 enforce(rawwidth, bmix);
1195 // std.bitmaip reqs total width to be one of 8/16/32/64, so:
1196 if (rawwidth!=bfwidth) {
1197 if (rawwidth<=8 && bfwidth>0 && bfwidth<8)
1198 bmix ~= ",\n" ~ indent ~ " uint, \"__dummy8"~bfcount~"\", " ~ to!string(8-bfwidth);
1199 else if (rawwidth<=16 && bfwidth<16)
1200 bmix ~= ",\n" ~ indent ~ " uint, \"__dummy16"~bfcount~"\", " ~ to!string(16-bfwidth);
1201 else if (rawwidth<=32 && bfwidth<32)
1202 bmix ~= ",\n" ~ indent ~ " uint, \"__dummy32"~bfcount~"\", " ~ to!string(32-bfwidth);
1203 else if (bfwidth<64)
1204 bmix ~= ",\n" ~ indent ~ " uint, \"__dummy64"~bfcount~"\", " ~ to!string(64-bfwidth);
1205 else if (bfwidth==64)
1207 else
1208 enforce(0);
1210 r ~= bmix ~ "))";
1211 bmix = null;
1212 bfwidth = 0;
1213 bfcount++;
1216 // Unfortunately there are methods that end up using the same name
1217 // as struct fields. Build a list of all method names, and in the
1218 // code below check for clashes. Rename the fields as these are
1219 // less likely to be used by apps.
1220 bool[string] m_names;
1221 foreach (m ; methods)
1222 m_names[m.name] = 1;
1224 foreach (i, f; fields) {
1225 string privstr = f.private_ ? "private " : "";
1227 if (f.callback) {
1228 eofbf();
1229 if (i!=0)
1230 r ~= ";\n";
1232 r ~= privstr ~ f.callback.toString();
1234 else if (f.struct_) {
1235 eofbf();
1236 if (i!=0)
1237 r ~= ";\n";
1238 else
1239 r ~= "\n";
1241 r ~= "\n" ~ privstr ~ f.struct_.toString();
1243 else if (f.bitfield) {
1244 if (!bmix) {
1245 if (i!=0)
1246 r ~= ";\n";
1247 bmix = indent ~ " static import std.bitmanip;"
1248 ~ " mixin(std.bitmanip.bitfields!(\n" ~ indent;
1250 else
1251 bmix ~= ",\n" ~ indent;
1252 string bftype = Dtype(f.type, f.ctype);
1253 switch (bftype) {
1254 case "uint", "int", "c_uint", "c_int":
1255 bmix ~= bftype ~ ", \"" ~ f.name~"\", "~ to!string(f.bitfield);
1256 bfwidth += f.bitfield;
1257 break;
1258 default:
1259 enforce(0, "Unknown bitfield type " ~
1260 bftype ~ "[" ~ f.type ~ "," ~ f.ctype ~ "] " ~
1261 f.name ~ ":" ~ to!string(f.bitfield));
1264 else {
1265 eofbf();
1266 string fname = nodkeyword(f.name);
1267 if (fname in m_names)
1268 fname ~= "_";
1269 if (i==0)
1270 r ~= indent ~ privstr ~ Dtype(f.type, f.ctype) ~ " " ~ fname;
1271 else {
1272 string prevtype, currtype;
1273 if (fields[i-1].type || fields[i-1].ctype)
1274 prevtype = Dtype(fields[i-1].type, fields[i-1].ctype);
1275 currtype = Dtype(f.type, f.ctype);
1276 if ( (prevtype == currtype && fields[i-1].type==f.type)
1277 && (fields[i-1].private_ == f.private_)
1278 && !fields[i-1].bitfield )
1279 r ~= ", " ~ fname;
1280 else {
1281 r ~= ((!fields[i-1].struct_) ? ";" : "");
1282 r ~= "\n"~ indent ~ privstr ~ currtype ~ " " ~ fname;
1288 eofbf();
1290 if (fields.length>0 && !fields[$-1].struct_)
1291 r ~= ";\n";
1293 if (fields.length>0 && methods.length>0)
1294 r ~= "\n";
1296 foreach (f; methods)
1297 r ~= f.toString();
1299 // Pull in the right mixin, if any:
1301 string mixname = name;
1303 for (auto ei = embedded_in; ei; ei = ei.embedded_in)
1304 mixname = ei.name ~ "_" ~ mixname;
1306 r ~= load_mixin(mixname);
1308 return r ~ --indent ~ "}\n";
1311 void parse(ElementParser ep) {
1312 trace("STRUCT", tagname, ep.tag().toString());
1313 auto tag = ep.tag();
1314 name = tag.attr["name"];
1315 aassign(parent, tag.attr, "parent");
1317 register_doc_parser(ep);
1318 ep.onStartTag["field"] = (ElementParser ep) {
1319 auto nf = new x_field("field"); fields ~= nf; nf.parse(ep);
1321 ep.onStartTag["union"] = (ElementParser ep) {
1322 auto nf = new x_field("union"); fields ~= nf;
1323 auto na = new x_struct("union", this); nf.struct_ = na; na.parse(ep);
1325 ep.onStartTag["record"] = (ElementParser ep) {
1326 auto nf = new x_field("record"); fields ~= nf;
1327 auto na = new x_struct("record", this); nf.struct_ = na; na.parse(ep);
1329 ep.onStartTag["property"] = (ElementParser p) {
1330 p.parse(); // Drop them.
1332 ep.onStartTag["method"] = (ElementParser ep) {
1333 auto nm = new x_function("method", this); methods ~= nm; nm.parse(ep);
1335 ep.onStartTag["virtual-method"] = (ElementParser p) {
1336 p.parse(); // Drop them.
1338 ep.onStartTag["function"] = (ElementParser ep) {
1339 auto nm = new x_function("function"); methods ~= nm; nm.parse(ep);
1341 ep.onStartTag["constructor"] = (ElementParser ep) {
1342 auto nm = new x_function("constructor", this); methods ~= nm; nm.parse(ep);
1344 ep.onStartTag["glib:signal"] = (ElementParser ep) {
1345 auto nm = new x_function("glib:signal", this); methods ~= nm; nm.parse(ep);
1348 ep.onStartTag["prerequisite"] = (ElementParser ep) { ep.parse(); }; // Drop.
1349 ep.onEndTag["prerequisite"] = (in Element e) {};
1351 // Drop them.
1352 ep.onStartTag["implements"] = (ElementParser p) { p.parse(); };
1353 ep.onEndTag["implements"] = (in Element e) {};
1355 ep.parse();
1359 void onUnknownTag(ElementParser ep, string tagname) {
1360 ep.onStartTag[null] = (ElementParser ep) {
1361 enforce(0, "Unhandled <"~tagname~"> start tag: "
1362 ~ ep.tag.name ~ "\n# " ~ ep.tag().toString());
1364 ep.onEndTag[null] = (in Element e) {
1365 enforce(0, "Unhandled <"~tagname~"> end tag: " ~ "\n# " ~ e.toString());
1369 void SkipEndTag(ElementParser p, string tagname) {
1370 onUnknownTag(p, tagname);
1371 p.onEndTag[tagname] = (in Element e) { };
1374 void onEmptyTag(ElementParser p, string tagname, void delegate(in Element e) deleg) {
1375 p.onStartTag[tagname] = (ElementParser ep) {
1376 /* do nothing; only tells the parser that we know about this tag */
1378 p.onEndTag[tagname] = (in Element e) {
1379 auto tag = e.tag;
1380 enforce(tag.type==TagType.EMPTY, tag.toString());
1382 deleg(e);
1386 void onStartTag(ElementParser p, string tagname, void delegate(ElementParser ep) deleg) {
1387 p.onStartTag[tagname] = (ElementParser ep) {
1388 onUnknownTag(ep, tagname);
1389 ep.onEndTag[tagname] = (in Element e) {/*Reached our own end tag*/};
1390 deleg(ep);
1392 p.onEndTag[tagname] = (in Element e) {
1393 /* Do nothing; only tells the parser that we know about this tag */
1394 /* Ugh. "<record name="AppLaunchContextPrivate" />" happens... */
1398 template OnTag(alias pa, string Kind, string xmlname, string tagname) {
1399 enum OnTag = "
1400 on"~Kind~"Tag(" ~ pa.stringof ~ ", \"" ~ xmlname ~ "\","
1401 " (" ~ ((Kind=="Start") ? "ElementParser e" : "in Element e") ~ ") {
1402 auto a = new x_"~tagname~"(" ~ xmlname.stringof ~ ");
1403 a.parse(e);
1404 "`write(a.toString() ~"\n");`
1405 "} );";
1408 string delete_symbol[string]; // [name: reason]
1410 void load_delete() {
1411 string filename = "mixin/" ~ thismodulename ~ "_" ~ "_DELETE_";
1412 string s;
1414 try {
1415 s = cast(string)std.file.read(filename);
1416 } catch (std.file.FileException) {
1417 if (tracemixins)
1418 ew("DELETE Failed: " ~ filename);
1419 return;
1422 foreach (line; split(s, "\n")) {
1423 auto arr = split(line, ":");
1424 if (arr.length==0)
1425 continue;
1426 if (arr.length==1)
1427 delete_symbol[line] = "";
1428 else
1429 delete_symbol[arr[0]] = join(arr[1..$], ":");
1433 bool tracemixins;
1435 string load_mixin(string id) {
1436 string filename = "mixin/" ~ thismodulename ~ "_" ~ id ~ ".d";
1437 string s;
1439 static bool uniq_mix[string];
1441 enforce(filename !in uniq_mix, "Mixin collission: " ~ filename);
1442 uniq_mix[filename] = 1;
1444 try {
1445 s = cast(string)std.file.read(filename);
1446 } catch (std.file.FileException) {
1447 if (tracemixins)
1448 ew("MIXIN Failed: " ~ filename);
1449 return null;
1452 if (tracemixins)
1453 ew("MIXIN Found: " ~ filename);
1455 string r = "\n" ~ indent ~ "// --- " ~ filename ~ " --->\n\n";
1457 foreach (sp; split(s, "\n"))
1458 r ~= indent ~ sp ~ "\n";
1460 r ~= indent ~ "// <--- " ~ filename ~ " ---\n";
1462 return r;
1465 bool reftypes = 0;
1467 void main(string[] args) {
1468 string infilename, outfilename, modulename;
1469 bool xmlcheck;
1471 try {
1472 // Parse cmd line:
1474 bool delegate(string arg) eatarg;
1476 cmdargloop: foreach(arg; args[1..$]) {
1477 if (eatarg) {
1478 if (eatarg(arg))
1479 eatarg = null;
1480 continue;
1482 if (arg[0]!='-') {
1483 if (!infilename) {
1484 infilename = arg;
1485 continue;
1487 die("Unknown extra cmdline file name: " ~ arg);
1489 if (arg.length<2)
1490 die("Missing cmdline option: " ~ arg);
1491 switch (arg[1]) {
1492 case '-':
1493 switch (arg[2..$]) {
1494 debug(TRACE) {
1495 case "trace": showtrace = 1; break;
1497 case "trace-mixins": tracemixins = 1; break;
1498 case "check": xmlcheck = 1; break;
1499 case "reftypes": reftypes = 1; break;
1500 case "no-reftypes": reftypes = 0; break;
1501 default: die("Unknown long cmdline option: " ~ arg);
1503 break;
1504 case 'o':
1505 if (arg.length>2)
1506 outfilename = arg[2..$];
1507 else
1508 eatarg = (string arg) { outfilename = arg; return true; };
1509 continue cmdargloop;
1510 case 'M':
1511 if (arg.length>2)
1512 outfilename = arg[2..$];
1513 else
1514 eatarg = (string arg) { modulename = arg; return true; };
1515 continue cmdargloop;
1516 default:
1517 die("Unknown cmdline option: " ~ arg);
1521 if (eatarg)
1522 die("Missing cmdline arg");
1524 string s = cast(string)std.file.read(infilename);
1526 if (xmlcheck)
1527 check(s); // Running the checks increases the execution time from 0.22s to 1.74s...
1529 if (outfilename)
1530 stdout.open(outfilename, "wt");
1532 writef("// *** DO NOT EDIT ***\n// Automatically generated from \"%s\"\n\n", infilename);
1534 if (modulename)
1535 write("module " ~ modulename ~ ";\n");
1537 auto dp = new DocumentParser(s);
1539 void delegate (ElementParser ep) epskip = (ElementParser ep) { ep.parse(); };
1540 void delegate (ElementParser p) epfixme = (ElementParser p)
1541 { p.parse(); ew("FIXME: " ~ p.toString()); };
1542 void delegate (in Element e) efixme = (in Element e) { ew("FIXME: " ~ e.toString()); };
1544 onUnknownTag(dp, "Document");
1546 onEmptyTag(dp, "package", (in Element e) {
1547 auto name = get_name_attr(e);
1548 write("\n// package: \"" ~ name ~ "\";\n");
1549 } );
1551 onEmptyTag(dp, "c:include", (in Element e) {
1552 auto name = get_name_attr(e);
1553 write("// C header: \"" ~ name ~ "\";\n");
1554 } );
1556 mixin( OnTag!(dp, "Empty", "include", "include") );
1557 mixin( OnTag!(dp, "Start", "namespace", "namespace") );
1558 dp.onEndTag["namespace"] = (in Element e) { }; // We're assuming one ns per file.
1560 dp.onEndTag["repository"] = (in Element e) { }; // We're assuming one repo per file.
1562 mixin( OnTag!(dp, "Start", "class", "struct") );
1563 mixin( OnTag!(dp, "Start", "record", "struct") );
1564 mixin( OnTag!(dp, "Start", "union", "struct") );
1565 mixin( OnTag!(dp, "Start", "interface", "struct") );
1567 mixin( OnTag!(dp, "Start", "constant", "field") );
1568 mixin( OnTag!(dp, "Start", "alias", "field") );
1570 mixin( OnTag!(dp, "Start", "bitfield", "bitfield") );
1571 mixin( OnTag!(dp, "Start", "enumeration", "bitfield") );
1573 mixin( OnTag!(dp, "Start", "function", "function") );
1574 mixin( OnTag!(dp, "Start", "callback", "function") );
1576 dp.onStartTag["glib:boxed"] = epskip;
1578 // Not handled yet. Can't eat them here, we need the types anyway.
1579 //dp.onStartTag["interface"] = epfixme;
1581 dp.parse();
1583 write(load_mixin("_MODULE"));
1585 write("\n// C prototypes:\n\nextern (C) {\n");
1586 // Some methods are also documented as functions and the D
1587 // compiler does not like duplicate prototypes; drop them.
1588 bool uniq_proto[string];
1589 foreach(s; cprotos) {
1590 if (s !in uniq_proto)
1591 writeln(s);
1592 uniq_proto[s] = 1;
1594 write("}\n");
1597 catch (Exception e) {
1598 import std.file;
1599 ew("ERROR: ", e);
1600 if (outfilename)
1601 remove(outfilename);
1602 else
1603 ew("WARNING: Unable to remove destination file.");
1604 die();
1608 void die(S...)(S args) {
1609 import core.stdc.stdlib;
1610 ew(args);
1611 exit(1);
1612 assert(0);