Add another simple clutter example, showing actors and Container i/f.
[girtod.git] / girtod.d
blob95f1717bac2def1e57a352b65dad5dddcb5a38c1
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]=='*' && gt[$-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 return Dtype(gt, ct[0..$-alen]) ~ suf;
155 // Obvious conversions first:
156 if (gt=="gint8" && ct=="gint8") return "byte";
157 if (gt=="guint8" && ct=="guint8") return "ubyte";
158 if (gt=="guint8" && ct=="gchar") return "ubyte";
159 if (gt=="guint8" && ct=="guchar") return "ubyte";
160 if (gt=="guint8" && ct=="unsigned char") return "ubyte";
161 if (gt=="gint16" && ct=="gint16") return "short";
162 if (gt=="guint16" && ct=="guint16") return "ushort";
163 if (gt=="gint32" && ct=="gint32") return "int";
164 if (gt=="guint32" && ct=="guint32") return "uint";
165 if (gt=="gint64" && ct=="gint64") return "long";
166 if (gt=="guint64" && ct=="guint64") return "ulong";
169 if (gt=="gshort" && ct=="gshort") return "short";
170 if (gt=="gshort" && ct=="short") return "short";
171 if (gt=="gushort" && ct=="gushort") return "ushort";
173 version (None) {
174 if (gt=="gint" && ct=="signed") return "c_int";
175 if (gt=="gint" && ct=="int") return "c_int";
176 if (gt=="gint" && ct=="gint") return "c_int";
177 if (gt=="gint" && !ct) return "c_int"; // gtk2 needs it.
178 if (gt=="guint" && ct=="guint") return "c_uint";
179 if (gt=="guint" && ct=="unsigned int") return "c_uint";
180 if (gt=="guint" && ct=="unsigned") return "c_uint";
181 } else {
182 // Assumes 32-bit wide target ints, but is far more readable...
183 if (gt=="gint" && ct=="signed") return "int";
184 if (gt=="gint" && ct=="int") return "int";
185 if (gt=="gint" && ct=="gint") return "int";
186 if (gt=="gint" && !ct) return "int"; // gtk2 needs it.
187 if (gt=="guint" && ct=="guint") return "uint";
188 if (gt=="guint" && ct=="unsigned int") return "uint";
189 if (gt=="guint" && ct=="unsigned") return "uint";
191 if (gt=="gfloat" && ct=="gfloat") return "float";
192 if (gt=="gfloat" && ct=="float") return "float";
193 if (gt=="gdouble" && ct=="double") return "double";
194 if (gt=="gdouble" && ct=="gdouble") return "double";
195 if (gt=="long double" && ct=="long double") return "real";
197 if (gt=="none" && ct=="void") return "void";
199 if (gt=="gunichar" && ct=="gunichar") return "dchar";
200 if (gt=="guint16" && ct=="gunichar2") return "wchar";
202 if (gt=="glong" && ct=="long") return "c_long";
203 if (gt=="glong" && ct=="glong") return "c_long";
204 if (gt=="gulong" && ct=="gulong") return "c_ulong";
205 if (gt=="gulong" && ct=="unsigned long") return "c_ulong";
207 if (gt=="gsize" && ct=="gsize") return "size_t";
208 if (gt=="gssize" && ct=="gssize") return "ssize_t";
209 if (gt=="gint64" && ct=="goffset") return "long"; // signed 64bit int, C99: off64_t
211 if (gt=="gchar" && ct=="gchar") return "char";
212 if (gt=="gchar" && ct=="char") return "char";
213 if (gt=="gchar" && ct=="guchar") return "ubyte";
215 if (gt=="utf8" && ct=="char") return "char";
216 if (gt=="utf8" && ct=="gchar") return "char";
218 if (gt=="guint8" && ct=="char") return "ubyte";
220 if (gt=="gpointer" && ct=="void") return "void";
221 if (gt=="gpointer" && !ct) return "void*"; // GTK2
222 if (gt=="gpointer" && ct=="gpointer") return "void*";
224 if (gt=="guint8" && ct=="void") return "ubyte"; // used for buffer *'s.
225 if (gt=="guint8" && ct=="gpointer") return "void*";
226 if (gt=="gpointer" && ct=="gconstpointer") return "const(void)*";
228 if (gt=="guint8" && ct=="gconstpointer") return "const(ubyte)*";
229 if (gt=="Variant" && ct=="gconstpointer") return "const(Variant)*";
230 if (gt=="VariantType" && ct=="gconstpointer") return "const(VariantType)*";
231 // gobject:
232 if (gt=="Object" && ct=="gpointer") return "Object*";
233 if (gt=="TypeClass" && ct=="gpointer") return "TypeClass*";
234 if (gt=="TypeInterface" && ct=="gpointer") return "TypeInterface*";
236 if (gt=="DBusObjectManagerClient" && ct=="GDBusObjectManager") return "DBusObjectManagerClient";
239 if (gt=="gboolean" && ct=="gboolean") return "int";
240 if (gt=="glong" && ct=="time_t") return "time_t"; // well...
242 if (gt=="guint" && ct=="uid_t") return "uint"; // Good enough?
244 // GLib:
245 if (gt=="gpointer" && ct=="GArray") return "Array";
246 if (gt=="gpointer" && ct=="GByteArray") return "ByteArray";
247 if (gt=="gpointer" && ct=="GPtrArray") return "PtrArray";
248 if (gt=="gpointer" && ct=="FILE") return "FILE";
250 if (gt=="guint8" && ct=="GByteArray") return "ByteArray";
252 // Not even the module names are used consistently...
254 void fixupmodnames(scope string from, scope string to) {
255 auto orggt = gt;
256 auto fl = from.length+1; // '+1' cause of the trailing '.'.
257 if (gt.length>=fl && gt[0..fl]==from ~ ".") {
258 gt = to ~ "." ~ gt[fl..$];
260 // Don't change ct for "GObject.ObjectClass" "GObjectClass"
261 if (ct.length>1 && orggt[fl..$]==ct[1..$])
262 return;
264 fl--;
265 if (ct.length>fl && from == ct[0..fl])
266 ct = to ~ ct[fl..$];
270 fixupmodnames("GLib", "GLib2");
271 fixupmodnames("GdkPixbuf", "GdkPixbuf2");
272 fixupmodnames("GObject", "GObject2");
273 fixupmodnames("Gio", "Gio2");
274 fixupmodnames("Gdk", "Gdk2");
275 fixupmodnames("fontconfig", "fontconfig2");
277 if (gt=="va_list" && ct=="va_list") return "va_list";
278 if (gt=="TypeInfo" ) return "GTypeInfo"; // To avoid clashes with D's base object.
279 // Note: "GTypeInfo" maps to different things in different scopes (modules).
281 // gobject
282 if (gt=="GType" && ct=="GType") return "Type";
283 if (gt=="GObject2.Object" && ct=="gpointer") return "GObject2.Object*";
285 if (gt=="GdkPixbuf2.Pixbuf" && ct=="GdkPixbuf") return "GdkPixbuf2.Pixbuf"; // in gdk
287 if (gt=="cairo.Surface" && ct=="cairo_surface_t") return "cairo.Surface"; // in gdk
288 if (gt=="cairo.Context" && ct=="cairo_t") return "cairo.Context"; // in gdk
289 if (gt=="cairo.FontOptions" && ct=="cairo_font_options_t") return "cairo.FontOptions"; // in gdk
290 if (gt=="cairo.Content" && ct=="cairo_content_t") return "cairo.Content"; // in gdk
291 if (gt=="cairo.Pattern" && ct=="cairo_pattern_t") return "cairo.Pattern"; // in gdk
293 // Clutter:
295 if (gt=="cairo.RectangleInt" && ct=="cairo_rectangle_int_t") return "cairo.RectangleInt";
296 if (gt=="cairo.Path" && ct=="cairo_path_t") return "cairo.Path";
298 if (gt=="Stage" && ct=="ClutterActor") return "Clutter.Stage";
300 // ClutterX11:
302 "xlib.XEvent" "XEvent" "xlib.XEvent"
303 "xlib.Pixmap" "Pixmap" "xlib.Pixmap"
304 "xlib.Window" "Window" "xlib.Window"
305 "xlib.Time" "Time" "xlib.Time"
306 "xlib.Display" "Display" "xlib.Display"
308 if (gt.length>ct.length+2) {
309 auto l = ct.length;
310 if (gt[$-l-1..$]==("."~ct))
311 return gt;
314 // PangoCairo:
315 if (gt=="freetype2.Library" && ct=="FT_Library") return "freetype2.Library";
316 if (gt=="cairo.ScaledFont" && ct=="cairo_scaled_font_t") return "cairo.ScaledFont";
317 if (gt=="cairo.FontType" && ct=="cairo_font_type_t") return "cairo.FontType";
319 if (ct=="PangoFcFontMap") return "Pango.FontMap";
321 // PangoFT2:
322 if (gt=="fontconfig2.Pattern" && ct=="FcPattern") return "fontconfig2.Pattern";
323 if (gt=="freetype2.Face" && ct=="FT_Face") return "freetype2.Face";
324 if (gt=="freetype2.Bitmap" && ct=="FT_Bitmap") return "freetype2.Bitmap";
327 // Ugh:
328 // "Pango.Font" "PangoFont*" => "Pango.Font*"; // in gdk
329 // "Pango.GlyphString" "PangoGlyphString*" => "Pango.GlyphString*"; // in gdk
330 // "Pango.Direction" "PangoDirection" => "Pango.Direction"
332 auto mlen = countUntil(gt, ".");
333 if (mlen>0 && ct.length>mlen && gt[0..mlen]==ct[0..mlen]) {
334 auto ctapos = countUntil(ct, "*");
335 if (ctapos==-1)
336 ctapos = ct.length;
337 if (ctapos>0 && gt[mlen+1..$]==ct[mlen..ctapos] ) {
338 gt = gt ~ ct[ctapos..$];
340 // Make things like "GL.uint" valid:
341 auto lastpi = lastIndexOf(gt, '.');
342 if (lastpi>1)
343 gt = gt[0..lastpi+1] ~ subDKeyword(gt[lastpi+1..$]);
344 return gt;
349 // ATK needs this one, as the datatype is missing both name and definition; this lets it build.
350 if (gt=="AtkPropertyValues*" && ct=="AtkPropertyValues*") return "_PropertyValues*";
352 // GDK:
353 if (gt=="GdkPixbuf2.PixbufAlphaMode" && ct=="GdkPixbuf2AlphaMode") return "GdkPixbuf2.PixbufAlphaMode";
355 // GTK:
356 if (gt=="filename" && ct=="gchar") return "char"; // Probably const, but...
357 if (gt=="filename" && !ct) return "char*"; // Probably const, but...
359 if (gt=="GdkPixbuf2.PixbufAnimation" && ct=="GdkPixbuf2Animation") return "GdkPixbuf2.PixbufAnimation";
360 if (gt=="GdkPixbuf2.PixbufAnimationIter" && ct=="GdkPixbuf2AnimationIter") return "GdkPixbuf2.PixbufAnimationIter";
362 // Signals:
363 // In fact if this is for a signal and has no <ct> it probably should be pointer...
364 // We're gonna guess, and we'll be wrong sometimes. But we will be right in many
365 // cases and there isn't really much else that we could do...
366 if (signals && !ct && gt[$-1]!='*') {
367 if (gt=="GObject2.Error") return "GLib2.Error*";
369 if (std.string.indexOf(gt,'.')!=-1) return gt ~ "*";
370 if (gt[0]>='A' && gt[0]<='Z') return gt ~ "*";
372 switch (gt) {
373 case "gfloat": return "float";
374 case "gdouble": return "double";
375 case "none": return "void";
376 case "utf8": return "char*";
377 case "gint": return "c_int";
378 case "guint": return "c_uint";
379 case "gboolean": return "c_int";
380 default:
384 // Fix up some obviously broken types.
385 if (!ct) {
386 if (gt=="Object") return "Object*";
389 version(all) { // Disable this while debugging.
390 if (gt==ct)
391 return gt;
394 if (gt && (!ct || ct==""))
395 return gt; // what else?...
397 enforce(gt && ct, "Missing type: GTK: \"" ~ gt ~ "\" C: \"" ~ ct ~ "\"");
399 foreach (prefix; ident_prefixes) {
400 auto l = prefix.length;
401 if (ct.length>l && ct[0..l]==prefix) {
402 // Try Quark" "GQuark -> "Quark"
403 string stripped_ct = ct[l..$];
404 if (stripped_ct==gt)
405 return stripped_ct;
409 // Last try: if ctype starts with 'G' and last dot-delimited component
410 // of gtktype equals the post-G ctype, use that.
411 // Eg. "Gio.AppLaunchContext", "GAppLaunchContext" => "Gio.AppLaunchContext"
412 if (ct[0]=='G') {
413 auto tail = "." ~ ct[1..$];
414 if(gt.length>=tail.length && gt[$-tail.length..$] == tail)
415 return gt;
418 enforce(0, "Unknown type: GTK: \"" ~ gt ~ "\" C: \"" ~ ct ~ "\"");
419 assert(0); // @noreturn
422 // Set string <dst> to <aa[index]>, but only if <aa> contains such value.
423 // if <req> is given assert if the value is missing.
424 void aassign(ref string dst, in string[string] aa, in string index, string req=null) {
425 if (auto p = index in aa)
426 dst = *p;
427 else if (req)
428 enforce(0, "Missing " ~ req ~ "; Available: " ~ to!string(aa));
430 string get_name_attr(in Element e) {
431 auto tag = e.tag;
432 if (auto p = "name" in tag.attr) {
433 // Debugging checks, harmless, can be left enabled.
434 if (*p!="GLib.List" && *p!="GLib.SList"&& *p!="GLib.HashTable")
435 enforce(tag.type==TagType.EMPTY, tag.toString());
436 // <alias>es trigger this one.
437 //enforce(tag.attr.length==1, to!string(tag.attr.length));
438 return *p;
440 return null;
443 string simplify_version(scope string v, scope string modname=null) pure {
444 string result = v;
445 if (v=="1.0")
446 return "";
447 else if (v[1..$]==".0")
448 result = v[0..1];
449 if (modname && result.length==1 && modname[$-1]==result[0])
450 return "";
451 return result;
454 class x_elem {
455 string tagname, doc, dochead, ver;
457 this(string s) { tagname = s; }
459 final void parse_doc_text(ElementParser ep) {
460 ep.onText = (string s) {
461 trace("DOC", "", s);
462 if (dochead)
463 doc ~= "\n" ~ dochead ~ ": ";
464 doc ~= s;
466 SkipEndTag(ep, "doc");
467 ep.parse();
470 final void register_doc_parser(ElementParser ep, string dochead=null) {
471 ep.onStartTag["doc"] = &parse_doc_text;
473 auto tag = ep.tag();
475 if (auto p = "version" in tag.attr)
476 ver = " " ~ *p;
477 this.dochead = dochead;
479 final string comment_to_D() {
480 import std.array;
481 string r;
482 if (doc) {
483 while (doc[$-1]=='\n')
484 doc = doc[0..$-1];
485 while (doc[0]=='\n')
486 doc = doc[1..$];
487 r = "\n// " ~ replace(doc, "\n", "\n// ");
488 r = replace(r, "\n// ", "\n" ~ indent ~ "// ") ~ "\n";
489 // Remove the newline before one-line comments.
490 import std.algorithm;
491 if (count(r, '\n')<=2)
492 r = r[1..$];
494 return r;
497 final void parsearray(ElementParser p, ref string _type, ref string _ctype,
498 bool makepointer=0) {
499 p.onStartTag["array"] = (ElementParser ep) {
500 aassign(_ctype, ep.tag().attr, "c:type");
501 if (makepointer)
502 _ctype ~= "*";
503 ep.onStartTag["type"] = (ElementParser ep) { ep.parse(); };
504 ep.onEndTag["type"] = (in Element e) {
505 trace("ELEMARR", tagname, e.tag.toString());
506 aassign(_type, e.tag.attr, "name");
508 ep.parse();
513 final class x_include : x_elem {
514 string name, ver;
516 this(string s) { super(s); }
518 void parse(in Element e) {
519 auto tag = e.tag;
520 enforce(tag.attr.length==2);
521 name = tag.attr["name"];
522 ver = tag.attr["version"];
525 override string toString() {
526 string modulebasename = name ~ simplify_version(ver, name);
527 string modulebasenamel = toLower(modulebasename);
528 // If we use "import GLib2" the compiler will fail to find the module
529 // while building the other ones.
530 // If we use "import gtk2.GLib2" the compiler will complain about
531 // missing Glib2.* types.
532 // NOTE: Imports are "public" so that apps don't have to import Gdk
533 // just for types only used for callback args etc.
534 return indent ~ "public import " ~ PACKAGE_NAME ~ "." ~ modulebasenamel ~ ";\n"
535 ~ indent ~ "alias " ~ PACKAGE_NAME ~ "." ~ modulebasenamel ~ " "
536 ~ modulebasename ~ ";";
540 string thismodulename;
542 final class x_namespace : x_elem {
543 string name, ver;
545 this(string s) { super(s); }
547 void parse(ElementParser ep) {
548 auto attr = ep.tag().attr;
549 name = attr["name"];
550 ver = attr["version"];
552 writeln();
554 if (auto p = "c:symbol-prefixes" in attr) {
555 symbol_prefixes = csv2arr(*p);
556 writeln("// c:symbol-prefixes: ", symbol_prefixes);
558 if (auto p = "c:identifier-prefixes" in attr) {
559 ident_prefixes = csv2arr(*p);
560 write("// c:identifier-prefixes: ", ident_prefixes, "\n\n");
563 // We do not parse further, ie no ep.parse() here.
566 override string toString() {
567 thismodulename = name ~ simplify_version(ver, name);
568 string s = indent ~ "// module " ~ thismodulename ~ ";\n";
569 // Per module fixups:
570 s ~= load_mixin("_MODULE_HEAD");
571 load_delete();
573 return s;
577 // "ALNUM" <= shortenconst("alnum", "G_ASCII_ALNUM")
578 string shortenconst(scope string name, scope string cname) {
579 if (cname.length>=name.length) {
580 auto shortname = cname[($-name.length)..$];
581 if (shortname==toUpper(name))
582 return validDSym(shortname);
584 return cname;
587 final class x_bitfield : x_elem {
588 string name;
589 x_field[] members;
591 this(string s) { super(s); }
593 void parse(ElementParser ep) {
594 trace("BITFIELD", tagname, ep.tag().toString());
595 auto tag = ep.tag();
596 name = tag.attr["name"];
598 register_doc_parser(ep);
599 ep.onStartTag["member"] = (ElementParser ep) {};
600 ep.onEndTag["member"] = (in Element e) {
601 enforce(e.tag.type==TagType.EMPTY, e.tag.toString());
603 auto m = new x_field("member");
604 members ~= m;
606 auto mattr = e.tag.attr;
607 aassign(m.name, mattr, "name", "bitfield name");
608 aassign(m.value, mattr, "value", "bitfield value");
609 aassign(m.identifier, mattr, "c:identifier", "bitfield c:identifier");
612 ep.onStartTag["function"] = (ElementParser ep) {
613 ew("FIXME: Skipping enum func: ", name, ".", ep.tag().attr["name"]);
614 // a gobject-introspection update made functions appear inside
615 // <enumeration> tags. We could handle this by using struct
616 // namespaces, but let's ignore these "functions" for now.
617 ep.parse();
619 ep.onEndTag["function"] = (in Element e) {
622 ep.parse();
625 override string toString() {
626 // Do we care about the basetype (ie do 31bits+ values happen?)
627 string r = comment_to_D() ~ indent ~ "enum " ~ name;
628 if (ver)
629 r ~= " /* Version" ~ ver ~ " */";
630 r ~= " {\n";
631 indent++;
633 foreach (i, m; members) {
634 long v = to!long(m.value);
635 if (v==0x8000_0000L)
636 m.value = "cast(uint)" ~ m.value;
637 r ~= indent ~ shortenconst(m.name, m.identifier) ~ " = " ~ m.value ~
638 (i!=members.length-1 ? "," : "") ~ "\n";
641 return r ~ --indent ~ "}";
645 x_field gerrfield; // pseudo field; used by functions that "throw" in gtk-speak.
646 static this() {
647 gerrfield = new x_field("GError");
648 gerrfield.type = "GLib2.Error**";
649 gerrfield.name = "error=null";
650 gerrfield.cname = "error";
653 final class x_field : x_elem {
654 string type, name, cname;
655 string value; // for constants
656 string identifier; // for bitfields
657 string ctype;
658 bool varargs; // true if this parameter is "..."
659 bool private_;
660 string def_init;
661 string direction;
662 x_struct struct_; // embedded struct/union;
663 x_function callback; // a callback, ie embedded function pointer.
664 x_elem docparent; // parent element, where we place our doc.
665 int bitfield; // if >0 then we need to handle it specially...
667 this(string s, x_elem parent=null) { super(s); docparent=parent;}
669 override string toString() {
670 string r = comment_to_D();
672 if (auto p = name in delete_symbol)
673 return "\n" ~ indent ~ "// " ~ tagname ~ " \"" ~ name ~ "\" removed: " ~ *p ~ "\n";
675 if (struct_)
676 return r ~ struct_.toString();
678 switch (tagname) {
679 case "constant":
680 switch (type) {
681 case "utf8":
682 r ~= indent ~ "enum " ~ name ~ " = \"" ~ escdquote(value) ~ "\";";
683 break;
684 default:
685 string contype = Dtype(type, ctype);
687 // "int MUTEX_DEBUG_MAGIC = cast(int)4175530711;" needs the cast...
688 string concast;
689 if (contype=="int") {
690 long conval = to!long(value);
691 if (conval>int.max && conval<=uint.max)
692 concast = "cast(int)";
695 // Clutter has constants with *names* starting with a digit...
696 auto validname = validDSym(name);
698 r ~= indent ~ "enum " ~ contype ~ " " ~ validname ~ " = " ~ concast ~ value ~ ";";
700 break;
701 case "alias":
702 string dtype = Dtype(type, ctype);
703 // We're renaming types, and that can cause problems, as in Gtk2.Type case.
704 if (dtype==name)
705 r ~= "/* ";
706 r ~= indent ~ "alias " ~ dtype ~ " " ~ name ~ ";" /*~ "\t// " ~ ctype*/;
707 if (dtype==name)
708 r ~= " */";
709 break;
710 // case "field": /* Fields in classes/structs are handled in containers. */
711 default:
712 enforce(0, "Unknown field type: " ~ tagname);
714 return r;
717 void parse(ElementParser ep) {
718 trace("FIELD", tagname, ep.tag().toString());
719 SkipEndTag(ep, tagname);
720 auto tag = ep.tag();
722 auto field_attr = tag.attr;
723 aassign(name, field_attr, "name");
724 aassign(value, field_attr, "value"); // Constants.
726 if (docparent)
727 docparent.register_doc_parser(ep, name ? "<" ~ name ~ ">": "PARAM");
728 else
729 register_doc_parser(ep);
731 if (auto p = "private" in field_attr)
732 private_ = *p=="1";
734 if (auto p = "bits" in field_attr) // Bitfields need special treatment.
735 bitfield = to!int(*p);
737 ep.onStartTag["type"] = (ElementParser ep) {};
738 ep.onEndTag["type"] = (in Element e) {
739 auto a = e.tag.attr;
740 aassign(ctype, a, "c:type");
741 if (tagname=="alias" || tagname=="parameter") {
742 /* GLib.List etc have extra info in child, ignored for now */
743 auto p = "name" in a;
744 if ( !p || ( *p!="GLib.List" && *p!="GLib.SList" && *p!="GLib.HashTable" ) )
745 enforce(e.tag.type==TagType.EMPTY, e.tag.toString());
747 switch (tagname) {
748 default:
749 type = get_name_attr(e);
753 onEmptyTag(ep, "attribute", (in Element e) {
754 auto tag = e.tag;
755 enforce(tag.attr.length==2);
756 enforce(tag.attr["name"]=="c:identifier");
757 identifier = tag.attr["value"];
760 if (tagname=="parameter") {
761 if (auto p = "allow-none" in field_attr) {
762 if (*p=="1")
763 def_init = "=null";
765 if (auto p = "direction" in field_attr)
766 direction = "/*" ~ *p ~ "*/ ";
768 parsearray(ep, type, ctype);
769 ep.onStartTag["varargs"] = (ElementParser ep) {
770 name = "...";
771 varargs = 1;
772 SkipEndTag(ep, "varargs");
773 ep.parse();
777 if (tagname=="field") {
778 ep.onStartTag["array"] = (ElementParser ep) {};
779 ep.onEndTag["array"] = (in Element e) {
780 trace("FIELDARR", tagname, e.tag.toString());
781 string size;
782 aassign(size, e.tag.attr, "fixed-size");
783 if (auto p = "readable" in field_attr) {
784 if (!size && *p=="0")
785 // Generates zero sized array, but as long as all instances
786 // are private and not followed by public fields - harmless.
787 size = "0";
789 //enforce(size);
790 // Ugh, eg GTK.InputDialogClass contains embedded arrays w/o size.
791 // Not usuable anyway, so we only care about it being syntactically correct.
792 if (!size)
793 size = "666";
795 if (size=="-1")
796 size = "/*GIR: -1*/ 0"; // Hmm, does this really belong in GIR...
798 // Using D syntax for arrays.
799 type ~= "[" ~ size ~ "]";
800 ctype ~= "[" ~ size ~ "]";
804 ep.onStartTag["callback"] = (ElementParser ep) {
805 callback = new x_function("functionp");
806 callback.parse(ep);
809 ep.parse();
813 final class x_function : x_elem {
814 string name, cname;
815 x_struct obj; // object if this is a method.
816 string rettype, crettype, retanno;
817 string throws; // actually gerrors, oh well.
818 bool is_variadic;
819 x_field[] args;
821 this(string s, x_struct pobj=null) { super(s); obj = pobj; }
823 string c_func(bool proto, bool skipthis, scope const(x_field)[] args) {
824 string r;
826 if (skipthis)
827 args = args[1..$];
829 r ~= validDSym(cname) ~ "(";
831 foreach(i, a; args) {
832 if (a.varargs)
833 r ~= "..."; // Variadic - have to be aliased (avoids having to deal w/ varargs).
834 else {
835 if (proto)
836 r ~= a.direction ~ Dtype(a.type, a.ctype) ~ " ";
837 r ~= a.cname ? validDSym(a.cname) : validDSym(a.name);
838 if (proto)
839 r ~= a.def_init;
840 if (i!=args.length-1)
841 r ~= ", ";
845 r ~= ")";
846 return r;
849 override string toString() {
850 string Drettype = Dtype(rettype, crettype);
851 string r = comment_to_D() ~ indent;
852 bool hasthis = 0;
853 bool refret;
855 if (auto p = cname in delete_symbol) {
856 r ~= "\n" ~ indent ~ "// " ~ tagname ~ " \"" ~ name ~ "\" removed: " ~ *p ~ "\n";
859 if (name=="") {
860 auto pos = std.string.indexOf(cname, '_');
861 name = "/*NAME MISSING IN GIR*/ ";
862 if (pos==-1)
863 name ~= "missing_" ~ cname;
864 else
865 name ~= cname[pos+1..$];
868 bool is_funcp = tagname=="callback" ||
869 tagname=="functionp" ||
870 tagname=="virtual-method";
872 // Fix up the argument list.
874 auto allargs = args;
876 if (obj) {
877 auto t = new x_field("this_");
878 t.type = obj.name ~ "*";
879 t.name = "this_";
881 if (tagname!="constructor") {
882 // Methods have a this pointer as the first arg.
883 allargs = t ~ allargs;
885 if (tagname!="function")
886 hasthis = 1;
888 else {
889 // Constructors return pointers to new objects, not some base type.
890 Drettype = obj.name ~ "*";
894 if (throws)
895 allargs ~= gerrfield;
897 foreach(i, a; allargs) {
898 // Fix up any missing arg names.
899 if (!a.name)
900 a.name = "arg_" ~ cast(char)('a'+i);
901 // Gtk2 has out params w/o a ctype and "gint" type - we'll do our best to fix it...
902 if (a.direction=="/*out*/ " && !a.ctype /*&& a.type[$-1]!='*'*/) {
903 a.ctype = a.type ~ "*";
904 a.direction ~= "/*POINTER*/ ";
906 // Fix up default params.
907 if (a.def_init=="=null")
908 switch (Dtype(a.type, a.ctype)) {
909 case "int": a.def_init = "=0"; break;
910 default: /**/; break;
914 // Remove default initializers from args that are followed by ones w/o defaults.
916 bool nondefarg_seen = 0;
917 foreach_reverse(a; allargs) {
918 if (!a.def_init && !a.varargs)
919 nondefarg_seen = 1;
920 if (nondefarg_seen)
921 a.def_init = null;
925 // Don't emit anything, but the C prototype.
926 if (auto p = cname in delete_symbol)
927 goto add_cproto;
929 if (tagname=="glib:signal") {
930 Drettype = Dtype(rettype, crettype, 1);
932 // Signals have an extra 'user_data' pointer as the last arg.
933 auto udata = new x_field("user_data");
934 udata.type = "void*";
935 udata.name = "user_data=null"; // "=null" JIC previous args were optional.
936 allargs = allargs ~ udata;
938 r ~= "extern (C) ";
939 r ~= "alias ";
940 r ~= "static ";
941 r ~= Drettype ~ " " ~ retanno;
942 r ~= "function (";
944 foreach(i, a; allargs) {
945 r ~= a.direction ~ Dtype(a.type, a.ctype, 1) ~ " " ~ validDSym(a.name) ~ a.def_init;
946 if (i!=allargs.length-1)
947 r ~= ", ";
950 r ~= ")";
951 r ~= " signal_" ~ validDSym(name);
952 r ~= ";\n";
954 // Add some convenience templates:
956 if (!obj.emitted("signal_connect")) {
957 r ~= "\n";
958 r ~= indent ~ "ulong signal_connect(string name, CB)(CB cb, void* data=null, ConnectFlags cf=cast(ConnectFlags)0) {\n";
959 r ~= ++indent ~ "return super_.signal_connect!name(cb, data, cf);\n";
960 r ~= --indent ~ "}\n\n";
963 // Callback /type/ not checked, as doing it results in weird errors
964 // and very unhelpful template instantiation error messages, like:
966 // Error: template instance Clutter.Actor.signal_connect!(name,int function(Actor* stage, Event* event, void* data)) error instantiating
967 // instantiated from here: signal_connect!("button-press-event",int function(Actor* stage, Event* event, void* data))
968 // Error: template instance Clutter.Stage.signal_connect!("button-press-event",int function(Actor* stage, Event* event, void* data)) error instantiating
969 r ~= indent ~ `ulong signal_connect(string name:"` ~ name ~ `", `;
970 r ~= `CB/*:signal_` ~ validDSym(name) ~ "*/)(CB cb, void* data=null, ConnectFlags cf=cast(ConnectFlags)0) {\n";
971 r ~= ++indent ~ `return signal_connect_data(&this, cast(char*)"` ~ name ~ "\",\n";
972 r ~= indent ~ "cast(GObject2.Callback)cb, data, null, cf);\n";
973 r ~= --indent ~ "}\n";
975 return r;
978 if (is_variadic) {
979 bool unsupported = tagname!="function" &&
980 tagname!="constructor";
981 if (unsupported) {
982 r ~= "/+ Not available -- variadic " ~
983 ( hasthis ?
984 "methods unsupported - use the C function directly.\n"
986 "functions with non-D linkage must have >1 parameter.\n" );
987 r ~= ++indent;
990 r ~= "alias " ~ cname ~ " " ~ validDSym(name) ~ "; // Variadic\n";
991 if (unsupported)
992 r ~= --indent ~ "+/\n";
994 goto add_cproto;
997 bool hasUpperCase(string s) {
998 import std.uni;
999 foreach(c; s)
1000 if (isUpper(c))
1001 return 1;
1002 return 0;
1005 string refDrettype = Drettype;
1006 if (reftypes && Drettype[$-1]=='*' && hasUpperCase(Drettype)) {
1007 refDrettype = Drettype[0..$-1];
1008 refret = 1;
1011 if (is_funcp)
1012 r ~= "extern (C) ";
1013 if (tagname=="callback")
1014 r ~= "alias ";
1015 if (tagname=="constructor" || tagname=="function")
1016 r ~= "static ";
1018 if (refret)
1019 r ~= "Ref!(" ~ refDrettype ~ ")";
1020 else
1021 r ~= Drettype;
1023 r ~= " " ~ retanno;
1025 if (is_funcp)
1026 r ~= "function (";
1027 else
1028 r ~= validDSym(name) ~ "(";
1030 auto args = allargs;
1032 if (hasthis)
1033 args = args[1..$];
1035 foreach(i, a; args) {
1036 if (a.varargs)
1037 r ~= "...";
1038 else {
1039 r ~= a.direction ~ Dtype(a.type, a.ctype) ~ " " ~ validDSym(a.name) ~ a.def_init;
1040 if (i!=args.length-1)
1041 r ~= ", ";
1045 r ~= ")";
1047 if (is_funcp)
1048 r ~= " " ~ validDSym(name);
1050 if (tagname=="functionp")
1051 return r;
1053 if (tagname=="callback" || tagname=="virtual-method") {
1054 r ~= ";\n";
1055 return r;
1058 r ~= " {\n" ~ ++indent;
1060 if (Drettype!="void")
1061 r ~= "return ";
1063 if (refret)
1064 r ~= "Ref!(" ~ refDrettype ~ ")(";
1066 string oname;
1067 if (hasthis) {
1068 oname = allargs[0].name;
1069 allargs[0].name = "&this";
1070 // If we know that the object implements something, then
1071 // the following cast should be safe.
1072 if (obj.tagname=="interface")
1073 allargs[0].name = "cast(" ~ obj.name ~ "*)&this";
1075 r ~= c_func(0, 0, allargs);
1076 if (refret)
1077 r ~= ")";
1078 r ~= ";\n" ~ --indent ~ "}\n";
1079 if (hasthis)
1080 allargs[0].name = oname;
1082 add_cproto:
1083 if (!is_funcp /*&& tagname=="function" || obj*/)
1084 cprotos ~= Drettype ~ " " ~ retanno ~ c_func(1, 0, allargs) ~ ";";
1086 return r;
1089 void parse(ElementParser ep) {
1090 trace("FUNC", tagname ~ (obj ? ": " ~ obj.name : ""), ep.tag().toString());
1091 // "callbacks" in structs are actually pointers.
1092 SkipEndTag(ep, tagname!="functionp" ? tagname : "callback");
1093 register_doc_parser(ep);
1094 auto tag = ep.tag();
1096 name = tag.attr["name"];
1097 aassign(cname, tag.attr, "c:identifier");
1098 aassign(throws, tag.attr, "throws");
1100 auto p = "introspectable" in tag.attr;
1101 if (p && *p=="0")
1102 doc ~= "Unintrospectable " ~ tagname ~ ": " ~ name ~ "() / " ~ cname ~ "()\n";
1104 ep.onStartTag["return-value"] = (ElementParser ep) {
1105 SkipEndTag(ep, "return-value");
1106 register_doc_parser(ep, "RETURNS");
1108 auto tag = ep.tag();
1110 if (auto p = "transfer-ownership" in tag.attr) {
1111 switch (*p) {
1112 case "none": retanno = ""; break;
1113 case "full": retanno = "/*new*/ "; break;
1114 case "container": retanno = "/*new container*/ "; break;
1115 default: enforce(0, "Unknown transfer-ownership: " ~ *p);
1119 parsearray(ep, rettype, crettype);
1121 ep.onStartTag["type"] = (ElementParser ep) {};
1122 ep.onEndTag["type"] = (in Element e) {
1123 auto tag = e.tag;
1124 aassign(crettype, tag.attr, "c:type");
1125 aassign(rettype, tag.attr, "name"/*, "func retval typename"*/);
1126 // If we have a ctype, but no type, try using the ctype.
1127 if (!rettype && crettype) {
1128 crettype = "/*CTYPE*/ " ~ crettype;
1129 rettype = crettype;
1131 enforce(rettype, "Missing func retval typename" );
1134 ep.parse();
1136 // Note the trick is there can be multiple nested <type>s;
1137 // once we get here we should have filled in the types properly.
1138 // Keep in mind we only care about the outermost type, that's
1139 // why parsing the _end_ </type> tag works for us.
1141 // Unfortunately c:type is often missing, so this check is too strict.
1142 //enforce(crettype, "Missing func retval c:type" ~ to!string(this));
1145 ep.onStartTag["parameters"] = (ElementParser pa) {
1146 trace("FUNCPARM", tagname, ep.tag().toString());
1147 SkipEndTag(pa, "parameters");
1148 pa.onStartTag["parameter"] = (ElementParser pa) {
1149 SkipEndTag(pa, "parameter");
1150 auto arg = new x_field("parameter", this); args ~= arg; arg.parse(pa);
1151 if (arg.varargs)
1152 is_variadic = 1;
1154 pa.parse();
1157 ep.parse();
1161 final class x_struct : x_elem {
1162 string name, parent;
1163 x_struct embedded_in;
1164 x_field[] fields;
1165 x_function[] methods;
1166 string[] implements;
1167 bool[string] emitted_;
1169 this(string tn, x_struct emb=null) {
1170 super(tn);
1171 embedded_in = emb;
1172 switch (tagname) {
1173 case "record": break;
1174 case "class": break;
1175 case "union": break;
1176 case "interface": break;
1177 default: enforce(0, "Unknown struct type: " ~ tagname);
1181 private bool emitted(string s) {
1182 if (auto p = s in emitted_)
1183 return 1;
1184 emitted_[s] = 1;
1185 return 0;
1188 private string basetypename(scope string type) {
1189 auto i = lastIndexOf(type, '.');
1190 return i==-1 ? type : type[i+1..$];
1193 override string toString() {
1194 // Everything looks like a struct to us. Except unions.
1195 string r = comment_to_D() ~ indent ~
1196 ((tagname=="union") ? "union " : "struct ") ~ subDKeyword(name);
1198 if (tagname=="interface") {
1199 enforce(fields.length==0, "Interface field overflow: " ~ name);
1200 r ~= " /* Interface */";
1202 if (parent)
1203 r ~= " /* : " ~ parent ~ " */";
1204 if (ver)
1205 r ~= " /* Version" ~ ver~ " */";
1206 r ~= " {\n";
1208 indent++;
1210 if (tagname=="interface") {
1211 r ~= indent++ ~ "mixin template __interface__() {";
1214 foreach (iface; implements)
1215 r ~= indent ~ "mixin " ~ Dtype(iface, null) ~ ".__interface__;\n";
1217 // "alias super this".
1219 // First, if this struct has a parent, but we don't know any of its
1220 // fields then add the parent as the only (sub)struct -- harmless as
1221 // we couldn't access the opaque empty struct anyway and this lets
1222 // the pseudo-inheritance work.
1223 if (parent && fields.length==0) {
1224 auto pf = new x_field("record");
1225 pf.type = parent;
1226 pf.ctype = parent;
1227 pf.name = "method_parent";
1228 fields ~= pf;
1231 if (parent && fields.length>0 && (
1232 Dtype(fields[0].type, fields[0].ctype)==Dtype(parent, null) ||
1233 Dtype(fields[0].type, fields[0].ctype)==parent
1234 )) {
1235 auto fieldname = validDSym(fields[0].name);
1237 r ~= indent ~ "alias " ~ fieldname ~ " this;\n";
1238 r ~= indent ~ "alias " ~ fieldname ~ " super_;\n";
1240 // Add an extra alias for easier upcasting.
1241 // NOTE: The name we add could clash with existing symbols;
1242 // this does not happen in practice, will be caught at
1243 // compiletime if it does, and can be addressed then.
1244 // Also "object" is not the safest symbol to add, but
1245 // works so far, let's wait until it causes problems
1246 // before renaming it to something like "gobject".
1247 auto partype = fields[0].type;
1248 if (!partype)
1249 partype = Dtype(null, fields[0].ctype);
1250 auto aname = basetypename(validDSym(partype));
1251 aname = toLower(aname);
1252 if (aname!=fieldname)
1253 r ~= indent ~ "alias " ~ fieldname ~ " " ~ aname ~ ";\n";
1255 // The parent cannot be private, so fix it:
1256 if (fields[0].private_)
1257 fields[0].private_ = 0;
1260 // No bitfields in D.
1261 string bmix;
1262 int bfwidth;
1263 char bfcount='A'; // More than one __dummyNN names? Must be different.
1265 void eofbf() {
1266 if (!bmix)
1267 return;
1269 // For now we'll assume all bitfields are at least 32bits wide (C int-sized)
1270 int rawwidth;
1271 if (bfwidth>0 && bfwidth<=32)
1272 rawwidth = 32;
1273 else if (bfwidth>32 && bfwidth<=64)
1274 rawwidth = 64;
1275 enforce(rawwidth, bmix);
1277 // std.bitmaip reqs total width to be one of 8/16/32/64, so:
1278 if (rawwidth!=bfwidth) {
1279 if (rawwidth<=8 && bfwidth>0 && bfwidth<8)
1280 bmix ~= ",\n" ~ indent ~ " uint, \"__dummy8"~bfcount~"\", " ~ to!string(8-bfwidth);
1281 else if (rawwidth<=16 && bfwidth<16)
1282 bmix ~= ",\n" ~ indent ~ " uint, \"__dummy16"~bfcount~"\", " ~ to!string(16-bfwidth);
1283 else if (rawwidth<=32 && bfwidth<32)
1284 bmix ~= ",\n" ~ indent ~ " uint, \"__dummy32"~bfcount~"\", " ~ to!string(32-bfwidth);
1285 else if (bfwidth<64)
1286 bmix ~= ",\n" ~ indent ~ " uint, \"__dummy64"~bfcount~"\", " ~ to!string(64-bfwidth);
1287 else if (bfwidth==64)
1289 else
1290 enforce(0);
1292 r ~= bmix ~ "))";
1293 bmix = null;
1294 bfwidth = 0;
1295 bfcount++;
1298 // Unfortunately there are methods that end up using the same name
1299 // as struct fields. Build a list of all method names, and in the
1300 // code below check for clashes. Rename the fields as these are
1301 // less likely to be used by apps.
1302 bool[string] m_names;
1303 foreach (m ; methods)
1304 m_names[m.name] = 1;
1306 foreach (i, f; fields) {
1307 string privstr = f.private_ ? "private " : "";
1309 if (f.callback) {
1310 eofbf();
1311 if (i!=0)
1312 r ~= ";\n";
1314 r ~= privstr ~ f.callback.toString();
1316 else if (f.struct_) {
1317 eofbf();
1318 if (i!=0)
1319 r ~= ";\n";
1320 else
1321 r ~= "\n";
1323 r ~= "\n" ~ privstr ~ f.struct_.toString();
1325 else if (f.bitfield) {
1326 if (!bmix) {
1327 if (i!=0)
1328 r ~= ";\n";
1329 bmix = indent ~ " static import std.bitmanip;"
1330 ~ " mixin(std.bitmanip.bitfields!(\n" ~ indent;
1332 else
1333 bmix ~= ",\n" ~ indent;
1334 string bftype = Dtype(f.type, f.ctype);
1335 switch (bftype) {
1336 case "uint", "int", "c_uint", "c_int":
1337 bmix ~= bftype ~ ", \"" ~ f.name~"\", "~ to!string(f.bitfield);
1338 bfwidth += f.bitfield;
1339 break;
1340 default:
1341 enforce(0, "Unknown bitfield type " ~
1342 bftype ~ "[" ~ f.type ~ "," ~ f.ctype ~ "] " ~
1343 f.name ~ ":" ~ to!string(f.bitfield));
1346 else {
1347 eofbf();
1348 string fname = validDSym(f.name);
1349 if (fname in m_names)
1350 fname ~= "_";
1351 if (i==0)
1352 r ~= indent ~ privstr ~ Dtype(f.type, f.ctype) ~ " " ~ fname;
1353 else {
1354 string prevtype, currtype;
1355 if (fields[i-1].type || fields[i-1].ctype)
1356 prevtype = Dtype(fields[i-1].type, fields[i-1].ctype);
1357 currtype = Dtype(f.type, f.ctype);
1358 if ( (prevtype == currtype && fields[i-1].type==f.type)
1359 && (fields[i-1].private_ == f.private_)
1360 && !fields[i-1].bitfield )
1361 r ~= ", " ~ fname;
1362 else {
1363 r ~= ((!fields[i-1].struct_) ? ";" : "");
1364 r ~= "\n"~ indent ~ privstr ~ currtype ~ " " ~ fname;
1370 eofbf();
1372 if (fields.length>0 && !fields[$-1].struct_)
1373 r ~= ";\n";
1375 if (fields.length>0 && methods.length>0)
1376 r ~= "\n";
1378 foreach (f; methods)
1379 r ~= f.toString();
1381 // Pull in the right mixin, if any:
1383 string mixname = name;
1385 for (auto ei = embedded_in; ei; ei = ei.embedded_in)
1386 mixname = ei.name ~ "_" ~ mixname;
1388 r ~= load_mixin(mixname);
1390 if (tagname=="interface") {
1391 r ~= --indent ~ "}\n";
1392 r ~= indent ~ "mixin __interface__;\n";
1395 return r ~ --indent ~ "}\n";
1398 void parse(ElementParser ep) {
1399 trace("STRUCT", tagname, ep.tag().toString());
1400 auto tag = ep.tag();
1401 name = tag.attr["name"];
1402 aassign(parent, tag.attr, "parent");
1404 register_doc_parser(ep);
1405 ep.onStartTag["field"] = (ElementParser ep) {
1406 auto nf = new x_field("field"); fields ~= nf; nf.parse(ep);
1408 ep.onStartTag["union"] = (ElementParser ep) {
1409 auto nf = new x_field("union"); fields ~= nf;
1410 auto na = new x_struct("union", this); nf.struct_ = na; na.parse(ep);
1412 ep.onStartTag["record"] = (ElementParser ep) {
1413 auto nf = new x_field("record"); fields ~= nf;
1414 auto na = new x_struct("record", this); nf.struct_ = na; na.parse(ep);
1416 ep.onStartTag["property"] = (ElementParser p) {
1417 p.parse(); // Drop them.
1419 ep.onStartTag["method"] = (ElementParser ep) {
1420 auto nm = new x_function("method", this); methods ~= nm; nm.parse(ep);
1422 ep.onStartTag["virtual-method"] = (ElementParser p) {
1423 p.parse(); // Drop them.
1425 ep.onStartTag["function"] = (ElementParser ep) {
1426 auto nm = new x_function("function"); methods ~= nm; nm.parse(ep);
1428 ep.onStartTag["constructor"] = (ElementParser ep) {
1429 auto nm = new x_function("constructor", this); methods ~= nm; nm.parse(ep);
1431 ep.onStartTag["glib:signal"] = (ElementParser ep) {
1432 auto nm = new x_function("glib:signal", this); methods ~= nm; nm.parse(ep);
1435 ep.onStartTag["implements"] = (ElementParser p) { p.parse(); };
1436 ep.onEndTag["implements"] = (in Element e) {
1437 string s;
1438 aassign(s, e.tag.attr, "name", "interface name");
1439 implements ~= s;
1442 ep.onStartTag["prerequisite"] = (ElementParser ep) { ep.parse(); }; // Drop.
1443 ep.onEndTag["prerequisite"] = (in Element e) {};
1445 ep.parse();
1449 void onUnknownTag(ElementParser ep, string tagname) {
1450 ep.onStartTag[null] = (ElementParser ep) {
1451 enforce(0, "Unhandled <"~tagname~"> start tag: "
1452 ~ ep.tag.name ~ "\n# " ~ ep.tag().toString());
1454 ep.onEndTag[null] = (in Element e) {
1455 enforce(0, "Unhandled <"~tagname~"> end tag: " ~ "\n# " ~ e.toString());
1459 void SkipEndTag(ElementParser p, string tagname) {
1460 onUnknownTag(p, tagname);
1461 p.onEndTag[tagname] = (in Element e) { };
1464 void onEmptyTag(ElementParser p, string tagname, void delegate(in Element e) deleg) {
1465 p.onStartTag[tagname] = (ElementParser ep) {
1466 /* do nothing; only tells the parser that we know about this tag */
1468 p.onEndTag[tagname] = (in Element e) {
1469 auto tag = e.tag;
1470 enforce(tag.type==TagType.EMPTY, tag.toString());
1472 deleg(e);
1476 void onStartTag(ElementParser p, string tagname, void delegate(ElementParser ep) deleg) {
1477 p.onStartTag[tagname] = (ElementParser ep) {
1478 onUnknownTag(ep, tagname);
1479 ep.onEndTag[tagname] = (in Element e) {/*Reached our own end tag*/};
1480 deleg(ep);
1482 p.onEndTag[tagname] = (in Element e) {
1483 /* Do nothing; only tells the parser that we know about this tag */
1484 /* Ugh. "<record name="AppLaunchContextPrivate" />" happens... */
1488 template OnTag(alias pa, string Kind, string xmlname, string tagname) {
1489 enum OnTag = "
1490 on"~Kind~"Tag(" ~ pa.stringof ~ ", \"" ~ xmlname ~ "\","
1491 " (" ~ ((Kind=="Start") ? "ElementParser e" : "in Element e") ~ ") {
1492 auto a = new x_"~tagname~"(" ~ xmlname.stringof ~ ");
1493 a.parse(e);
1494 "`write(a.toString() ~"\n");`
1495 "} );";
1498 string delete_symbol[string]; // [name: reason]
1500 void load_delete() {
1501 string filename = "mixin/" ~ thismodulename ~ "_" ~ "_DELETE_";
1502 string s;
1504 try {
1505 s = cast(string)std.file.read(filename);
1506 } catch (std.file.FileException) {
1507 if (tracemixins)
1508 ew("DELETE Failed: " ~ filename);
1509 return;
1512 foreach (line; split(s, "\n")) {
1513 auto arr = split(line, ":");
1514 if (arr.length==0)
1515 continue;
1516 if (arr.length==1)
1517 delete_symbol[line] = "";
1518 else
1519 delete_symbol[arr[0]] = strip(join(arr[1..$], ":"));
1523 bool tracemixins;
1525 string load_mixin(string id) {
1526 string filename = "mixin/" ~ thismodulename ~ "_" ~ id ~ ".d";
1527 string s;
1529 static bool uniq_mix[string];
1531 enforce(filename !in uniq_mix, "Mixin collission: " ~ filename);
1532 uniq_mix[filename] = 1;
1534 try {
1535 s = cast(string)std.file.read(filename);
1536 } catch (std.file.FileException) {
1537 if (tracemixins)
1538 ew("MIXIN Failed: " ~ filename);
1539 return null;
1542 if (tracemixins)
1543 ew("MIXIN Found: " ~ filename);
1545 string r = "\n" ~ indent ~ "// --- " ~ filename ~ " --->\n\n";
1547 foreach (sp; split(s, "\n"))
1548 r ~= indent ~ sp ~ "\n";
1550 r ~= indent ~ "// <--- " ~ filename ~ " ---\n";
1552 return r;
1555 bool reftypes = 0;
1557 void main(string[] args) {
1558 string infilename, outfilename, modulename;
1559 bool xmlcheck;
1561 try {
1562 // Parse cmd line:
1564 bool delegate(string arg) eatarg;
1566 cmdargloop: foreach(arg; args[1..$]) {
1567 if (eatarg) {
1568 if (eatarg(arg))
1569 eatarg = null;
1570 continue;
1572 if (arg[0]!='-') {
1573 if (!infilename) {
1574 infilename = arg;
1575 continue;
1577 die("Unknown extra cmdline file name: " ~ arg);
1579 if (arg.length<2)
1580 die("Missing cmdline option: " ~ arg);
1581 switch (arg[1]) {
1582 case '-':
1583 switch (arg[2..$]) {
1584 debug(TRACE) {
1585 case "trace": showtrace = 1; break;
1587 case "trace-mixins": tracemixins = 1; break;
1588 case "check": xmlcheck = 1; break;
1589 case "reftypes": reftypes = 1; break;
1590 case "no-reftypes": reftypes = 0; break;
1591 default: die("Unknown long cmdline option: " ~ arg);
1593 break;
1594 case 'o':
1595 if (arg.length>2)
1596 outfilename = arg[2..$];
1597 else
1598 eatarg = (string arg) { outfilename = arg; return true; };
1599 continue cmdargloop;
1600 case 'M':
1601 if (arg.length>2)
1602 outfilename = arg[2..$];
1603 else
1604 eatarg = (string arg) { modulename = arg; return true; };
1605 continue cmdargloop;
1606 default:
1607 die("Unknown cmdline option: " ~ arg);
1611 if (eatarg)
1612 die("Missing cmdline arg");
1614 string s = cast(string)std.file.read(infilename);
1616 if (xmlcheck)
1617 check(s); // Running the checks increases the execution time from 0.22s to 1.74s...
1619 if (outfilename)
1620 stdout.open(outfilename, "wt");
1622 writef("// *** DO NOT EDIT ***\n// Automatically generated from \"%s\"\n\n", infilename);
1624 if (modulename)
1625 write("module " ~ modulename ~ ";\n");
1627 auto dp = new DocumentParser(s);
1629 void delegate (ElementParser ep) epskip = (ElementParser ep) { ep.parse(); };
1630 void delegate (ElementParser p) epfixme = (ElementParser p)
1631 { p.parse(); ew("FIXME: " ~ p.toString()); };
1632 void delegate (in Element e) efixme = (in Element e) { ew("FIXME: " ~ e.toString()); };
1634 onUnknownTag(dp, "Document");
1636 onEmptyTag(dp, "package", (in Element e) {
1637 auto name = get_name_attr(e);
1638 write("\n// package: \"" ~ name ~ "\";\n");
1639 } );
1641 onEmptyTag(dp, "c:include", (in Element e) {
1642 auto name = get_name_attr(e);
1643 write("// C header: \"" ~ name ~ "\";\n");
1644 } );
1646 mixin( OnTag!(dp, "Empty", "include", "include") );
1647 mixin( OnTag!(dp, "Start", "namespace", "namespace") );
1648 dp.onEndTag["namespace"] = (in Element e) { }; // We're assuming one ns per file.
1650 dp.onEndTag["repository"] = (in Element e) { }; // We're assuming one repo per file.
1652 mixin( OnTag!(dp, "Start", "class", "struct") );
1653 mixin( OnTag!(dp, "Start", "record", "struct") );
1654 mixin( OnTag!(dp, "Start", "union", "struct") );
1655 mixin( OnTag!(dp, "Start", "interface", "struct") );
1657 mixin( OnTag!(dp, "Start", "constant", "field") );
1658 mixin( OnTag!(dp, "Start", "alias", "field") );
1660 mixin( OnTag!(dp, "Start", "bitfield", "bitfield") );
1661 mixin( OnTag!(dp, "Start", "enumeration", "bitfield") );
1663 mixin( OnTag!(dp, "Start", "function", "function") );
1664 mixin( OnTag!(dp, "Start", "callback", "function") );
1666 dp.onStartTag["glib:boxed"] = epskip;
1668 // Not handled yet. Can't eat them here, we need the types anyway.
1669 //dp.onStartTag["interface"] = epfixme;
1671 dp.parse();
1673 write(load_mixin("_MODULE"));
1675 write("\n// C prototypes:\n\nextern (C) {\n");
1676 // Some methods are also documented as functions and the D
1677 // compiler does not like duplicate prototypes; drop them.
1678 bool uniq_proto[string];
1679 foreach(s; cprotos) {
1680 if (s !in uniq_proto)
1681 writeln(s);
1682 uniq_proto[s] = 1;
1684 write("}\n");
1687 catch (Exception e) {
1688 import std.file;
1689 ew("ERROR: ", e);
1690 if (outfilename)
1691 remove(outfilename);
1692 else
1693 ew("WARNING: Unable to remove destination file.");
1694 die();
1698 void die(S...)(S args) {
1699 import core.stdc.stdlib;
1700 ew(args);
1701 exit(1);
1702 assert(0);