Clutter working; Add a trivial example showing how to use it from D.
[girtod.git] / girtod.d
blob318ad2dd4ca9ce0cebe0619f7f01b7dda48cc811
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 ~ "_";
108 private string escdquote(scope string s) {
109 import std.array;
110 return replace(s, "\\", "\\\\");
114 // From namespace tags:
115 string[] ident_prefixes; // eg. ["G"];
116 string[] symbol_prefixes; // eg. ["g", "glib"]
118 // Figure out the native D type from the gtktype and ctype.
119 // "signals" turns on heuristics, which isn't needed elsewhere,
120 // but the signal callback descriptions are lacking vital info...
121 string Dtype(scope string gt, scope string ct, bool signals=false) {
123 if (!gt && ct)
124 gt = ct;
126 // Some gtk signals have /no/ info; at least don't break the build.
127 if (signals && !gt)
128 return "void*";
130 // If we're given an array, strip the "[n]" part, do the conversion,
131 // then append the "[n]" back and return the result. Keeps things simple.
132 if (gt.length>=1 && gt[$-1]==']') {
133 size_t slen;
134 auto spos = std.string.indexOf(gt, '[');
135 if (spos>1)
136 slen = gt.length - spos;
137 if (slen>0 && gt[$-slen..$]==ct[$-slen..$]) {
138 string suf = gt[$-slen..$];
139 return Dtype(gt[0..$-slen], ct[0..$-slen]) ~ suf;
142 // Ditto for pointers; note - only the ctype has the right type
143 // (number of '*'). Keep in mind arrays of pointers exist.
144 if (ct.length>=1 && ct[$-1]=='*' && gt[$-1]!='*') {
145 size_t alen;
146 auto apos = std.string.indexOf(ct, '*');
147 if (apos>=0)
148 alen = ct.length - apos;
149 string suf = ct[$-alen..$];
150 return Dtype(gt, ct[0..$-alen]) ~ suf;
153 // Obvious conversions first:
154 if (gt=="gint8" && ct=="gint8") return "byte";
155 if (gt=="guint8" && ct=="guint8") return "ubyte";
156 if (gt=="guint8" && ct=="gchar") return "ubyte";
157 if (gt=="guint8" && ct=="guchar") return "ubyte";
158 if (gt=="guint8" && ct=="unsigned char") return "ubyte";
159 if (gt=="gint16" && ct=="gint16") return "short";
160 if (gt=="guint16" && ct=="guint16") return "ushort";
161 if (gt=="gint32" && ct=="gint32") return "int";
162 if (gt=="guint32" && ct=="guint32") return "uint";
163 if (gt=="gint64" && ct=="gint64") return "long";
164 if (gt=="guint64" && ct=="guint64") return "ulong";
167 if (gt=="gshort" && ct=="gshort") return "short";
168 if (gt=="gshort" && ct=="short") return "short";
169 if (gt=="gushort" && ct=="gushort") return "ushort";
171 version (None) {
172 if (gt=="gint" && ct=="signed") return "c_int";
173 if (gt=="gint" && ct=="int") return "c_int";
174 if (gt=="gint" && ct=="gint") return "c_int";
175 if (gt=="gint" && !ct) return "c_int"; // gtk2 needs it.
176 if (gt=="guint" && ct=="guint") return "c_uint";
177 if (gt=="guint" && ct=="unsigned int") return "c_uint";
178 if (gt=="guint" && ct=="unsigned") return "c_uint";
179 } else {
180 // Assumes 32-bit wide target ints, but is far more readable...
181 if (gt=="gint" && ct=="signed") return "int";
182 if (gt=="gint" && ct=="int") return "int";
183 if (gt=="gint" && ct=="gint") return "int";
184 if (gt=="gint" && !ct) return "int"; // gtk2 needs it.
185 if (gt=="guint" && ct=="guint") return "uint";
186 if (gt=="guint" && ct=="unsigned int") return "uint";
187 if (gt=="guint" && ct=="unsigned") return "uint";
189 if (gt=="gfloat" && ct=="gfloat") return "float";
190 if (gt=="gfloat" && ct=="float") return "float";
191 if (gt=="gdouble" && ct=="double") return "double";
192 if (gt=="gdouble" && ct=="gdouble") return "double";
193 if (gt=="long double" && ct=="long double") return "real";
195 if (gt=="none" && ct=="void") return "void";
197 if (gt=="gunichar" && ct=="gunichar") return "dchar";
198 if (gt=="guint16" && ct=="gunichar2") return "wchar";
200 if (gt=="glong" && ct=="long") return "c_long";
201 if (gt=="glong" && ct=="glong") return "c_long";
202 if (gt=="gulong" && ct=="gulong") return "c_ulong";
203 if (gt=="gulong" && ct=="unsigned long") return "c_ulong";
205 if (gt=="gsize" && ct=="gsize") return "size_t";
206 if (gt=="gssize" && ct=="gssize") return "ssize_t";
207 if (gt=="gint64" && ct=="goffset") return "long"; // signed 64bit int, C99: off64_t
209 if (gt=="gchar" && ct=="gchar") return "char";
210 if (gt=="gchar" && ct=="char") return "char";
211 if (gt=="gchar" && ct=="guchar") return "ubyte";
213 if (gt=="utf8" && ct=="char") return "char";
214 if (gt=="utf8" && ct=="gchar") return "char";
216 if (gt=="guint8" && ct=="char") return "ubyte";
218 if (gt=="gpointer" && ct=="void") return "void";
219 if (gt=="gpointer" && !ct) return "void*"; // GTK2
220 if (gt=="gpointer" && ct=="gpointer") return "void*";
222 if (gt=="guint8" && ct=="void") return "ubyte"; // used for buffer *'s.
223 if (gt=="guint8" && ct=="gpointer") return "void*";
224 if (gt=="gpointer" && ct=="gconstpointer") return "const(void)*";
226 if (gt=="guint8" && ct=="gconstpointer") return "const(ubyte)*";
227 if (gt=="Variant" && ct=="gconstpointer") return "const(Variant)*";
228 if (gt=="VariantType" && ct=="gconstpointer") return "const(VariantType)*";
229 // gobject:
230 if (gt=="Object" && ct=="gpointer") return "Object*";
231 if (gt=="TypeClass" && ct=="gpointer") return "TypeClass*";
232 if (gt=="TypeInterface" && ct=="gpointer") return "TypeInterface*";
234 if (gt=="DBusObjectManagerClient" && ct=="GDBusObjectManager") return "DBusObjectManagerClient";
237 if (gt=="gboolean" && ct=="gboolean") return "int";
238 if (gt=="glong" && ct=="time_t") return "time_t"; // well...
240 if (gt=="guint" && ct=="uid_t") return "uint"; // Good enough?
242 // GLib:
243 if (gt=="gpointer" && ct=="GArray") return "Array";
244 if (gt=="gpointer" && ct=="GByteArray") return "ByteArray";
245 if (gt=="gpointer" && ct=="GPtrArray") return "PtrArray";
246 if (gt=="gpointer" && ct=="FILE") return "FILE";
248 if (gt=="guint8" && ct=="GByteArray") return "ByteArray";
250 // Not even the module names are used consistently...
252 void fixupmodnames(scope string from, scope string to) {
253 auto orggt = gt;
254 auto fl = from.length+1; // '+1' cause of the trailing '.'.
255 if (gt.length>=fl && gt[0..fl]==from ~ ".") {
256 gt = to ~ "." ~ gt[fl..$];
258 // Don't change ct for "GObject.ObjectClass" "GObjectClass"
259 if (ct.length>1 && orggt[fl..$]==ct[1..$])
260 return;
262 fl--;
263 if (ct.length>fl && from == ct[0..fl])
264 ct = to ~ ct[fl..$];
268 fixupmodnames("GLib", "GLib2");
269 fixupmodnames("GdkPixbuf", "GdkPixbuf2");
270 fixupmodnames("GObject", "GObject2");
271 fixupmodnames("Gio", "Gio2");
272 fixupmodnames("Gdk", "Gdk2");
273 fixupmodnames("fontconfig", "fontconfig2");
275 if (gt=="va_list" && ct=="va_list") return "va_list";
277 // gobject
278 if (gt=="GType" && ct=="GType") return "Type";
279 if (gt=="GObject2.Object" && ct=="gpointer") return "GObject2.Object*";
281 if (gt=="GdkPixbuf2.Pixbuf" && ct=="GdkPixbuf") return "GdkPixbuf2.Pixbuf"; // in gdk
283 if (gt=="cairo.Surface" && ct=="cairo_surface_t") return "cairo.Surface"; // in gdk
284 if (gt=="cairo.Context" && ct=="cairo_t") return "cairo.Context"; // in gdk
285 if (gt=="cairo.FontOptions" && ct=="cairo_font_options_t") return "cairo.FontOptions"; // in gdk
286 if (gt=="cairo.Content" && ct=="cairo_content_t") return "cairo.Content"; // in gdk
287 if (gt=="cairo.Pattern" && ct=="cairo_pattern_t") return "cairo.Pattern"; // in gdk
289 // Clutter:
291 if (gt=="cairo.RectangleInt" && ct=="cairo_rectangle_int_t") return "cairo.RectangleInt";
292 if (gt=="cairo.Path" && ct=="cairo_path_t") return "cairo.Path";
294 if (gt=="Stage" && ct=="ClutterActor") return "Clutter.Stage";
296 // ClutterX11:
298 "xlib.XEvent" "XEvent" "xlib.XEvent"
299 "xlib.Pixmap" "Pixmap" "xlib.Pixmap"
300 "xlib.Window" "Window" "xlib.Window"
301 "xlib.Time" "Time" "xlib.Time"
302 "xlib.Display" "Display" "xlib.Display"
304 if (gt.length>ct.length+2) {
305 auto l = ct.length;
306 if (gt[$-l-1..$]==("."~ct))
307 return gt;
310 // PangoCairo:
311 if (gt=="freetype2.Library" && ct=="FT_Library") return "freetype2.Library";
312 if (gt=="cairo.ScaledFont" && ct=="cairo_scaled_font_t") return "cairo.ScaledFont";
313 if (gt=="cairo.FontType" && ct=="cairo_font_type_t") return "cairo.FontType";
315 if (ct=="PangoFcFontMap") return "Pango.FontMap";
317 // PangoFT2:
318 if (gt=="fontconfig2.Pattern" && ct=="FcPattern") return "fontconfig2.Pattern";
319 if (gt=="freetype2.Face" && ct=="FT_Face") return "freetype2.Face";
320 if (gt=="freetype2.Bitmap" && ct=="FT_Bitmap") return "freetype2.Bitmap";
323 // Ugh:
324 // "Pango.Font" "PangoFont*" => "Pango.Font*"; // in gdk
325 // "Pango.GlyphString" "PangoGlyphString*" => "Pango.GlyphString*"; // in gdk
326 // "Pango.Direction" "PangoDirection" => "Pango.Direction"
328 auto mlen = countUntil(gt, ".");
329 if (mlen>0 && ct.length>mlen && gt[0..mlen]==ct[0..mlen]) {
330 auto ctapos = countUntil(ct, "*");
331 if (ctapos==-1)
332 ctapos = ct.length;
333 if (ctapos>0 && gt[mlen+1..$]==ct[mlen..ctapos] ) {
334 gt = gt ~ ct[ctapos..$];
336 // Make things like "GL.uint" valid:
337 auto lastpi = lastIndexOf(gt, '.');
338 if (lastpi>1)
339 gt = gt[0..lastpi+1] ~ subDKeyword(gt[lastpi+1..$]);
340 return gt;
345 // ATK needs this one, as the datatype is missing both name and definition; this lets it build.
346 if (gt=="AtkPropertyValues*" && ct=="AtkPropertyValues*") return "_PropertyValues*";
348 // GDK:
349 if (gt=="GdkPixbuf2.PixbufAlphaMode" && ct=="GdkPixbuf2AlphaMode") return "GdkPixbuf2.PixbufAlphaMode";
351 // GTK:
352 if (gt=="filename" && ct=="gchar") return "char"; // Probably const, but...
353 if (gt=="filename" && !ct) return "char*"; // Probably const, but...
355 if (gt=="GdkPixbuf2.PixbufAnimation" && ct=="GdkPixbuf2Animation") return "GdkPixbuf2.PixbufAnimation";
356 if (gt=="GdkPixbuf2.PixbufAnimationIter" && ct=="GdkPixbuf2AnimationIter") return "GdkPixbuf2.PixbufAnimationIter";
358 // Signals:
359 // In fact if this is for a signal and has no <ct> it probably should be pointer...
360 // We're gonna guess, and we'll be wrong sometimes. But we will be right in many
361 // cases and there isn't really much else that we could do...
362 if (signals && !ct && gt[$-1]!='*') {
363 if (gt=="GObject2.Error") return "GLib2.Error*";
365 if (std.string.indexOf(gt,'.')!=-1) return gt ~ "*";
366 if (gt[0]>='A' && gt[0]<='Z') return gt ~ "*";
368 switch (gt) {
369 case "gfloat": return "float";
370 case "gdouble": return "double";
371 case "none": return "void";
372 case "utf8": return "char*";
373 case "gint": return "c_int";
374 case "guint": return "c_uint";
375 case "gboolean": return "c_int";
376 default:
380 // Fix up some obviously broken types.
381 if (!ct) {
382 if (gt=="Object") return "Object*";
385 version(all) { // Disable this while debugging.
386 if (gt==ct)
387 return gt;
390 if (gt && (!ct || ct==""))
391 return gt; // what else?...
393 enforce(gt && ct, "Missing type: GTK: \"" ~ gt ~ "\" C: \"" ~ ct ~ "\"");
395 foreach (prefix; ident_prefixes) {
396 auto l = prefix.length;
397 if (ct.length>l && ct[0..l]==prefix) {
398 // Try Quark" "GQuark -> "Quark"
399 string stripped_ct = ct[l..$];
400 if (stripped_ct==gt)
401 return stripped_ct;
405 // Last try: if ctype starts with 'G' and last dot-delimited component
406 // of gtktype equals the post-G ctype, use that.
407 // Eg. "Gio.AppLaunchContext", "GAppLaunchContext" => "Gio.AppLaunchContext"
408 if (ct[0]=='G') {
409 auto tail = "." ~ ct[1..$];
410 if(gt.length>=tail.length && gt[$-tail.length..$] == tail)
411 return gt;
414 enforce(0, "Unknown type: GTK: \"" ~ gt ~ "\" C: \"" ~ ct ~ "\"");
415 assert(0); // @noreturn
418 // Set string <dst> to <aa[index]>, but only if <aa> contains such value.
419 // if <req> is given assert if the value is missing.
420 void aassign(ref string dst, in string[string] aa, in string index, string req=null) {
421 if (auto p = index in aa)
422 dst = *p;
423 else if (req)
424 enforce(0, "Missing " ~ req ~ "; Available: " ~ to!string(aa));
426 string get_name_attr(in Element e) {
427 auto tag = e.tag;
428 if (auto p = "name" in tag.attr) {
429 // Debugging checks, harmless, can be left enabled.
430 if (*p!="GLib.List" && *p!="GLib.SList"&& *p!="GLib.HashTable")
431 enforce(tag.type==TagType.EMPTY, tag.toString());
432 // <alias>es trigger this one.
433 //enforce(tag.attr.length==1, to!string(tag.attr.length));
434 return *p;
436 return null;
439 string simplify_version(scope string v, scope string modname=null) pure {
440 string result = v;
441 if (v=="1.0")
442 return "";
443 else if (v[1..$]==".0")
444 result = v[0..1];
445 if (modname && result.length==1 && modname[$-1]==result[0])
446 return "";
447 return result;
450 class x_elem {
451 string tagname, doc, dochead, ver;
453 this(string s) { tagname = s; }
455 final void parse_doc_text(ElementParser ep) {
456 ep.onText = (string s) {
457 trace("DOC", "", s);
458 if (dochead)
459 doc ~= "\n" ~ dochead ~ ": ";
460 doc ~= s;
462 SkipEndTag(ep, "doc");
463 ep.parse();
466 final void register_doc_parser(ElementParser ep, string dochead=null) {
467 ep.onStartTag["doc"] = &parse_doc_text;
469 auto tag = ep.tag();
471 if (auto p = "version" in tag.attr)
472 ver = " " ~ *p;
473 this.dochead = dochead;
475 final string comment_to_D() {
476 import std.array;
477 string r;
478 if (doc) {
479 while (doc[$-1]=='\n')
480 doc = doc[0..$-1];
481 while (doc[0]=='\n')
482 doc = doc[1..$];
483 r = "\n// " ~ replace(doc, "\n", "\n// ");
484 r = replace(r, "\n// ", "\n" ~ indent ~ "// ") ~ "\n";
485 // Remove the newline before one-line comments.
486 import std.algorithm;
487 if (count(r, '\n')<=2)
488 r = r[1..$];
490 return r;
493 final void parsearray(ElementParser p, ref string _type, ref string _ctype,
494 bool makepointer=0) {
495 p.onStartTag["array"] = (ElementParser ep) {
496 aassign(_ctype, ep.tag().attr, "c:type");
497 if (makepointer)
498 _ctype ~= "*";
499 ep.onStartTag["type"] = (ElementParser ep) { ep.parse(); };
500 ep.onEndTag["type"] = (in Element e) {
501 trace("ELEMARR", tagname, e.tag.toString());
502 aassign(_type, e.tag.attr, "name");
504 ep.parse();
509 final class x_include : x_elem {
510 string name, ver;
512 this(string s) { super(s); }
514 void parse(in Element e) {
515 auto tag = e.tag;
516 enforce(tag.attr.length==2);
517 name = tag.attr["name"];
518 ver = tag.attr["version"];
521 override string toString() {
522 string modulebasename = name ~ simplify_version(ver, name);
523 string modulebasenamel = toLower(modulebasename);
524 // If we use "import GLib2" the compiler will fail to find the module
525 // while building the other ones.
526 // If we use "import gtk2.GLib2" the compiler will complain about
527 // missing Glib2.* types.
528 // NOTE: Imports are "public" so that apps don't have to import Gdk
529 // just for types only used for callback args etc.
530 return indent ~ "public import " ~ PACKAGE_NAME ~ "." ~ modulebasenamel ~ ";\n"
531 ~ indent ~ "alias " ~ PACKAGE_NAME ~ "." ~ modulebasenamel ~ " "
532 ~ modulebasename ~ ";";
536 string thismodulename;
538 final class x_namespace : x_elem {
539 string name, ver;
541 this(string s) { super(s); }
543 void parse(ElementParser ep) {
544 auto attr = ep.tag().attr;
545 name = attr["name"];
546 ver = attr["version"];
548 writeln();
550 if (auto p = "c:symbol-prefixes" in attr) {
551 symbol_prefixes = csv2arr(*p);
552 writeln("// c:symbol-prefixes: ", symbol_prefixes);
554 if (auto p = "c:identifier-prefixes" in attr) {
555 ident_prefixes = csv2arr(*p);
556 write("// c:identifier-prefixes: ", ident_prefixes, "\n\n");
559 // We do not parse further, ie no ep.parse() here.
562 override string toString() {
563 thismodulename = name ~ simplify_version(ver, name);
564 string s = indent ~ "// module " ~ thismodulename ~ ";\n";
565 // Per module fixups:
566 s ~= load_mixin("_MODULE_HEAD");
567 load_delete();
569 return s;
573 // "ALNUM" <= shortenconst("alnum", "G_ASCII_ALNUM")
574 string shortenconst(scope string name, scope string cname) {
575 if (cname.length>=name.length) {
576 auto shortname = cname[($-name.length)..$];
577 if (shortname==toUpper(name))
578 return validDSym(shortname);
580 return cname;
583 final class x_bitfield : x_elem {
584 string name;
585 x_field[] members;
587 this(string s) { super(s); }
589 void parse(ElementParser ep) {
590 trace("BITFIELD", tagname, ep.tag().toString());
591 auto tag = ep.tag();
592 name = tag.attr["name"];
594 register_doc_parser(ep);
595 ep.onStartTag["member"] = (ElementParser ep) {};
596 ep.onEndTag["member"] = (in Element e) {
597 enforce(e.tag.type==TagType.EMPTY, e.tag.toString());
599 auto m = new x_field("member");
600 members ~= m;
602 auto mattr = e.tag.attr;
603 aassign(m.name, mattr, "name", "bitfield name");
604 aassign(m.value, mattr, "value", "bitfield value");
605 aassign(m.identifier, mattr, "c:identifier", "bitfield c:identifier");
608 ep.onStartTag["function"] = (ElementParser ep) {
609 ew("FIXME: Skipping enum func: ", name, ".", ep.tag().attr["name"]);
610 // a gobject-introspection update made functions appear inside
611 // <enumeration> tags. We could handle this by using struct
612 // namespaces, but let's ignore these "functions" for now.
613 ep.parse();
615 ep.onEndTag["function"] = (in Element e) {
618 ep.parse();
621 override string toString() {
622 // Do we care about the basetype (ie do 31bits+ values happen?)
623 string r = comment_to_D() ~ indent ~ "enum " ~ name;
624 if (ver)
625 r ~= " /* Version" ~ ver ~ " */";
626 r ~= " {\n";
627 indent++;
629 foreach (i, m; members) {
630 long v = to!long(m.value);
631 if (v==0x8000_0000L)
632 m.value = "cast(uint)" ~ m.value;
633 r ~= indent ~ shortenconst(m.name, m.identifier) ~ " = " ~ m.value ~
634 (i!=members.length-1 ? "," : "") ~ "\n";
637 return r ~ --indent ~ "}";
641 x_field gerrfield; // pseudo field; used by functions that "throw" in gtk-speak.
642 static this() {
643 gerrfield = new x_field("GError");
644 gerrfield.type = "GLib2.Error**";
645 gerrfield.name = "error=null";
646 gerrfield.cname = "error";
649 final class x_field : x_elem {
650 string type, name, cname;
651 string value; // for constants
652 string identifier; // for bitfields
653 string ctype;
654 bool varargs; // true if this parameter is "..."
655 bool private_;
656 string def_init;
657 string direction;
658 x_struct struct_; // embedded struct/union;
659 x_function callback; // a callback, ie embedded function pointer.
660 x_elem docparent; // parent element, where we place our doc.
661 int bitfield; // if >0 then we need to handle it specially...
663 this(string s, x_elem parent=null) { super(s); docparent=parent;}
665 override string toString() {
666 string r = comment_to_D();
668 if (auto p = name in delete_symbol)
669 return "\n" ~ indent ~ "// " ~ tagname ~ " \"" ~ name ~ "\" removed: " ~ *p ~ "\n";
671 if (struct_)
672 return r ~ struct_.toString();
674 switch (tagname) {
675 case "constant":
676 switch (type) {
677 case "utf8":
678 r ~= indent ~ "enum " ~ name ~ " = \"" ~ escdquote(value) ~ "\";";
679 break;
680 default:
681 string contype = Dtype(type, ctype);
683 // "int MUTEX_DEBUG_MAGIC = cast(int)4175530711;" needs the cast...
684 string concast;
685 if (contype=="int") {
686 long conval = to!long(value);
687 if (conval>int.max && conval<=uint.max)
688 concast = "cast(int)";
691 // Clutter has constants with *names* starting with a digit...
692 auto validname = validDSym(name);
694 r ~= indent ~ "enum " ~ contype ~ " " ~ validname ~ " = " ~ concast ~ value ~ ";";
696 break;
697 case "alias":
698 string dtype = Dtype(type, ctype);
699 // We're renaming types, and that can cause problems, as in Gtk2.Type case.
700 if (dtype==name)
701 r ~= "/* ";
702 r ~= indent ~ "alias " ~ dtype ~ " " ~ name ~ ";" /*~ "\t// " ~ ctype*/;
703 if (dtype==name)
704 r ~= " */";
705 break;
706 // case "field": /* Fields in classes/structs are handled in containers. */
707 default:
708 enforce(0, "Unknown field type: " ~ tagname);
710 return r;
713 void parse(ElementParser ep) {
714 trace("FIELD", tagname, ep.tag().toString());
715 SkipEndTag(ep, tagname);
716 auto tag = ep.tag();
718 auto field_attr = tag.attr;
719 aassign(name, field_attr, "name");
720 aassign(value, field_attr, "value"); // Constants.
722 if (docparent)
723 docparent.register_doc_parser(ep, name ? "<" ~ name ~ ">": "PARAM");
724 else
725 register_doc_parser(ep);
727 if (auto p = "private" in field_attr)
728 private_ = *p=="1";
730 if (auto p = "bits" in field_attr) // Bitfields need special treatment.
731 bitfield = to!int(*p);
733 ep.onStartTag["type"] = (ElementParser ep) {};
734 ep.onEndTag["type"] = (in Element e) {
735 auto a = e.tag.attr;
736 aassign(ctype, a, "c:type");
737 if (tagname=="alias" || tagname=="parameter") {
738 /* GLib.List etc have extra info in child, ignored for now */
739 auto p = "name" in a;
740 if ( !p || ( *p!="GLib.List" && *p!="GLib.SList" && *p!="GLib.HashTable" ) )
741 enforce(e.tag.type==TagType.EMPTY, e.tag.toString());
743 switch (tagname) {
744 default:
745 type = get_name_attr(e);
749 onEmptyTag(ep, "attribute", (in Element e) {
750 auto tag = e.tag;
751 enforce(tag.attr.length==2);
752 enforce(tag.attr["name"]=="c:identifier");
753 identifier = tag.attr["value"];
756 if (tagname=="parameter") {
757 if (auto p = "allow-none" in field_attr) {
758 if (*p=="1")
759 def_init = "=null";
761 if (auto p = "direction" in field_attr)
762 direction = "/*" ~ *p ~ "*/ ";
764 parsearray(ep, type, ctype);
765 ep.onStartTag["varargs"] = (ElementParser ep) {
766 name = "...";
767 varargs = 1;
768 SkipEndTag(ep, "varargs");
769 ep.parse();
773 if (tagname=="field") {
774 ep.onStartTag["array"] = (ElementParser ep) {};
775 ep.onEndTag["array"] = (in Element e) {
776 trace("FIELDARR", tagname, e.tag.toString());
777 string size;
778 aassign(size, e.tag.attr, "fixed-size");
779 if (auto p = "readable" in field_attr) {
780 if (!size && *p=="0")
781 // Generates zero sized array, but as long as all instances
782 // are private and not followed by public fields - harmless.
783 size = "0";
785 //enforce(size);
786 // Ugh, eg GTK.InputDialogClass contains embedded arrays w/o size.
787 // Not usuable anyway, so we only care about it being syntactically correct.
788 if (!size)
789 size = "666";
791 if (size=="-1")
792 size = "/*GIR: -1*/ 0"; // Hmm, does this really belong in GIR...
794 // Using D syntax for arrays.
795 type ~= "[" ~ size ~ "]";
796 ctype ~= "[" ~ size ~ "]";
800 ep.onStartTag["callback"] = (ElementParser ep) {
801 callback = new x_function("functionp");
802 callback.parse(ep);
805 ep.parse();
809 final class x_function : x_elem {
810 string name, cname;
811 x_struct obj; // object if this is a method.
812 string rettype, crettype, retanno;
813 string throws; // actually gerrors, oh well.
814 bool is_variadic;
815 x_field[] args;
817 this(string s, x_struct pobj=null) { super(s); obj = pobj; }
819 string c_func(bool proto, bool skipthis, scope const(x_field)[] args) {
820 string r;
822 if (skipthis)
823 args = args[1..$];
825 r ~= validDSym(cname) ~ "(";
827 foreach(i, a; args) {
828 if (a.varargs)
829 r ~= "..."; // Variadic - have to be aliased (avoids having to deal w/ varargs).
830 else {
831 if (proto)
832 r ~= a.direction ~ Dtype(a.type, a.ctype) ~ " ";
833 r ~= a.cname ? validDSym(a.cname) : validDSym(a.name);
834 if (proto)
835 r ~= a.def_init;
836 if (i!=args.length-1)
837 r ~= ", ";
841 r ~= ")";
842 return r;
845 override string toString() {
846 string Drettype = Dtype(rettype, crettype);
847 string r = comment_to_D() ~ indent;
848 bool hasthis = 0;
849 bool refret;
851 if (auto p = cname in delete_symbol)
852 return "\n" ~ indent ~ "// " ~ tagname ~ " \"" ~ name ~ "\" removed: " ~ *p ~ "\n";
854 if (name=="") {
855 auto pos = std.string.indexOf(cname, '_');
856 name = "/*NAME MISSING IN GIR*/ ";
857 if (pos==-1)
858 name ~= "missing_" ~ cname;
859 else
860 name ~= cname[pos+1..$];
863 bool is_funcp = tagname=="callback" ||
864 tagname=="functionp" ||
865 tagname=="virtual-method";
867 // Fix up the argument list.
869 auto allargs = args;
871 if (obj) {
872 auto t = new x_field("this_");
873 t.type = obj.name ~ "*";
874 t.name = "this_";
876 if (tagname!="constructor") {
877 // Methods have a this pointer as the first arg.
878 allargs = t ~ allargs;
880 if (tagname!="function")
881 hasthis = 1;
883 else {
884 // Constructors return pointers to new objects, not some base type.
885 Drettype = obj.name ~ "*";
889 if (throws)
890 allargs ~= gerrfield;
892 foreach(i, a; allargs) {
893 // Fix up any missing arg names.
894 if (!a.name)
895 a.name = "arg_" ~ cast(char)('a'+i);
896 // Gtk2 has out params w/o a ctype and "gint" type - we'll do our best to fix it...
897 if (a.direction=="/*out*/ " && !a.ctype /*&& a.type[$-1]!='*'*/) {
898 a.ctype = a.type ~ "*";
899 a.direction ~= "/*POINTER*/ ";
901 // Fix up default params.
902 if (a.def_init=="=null")
903 switch (Dtype(a.type, a.ctype)) {
904 case "int": a.def_init = "=0"; break;
905 default: /**/; break;
909 // Remove default initializers from args that are followed by ones w/o defaults.
911 bool nondefarg_seen = 0;
912 foreach_reverse(a; allargs) {
913 if (!a.def_init && !a.varargs)
914 nondefarg_seen = 1;
915 if (nondefarg_seen)
916 a.def_init = null;
920 if (tagname=="glib:signal") {
921 Drettype = Dtype(rettype, crettype, 1);
923 // Signals have an extra 'user_data' pointer as the last arg.
924 auto udata = new x_field("user_data");
925 udata.type = "void*";
926 udata.name = "user_data=null"; // "=null" JIC previous args were optional.
927 allargs = allargs ~ udata;
929 r ~= "extern (C) ";
930 r ~= "alias ";
931 r ~= "static ";
932 r ~= Drettype ~ " " ~ retanno;
933 r ~= "function (";
935 foreach(i, a; allargs) {
936 r ~= a.direction ~ Dtype(a.type, a.ctype, 1) ~ " " ~ validDSym(a.name) ~ a.def_init;
937 if (i!=allargs.length-1)
938 r ~= ", ";
941 r ~= ")";
942 r ~= " signal_" ~ validDSym(name);
943 r ~= ";\n";
945 // Add some convenience templates:
947 if (!obj.emitted("signal_connect")) {
948 r ~= "\n";
949 r ~= indent ~ "ulong signal_connect(string name, CB)(CB cb, void* data=null, ConnectFlags cf=cast(ConnectFlags)0) {\n";
950 r ~= ++indent ~ "return super_.signal_connect!name(cb, data, cf);\n";
951 r ~= --indent ~ "}\n\n";
954 // Callback /type/ not checked, as doing it results in weird errors
955 // and very unhelpful template instantiation error messages, like:
957 // Error: template instance Clutter.Actor.signal_connect!(name,int function(Actor* stage, Event* event, void* data)) error instantiating
958 // instantiated from here: signal_connect!("button-press-event",int function(Actor* stage, Event* event, void* data))
959 // Error: template instance Clutter.Stage.signal_connect!("button-press-event",int function(Actor* stage, Event* event, void* data)) error instantiating
960 r ~= indent ~ `ulong signal_connect(string name:"` ~ name ~ `", `;
961 r ~= `CB/*:signal_` ~ validDSym(name) ~ "*/)(CB cb, void* data=null, ConnectFlags cf=cast(ConnectFlags)0) {\n";
962 r ~= ++indent ~ `return signal_connect_data(&this, cast(char*)"` ~ name ~ "\",\n";
963 r ~= indent ~ "cast(GObject2.Callback)cb, data, null, cf);\n";
964 r ~= --indent ~ "}\n";
966 return r;
969 if (is_variadic) {
970 bool unsupported = tagname!="function" &&
971 tagname!="constructor";
972 if (unsupported) {
973 r ~= "/+ Not available -- variadic " ~
974 ( hasthis ?
975 "methods unsupported - use the C function directly.\n"
977 "functions with non-D linkage must have >1 parameter.\n" );
978 r ~= ++indent;
981 r ~= "alias " ~ cname ~ " " ~ validDSym(name) ~ "; // Variadic\n";
982 if (unsupported)
983 r ~= --indent ~ "+/\n";
985 goto add_cproto;
988 bool hasUpperCase(string s) {
989 import std.uni;
990 foreach(c; s)
991 if (isUpper(c))
992 return 1;
993 return 0;
996 string refDrettype = Drettype;
997 if (reftypes && Drettype[$-1]=='*' && hasUpperCase(Drettype)) {
998 refDrettype = Drettype[0..$-1];
999 refret = 1;
1002 if (is_funcp)
1003 r ~= "extern (C) ";
1004 if (tagname=="callback")
1005 r ~= "alias ";
1006 if (tagname=="constructor" || tagname=="function")
1007 r ~= "static ";
1009 if (refret)
1010 r ~= "Ref!(" ~ refDrettype ~ ")";
1011 else
1012 r ~= Drettype;
1014 r ~= " " ~ retanno;
1016 if (is_funcp)
1017 r ~= "function (";
1018 else
1019 r ~= validDSym(name) ~ "(";
1021 auto args = allargs;
1023 if (hasthis)
1024 args = args[1..$];
1026 foreach(i, a; args) {
1027 if (a.varargs)
1028 r ~= "...";
1029 else {
1030 r ~= a.direction ~ Dtype(a.type, a.ctype) ~ " " ~ validDSym(a.name) ~ a.def_init;
1031 if (i!=args.length-1)
1032 r ~= ", ";
1036 r ~= ")";
1038 if (is_funcp)
1039 r ~= " " ~ validDSym(name);
1041 if (tagname=="functionp")
1042 return r;
1044 if (tagname=="callback" || tagname=="virtual-method") {
1045 r ~= ";\n";
1046 return r;
1049 r ~= " {\n" ~ ++indent;
1051 if (Drettype!="void")
1052 r ~= "return ";
1054 if (refret)
1055 r ~= "Ref!(" ~ refDrettype ~ ")(";
1057 string oname;
1058 if (hasthis) {
1059 oname = allargs[0].name;
1060 allargs[0].name = "&this";
1062 r ~= c_func(0, 0, allargs);
1063 if (refret)
1064 r ~= ")";
1065 r ~= ";\n" ~ --indent ~ "}\n";
1066 if (hasthis)
1067 allargs[0].name = oname;
1069 add_cproto:
1070 if (!is_funcp /*&& tagname=="function" || obj*/)
1071 cprotos ~= Drettype ~ " " ~ retanno ~ c_func(1, 0, allargs) ~ ";";
1073 return r;
1076 void parse(ElementParser ep) {
1077 trace("FUNC", tagname ~ (obj ? ": " ~ obj.name : ""), ep.tag().toString());
1078 // "callbacks" in structs are actually pointers.
1079 SkipEndTag(ep, tagname!="functionp" ? tagname : "callback");
1080 register_doc_parser(ep);
1081 auto tag = ep.tag();
1083 name = tag.attr["name"];
1084 aassign(cname, tag.attr, "c:identifier");
1085 aassign(throws, tag.attr, "throws");
1087 auto p = "introspectable" in tag.attr;
1088 if (p && *p=="0")
1089 doc ~= "Unintrospectable " ~ tagname ~ ": " ~ name ~ "() / " ~ cname ~ "()\n";
1091 ep.onStartTag["return-value"] = (ElementParser ep) {
1092 SkipEndTag(ep, "return-value");
1093 register_doc_parser(ep, "RETURNS");
1095 auto tag = ep.tag();
1097 if (auto p = "transfer-ownership" in tag.attr) {
1098 switch (*p) {
1099 case "none": retanno = ""; break;
1100 case "full": retanno = "/*new*/ "; break;
1101 case "container": retanno = "/*new container*/ "; break;
1102 default: enforce(0, "Unknown transfer-ownership: " ~ *p);
1106 parsearray(ep, rettype, crettype);
1108 ep.onStartTag["type"] = (ElementParser ep) {};
1109 ep.onEndTag["type"] = (in Element e) {
1110 auto tag = e.tag;
1111 aassign(crettype, tag.attr, "c:type");
1112 aassign(rettype, tag.attr, "name"/*, "func retval typename"*/);
1113 // If we have a ctype, but no type, try using the ctype.
1114 if (!rettype && crettype) {
1115 crettype = "/*CTYPE*/ " ~ crettype;
1116 rettype = crettype;
1118 enforce(rettype, "Missing func retval typename" );
1121 ep.parse();
1123 // Note the trick is there can be multiple nested <type>s;
1124 // once we get here we should have filled in the types properly.
1125 // Keep in mind we only care about the outermost type, that's
1126 // why parsing the _end_ </type> tag works for us.
1128 // Unfortunately c:type is often missing, so this check is too strict.
1129 //enforce(crettype, "Missing func retval c:type" ~ to!string(this));
1132 ep.onStartTag["parameters"] = (ElementParser pa) {
1133 trace("FUNCPARM", tagname, ep.tag().toString());
1134 SkipEndTag(pa, "parameters");
1135 pa.onStartTag["parameter"] = (ElementParser pa) {
1136 SkipEndTag(pa, "parameter");
1137 auto arg = new x_field("parameter", this); args ~= arg; arg.parse(pa);
1138 if (arg.varargs)
1139 is_variadic = 1;
1141 pa.parse();
1144 ep.parse();
1148 final class x_struct : x_elem {
1149 string name, parent;
1150 x_struct embedded_in;
1151 x_field[] fields;
1152 x_function[] methods;
1153 bool[string] emitted_;
1155 this(string tn, x_struct emb=null) {
1156 super(tn);
1157 embedded_in = emb;
1158 switch (tagname) {
1159 case "record": break;
1160 case "class": break;
1161 case "union": break;
1162 case "interface": break;
1163 default: enforce(0, "Unknown struct type: " ~ tagname);
1167 private bool emitted(string s) {
1168 if (auto p = s in emitted_)
1169 return 1;
1170 emitted_[s] = 1;
1171 return 0;
1174 private string basetypename(scope string type) {
1175 auto i = lastIndexOf(type, '.');
1176 return i==-1 ? type : type[i+1..$];
1179 override string toString() {
1180 // Everything looks like a struct to us. Except unions.
1181 string r = comment_to_D() ~ indent ~
1182 ((tagname=="union") ? "union " : "struct ") ~ subDKeyword(name);
1184 if (parent)
1185 r ~= " /* : " ~ parent ~ " */";
1186 if (ver)
1187 r ~= " /* Version" ~ ver~ " */";
1188 r ~= " {\n";
1190 indent++;
1192 // "alias super this".
1194 // First, if this struct has a parent, but we don't know any of its
1195 // fields then add the parent as the only (sub)struct -- harmless as
1196 // we couldn't access the opaque empty struct anyway and this lets
1197 // the pseudo-inheritance work.
1198 if (parent && fields.length==0) {
1199 auto pf = new x_field("record");
1200 pf.type = parent;
1201 pf.ctype = parent;
1202 pf.name = "method_parent";
1203 fields ~= pf;
1206 if (parent && fields.length>0 && (
1207 Dtype(fields[0].type, fields[0].ctype)==Dtype(parent, null) ||
1208 Dtype(fields[0].type, fields[0].ctype)==parent
1209 )) {
1210 auto fieldname = validDSym(fields[0].name);
1212 r ~= indent ~ "alias " ~ fieldname ~ " this;\n";
1213 r ~= indent ~ "alias " ~ fieldname ~ " super_;\n";
1215 // Add an extra alias for easier upcasting.
1216 // NOTE: The name we add could clash with existing symbols;
1217 // this does not happen in practice, will be caught at
1218 // compiletime if it does, and can be addressed then.
1219 // Also "object" is not the safest symbol to add, but
1220 // works so far, let's wait until it causes problems
1221 // before renaming it to something like "gobject".
1222 auto partype = fields[0].type;
1223 if (!partype)
1224 partype = Dtype(null, fields[0].ctype);
1225 auto aname = basetypename(validDSym(partype));
1226 aname = toLower(aname);
1227 if (aname!=fieldname)
1228 r ~= indent ~ "alias " ~ fieldname ~ " " ~ aname ~ ";\n";
1230 // The parent cannot be private, so fix it:
1231 if (fields[0].private_)
1232 fields[0].private_ = 0;
1235 // No bitfields in D.
1236 string bmix;
1237 int bfwidth;
1238 char bfcount='A'; // More than one __dummyNN names? Must be different.
1240 void eofbf() {
1241 if (!bmix)
1242 return;
1244 // For now we'll assume all bitfields are at least 32bits wide (C int-sized)
1245 int rawwidth;
1246 if (bfwidth>0 && bfwidth<=32)
1247 rawwidth = 32;
1248 else if (bfwidth>32 && bfwidth<=64)
1249 rawwidth = 64;
1250 enforce(rawwidth, bmix);
1252 // std.bitmaip reqs total width to be one of 8/16/32/64, so:
1253 if (rawwidth!=bfwidth) {
1254 if (rawwidth<=8 && bfwidth>0 && bfwidth<8)
1255 bmix ~= ",\n" ~ indent ~ " uint, \"__dummy8"~bfcount~"\", " ~ to!string(8-bfwidth);
1256 else if (rawwidth<=16 && bfwidth<16)
1257 bmix ~= ",\n" ~ indent ~ " uint, \"__dummy16"~bfcount~"\", " ~ to!string(16-bfwidth);
1258 else if (rawwidth<=32 && bfwidth<32)
1259 bmix ~= ",\n" ~ indent ~ " uint, \"__dummy32"~bfcount~"\", " ~ to!string(32-bfwidth);
1260 else if (bfwidth<64)
1261 bmix ~= ",\n" ~ indent ~ " uint, \"__dummy64"~bfcount~"\", " ~ to!string(64-bfwidth);
1262 else if (bfwidth==64)
1264 else
1265 enforce(0);
1267 r ~= bmix ~ "))";
1268 bmix = null;
1269 bfwidth = 0;
1270 bfcount++;
1273 // Unfortunately there are methods that end up using the same name
1274 // as struct fields. Build a list of all method names, and in the
1275 // code below check for clashes. Rename the fields as these are
1276 // less likely to be used by apps.
1277 bool[string] m_names;
1278 foreach (m ; methods)
1279 m_names[m.name] = 1;
1281 foreach (i, f; fields) {
1282 string privstr = f.private_ ? "private " : "";
1284 if (f.callback) {
1285 eofbf();
1286 if (i!=0)
1287 r ~= ";\n";
1289 r ~= privstr ~ f.callback.toString();
1291 else if (f.struct_) {
1292 eofbf();
1293 if (i!=0)
1294 r ~= ";\n";
1295 else
1296 r ~= "\n";
1298 r ~= "\n" ~ privstr ~ f.struct_.toString();
1300 else if (f.bitfield) {
1301 if (!bmix) {
1302 if (i!=0)
1303 r ~= ";\n";
1304 bmix = indent ~ " static import std.bitmanip;"
1305 ~ " mixin(std.bitmanip.bitfields!(\n" ~ indent;
1307 else
1308 bmix ~= ",\n" ~ indent;
1309 string bftype = Dtype(f.type, f.ctype);
1310 switch (bftype) {
1311 case "uint", "int", "c_uint", "c_int":
1312 bmix ~= bftype ~ ", \"" ~ f.name~"\", "~ to!string(f.bitfield);
1313 bfwidth += f.bitfield;
1314 break;
1315 default:
1316 enforce(0, "Unknown bitfield type " ~
1317 bftype ~ "[" ~ f.type ~ "," ~ f.ctype ~ "] " ~
1318 f.name ~ ":" ~ to!string(f.bitfield));
1321 else {
1322 eofbf();
1323 string fname = validDSym(f.name);
1324 if (fname in m_names)
1325 fname ~= "_";
1326 if (i==0)
1327 r ~= indent ~ privstr ~ Dtype(f.type, f.ctype) ~ " " ~ fname;
1328 else {
1329 string prevtype, currtype;
1330 if (fields[i-1].type || fields[i-1].ctype)
1331 prevtype = Dtype(fields[i-1].type, fields[i-1].ctype);
1332 currtype = Dtype(f.type, f.ctype);
1333 if ( (prevtype == currtype && fields[i-1].type==f.type)
1334 && (fields[i-1].private_ == f.private_)
1335 && !fields[i-1].bitfield )
1336 r ~= ", " ~ fname;
1337 else {
1338 r ~= ((!fields[i-1].struct_) ? ";" : "");
1339 r ~= "\n"~ indent ~ privstr ~ currtype ~ " " ~ fname;
1345 eofbf();
1347 if (fields.length>0 && !fields[$-1].struct_)
1348 r ~= ";\n";
1350 if (fields.length>0 && methods.length>0)
1351 r ~= "\n";
1353 foreach (f; methods)
1354 r ~= f.toString();
1356 // Pull in the right mixin, if any:
1358 string mixname = name;
1360 for (auto ei = embedded_in; ei; ei = ei.embedded_in)
1361 mixname = ei.name ~ "_" ~ mixname;
1363 r ~= load_mixin(mixname);
1365 return r ~ --indent ~ "}\n";
1368 void parse(ElementParser ep) {
1369 trace("STRUCT", tagname, ep.tag().toString());
1370 auto tag = ep.tag();
1371 name = tag.attr["name"];
1372 aassign(parent, tag.attr, "parent");
1374 register_doc_parser(ep);
1375 ep.onStartTag["field"] = (ElementParser ep) {
1376 auto nf = new x_field("field"); fields ~= nf; nf.parse(ep);
1378 ep.onStartTag["union"] = (ElementParser ep) {
1379 auto nf = new x_field("union"); fields ~= nf;
1380 auto na = new x_struct("union", this); nf.struct_ = na; na.parse(ep);
1382 ep.onStartTag["record"] = (ElementParser ep) {
1383 auto nf = new x_field("record"); fields ~= nf;
1384 auto na = new x_struct("record", this); nf.struct_ = na; na.parse(ep);
1386 ep.onStartTag["property"] = (ElementParser p) {
1387 p.parse(); // Drop them.
1389 ep.onStartTag["method"] = (ElementParser ep) {
1390 auto nm = new x_function("method", this); methods ~= nm; nm.parse(ep);
1392 ep.onStartTag["virtual-method"] = (ElementParser p) {
1393 p.parse(); // Drop them.
1395 ep.onStartTag["function"] = (ElementParser ep) {
1396 auto nm = new x_function("function"); methods ~= nm; nm.parse(ep);
1398 ep.onStartTag["constructor"] = (ElementParser ep) {
1399 auto nm = new x_function("constructor", this); methods ~= nm; nm.parse(ep);
1401 ep.onStartTag["glib:signal"] = (ElementParser ep) {
1402 auto nm = new x_function("glib:signal", this); methods ~= nm; nm.parse(ep);
1405 ep.onStartTag["prerequisite"] = (ElementParser ep) { ep.parse(); }; // Drop.
1406 ep.onEndTag["prerequisite"] = (in Element e) {};
1408 // Drop them.
1409 ep.onStartTag["implements"] = (ElementParser p) { p.parse(); };
1410 ep.onEndTag["implements"] = (in Element e) {};
1412 ep.parse();
1416 void onUnknownTag(ElementParser ep, string tagname) {
1417 ep.onStartTag[null] = (ElementParser ep) {
1418 enforce(0, "Unhandled <"~tagname~"> start tag: "
1419 ~ ep.tag.name ~ "\n# " ~ ep.tag().toString());
1421 ep.onEndTag[null] = (in Element e) {
1422 enforce(0, "Unhandled <"~tagname~"> end tag: " ~ "\n# " ~ e.toString());
1426 void SkipEndTag(ElementParser p, string tagname) {
1427 onUnknownTag(p, tagname);
1428 p.onEndTag[tagname] = (in Element e) { };
1431 void onEmptyTag(ElementParser p, string tagname, void delegate(in Element e) deleg) {
1432 p.onStartTag[tagname] = (ElementParser ep) {
1433 /* do nothing; only tells the parser that we know about this tag */
1435 p.onEndTag[tagname] = (in Element e) {
1436 auto tag = e.tag;
1437 enforce(tag.type==TagType.EMPTY, tag.toString());
1439 deleg(e);
1443 void onStartTag(ElementParser p, string tagname, void delegate(ElementParser ep) deleg) {
1444 p.onStartTag[tagname] = (ElementParser ep) {
1445 onUnknownTag(ep, tagname);
1446 ep.onEndTag[tagname] = (in Element e) {/*Reached our own end tag*/};
1447 deleg(ep);
1449 p.onEndTag[tagname] = (in Element e) {
1450 /* Do nothing; only tells the parser that we know about this tag */
1451 /* Ugh. "<record name="AppLaunchContextPrivate" />" happens... */
1455 template OnTag(alias pa, string Kind, string xmlname, string tagname) {
1456 enum OnTag = "
1457 on"~Kind~"Tag(" ~ pa.stringof ~ ", \"" ~ xmlname ~ "\","
1458 " (" ~ ((Kind=="Start") ? "ElementParser e" : "in Element e") ~ ") {
1459 auto a = new x_"~tagname~"(" ~ xmlname.stringof ~ ");
1460 a.parse(e);
1461 "`write(a.toString() ~"\n");`
1462 "} );";
1465 string delete_symbol[string]; // [name: reason]
1467 void load_delete() {
1468 string filename = "mixin/" ~ thismodulename ~ "_" ~ "_DELETE_";
1469 string s;
1471 try {
1472 s = cast(string)std.file.read(filename);
1473 } catch (std.file.FileException) {
1474 if (tracemixins)
1475 ew("DELETE Failed: " ~ filename);
1476 return;
1479 foreach (line; split(s, "\n")) {
1480 auto arr = split(line, ":");
1481 if (arr.length==0)
1482 continue;
1483 if (arr.length==1)
1484 delete_symbol[line] = "";
1485 else
1486 delete_symbol[arr[0]] = strip(join(arr[1..$], ":"));
1490 bool tracemixins;
1492 string load_mixin(string id) {
1493 string filename = "mixin/" ~ thismodulename ~ "_" ~ id ~ ".d";
1494 string s;
1496 static bool uniq_mix[string];
1498 enforce(filename !in uniq_mix, "Mixin collission: " ~ filename);
1499 uniq_mix[filename] = 1;
1501 try {
1502 s = cast(string)std.file.read(filename);
1503 } catch (std.file.FileException) {
1504 if (tracemixins)
1505 ew("MIXIN Failed: " ~ filename);
1506 return null;
1509 if (tracemixins)
1510 ew("MIXIN Found: " ~ filename);
1512 string r = "\n" ~ indent ~ "// --- " ~ filename ~ " --->\n\n";
1514 foreach (sp; split(s, "\n"))
1515 r ~= indent ~ sp ~ "\n";
1517 r ~= indent ~ "// <--- " ~ filename ~ " ---\n";
1519 return r;
1522 bool reftypes = 0;
1524 void main(string[] args) {
1525 string infilename, outfilename, modulename;
1526 bool xmlcheck;
1528 try {
1529 // Parse cmd line:
1531 bool delegate(string arg) eatarg;
1533 cmdargloop: foreach(arg; args[1..$]) {
1534 if (eatarg) {
1535 if (eatarg(arg))
1536 eatarg = null;
1537 continue;
1539 if (arg[0]!='-') {
1540 if (!infilename) {
1541 infilename = arg;
1542 continue;
1544 die("Unknown extra cmdline file name: " ~ arg);
1546 if (arg.length<2)
1547 die("Missing cmdline option: " ~ arg);
1548 switch (arg[1]) {
1549 case '-':
1550 switch (arg[2..$]) {
1551 debug(TRACE) {
1552 case "trace": showtrace = 1; break;
1554 case "trace-mixins": tracemixins = 1; break;
1555 case "check": xmlcheck = 1; break;
1556 case "reftypes": reftypes = 1; break;
1557 case "no-reftypes": reftypes = 0; break;
1558 default: die("Unknown long cmdline option: " ~ arg);
1560 break;
1561 case 'o':
1562 if (arg.length>2)
1563 outfilename = arg[2..$];
1564 else
1565 eatarg = (string arg) { outfilename = arg; return true; };
1566 continue cmdargloop;
1567 case 'M':
1568 if (arg.length>2)
1569 outfilename = arg[2..$];
1570 else
1571 eatarg = (string arg) { modulename = arg; return true; };
1572 continue cmdargloop;
1573 default:
1574 die("Unknown cmdline option: " ~ arg);
1578 if (eatarg)
1579 die("Missing cmdline arg");
1581 string s = cast(string)std.file.read(infilename);
1583 if (xmlcheck)
1584 check(s); // Running the checks increases the execution time from 0.22s to 1.74s...
1586 if (outfilename)
1587 stdout.open(outfilename, "wt");
1589 writef("// *** DO NOT EDIT ***\n// Automatically generated from \"%s\"\n\n", infilename);
1591 if (modulename)
1592 write("module " ~ modulename ~ ";\n");
1594 auto dp = new DocumentParser(s);
1596 void delegate (ElementParser ep) epskip = (ElementParser ep) { ep.parse(); };
1597 void delegate (ElementParser p) epfixme = (ElementParser p)
1598 { p.parse(); ew("FIXME: " ~ p.toString()); };
1599 void delegate (in Element e) efixme = (in Element e) { ew("FIXME: " ~ e.toString()); };
1601 onUnknownTag(dp, "Document");
1603 onEmptyTag(dp, "package", (in Element e) {
1604 auto name = get_name_attr(e);
1605 write("\n// package: \"" ~ name ~ "\";\n");
1606 } );
1608 onEmptyTag(dp, "c:include", (in Element e) {
1609 auto name = get_name_attr(e);
1610 write("// C header: \"" ~ name ~ "\";\n");
1611 } );
1613 mixin( OnTag!(dp, "Empty", "include", "include") );
1614 mixin( OnTag!(dp, "Start", "namespace", "namespace") );
1615 dp.onEndTag["namespace"] = (in Element e) { }; // We're assuming one ns per file.
1617 dp.onEndTag["repository"] = (in Element e) { }; // We're assuming one repo per file.
1619 mixin( OnTag!(dp, "Start", "class", "struct") );
1620 mixin( OnTag!(dp, "Start", "record", "struct") );
1621 mixin( OnTag!(dp, "Start", "union", "struct") );
1622 mixin( OnTag!(dp, "Start", "interface", "struct") );
1624 mixin( OnTag!(dp, "Start", "constant", "field") );
1625 mixin( OnTag!(dp, "Start", "alias", "field") );
1627 mixin( OnTag!(dp, "Start", "bitfield", "bitfield") );
1628 mixin( OnTag!(dp, "Start", "enumeration", "bitfield") );
1630 mixin( OnTag!(dp, "Start", "function", "function") );
1631 mixin( OnTag!(dp, "Start", "callback", "function") );
1633 dp.onStartTag["glib:boxed"] = epskip;
1635 // Not handled yet. Can't eat them here, we need the types anyway.
1636 //dp.onStartTag["interface"] = epfixme;
1638 dp.parse();
1640 write(load_mixin("_MODULE"));
1642 write("\n// C prototypes:\n\nextern (C) {\n");
1643 // Some methods are also documented as functions and the D
1644 // compiler does not like duplicate prototypes; drop them.
1645 bool uniq_proto[string];
1646 foreach(s; cprotos) {
1647 if (s !in uniq_proto)
1648 writeln(s);
1649 uniq_proto[s] = 1;
1651 write("}\n");
1654 catch (Exception e) {
1655 import std.file;
1656 ew("ERROR: ", e);
1657 if (outfilename)
1658 remove(outfilename);
1659 else
1660 ew("WARNING: Unable to remove destination file.");
1661 die();
1665 void die(S...)(S args) {
1666 import core.stdc.stdlib;
1667 ew(args);
1668 exit(1);
1669 assert(0);