Improve Cogl module fixups.
[girtod.git] / girtod.d
blobff5f4437c1d3f161b75cc5918648d376931d0c89
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;
67 private string validDSym(scope string s) {
68 s = subDKeyword(s);
70 // "signals" ie gtk callback functions have '-' both
71 // in function names and args...
72 s = replace(s, "-", "_");
74 // Unfortunately shortening the names of some enums leaves
75 // them with a leading digit, so turn "2BIG" into "_2BIG":
76 import std.ascii;
77 if (s && s.length>0 && isDigit(s[0]))
78 return "_" ~ s;
79 return s;
82 string subDKeyword(scope string word) {
83 if (auto subst = word in dKeywords)
84 return *subst;
85 return word;
88 string dKeywords[string];
90 static this() {
91 // Associative array literals are non-const expressions in D;
92 // until that gets fixed, doing it this way is as good as any...
93 immutable string[] reserved = [
94 "boolean", "enum", "float", "uint", "void",
95 "double", "int", "cent", "string", "function", "module", "union",
96 "in", "out", "ref", "scope", "final",
97 "foreach", "delete", "continue", "new", "break", "default", "interface",
98 "alias", "export", "body", "version",
99 "false", "true",
100 "main"
103 foreach(word; reserved)
104 dKeywords[word] = word ~ "_";
106 dKeywords["TypeInfo"] = "GTypeInfo";
110 private string escdquote(scope string s) {
111 import std.array;
112 return replace(s, "\\", "\\\\");
116 // From namespace tags:
117 string[] ident_prefixes; // eg. ["G"];
118 string[] symbol_prefixes; // eg. ["g", "glib"]
120 // Figure out the native D type from the gtktype and ctype.
121 // "signals" turns on heuristics, which isn't needed elsewhere,
122 // but the signal callback descriptions are lacking vital info...
123 string Dtype(scope string gt, scope string ct, bool signals=false) {
125 if (!gt && ct)
126 gt = ct;
128 // Some gtk signals have /no/ info; at least don't break the build.
129 if (signals && !gt)
130 return "void*";
132 // If we're given an array, strip the "[n]" part, do the conversion,
133 // then append the "[n]" back and return the result. Keeps things simple.
134 if (gt.length>=1 && gt[$-1]==']') {
135 size_t slen;
136 auto spos = std.string.indexOf(gt, '[');
137 if (spos>1)
138 slen = gt.length - spos;
139 if (slen>0 && gt[$-slen..$]==ct[$-slen..$]) {
140 string suf = gt[$-slen..$];
141 return Dtype(gt[0..$-slen], ct[0..$-slen]) ~ suf;
144 // Ditto for pointers; note - only the ctype has the right type
145 // (number of '*'). Keep in mind arrays of pointers exist.
146 if (ct.length>=1 && ct[$-1]=='*') {
147 size_t alen;
148 auto apos = std.string.indexOf(ct, '*');
149 if (apos>=0)
150 alen = ct.length - apos;
151 string suf = ct[$-alen..$];
152 if (gt==ct)
153 gt = gt[0..$-alen];
154 return Dtype(gt, ct[0..$-alen]) ~ suf;
157 // Obvious conversions first:
158 if (gt=="gint8" && ct=="gint8") return "byte";
159 if (gt=="guint8" && ct=="guint8") return "ubyte";
160 if (gt=="guint8" && ct=="gchar") return "ubyte";
161 if (gt=="guint8" && ct=="guchar") return "ubyte";
162 if (gt=="guint8" && ct=="unsigned char") return "ubyte";
163 if (gt=="gint16" && ct=="gint16") return "short";
164 if (gt=="guint16" && ct=="guint16") return "ushort";
165 if (gt=="gint32" && ct=="gint32") return "int";
166 if (gt=="guint32" && ct=="guint32") return "uint";
167 if (gt=="gint64" && ct=="gint64") return "long";
168 if (gt=="guint64" && ct=="guint64") return "ulong";
171 if (gt=="gshort" && ct=="gshort") return "short";
172 if (gt=="gshort" && ct=="short") return "short";
173 if (gt=="gushort" && ct=="gushort") return "ushort";
175 version (None) {
176 if (gt=="gint" && ct=="signed") return "c_int";
177 if (gt=="gint" && ct=="int") return "c_int";
178 if (gt=="gint" && ct=="gint") return "c_int";
179 if (gt=="gint" && !ct) return "c_int"; // gtk2 needs it.
180 if (gt=="guint" && ct=="guint") return "c_uint";
181 if (gt=="guint" && ct=="unsigned int") return "c_uint";
182 if (gt=="guint" && ct=="unsigned") return "c_uint";
183 } else {
184 // Assumes 32-bit wide target ints, but is far more readable...
185 if (gt=="gint" && ct=="signed") return "int";
186 if (gt=="gint" && ct=="int") return "int";
187 if (gt=="gint" && ct=="gint") return "int";
188 if (gt=="gint" && !ct) return "int"; // gtk2 needs it.
189 if (gt=="guint" && ct=="guint") return "uint";
190 if (gt=="guint" && ct=="unsigned int") return "uint";
191 if (gt=="guint" && ct=="unsigned") return "uint";
193 if (gt=="gfloat" && ct=="gfloat") return "float";
194 if (gt=="gfloat" && ct=="float") return "float";
195 if (gt=="gdouble" && ct=="double") return "double";
196 if (gt=="gdouble" && ct=="gdouble") return "double";
197 if (gt=="long double" && ct=="long double") return "real";
199 if (gt=="none" && ct=="void") return "void";
201 if (gt=="gunichar" && ct=="gunichar") return "dchar";
202 if (gt=="guint16" && ct=="gunichar2") return "wchar";
204 if (gt=="glong" && ct=="long") return "c_long";
205 if (gt=="glong" && ct=="glong") return "c_long";
206 if (gt=="gulong" && ct=="gulong") return "c_ulong";
207 if (gt=="gulong" && ct=="unsigned long") return "c_ulong";
209 if ( ct=="size_t") return "size_t";
210 if (gt=="gsize" && ct=="gsize") return "size_t";
211 if (gt=="gssize" && ct=="gssize") return "ssize_t";
212 if (gt=="gint64" && ct=="goffset") return "long"; // signed 64bit int, C99: off64_t
214 if (gt=="gchar" && ct=="gchar") return "char";
215 if (gt=="gchar" && ct=="char") return "char";
216 if (gt=="gchar" && ct=="guchar") return "ubyte";
218 if (gt=="utf8" && ct=="char") return "char";
219 if (gt=="utf8" && ct=="gchar") return "char";
221 if (gt=="guint8" && ct=="char") return "ubyte";
223 if (gt=="gpointer" && ct=="void") return "void";
224 if (gt=="gpointer" && !ct) return "void*"; // GTK2
225 if (gt=="gpointer" && ct=="gpointer") return "void*";
227 if (gt=="guint8" && ct=="void") return "ubyte"; // used for buffer *'s.
228 if (gt=="guint8" && ct=="gpointer") return "void*";
229 if (gt=="gpointer" && ct=="gconstpointer") return "const(void)*";
231 if (gt=="guint8" && ct=="gconstpointer") return "const(ubyte)*";
232 if (gt=="Variant" && ct=="gconstpointer") return "const(Variant)*";
233 if (gt=="VariantType" && ct=="gconstpointer") return "const(VariantType)*";
234 // gobject:
235 if (gt=="Object" && ct=="gpointer") return "Object*";
236 if (gt=="TypeClass" && ct=="gpointer") return "TypeClass*";
237 if (gt=="TypeInterface" && ct=="gpointer") return "TypeInterface*";
239 if (gt=="DBusObjectManagerClient" && ct=="GDBusObjectManager") return "DBusObjectManagerClient";
242 if (gt=="gboolean" && ct=="gboolean") return "int";
243 if (gt=="glong" && ct=="time_t") return "time_t"; // well...
245 if (gt=="guint" && ct=="uid_t") return "uint"; // Good enough?
247 // GLib:
248 if (gt=="gpointer" && ct=="GArray") return "Array";
249 if (gt=="gpointer" && ct=="GByteArray") return "ByteArray";
250 if (gt=="gpointer" && ct=="GPtrArray") return "PtrArray";
251 if (gt=="gpointer" && ct=="FILE") return "FILE";
253 if (gt=="guint8" && ct=="GByteArray") return "ByteArray";
255 // Not even the module names are used consistently...
257 void fixupmodnames(scope string from, scope string to) {
258 auto orggt = gt;
259 auto fl = from.length+1; // '+1' cause of the trailing '.'.
260 if (gt.length>=fl && gt[0..fl]==from ~ ".") {
261 gt = to ~ "." ~ gt[fl..$];
263 // Don't change ct for "GObject.ObjectClass" "GObjectClass"
264 if (ct.length>1 && orggt[fl..$]==ct[1..$])
265 return;
267 fl--;
268 if (ct.length>fl && from == ct[0..fl])
269 ct = to ~ ct[fl..$];
273 fixupmodnames("GLib", "GLib2");
274 fixupmodnames("GdkPixbuf", "GdkPixbuf2");
275 fixupmodnames("GObject", "GObject2");
276 fixupmodnames("Gio", "Gio2");
277 fixupmodnames("Gdk", "Gdk2");
278 fixupmodnames("fontconfig", "fontconfig2");
280 if (gt=="va_list" && ct=="va_list") return "va_list";
281 if (gt=="TypeInfo" ) return "GTypeInfo"; // To avoid clashes with D's base object.
282 // Note: "GTypeInfo" maps to different things in different scopes (modules).
284 // gobject
285 if (gt=="GType" && ct=="GType") return "Type";
286 if (gt=="GObject2.Object" && ct=="gpointer") return "GObject2.Object*";
288 if (gt=="GdkPixbuf2.Pixbuf" && ct=="GdkPixbuf") return "GdkPixbuf2.Pixbuf"; // in gdk
290 if (gt=="cairo.Surface" && ct=="cairo_surface_t") return "cairo.Surface"; // in gdk
291 if (gt=="cairo.Context" && ct=="cairo_t") return "cairo.Context"; // in gdk
292 if (gt=="cairo.FontOptions" && ct=="cairo_font_options_t") return "cairo.FontOptions"; // in gdk
293 if (gt=="cairo.Content" && ct=="cairo_content_t") return "cairo.Content"; // in gdk
294 if (gt=="cairo.Pattern" && ct=="cairo_pattern_t") return "cairo.Pattern"; // in gdk
296 // Cogl:
297 if (ct=="CoglDisplay") return "Cogl.Display";
298 if (ct=="CoglContext") return "Cogl.Context";
299 if (ct=="CoglRenderer") return "Cogl.Renderer";
301 // Clutter:
303 if (gt=="cairo.RectangleInt" && ct=="cairo_rectangle_int_t") return "cairo.RectangleInt";
304 if (gt=="cairo.Path" && ct=="cairo_path_t") return "cairo.Path";
306 if (gt=="Stage" && ct=="ClutterActor") return "Clutter.Stage";
308 // ClutterX11:
310 "xlib.XEvent" "XEvent" "xlib.XEvent"
311 "xlib.Pixmap" "Pixmap" "xlib.Pixmap"
312 "xlib.Window" "Window" "xlib.Window"
313 "xlib.Time" "Time" "xlib.Time"
314 "xlib.Display" "Display" "xlib.Display"
316 if (gt.length>ct.length+2) {
317 auto l = ct.length;
318 if (gt[$-l-1..$]==("."~ct))
319 return gt;
322 // PangoCairo:
323 if (gt=="freetype2.Library" && ct=="FT_Library") return "freetype2.Library";
324 if (gt=="cairo.ScaledFont" && ct=="cairo_scaled_font_t") return "cairo.ScaledFont";
325 if (gt=="cairo.FontType" && ct=="cairo_font_type_t") return "cairo.FontType";
327 if (ct=="PangoFcFontMap") return "Pango.FontMap";
329 // PangoFT2:
330 if (gt=="fontconfig2.Pattern" && ct=="FcPattern") return "fontconfig2.Pattern";
331 if (gt=="freetype2.Face" && ct=="FT_Face") return "freetype2.Face";
332 if (gt=="freetype2.Bitmap" && ct=="FT_Bitmap") return "freetype2.Bitmap";
335 // Ugh:
336 // "Pango.Font" "PangoFont*" => "Pango.Font*"; // in gdk
337 // "Pango.GlyphString" "PangoGlyphString*" => "Pango.GlyphString*"; // in gdk
338 // "Pango.Direction" "PangoDirection" => "Pango.Direction"
340 auto mlen = countUntil(gt, ".");
341 if (mlen>0 && ct.length>mlen && gt[0..mlen]==ct[0..mlen]) {
342 auto ctapos = countUntil(ct, "*");
343 if (ctapos==-1)
344 ctapos = ct.length;
345 if (ctapos>0 && gt[mlen+1..$]==ct[mlen..ctapos] ) {
346 gt = gt ~ ct[ctapos..$];
348 // Make things like "GL.uint" valid:
349 auto lastpi = lastIndexOf(gt, '.');
350 if (lastpi>1)
351 gt = gt[0..lastpi+1] ~ subDKeyword(gt[lastpi+1..$]);
352 return gt;
357 // ATK needs this one, as the datatype is missing both name and definition; this lets it build.
358 if (gt=="AtkPropertyValues" && ct=="AtkPropertyValues") return "_PropertyValues";
360 // GDK:
361 if (gt=="GdkPixbuf2.PixbufAlphaMode" && ct=="GdkPixbuf2AlphaMode") return "GdkPixbuf2.PixbufAlphaMode";
363 // GTK:
364 if (gt=="filename" && ct=="gchar") return "char"; // Probably const, but...
365 if (gt=="filename" && !ct) return "char*"; // Probably const, but...
367 if (gt=="GdkPixbuf2.PixbufAnimation" && ct=="GdkPixbuf2Animation") return "GdkPixbuf2.PixbufAnimation";
368 if (gt=="GdkPixbuf2.PixbufAnimationIter" && ct=="GdkPixbuf2AnimationIter") return "GdkPixbuf2.PixbufAnimationIter";
370 // Signals:
371 // In fact if this is for a signal and has no <ct> it probably should be pointer...
372 // We're gonna guess, and we'll be wrong sometimes. But we will be right in many
373 // cases and there isn't really much else that we could do...
374 if (signals && !ct && gt[$-1]!='*') {
375 if (gt=="GObject2.Error") return "GLib2.Error*";
377 if (std.string.indexOf(gt,'.')!=-1) return gt ~ "*";
378 if (gt[0]>='A' && gt[0]<='Z') return gt ~ "*";
380 switch (gt) {
381 case "gfloat": return "float";
382 case "gdouble": return "double";
383 case "none": return "void";
384 case "utf8": return "char*";
385 case "gint": return "c_int";
386 case "guint": return "c_uint";
387 case "gboolean": return "c_int";
388 default:
392 // Fix up some obviously broken types.
393 if (!ct) {
394 if (gt=="Object") return "Object*";
397 version(all) { // Disable this while debugging.
398 if (gt==ct)
399 return gt;
402 if (gt && (!ct || ct==""))
403 return gt; // what else?...
405 enforce(gt && ct, "Missing type: GTK: \"" ~ gt ~ "\" C: \"" ~ ct ~ "\"");
407 foreach (prefix; ident_prefixes) {
408 auto l = prefix.length;
409 if (ct.length>l && ct[0..l]==prefix) {
410 // Try Quark" "GQuark -> "Quark"
411 string stripped_ct = ct[l..$];
412 if (stripped_ct==gt)
413 return stripped_ct;
417 // Last try: if ctype starts with 'G' and last dot-delimited component
418 // of gtktype equals the post-G ctype, use that.
419 // Eg. "Gio.AppLaunchContext", "GAppLaunchContext" => "Gio.AppLaunchContext"
420 if (ct[0]=='G') {
421 auto tail = "." ~ ct[1..$];
422 if(gt.length>=tail.length && gt[$-tail.length..$] == tail)
423 return gt;
426 enforce(0, "Unknown type: GTK: \"" ~ gt ~ "\" C: \"" ~ ct ~ "\"");
427 assert(0); // @noreturn
430 // Set string <dst> to <aa[index]>, but only if <aa> contains such value.
431 // if <req> is given assert if the value is missing.
432 void aassign(ref string dst, in string[string] aa, in string index, string req=null) {
433 if (auto p = index in aa)
434 dst = *p;
435 else if (req)
436 enforce(0, "Missing " ~ req ~ "; Available: " ~ to!string(aa));
438 string get_name_attr(in Element e) {
439 auto tag = e.tag;
440 if (auto p = "name" in tag.attr) {
441 // Debugging checks, harmless, can be left enabled.
442 if (*p!="GLib.List" && *p!="GLib.SList"&& *p!="GLib.HashTable")
443 enforce(tag.type==TagType.EMPTY, tag.toString());
444 // <alias>es trigger this one.
445 //enforce(tag.attr.length==1, to!string(tag.attr.length));
446 return *p;
448 return null;
451 string simplify_version(scope string v, scope string modname=null) pure {
452 string result = v;
453 if (v=="1.0")
454 return "";
455 else if (v[1..$]==".0")
456 result = v[0..1];
457 if (modname && result.length==1 && modname[$-1]==result[0])
458 return "";
459 return result;
462 class x_elem {
463 string tagname, doc, dochead, ver;
465 this(string s) { tagname = s; }
467 final void parse_doc_text(ElementParser ep) {
468 ep.onText = (string s) {
469 trace("DOC", "", s);
470 if (dochead)
471 doc ~= "\n" ~ dochead ~ ": ";
472 doc ~= s;
474 SkipEndTag(ep, "doc");
475 ep.parse();
478 final void register_doc_parser(ElementParser ep, string dochead=null) {
479 ep.onStartTag["doc"] = &parse_doc_text;
481 auto tag = ep.tag();
483 if (auto p = "version" in tag.attr)
484 ver = " " ~ *p;
485 this.dochead = dochead;
487 final string comment_to_D() {
488 import std.array;
489 string r;
490 if (doc) {
491 while (doc[$-1]=='\n')
492 doc = doc[0..$-1];
493 while (doc[0]=='\n')
494 doc = doc[1..$];
495 r = "\n// " ~ replace(doc, "\n", "\n// ");
496 r = replace(r, "\n// ", "\n" ~ indent ~ "// ") ~ "\n";
497 // Remove the newline before one-line comments.
498 import std.algorithm;
499 if (count(r, '\n')<=2)
500 r = r[1..$];
502 return r;
505 final void parsearray(ElementParser p, ref string _type, ref string _ctype,
506 bool makepointer=0) {
507 p.onStartTag["array"] = (ElementParser ep) {
508 aassign(_ctype, ep.tag().attr, "c:type");
509 if (makepointer)
510 _ctype ~= "*";
511 ep.onStartTag["type"] = (ElementParser ep) { ep.parse(); };
512 ep.onEndTag["type"] = (in Element e) {
513 trace("ELEMARR", tagname, e.tag.toString());
514 aassign(_type, e.tag.attr, "name");
516 ep.parse();
521 final class x_include : x_elem {
522 string name, ver;
524 this(string s) { super(s); }
526 void parse(in Element e) {
527 auto tag = e.tag;
528 enforce(tag.attr.length==2);
529 name = tag.attr["name"];
530 ver = tag.attr["version"];
533 override string toString() {
534 string modulebasename = name ~ simplify_version(ver, name);
535 string modulebasenamel = toLower(modulebasename);
536 // If we use "import GLib2" the compiler will fail to find the module
537 // while building the other ones.
538 // If we use "import gtk2.GLib2" the compiler will complain about
539 // missing Glib2.* types.
540 // NOTE: Imports are "public" so that apps don't have to import Gdk
541 // just for types only used for callback args etc.
542 return indent ~ "public import " ~ PACKAGE_NAME ~ "." ~ modulebasenamel ~ ";\n"
543 ~ indent ~ "alias " ~ PACKAGE_NAME ~ "." ~ modulebasenamel ~ " "
544 ~ modulebasename ~ ";";
548 string thismodulename;
550 final class x_namespace : x_elem {
551 string name, ver;
553 this(string s) { super(s); }
555 void parse(ElementParser ep) {
556 auto attr = ep.tag().attr;
557 name = attr["name"];
558 ver = attr["version"];
560 writeln();
562 if (auto p = "c:symbol-prefixes" in attr) {
563 symbol_prefixes = csv2arr(*p);
564 writeln("// c:symbol-prefixes: ", symbol_prefixes);
566 if (auto p = "c:identifier-prefixes" in attr) {
567 ident_prefixes = csv2arr(*p);
568 write("// c:identifier-prefixes: ", ident_prefixes, "\n\n");
571 // We do not parse further, ie no ep.parse() here.
574 override string toString() {
575 thismodulename = name ~ simplify_version(ver, name);
576 string s = indent ~ "// module " ~ thismodulename ~ ";\n";
577 // Per module fixups:
578 s ~= load_mixin("_MODULE_HEAD");
579 load_delete();
581 return s;
585 // "ALNUM" <= shortenconst("alnum", "G_ASCII_ALNUM")
586 string shortenconst(scope string name, scope string cname) {
587 if (cname.length>=name.length) {
588 auto shortname = cname[($-name.length)..$];
589 if (shortname==toUpper(name))
590 return validDSym(shortname);
592 return cname;
595 final class x_bitfield : x_elem {
596 string name;
597 x_field[] members;
599 this(string s) { super(s); }
601 void parse(ElementParser ep) {
602 trace("BITFIELD", tagname, ep.tag().toString());
603 auto tag = ep.tag();
604 name = tag.attr["name"];
606 register_doc_parser(ep);
607 ep.onStartTag["member"] = (ElementParser ep) {};
608 ep.onEndTag["member"] = (in Element e) {
609 enforce(e.tag.type==TagType.EMPTY, e.tag.toString());
611 auto m = new x_field("member");
612 members ~= m;
614 auto mattr = e.tag.attr;
615 aassign(m.name, mattr, "name", "bitfield name");
616 aassign(m.value, mattr, "value", "bitfield value");
617 aassign(m.identifier, mattr, "c:identifier", "bitfield c:identifier");
620 ep.onStartTag["function"] = (ElementParser ep) {
621 ew("FIXME: Skipping enum func: ", name, ".", ep.tag().attr["name"]);
622 // a gobject-introspection update made functions appear inside
623 // <enumeration> tags. We could handle this by using struct
624 // namespaces, but let's ignore these "functions" for now.
625 ep.parse();
627 ep.onEndTag["function"] = (in Element e) {
630 ep.parse();
633 override string toString() {
634 // Do we care about the basetype (ie do 31bits+ values happen?)
635 string r = comment_to_D() ~ indent ~ "enum " ~ name;
636 if (ver)
637 r ~= " /* Version" ~ ver ~ " */";
638 r ~= " {\n";
639 indent++;
641 foreach (i, m; members) {
642 long v = to!long(m.value);
643 if (v==0x8000_0000L)
644 m.value = "cast(uint)" ~ m.value;
645 r ~= indent ~ shortenconst(m.name, m.identifier) ~ " = " ~ m.value ~
646 (i!=members.length-1 ? "," : "") ~ "\n";
649 return r ~ --indent ~ "}";
653 x_field gerrfield; // pseudo field; used by functions that "throw" in gtk-speak.
654 static this() {
655 gerrfield = new x_field("GError");
656 gerrfield.type = "GLib2.Error**";
657 gerrfield.name = "error=null";
658 gerrfield.cname = "error";
661 final class x_field : x_elem {
662 string type, name, cname;
663 string value; // for constants
664 string identifier; // for bitfields
665 string ctype;
666 bool varargs; // true if this parameter is "..."
667 bool private_;
668 string def_init;
669 string direction;
670 x_struct struct_; // embedded struct/union;
671 x_function callback; // a callback, ie embedded function pointer.
672 x_elem docparent; // parent element, where we place our doc.
673 int bitfield; // if >0 then we need to handle it specially...
675 this(string s, x_elem parent=null) { super(s); docparent=parent;}
677 override string toString() {
678 string r = comment_to_D();
680 if (auto p = name in delete_symbol)
681 return "\n" ~ indent ~ "// " ~ tagname ~ " \"" ~ name ~ "\" removed: " ~ *p ~ "\n";
683 if (struct_)
684 return r ~ struct_.toString();
686 switch (tagname) {
687 case "constant":
688 switch (type) {
689 case "utf8":
690 r ~= indent ~ "enum " ~ name ~ " = \"" ~ escdquote(value) ~ "\";";
691 break;
692 default:
693 string contype = Dtype(type, ctype);
695 // "int MUTEX_DEBUG_MAGIC = cast(int)4175530711;" needs the cast...
696 string concast;
697 if (contype=="int") {
698 long conval = to!long(value);
699 if (conval>int.max && conval<=uint.max)
700 concast = "cast(int)";
703 // Clutter has constants with *names* starting with a digit...
704 auto validname = validDSym(name);
706 r ~= indent ~ "enum " ~ contype ~ " " ~ validname ~ " = " ~ concast ~ value ~ ";";
708 break;
709 case "alias":
710 string dtype = Dtype(type, ctype);
711 // We're renaming types, and that can cause problems, as in Gtk2.Type case.
712 if (dtype==name)
713 r ~= "/* ";
714 r ~= indent ~ "alias " ~ dtype ~ " " ~ name ~ ";" /*~ "\t// " ~ ctype*/;
715 if (dtype==name)
716 r ~= " */";
717 break;
718 // case "field": /* Fields in classes/structs are handled in containers. */
719 default:
720 enforce(0, "Unknown field type: " ~ tagname);
722 return r;
725 void parse(ElementParser ep) {
726 trace("FIELD", tagname, ep.tag().toString());
727 SkipEndTag(ep, tagname);
728 auto tag = ep.tag();
730 auto field_attr = tag.attr;
731 aassign(name, field_attr, "name");
732 aassign(value, field_attr, "value"); // Constants.
734 if (docparent)
735 docparent.register_doc_parser(ep, name ? "<" ~ name ~ ">": "PARAM");
736 else
737 register_doc_parser(ep);
739 if (auto p = "private" in field_attr)
740 private_ = *p=="1";
742 if (auto p = "bits" in field_attr) // Bitfields need special treatment.
743 bitfield = to!int(*p);
745 ep.onStartTag["type"] = (ElementParser ep) {};
746 ep.onEndTag["type"] = (in Element e) {
747 auto a = e.tag.attr;
748 aassign(ctype, a, "c:type");
749 if (tagname=="alias" || tagname=="parameter") {
750 /* GLib.List etc have extra info in child, ignored for now */
751 auto p = "name" in a;
752 if ( !p || ( *p!="GLib.List" && *p!="GLib.SList" && *p!="GLib.HashTable" ) )
753 enforce(e.tag.type==TagType.EMPTY, e.tag.toString());
755 switch (tagname) {
756 default:
757 type = get_name_attr(e);
761 onEmptyTag(ep, "attribute", (in Element e) {
762 auto tag = e.tag;
763 enforce(tag.attr.length==2);
764 enforce(tag.attr["name"]=="c:identifier");
765 identifier = tag.attr["value"];
768 if (tagname=="parameter") {
769 if (auto p = "allow-none" in field_attr) {
770 if (*p=="1")
771 def_init = "=null";
773 if (auto p = "direction" in field_attr)
774 direction = "/*" ~ *p ~ "*/ ";
776 parsearray(ep, type, ctype);
777 ep.onStartTag["varargs"] = (ElementParser ep) {
778 name = "...";
779 varargs = 1;
780 SkipEndTag(ep, "varargs");
781 ep.parse();
785 if (tagname=="field") {
786 ep.onStartTag["array"] = (ElementParser ep) {};
787 ep.onEndTag["array"] = (in Element e) {
788 trace("FIELDARR", tagname, e.tag.toString());
789 string size;
790 aassign(size, e.tag.attr, "fixed-size");
791 if (auto p = "readable" in field_attr) {
792 if (!size && *p=="0")
793 // Generates zero sized array, but as long as all instances
794 // are private and not followed by public fields - harmless.
795 size = "0";
797 //enforce(size);
798 // Ugh, eg GTK.InputDialogClass contains embedded arrays w/o size.
799 // Not usuable anyway, so we only care about it being syntactically correct.
800 if (!size)
801 size = "666";
803 if (size=="-1")
804 size = "/*GIR: -1*/ 0"; // Hmm, does this really belong in GIR...
806 // Using D syntax for arrays.
807 type ~= "[" ~ size ~ "]";
808 ctype ~= "[" ~ size ~ "]";
812 ep.onStartTag["callback"] = (ElementParser ep) {
813 callback = new x_function("functionp");
814 callback.parse(ep);
817 ep.parse();
821 bool methodtemplates = 1;
823 final class x_function : x_elem {
824 string name, cname;
825 x_struct obj; // object if this is a method.
826 string rettype, crettype, retanno;
827 string throws; // actually gerrors, oh well.
828 bool is_variadic;
829 x_field[] args;
831 this(string s, x_struct pobj=null) { super(s); obj = pobj; }
833 string c_func(bool proto, bool skipthis, scope const(x_field)[] args) {
834 string r;
836 if (skipthis)
837 args = args[1..$];
839 r ~= validDSym(cname) ~ "(";
841 foreach(i, a; args) {
842 if (a.varargs)
843 r ~= "..."; // Variadic - have to be aliased (avoids having to deal w/ varargs).
844 else {
845 if (proto)
846 r ~= a.direction ~ Dtype(a.type, a.ctype) ~ " ";
847 r ~= a.cname ? validDSym(a.cname) : validDSym(a.name);
848 if (proto)
849 r ~= a.def_init;
850 if (i!=args.length-1)
851 r ~= ", ";
855 r ~= ")";
856 return r;
859 override string toString() {
860 string Drettype = Dtype(rettype, crettype);
861 string r = comment_to_D() ~ indent;
862 bool hasthis = 0;
863 bool refret;
865 if (auto p = cname in delete_symbol) {
866 r ~= "\n" ~ indent ~ "// " ~ tagname ~ " \"" ~ name ~ "\" removed: " ~ *p ~ "\n";
869 if (name=="") {
870 auto pos = std.string.indexOf(cname, '_');
871 name = "/*NAME MISSING IN GIR*/ ";
872 if (pos==-1)
873 name ~= "missing_" ~ cname;
874 else
875 name ~= cname[pos+1..$];
878 bool is_funcp = tagname=="callback" ||
879 tagname=="functionp" ||
880 tagname=="virtual-method";
882 // Fix up the argument list.
884 auto allargs = args;
886 if (obj) {
887 auto t = new x_field("this_");
888 t.type = obj.name ~ "*";
889 t.name = "this_";
891 if (tagname!="constructor") {
892 // Methods have a this pointer as the first arg.
893 allargs = t ~ allargs;
895 if (tagname!="function")
896 hasthis = 1;
898 else {
899 // Constructors return pointers to new objects, not some base type.
900 Drettype = obj.name ~ "*";
904 if (throws)
905 allargs ~= gerrfield;
907 foreach(i, a; allargs) {
908 // Fix up any missing arg names.
909 if (!a.name)
910 a.name = "arg_" ~ cast(char)('a'+i);
911 // Gtk2 has out params w/o a ctype and "gint" type - we'll do our best to fix it...
912 if (a.direction=="/*out*/ " && !a.ctype /*&& a.type[$-1]!='*'*/) {
913 a.ctype = a.type ~ "*";
914 a.direction ~= "/*POINTER*/ ";
916 // Fix up default params.
917 if (a.def_init=="=null")
918 switch (Dtype(a.type, a.ctype)) {
919 case "int": a.def_init = "=0"; break;
920 default: /**/; break;
924 // Remove default initializers from args that are followed by ones w/o defaults.
926 bool nondefarg_seen = 0;
927 foreach_reverse(a; allargs) {
928 if (!a.def_init && !a.varargs)
929 nondefarg_seen = 1;
930 if (nondefarg_seen)
931 a.def_init = null;
935 if (auto p = cname in delete_symbol)
936 goto add_cproto; // Don't emit anything, but the C prototype.
938 if (tagname=="glib:signal") {
939 Drettype = Dtype(rettype, crettype, 1);
941 // Signals have an extra 'user_data' pointer as the last arg.
942 auto udata = new x_field("user_data");
943 udata.type = "void*";
944 udata.name = "user_data=null"; // "=null" JIC previous args were optional.
945 allargs = allargs ~ udata;
947 r ~= "extern (C) ";
948 r ~= "alias ";
949 r ~= "static ";
950 r ~= Drettype ~ " " ~ retanno;
951 r ~= "function (";
953 foreach(i, a; allargs) {
954 r ~= a.direction ~ Dtype(a.type, a.ctype, 1) ~ " " ~ validDSym(a.name) ~ a.def_init;
955 if (i!=allargs.length-1)
956 r ~= ", ";
959 r ~= ")";
960 r ~= " signal_" ~ validDSym(name);
961 r ~= ";\n";
963 // Add some convenience templates:
965 if (!obj.emitted("signal_connect")) {
966 r ~= "\n";
967 r ~= indent ~ "ulong signal_connect(string name, CB)(CB cb, void* data=null, ConnectFlags cf=cast(ConnectFlags)0) {\n";
968 r ~= ++indent ~ "return super_.signal_connect!name(cb, data, cf);\n";
969 r ~= --indent ~ "}\n\n";
972 // Callback /type/ not checked, as doing it results in weird errors
973 // and very unhelpful template instantiation error messages, like:
975 // Error: template instance Clutter.Actor.signal_connect!(name,int function(Actor* stage, Event* event, void* data)) error instantiating
976 // instantiated from here: signal_connect!("button-press-event",int function(Actor* stage, Event* event, void* data))
977 // Error: template instance Clutter.Stage.signal_connect!("button-press-event",int function(Actor* stage, Event* event, void* data)) error instantiating
978 r ~= indent ~ `ulong signal_connect(string name:"` ~ name ~ `", `;
979 r ~= `CB/*:signal_` ~ validDSym(name) ~ "*/)(CB cb, void* data=null, ConnectFlags cf=cast(ConnectFlags)0) {\n";
980 r ~= ++indent ~ `return signal_connect_data` ~
981 (methodtemplates ? "!()" : "") ~
982 `(&this, cast(char*)"` ~ name ~ "\",\n";
983 r ~= indent ~ "cast(GObject2.Callback)cb, data, null, cf);\n";
984 r ~= --indent ~ "}\n";
986 return r;
989 if (is_variadic) {
990 bool unsupported = tagname!="function" &&
991 tagname!="constructor";
992 if (unsupported) {
993 r ~= "/+ Not available -- variadic " ~
994 ( hasthis ?
995 "methods unsupported - use the C function directly.\n"
997 "functions with non-D linkage must have >1 parameter.\n" );
998 r ~= ++indent;
1001 r ~= "alias " ~ cname ~ " " ~ validDSym(name) ~ "; // Variadic\n";
1002 if (unsupported)
1003 r ~= --indent ~ "+/\n";
1005 goto add_cproto;
1008 bool hasUpperCase(string s) {
1009 import std.uni;
1010 foreach(c; s)
1011 if (isUpper(c))
1012 return 1;
1013 return 0;
1016 string refDrettype = Drettype;
1017 if (reftypes && Drettype[$-1]=='*' && hasUpperCase(Drettype)) {
1018 refDrettype = Drettype[0..$-1];
1019 refret = 1;
1022 if (is_funcp)
1023 r ~= "extern (C) ";
1024 if (tagname=="callback")
1025 r ~= "alias ";
1026 if (tagname=="constructor" || tagname=="function")
1027 r ~= "static ";
1029 if (refret)
1030 r ~= "Ref!(" ~ refDrettype ~ ")";
1031 else
1032 r ~= Drettype;
1034 r ~= " " ~ retanno;
1036 if (is_funcp)
1037 r ~= "function (";
1038 else
1039 r ~= validDSym(name) ~ (methodtemplates ? "()(" : "(");
1041 auto args = allargs;
1043 if (hasthis)
1044 args = args[1..$];
1046 foreach(i, a; args) {
1047 if (a.varargs)
1048 r ~= "...";
1049 else {
1050 r ~= a.direction ~ Dtype(a.type, a.ctype) ~ " " ~ validDSym(a.name) ~ a.def_init;
1051 if (i!=args.length-1)
1052 r ~= ", ";
1056 r ~= ")";
1058 if (is_funcp)
1059 r ~= " " ~ validDSym(name);
1061 if (tagname=="functionp")
1062 return r;
1064 if (tagname=="callback" || tagname=="virtual-method") {
1065 r ~= ";\n";
1066 return r;
1069 r ~= " {\n" ~ ++indent;
1071 if (Drettype!="void")
1072 r ~= "return ";
1074 if (refret)
1075 r ~= "Ref!(" ~ refDrettype ~ ")(";
1077 string oname;
1078 if (hasthis) {
1079 oname = allargs[0].name;
1080 allargs[0].name = "&this";
1081 // If we know that the object implements something, then
1082 // the following cast should be safe.
1083 if (obj.tagname=="interface")
1084 allargs[0].name = "cast(" ~ obj.name ~ "*)&this";
1086 r ~= c_func(0, 0, allargs);
1087 if (refret)
1088 r ~= ")";
1089 r ~= ";\n" ~ --indent ~ "}\n";
1090 if (hasthis)
1091 allargs[0].name = oname;
1093 add_cproto:
1094 if (!is_funcp /*&& tagname=="function" || obj*/)
1095 cprotos ~= Drettype ~ " " ~ retanno ~ c_func(1, 0, allargs) ~ ";";
1097 return r;
1100 void parse(ElementParser ep) {
1101 trace("FUNC", tagname ~ (obj ? ": " ~ obj.name : ""), ep.tag().toString());
1102 // "callbacks" in structs are actually pointers.
1103 SkipEndTag(ep, tagname!="functionp" ? tagname : "callback");
1104 register_doc_parser(ep);
1105 auto tag = ep.tag();
1107 name = tag.attr["name"];
1108 aassign(cname, tag.attr, "c:identifier");
1109 aassign(throws, tag.attr, "throws");
1111 auto p = "introspectable" in tag.attr;
1112 if (p && *p=="0")
1113 doc ~= "Unintrospectable " ~ tagname ~ ": " ~ name ~ "() / " ~ cname ~ "()\n";
1115 p = "version" in tag.attr;
1116 if (p)
1117 doc ~= "VERSION: " ~ *p ~ "\n";
1119 p = "deprecated" in tag.attr;
1120 if (p) {
1121 string dv;
1122 if (auto p1 = "deprecated-version" in tag.attr)
1123 dv = "(v" ~ *p1 ~ ") ";
1124 doc ~= "DEPRECATED " ~ dv ~ tagname ~ ": " ~ name ~ " - " ~ *p ~ "\n";
1127 p = "moved-to" in tag.attr;
1128 if (p)
1129 doc ~= "MOVED TO: " ~ *p ~ "\n";
1131 ep.onStartTag["return-value"] = (ElementParser ep) {
1132 SkipEndTag(ep, "return-value");
1133 register_doc_parser(ep, "RETURNS");
1135 auto tag = ep.tag();
1137 if (auto p = "transfer-ownership" in tag.attr) {
1138 switch (*p) {
1139 case "none": retanno = ""; break;
1140 case "full": retanno = "/*new*/ "; break;
1141 case "container": retanno = "/*new container*/ "; break;
1142 default: enforce(0, "Unknown transfer-ownership: " ~ *p);
1146 parsearray(ep, rettype, crettype);
1148 ep.onStartTag["type"] = (ElementParser ep) {};
1149 ep.onEndTag["type"] = (in Element e) {
1150 auto tag = e.tag;
1151 aassign(crettype, tag.attr, "c:type");
1152 aassign(rettype, tag.attr, "name"/*, "func retval typename"*/);
1153 // If we have a ctype, but no type, try using the ctype.
1154 if (!rettype && crettype) {
1155 crettype = "/*CTYPE*/ " ~ crettype;
1156 rettype = crettype;
1158 enforce(rettype, "Missing func retval typename" );
1161 ep.parse();
1163 // Note the trick is there can be multiple nested <type>s;
1164 // once we get here we should have filled in the types properly.
1165 // Keep in mind we only care about the outermost type, that's
1166 // why parsing the _end_ </type> tag works for us.
1168 // Unfortunately c:type is often missing, so this check is too strict.
1169 //enforce(crettype, "Missing func retval c:type" ~ to!string(this));
1172 ep.onStartTag["parameters"] = (ElementParser pa) {
1173 trace("FUNCPARM", tagname, ep.tag().toString());
1174 SkipEndTag(pa, "parameters");
1175 pa.onStartTag["parameter"] = (ElementParser pa) {
1176 SkipEndTag(pa, "parameter");
1177 auto arg = new x_field("parameter", this); args ~= arg; arg.parse(pa);
1178 if (arg.varargs)
1179 is_variadic = 1;
1181 pa.parse();
1184 ep.parse();
1188 final class x_struct : x_elem {
1189 string name, parent;
1190 x_struct embedded_in;
1191 x_field[] fields;
1192 x_function[] methods;
1193 string[] implements;
1194 bool[string] emitted_;
1196 this(string tn, x_struct emb=null) {
1197 super(tn);
1198 embedded_in = emb;
1199 switch (tagname) {
1200 case "record": break;
1201 case "class": break;
1202 case "union": break;
1203 case "interface": break;
1204 default: enforce(0, "Unknown struct type: " ~ tagname);
1208 private bool emitted(string s) {
1209 if (auto p = s in emitted_)
1210 return 1;
1211 emitted_[s] = 1;
1212 return 0;
1215 private string basetypename(scope string type) {
1216 auto i = lastIndexOf(type, '.');
1217 return i==-1 ? type : type[i+1..$];
1220 override string toString() {
1221 // Everything looks like a struct to us. Except unions.
1222 string r = comment_to_D() ~ indent ~
1223 ((tagname=="union") ? "union " : "struct ") ~ subDKeyword(name);
1225 if (tagname=="interface") {
1226 enforce(fields.length==0, "Interface field overflow: " ~ name);
1227 r ~= " /* Interface */";
1229 if (parent)
1230 r ~= " /* : " ~ parent ~ " */";
1231 if (ver)
1232 r ~= " /* Version" ~ ver~ " */";
1233 r ~= " {\n";
1235 indent++;
1237 if (tagname=="interface") {
1238 r ~= indent++ ~ "mixin template __interface__() {";
1241 foreach (iface; implements)
1242 r ~= indent ~ "mixin " ~ Dtype(iface, null) ~ ".__interface__;\n";
1244 // "alias super this".
1246 // First, if this struct has a parent, but we don't know any of its
1247 // fields then add the parent as the only (sub)struct -- harmless as
1248 // we couldn't access the opaque empty struct anyway and this lets
1249 // the pseudo-inheritance work.
1250 if (parent && fields.length==0) {
1251 auto pf = new x_field("record");
1252 pf.type = parent;
1253 pf.ctype = parent;
1254 pf.name = "method_parent";
1255 fields ~= pf;
1258 if (parent && fields.length>0 && (
1259 Dtype(fields[0].type, fields[0].ctype)==Dtype(parent, null) ||
1260 Dtype(fields[0].type, fields[0].ctype)==parent
1261 )) {
1262 auto fieldname = validDSym(fields[0].name);
1264 r ~= indent ~ "alias " ~ fieldname ~ " this;\n";
1265 r ~= indent ~ "alias " ~ fieldname ~ " super_;\n";
1267 // Add an extra alias for easier upcasting.
1268 // NOTE: The name we add could clash with existing symbols;
1269 // this does not happen in practice, will be caught at
1270 // compiletime if it does, and can be addressed then.
1271 // Also "object" is not the safest symbol to add, but
1272 // works so far, let's wait until it causes problems
1273 // before renaming it to something like "gobject".
1274 auto partype = fields[0].type;
1275 if (!partype)
1276 partype = Dtype(null, fields[0].ctype);
1277 auto aname = basetypename(validDSym(partype));
1278 aname = toLower(aname);
1279 if (aname!=fieldname)
1280 r ~= indent ~ "alias " ~ fieldname ~ " " ~ aname ~ ";\n";
1282 // The parent cannot be private, so fix it:
1283 if (fields[0].private_)
1284 fields[0].private_ = 0;
1287 // No bitfields in D.
1288 string bmix;
1289 int bfwidth;
1290 char bfcount='A'; // More than one __dummyNN names? Must be different.
1292 void eofbf() {
1293 if (!bmix)
1294 return;
1296 // For now we'll assume all bitfields are at least 32bits wide (C int-sized)
1297 int rawwidth;
1298 if (bfwidth>0 && bfwidth<=32)
1299 rawwidth = 32;
1300 else if (bfwidth>32 && bfwidth<=64)
1301 rawwidth = 64;
1302 enforce(rawwidth, bmix);
1304 // std.bitmaip reqs total width to be one of 8/16/32/64, so:
1305 if (rawwidth!=bfwidth) {
1306 if (rawwidth<=8 && bfwidth>0 && bfwidth<8)
1307 bmix ~= ",\n" ~ indent ~ " uint, \"__dummy8"~bfcount~"\", " ~ to!string(8-bfwidth);
1308 else if (rawwidth<=16 && bfwidth<16)
1309 bmix ~= ",\n" ~ indent ~ " uint, \"__dummy16"~bfcount~"\", " ~ to!string(16-bfwidth);
1310 else if (rawwidth<=32 && bfwidth<32)
1311 bmix ~= ",\n" ~ indent ~ " uint, \"__dummy32"~bfcount~"\", " ~ to!string(32-bfwidth);
1312 else if (bfwidth<64)
1313 bmix ~= ",\n" ~ indent ~ " uint, \"__dummy64"~bfcount~"\", " ~ to!string(64-bfwidth);
1314 else if (bfwidth==64)
1316 else
1317 enforce(0);
1319 r ~= bmix ~ "))";
1320 bmix = null;
1321 bfwidth = 0;
1322 bfcount++;
1325 // Unfortunately there are methods that end up using the same name
1326 // as struct fields. Build a list of all method names, and in the
1327 // code below check for clashes. Rename the fields as these are
1328 // less likely to be used by apps.
1329 bool[string] m_names;
1330 foreach (m ; methods)
1331 m_names[m.name] = 1;
1333 foreach (i, f; fields) {
1334 string privstr = f.private_ ? "private " : "";
1336 if (f.callback) {
1337 eofbf();
1338 if (i!=0)
1339 r ~= ";\n";
1341 r ~= privstr ~ f.callback.toString();
1343 else if (f.struct_) {
1344 eofbf();
1345 if (i!=0)
1346 r ~= ";\n";
1347 else
1348 r ~= "\n";
1350 r ~= "\n" ~ privstr ~ f.struct_.toString();
1352 else if (f.bitfield) {
1353 if (!bmix) {
1354 if (i!=0)
1355 r ~= ";\n";
1356 bmix = indent ~ " static import std.bitmanip;"
1357 ~ " mixin(std.bitmanip.bitfields!(\n" ~ indent;
1359 else
1360 bmix ~= ",\n" ~ indent;
1361 string bftype = Dtype(f.type, f.ctype);
1362 switch (bftype) {
1363 case "uint", "int", "c_uint", "c_int":
1364 string fname = validDSym(f.name);
1365 if (fname in m_names)
1366 fname ~= "_";
1367 bmix ~= bftype ~ ", \"" ~ fname ~ "\", " ~ to!string(f.bitfield);
1368 bfwidth += f.bitfield;
1369 break;
1370 default:
1371 enforce(0, "Unknown bitfield type " ~
1372 bftype ~ "[" ~ f.type ~ "," ~ f.ctype ~ "] " ~
1373 f.name ~ ":" ~ to!string(f.bitfield));
1376 else {
1377 eofbf();
1378 string fname = validDSym(f.name);
1379 if (fname in m_names)
1380 fname ~= "_";
1381 if (i==0)
1382 r ~= indent ~ privstr ~ Dtype(f.type, f.ctype) ~ " " ~ fname;
1383 else {
1384 string prevtype, currtype;
1385 if (fields[i-1].type || fields[i-1].ctype)
1386 prevtype = Dtype(fields[i-1].type, fields[i-1].ctype);
1387 currtype = Dtype(f.type, f.ctype);
1388 if ( (prevtype == currtype && fields[i-1].type==f.type)
1389 && (fields[i-1].private_ == f.private_)
1390 && !fields[i-1].bitfield )
1391 r ~= ", " ~ fname;
1392 else {
1393 r ~= ((!fields[i-1].struct_) ? ";" : "");
1394 r ~= "\n"~ indent ~ privstr ~ currtype ~ " " ~ fname;
1400 eofbf();
1402 if (fields.length>0 && !fields[$-1].struct_)
1403 r ~= ";\n";
1405 if (fields.length>0 && methods.length>0)
1406 r ~= "\n";
1408 foreach (f; methods)
1409 r ~= f.toString();
1411 // Pull in the right mixin, if any:
1413 string mixname = name;
1415 for (auto ei = embedded_in; ei; ei = ei.embedded_in)
1416 mixname = ei.name ~ "_" ~ mixname;
1418 r ~= load_mixin(mixname);
1420 if (tagname=="interface") {
1421 r ~= --indent ~ "}\n";
1422 r ~= indent ~ "mixin __interface__;\n";
1425 return r ~ --indent ~ "}\n";
1428 void parse(ElementParser ep) {
1429 trace("STRUCT", tagname, ep.tag().toString());
1430 auto tag = ep.tag();
1431 name = tag.attr["name"];
1432 aassign(parent, tag.attr, "parent");
1434 register_doc_parser(ep);
1435 ep.onStartTag["field"] = (ElementParser ep) {
1436 auto nf = new x_field("field"); fields ~= nf; nf.parse(ep);
1438 ep.onStartTag["union"] = (ElementParser ep) {
1439 auto nf = new x_field("union"); fields ~= nf;
1440 auto na = new x_struct("union", this); nf.struct_ = na; na.parse(ep);
1442 ep.onStartTag["record"] = (ElementParser ep) {
1443 auto nf = new x_field("record"); fields ~= nf;
1444 auto na = new x_struct("record", this); nf.struct_ = na; na.parse(ep);
1446 ep.onStartTag["property"] = (ElementParser p) {
1447 p.parse(); // Drop them.
1449 ep.onStartTag["method"] = (ElementParser ep) {
1450 auto nm = new x_function("method", this); methods ~= nm; nm.parse(ep);
1452 ep.onStartTag["virtual-method"] = (ElementParser p) {
1453 p.parse(); // Drop them.
1455 ep.onStartTag["function"] = (ElementParser ep) {
1456 auto nm = new x_function("function"); methods ~= nm; nm.parse(ep);
1458 ep.onStartTag["constructor"] = (ElementParser ep) {
1459 auto nm = new x_function("constructor", this); methods ~= nm; nm.parse(ep);
1461 ep.onStartTag["glib:signal"] = (ElementParser ep) {
1462 auto nm = new x_function("glib:signal", this); methods ~= nm; nm.parse(ep);
1465 ep.onStartTag["implements"] = (ElementParser p) { p.parse(); };
1466 ep.onEndTag["implements"] = (in Element e) {
1467 string s;
1468 aassign(s, e.tag.attr, "name", "interface name");
1469 implements ~= s;
1472 ep.onStartTag["prerequisite"] = (ElementParser ep) { ep.parse(); }; // Drop.
1473 ep.onEndTag["prerequisite"] = (in Element e) {};
1475 ep.parse();
1479 void onUnknownTag(ElementParser ep, string tagname) {
1480 ep.onStartTag[null] = (ElementParser ep) {
1481 enforce(0, "Unhandled <"~tagname~"> start tag: "
1482 ~ ep.tag.name ~ "\n# " ~ ep.tag().toString());
1484 ep.onEndTag[null] = (in Element e) {
1485 enforce(0, "Unhandled <"~tagname~"> end tag: " ~ "\n# " ~ e.toString());
1489 void SkipEndTag(ElementParser p, string tagname) {
1490 onUnknownTag(p, tagname);
1491 p.onEndTag[tagname] = (in Element e) { };
1494 void onEmptyTag(ElementParser p, string tagname, void delegate(in Element e) deleg) {
1495 p.onStartTag[tagname] = (ElementParser ep) {
1496 /* do nothing; only tells the parser that we know about this tag */
1498 p.onEndTag[tagname] = (in Element e) {
1499 auto tag = e.tag;
1500 enforce(tag.type==TagType.EMPTY, tag.toString());
1502 deleg(e);
1506 void onStartTag(ElementParser p, string tagname, void delegate(ElementParser ep) deleg) {
1507 p.onStartTag[tagname] = (ElementParser ep) {
1508 onUnknownTag(ep, tagname);
1509 ep.onEndTag[tagname] = (in Element e) {/*Reached our own end tag*/};
1510 deleg(ep);
1512 p.onEndTag[tagname] = (in Element e) {
1513 /* Do nothing; only tells the parser that we know about this tag */
1514 /* Ugh. "<record name="AppLaunchContextPrivate" />" happens... */
1518 template OnTag(alias pa, string Kind, string xmlname, string tagname) {
1519 enum OnTag = "
1520 on"~Kind~"Tag(" ~ pa.stringof ~ ", \"" ~ xmlname ~ "\","
1521 " (" ~ ((Kind=="Start") ? "ElementParser e" : "in Element e") ~ ") {
1522 auto a = new x_"~tagname~"(" ~ xmlname.stringof ~ ");
1523 a.parse(e);
1524 "`write(a.toString() ~"\n");`
1525 "} );";
1528 string delete_symbol[string]; // [name: reason]
1530 void load_delete() {
1531 string filename = "mixin/" ~ thismodulename ~ "_" ~ "_DELETE_";
1532 string s;
1534 try {
1535 s = cast(string)std.file.read(filename);
1536 } catch (std.file.FileException) {
1537 if (tracemixins)
1538 ew("DELETE Failed: " ~ filename);
1539 return;
1542 foreach (line; split(s, "\n")) {
1543 auto arr = split(line, ":");
1544 if (arr.length==0)
1545 continue;
1546 if (arr.length==1)
1547 delete_symbol[line] = "";
1548 else
1549 delete_symbol[arr[0]] = strip(join(arr[1..$], ":"));
1553 bool tracemixins;
1555 string load_mixin(string id) {
1556 string filename = "mixin/" ~ thismodulename ~ "_" ~ id ~ ".d";
1557 string s;
1559 static bool uniq_mix[string];
1561 enforce(filename !in uniq_mix, "Mixin collission: " ~ filename);
1562 uniq_mix[filename] = 1;
1564 try {
1565 s = cast(string)std.file.read(filename);
1566 } catch (std.file.FileException) {
1567 if (tracemixins)
1568 ew("MIXIN Failed: " ~ filename);
1569 return null;
1572 if (tracemixins)
1573 ew("MIXIN Found: " ~ filename);
1575 string r = "\n" ~ indent ~ "// --- " ~ filename ~ " --->\n\n";
1577 foreach (sp; split(s, "\n"))
1578 r ~= indent ~ sp ~ "\n";
1580 r ~= indent ~ "// <--- " ~ filename ~ " ---\n";
1582 return r;
1585 bool reftypes = 0;
1587 void main(string[] args) {
1588 string infilename, outfilename, modulename;
1589 bool xmlcheck;
1591 try {
1592 // Parse cmd line:
1594 bool delegate(string arg) eatarg;
1596 cmdargloop: foreach(arg; args[1..$]) {
1597 if (eatarg) {
1598 if (eatarg(arg))
1599 eatarg = null;
1600 continue;
1602 if (arg[0]!='-') {
1603 if (!infilename) {
1604 infilename = arg;
1605 continue;
1607 die("Unknown extra cmdline file name: " ~ arg);
1609 if (arg.length<2)
1610 die("Missing cmdline option: " ~ arg);
1611 switch (arg[1]) {
1612 case '-':
1613 switch (arg[2..$]) {
1614 debug(TRACE) {
1615 case "trace": showtrace = 1; break;
1617 case "trace-mixins": tracemixins = 1; break;
1618 case "check": xmlcheck = 1; break;
1619 case "reftypes": reftypes = 1; break;
1620 case "no-reftypes": reftypes = 0; break;
1621 default: die("Unknown long cmdline option: " ~ arg);
1623 break;
1624 case 'o':
1625 if (arg.length>2)
1626 outfilename = arg[2..$];
1627 else
1628 eatarg = (string arg) { outfilename = arg; return true; };
1629 continue cmdargloop;
1630 case 'M':
1631 if (arg.length>2)
1632 outfilename = arg[2..$];
1633 else
1634 eatarg = (string arg) { modulename = arg; return true; };
1635 continue cmdargloop;
1636 default:
1637 die("Unknown cmdline option: " ~ arg);
1641 if (eatarg)
1642 die("Missing cmdline arg");
1644 string s = cast(string)std.file.read(infilename);
1646 if (xmlcheck)
1647 check(s); // Running the checks increases the execution time from 0.22s to 1.74s...
1649 if (outfilename)
1650 stdout.open(outfilename, "wt");
1652 writef("// *** DO NOT EDIT ***\n// Automatically generated from \"%s\"\n\n", infilename);
1654 if (modulename)
1655 write("module " ~ modulename ~ ";\n");
1657 auto dp = new DocumentParser(s);
1659 void delegate (ElementParser ep) epskip = (ElementParser ep) { ep.parse(); };
1660 void delegate (ElementParser p) epfixme = (ElementParser p)
1661 { p.parse(); ew("FIXME: " ~ p.toString()); };
1662 void delegate (in Element e) efixme = (in Element e) { ew("FIXME: " ~ e.toString()); };
1664 onUnknownTag(dp, "Document");
1666 onEmptyTag(dp, "package", (in Element e) {
1667 auto name = get_name_attr(e);
1668 write("\n// package: \"" ~ name ~ "\";\n");
1669 } );
1671 onEmptyTag(dp, "c:include", (in Element e) {
1672 auto name = get_name_attr(e);
1673 write("// C header: \"" ~ name ~ "\";\n");
1674 } );
1676 mixin( OnTag!(dp, "Empty", "include", "include") );
1677 mixin( OnTag!(dp, "Start", "namespace", "namespace") );
1678 dp.onEndTag["namespace"] = (in Element e) { }; // We're assuming one ns per file.
1680 dp.onEndTag["repository"] = (in Element e) { }; // We're assuming one repo per file.
1682 mixin( OnTag!(dp, "Start", "class", "struct") );
1683 mixin( OnTag!(dp, "Start", "record", "struct") );
1684 mixin( OnTag!(dp, "Start", "union", "struct") );
1685 mixin( OnTag!(dp, "Start", "interface", "struct") );
1687 mixin( OnTag!(dp, "Start", "constant", "field") );
1688 mixin( OnTag!(dp, "Start", "alias", "field") );
1690 mixin( OnTag!(dp, "Start", "bitfield", "bitfield") );
1691 mixin( OnTag!(dp, "Start", "enumeration", "bitfield") );
1693 mixin( OnTag!(dp, "Start", "function", "function") );
1694 mixin( OnTag!(dp, "Start", "callback", "function") );
1696 dp.onStartTag["glib:boxed"] = epskip;
1698 // Not handled yet. Can't eat them here, we need the types anyway.
1699 //dp.onStartTag["interface"] = epfixme;
1701 dp.parse();
1703 write(load_mixin("_MODULE"));
1705 write("\n// C prototypes:\n\nextern (C) {\n");
1706 // Some methods are also documented as functions and the D
1707 // compiler does not like duplicate prototypes; drop them.
1708 bool uniq_proto[string];
1709 foreach(s; cprotos) {
1710 if (s !in uniq_proto)
1711 writeln(s);
1712 uniq_proto[s] = 1;
1714 write("}\n");
1717 catch (Exception e) {
1718 import std.file;
1719 ew("ERROR: ", e);
1720 if (outfilename)
1721 remove(outfilename);
1722 else
1723 ew("WARNING: Unable to remove destination file.");
1724 die();
1728 void die(S...)(S args) {
1729 import core.stdc.stdlib;
1730 ew(args);
1731 exit(1);
1732 assert(0);