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)
18 // Compile in tracing support? (also configurable at runtime)
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
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
); }
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
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
, ",") ) ); }
57 private static immutable spaces
=
59 @property string
s() { return spaces
[0..level
]; }
61 string
opUnary(string op
:"++")() { level
+= by
; return s
; }
62 string
opUnary(string op
:"--")() { level
-= by
; assert (level
>=0); return s
; }
67 private string
validDSym(scope string 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":
77 if (s
&& s
.length
>0 && isDigit(s
[0]))
82 string
subDKeyword(scope string word
) {
83 if (auto subst
= word
in dKeywords
)
88 string dKeywords
[string
];
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",
103 foreach(word
; reserved
)
104 dKeywords
[word
] = word
~ "_";
106 dKeywords
["TypeInfo"] = "GTypeInfo";
110 private string
escdquote(scope string s
) {
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) {
128 // Some gtk signals have /no/ info; at least don't break the build.
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]==']') {
136 auto spos
= std
.string
.indexOf(gt
, '[');
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]=='*') {
148 auto apos
= std
.string
.indexOf(ct
, '*');
150 alen
= ct
.length
- apos
;
151 string suf
= ct
[$-alen
..$];
154 return Dtype(gt
, ct
[0..$-alen
]) ~ suf
;
157 // Obvious conversions first:
158 if (gt
=="gint8" && ct
=="gint8") return "byte";
159 if (gt
=="guint8" && ct
=="guint8") return "ubyte";
160 if (gt
=="guint8" && ct
=="gchar") return "ubyte";
161 if (gt
=="guint8" && ct
=="guchar") return "ubyte";
162 if (gt
=="guint8" && ct
=="unsigned char") return "ubyte";
163 if (gt
=="gint16" && ct
=="gint16") return "short";
164 if (gt
=="guint16" && ct
=="guint16") return "ushort";
165 if (gt
=="gint32" && ct
=="gint32") return "int";
166 if (gt
=="guint32" && ct
=="guint32") return "uint";
167 if (gt
=="gint64" && ct
=="gint64") return "long";
168 if (gt
=="guint64" && ct
=="guint64") return "ulong";
171 if (gt
=="gshort" && ct
=="gshort") return "short";
172 if (gt
=="gshort" && ct
=="short") return "short";
173 if (gt
=="gushort" && ct
=="gushort") return "ushort";
176 if (gt
=="gint" && ct
=="signed") return "c_int";
177 if (gt
=="gint" && ct
=="int") return "c_int";
178 if (gt
=="gint" && ct
=="gint") return "c_int";
179 if (gt
=="gint" && !ct
) return "c_int"; // gtk2 needs it.
180 if (gt
=="guint" && ct
=="guint") return "c_uint";
181 if (gt
=="guint" && ct
=="unsigned int") return "c_uint";
182 if (gt
=="guint" && ct
=="unsigned") return "c_uint";
184 // Assumes 32-bit wide target ints, but is far more readable...
185 if (gt
=="gint" && ct
=="signed") return "int";
186 if (gt
=="gint" && ct
=="int") return "int";
187 if (gt
=="gint" && ct
=="gint") return "int";
188 if (gt
=="gint" && !ct
) return "int"; // gtk2 needs it.
189 if (gt
=="guint" && ct
=="guint") return "uint";
190 if (gt
=="guint" && ct
=="unsigned int") return "uint";
191 if (gt
=="guint" && ct
=="unsigned") return "uint";
193 if (gt
=="gfloat" && ct
=="gfloat") return "float";
194 if (gt
=="gfloat" && ct
=="float") return "float";
195 if (gt
=="gdouble" && ct
=="double") return "double";
196 if (gt
=="gdouble" && ct
=="gdouble") return "double";
197 if (gt
=="long double" && ct
=="long double") return "real";
199 if (gt
=="none" && ct
=="void") return "void";
201 if (gt
=="gunichar" && ct
=="gunichar") return "dchar";
202 if (gt
=="guint16" && ct
=="gunichar2") return "wchar";
204 if (gt
=="glong" && ct
=="long") return "c_long";
205 if (gt
=="glong" && ct
=="glong") return "c_long";
206 if (gt
=="gulong" && ct
=="gulong") return "c_ulong";
207 if (gt
=="gulong" && ct
=="unsigned long") return "c_ulong";
209 if ( ct
=="size_t") return "size_t";
210 if (gt
=="gsize" && ct
=="gsize") return "size_t";
211 if (gt
=="gssize" && ct
=="gssize") return "ssize_t";
212 if (gt
=="gint64" && ct
=="goffset") return "long"; // signed 64bit int, C99: off64_t
214 if (gt
=="gchar" && ct
=="gchar") return "char";
215 if (gt
=="gchar" && ct
=="char") return "char";
216 if (gt
=="gchar" && ct
=="guchar") return "ubyte";
218 if (gt
=="utf8" && ct
=="char") return "char";
219 if (gt
=="utf8" && ct
=="gchar") return "char";
221 if (gt
=="guint8" && ct
=="char") return "ubyte";
223 if (gt
=="gpointer" && ct
=="void") return "void";
224 if (gt
=="gpointer" && !ct
) return "void*"; // GTK2
225 if (gt
=="gpointer" && ct
=="gpointer") return "void*";
227 if (gt
=="guint8" && ct
=="void") return "ubyte"; // used for buffer *'s.
228 if (gt
=="guint8" && ct
=="gpointer") return "void*";
229 if (gt
=="gpointer" && ct
=="gconstpointer") return "const(void)*";
231 if (gt
=="guint8" && ct
=="gconstpointer") return "const(ubyte)*";
232 if (gt
=="Variant" && ct
=="gconstpointer") return "const(Variant)*";
233 if (gt
=="VariantType" && ct
=="gconstpointer") return "const(VariantType)*";
235 if (gt
=="Object" && ct
=="gpointer") return "Object*";
236 if (gt
=="TypeClass" && ct
=="gpointer") return "TypeClass*";
237 if (gt
=="TypeInterface" && ct
=="gpointer") return "TypeInterface*";
239 if (gt
=="DBusObjectManagerClient" && ct
=="GDBusObjectManager") return "DBusObjectManagerClient";
242 if (gt
=="gboolean" && ct
=="gboolean") return "int";
243 if (gt
=="glong" && ct
=="time_t") return "time_t"; // well...
245 if (gt
=="guint" && ct
=="uid_t") return "uint"; // Good enough?
248 if (gt
=="gpointer" && ct
=="GArray") return "Array";
249 if (gt
=="gpointer" && ct
=="GByteArray") return "ByteArray";
250 if (gt
=="gpointer" && ct
=="GPtrArray") return "PtrArray";
251 if (gt
=="gpointer" && ct
=="FILE") return "FILE";
253 if (gt
=="guint8" && ct
=="GByteArray") return "ByteArray";
255 // Not even the module names are used consistently...
257 void fixupmodnames(scope string from
, scope string to
) {
259 auto fl
= from
.length
+1; // '+1' cause of the trailing '.'.
260 if (gt
.length
>=fl
&& gt
[0..fl
]==from
~ ".") {
261 gt
= to
~ "." ~ gt
[fl
..$];
263 // Don't change ct for "GObject.ObjectClass" "GObjectClass"
264 if (ct
.length
>1 && orggt
[fl
..$]==ct
[1..$])
268 if (ct
.length
>fl
&& from
== ct
[0..fl
])
273 fixupmodnames("GLib", "GLib2");
274 fixupmodnames("GdkPixbuf", "GdkPixbuf2");
275 fixupmodnames("GObject", "GObject2");
276 fixupmodnames("Gio", "Gio2");
277 fixupmodnames("Gdk", "Gdk2");
278 fixupmodnames("fontconfig", "fontconfig2");
280 if (gt
=="va_list" && ct
=="va_list") return "va_list";
281 if (gt
=="TypeInfo" ) return "GTypeInfo"; // To avoid clashes with D's base object.
282 // Note: "GTypeInfo" maps to different things in different scopes (modules).
285 if (gt
=="GType" && ct
=="GType") return "Type";
286 if (gt
=="GObject2.Object" && ct
=="gpointer") return "GObject2.Object*";
288 if (gt
=="GdkPixbuf2.Pixbuf" && ct
=="GdkPixbuf") return "GdkPixbuf2.Pixbuf"; // in gdk
290 if (gt
=="cairo.Surface" && ct
=="cairo_surface_t") return "cairo.Surface"; // in gdk
291 if (gt
=="cairo.Context" && ct
=="cairo_t") return "cairo.Context"; // in gdk
292 if (gt
=="cairo.FontOptions" && ct
=="cairo_font_options_t") return "cairo.FontOptions"; // in gdk
293 if (gt
=="cairo.Content" && ct
=="cairo_content_t") return "cairo.Content"; // in gdk
294 if (gt
=="cairo.Pattern" && ct
=="cairo_pattern_t") return "cairo.Pattern"; // in gdk
297 if (ct
=="CoglDisplay") return "Cogl.Display";
298 if (ct
=="CoglContext") return "Cogl.Context";
299 if (ct
=="CoglRenderer") return "Cogl.Renderer";
303 if (gt
=="cairo.RectangleInt" && ct
=="cairo_rectangle_int_t") return "cairo.RectangleInt";
304 if (gt
=="cairo.Path" && ct
=="cairo_path_t") return "cairo.Path";
306 if (gt
=="Stage" && ct
=="ClutterActor") return "Clutter.Stage";
310 "xlib.XEvent" "XEvent" "xlib.XEvent"
311 "xlib.Pixmap" "Pixmap" "xlib.Pixmap"
312 "xlib.Window" "Window" "xlib.Window"
313 "xlib.Time" "Time" "xlib.Time"
314 "xlib.Display" "Display" "xlib.Display"
316 if (gt
.length
>ct
.length
+2) {
318 if (gt
[$-l
-1..$]==("."~ct
))
323 if (gt
=="freetype2.Library" && ct
=="FT_Library") return "freetype2.Library";
324 if (gt
=="cairo.ScaledFont" && ct
=="cairo_scaled_font_t") return "cairo.ScaledFont";
325 if (gt
=="cairo.FontType" && ct
=="cairo_font_type_t") return "cairo.FontType";
327 if (ct
=="PangoFcFontMap") return "Pango.FontMap";
330 if (gt
=="fontconfig2.Pattern" && ct
=="FcPattern") return "fontconfig2.Pattern";
331 if (gt
=="freetype2.Face" && ct
=="FT_Face") return "freetype2.Face";
332 if (gt
=="freetype2.Bitmap" && ct
=="FT_Bitmap") return "freetype2.Bitmap";
336 // "Pango.Font" "PangoFont*" => "Pango.Font*"; // in gdk
337 // "Pango.GlyphString" "PangoGlyphString*" => "Pango.GlyphString*"; // in gdk
338 // "Pango.Direction" "PangoDirection" => "Pango.Direction"
340 auto mlen
= countUntil(gt
, ".");
341 if (mlen
>0 && ct
.length
>mlen
&& gt
[0..mlen
]==ct
[0..mlen
]) {
342 auto ctapos
= countUntil(ct
, "*");
345 if (ctapos
>0 && gt
[mlen
+1..$]==ct
[mlen
..ctapos
] ) {
346 gt
= gt
~ ct
[ctapos
..$];
348 // Make things like "GL.uint" valid:
349 auto lastpi
= lastIndexOf(gt
, '.');
351 gt
= gt
[0..lastpi
+1] ~ subDKeyword(gt
[lastpi
+1..$]);
357 // ATK needs this one, as the datatype is missing both name and definition; this lets it build.
358 if (gt
=="AtkPropertyValues" && ct
=="AtkPropertyValues") return "_PropertyValues";
361 if (gt
=="GdkPixbuf2.PixbufAlphaMode" && ct
=="GdkPixbuf2AlphaMode") return "GdkPixbuf2.PixbufAlphaMode";
364 if (gt
=="filename" && ct
=="gchar") return "char"; // Probably const, but...
365 if (gt
=="filename" && !ct
) return "char*"; // Probably const, but...
367 if (gt
=="GdkPixbuf2.PixbufAnimation" && ct
=="GdkPixbuf2Animation") return "GdkPixbuf2.PixbufAnimation";
368 if (gt
=="GdkPixbuf2.PixbufAnimationIter" && ct
=="GdkPixbuf2AnimationIter") return "GdkPixbuf2.PixbufAnimationIter";
371 // In fact if this is for a signal and has no <ct> it probably should be pointer...
372 // We're gonna guess, and we'll be wrong sometimes. But we will be right in many
373 // cases and there isn't really much else that we could do...
374 if (signals
&& !ct
&& gt
[$-1]!='*') {
375 if (gt
=="GObject2.Error") return "GLib2.Error*";
377 if (std
.string
.indexOf(gt
,'.')!=-1) return gt
~ "*";
378 if (gt
[0]>='A' && gt
[0]<='Z') return gt
~ "*";
381 case "gfloat": return "float";
382 case "gdouble": return "double";
383 case "none": return "void";
384 case "utf8": return "char*";
385 case "gint": return "c_int";
386 case "guint": return "c_uint";
387 case "gboolean": return "c_int";
392 // Fix up some obviously broken types.
394 if (gt
=="Object") return "Object*";
397 version(all
) { // Disable this while debugging.
402 if (gt
&& (!ct || ct
==""))
403 return gt
; // what else?...
405 enforce(gt
&& ct
, "Missing type: GTK: \"" ~ gt
~ "\" C: \"" ~ ct
~ "\"");
407 foreach (prefix
; ident_prefixes
) {
408 auto l
= prefix
.length
;
409 if (ct
.length
>l
&& ct
[0..l
]==prefix
) {
410 // Try Quark" "GQuark -> "Quark"
411 string stripped_ct
= ct
[l
..$];
417 // Last try: if ctype starts with 'G' and last dot-delimited component
418 // of gtktype equals the post-G ctype, use that.
419 // Eg. "Gio.AppLaunchContext", "GAppLaunchContext" => "Gio.AppLaunchContext"
421 auto tail
= "." ~ ct
[1..$];
422 if(gt
.length
>=tail
.length
&& gt
[$-tail
.length
..$] == tail
)
426 enforce(0, "Unknown type: GTK: \"" ~ gt
~ "\" C: \"" ~ ct
~ "\"");
427 assert(0); // @noreturn
430 // Set string <dst> to <aa[index]>, but only if <aa> contains such value.
431 // if <req> is given assert if the value is missing.
432 void aassign(ref string dst
, in string
[string
] aa
, in string index
, string req
=null) {
433 if (auto p
= index
in aa
)
436 enforce(0, "Missing " ~ req
~ "; Available: " ~ to
!string(aa
));
438 string
get_name_attr(in Element e
) {
440 if (auto p
= "name" in tag
.attr
) {
441 // Debugging checks, harmless, can be left enabled.
442 if (*p
!="GLib.List" && *p
!="GLib.SList"&& *p
!="GLib.HashTable")
443 enforce(tag
.type
==TagType
.EMPTY
, tag
.toString());
444 // <alias>es trigger this one.
445 //enforce(tag.attr.length==1, to!string(tag.attr.length));
451 string
simplify_version(scope string v
, scope string modname
=null) pure {
455 else if (v
[1..$]==".0")
457 if (modname
&& result
.length
==1 && modname
[$-1]==result
[0])
463 string tagname
, doc
, dochead
, ver
;
465 this(string s
) { tagname
= s
; }
467 final void parse_doc_text(ElementParser ep
) {
468 ep
.onText
= (string s
) {
471 doc
~= "\n" ~ dochead
~ ": ";
474 SkipEndTag(ep
, "doc");
478 final void register_doc_parser(ElementParser ep
, string dochead
=null) {
479 ep
.onStartTag
["doc"] = &parse_doc_text
;
483 if (auto p
= "version" in tag
.attr
)
485 this.dochead
= dochead
;
487 final string
comment_to_D() {
491 while (doc
[$-1]=='\n')
495 r
= "\n// " ~ replace(doc
, "\n", "\n// ");
496 r
= replace(r
, "\n// ", "\n" ~ indent
~ "// ") ~ "\n";
497 // Remove the newline before one-line comments.
498 import std
.algorithm
;
499 if (count(r
, '\n')<=2)
505 final void parsearray(ElementParser p
, ref string _type
, ref string _ctype
,
506 bool makepointer
=0) {
507 p
.onStartTag
["array"] = (ElementParser ep
) {
508 aassign(_ctype
, ep
.tag().attr
, "c:type");
511 ep
.onStartTag
["type"] = (ElementParser ep
) { ep
.parse(); };
512 ep
.onEndTag
["type"] = (in Element e
) {
513 trace("ELEMARR", tagname
, e
.tag
.toString());
514 aassign(_type
, e
.tag
.attr
, "name");
521 final class x_include
: x_elem
{
524 this(string s
) { super(s
); }
526 void parse(in Element e
) {
528 enforce(tag
.attr
.length
==2);
529 name
= tag
.attr
["name"];
530 ver
= tag
.attr
["version"];
533 override string
toString() {
534 string modulebasename
= name
~ simplify_version(ver
, name
);
535 string modulebasenamel
= toLower(modulebasename
);
536 // If we use "import GLib2" the compiler will fail to find the module
537 // while building the other ones.
538 // If we use "import gtk2.GLib2" the compiler will complain about
539 // missing Glib2.* types.
540 // NOTE: Imports are "public" so that apps don't have to import Gdk
541 // just for types only used for callback args etc.
542 return indent
~ "public import " ~ PACKAGE_NAME
~ "." ~ modulebasenamel
~ ";\n"
543 ~ indent
~ "alias " ~ PACKAGE_NAME
~ "." ~ modulebasenamel
~ " "
544 ~ modulebasename
~ ";";
548 string thismodulename
;
550 final class x_namespace
: x_elem
{
553 this(string s
) { super(s
); }
555 void parse(ElementParser ep
) {
556 auto attr
= ep
.tag().attr
;
558 ver
= attr
["version"];
562 if (auto p
= "c:symbol-prefixes" in attr
) {
563 symbol_prefixes
= csv2arr(*p
);
564 writeln("// c:symbol-prefixes: ", symbol_prefixes
);
566 if (auto p
= "c:identifier-prefixes" in attr
) {
567 ident_prefixes
= csv2arr(*p
);
568 write("// c:identifier-prefixes: ", ident_prefixes
, "\n\n");
571 // We do not parse further, ie no ep.parse() here.
574 override string
toString() {
575 thismodulename
= name
~ simplify_version(ver
, name
);
576 string s
= indent
~ "// module " ~ thismodulename
~ ";\n";
577 // Per module fixups:
578 s
~= load_mixin("_MODULE_HEAD");
585 // "ALNUM" <= shortenconst("alnum", "G_ASCII_ALNUM")
586 string
shortenconst(scope string name
, scope string cname
) {
587 if (cname
.length
>=name
.length
) {
588 auto shortname
= cname
[($-name
.length
)..$];
589 if (shortname
==toUpper(name
))
590 return validDSym(shortname
);
595 final class x_bitfield
: x_elem
{
599 this(string s
) { super(s
); }
601 void parse(ElementParser ep
) {
602 trace("BITFIELD", tagname
, ep
.tag().toString());
604 name
= tag
.attr
["name"];
606 register_doc_parser(ep
);
607 ep
.onStartTag
["member"] = (ElementParser ep
) {};
608 ep
.onEndTag
["member"] = (in Element e
) {
609 enforce(e
.tag
.type
==TagType
.EMPTY
, e
.tag
.toString());
611 auto m
= new x_field("member");
614 auto mattr
= e
.tag
.attr
;
615 aassign(m
.name
, mattr
, "name", "bitfield name");
616 aassign(m
.value
, mattr
, "value", "bitfield value");
617 aassign(m
.identifier
, mattr
, "c:identifier", "bitfield c:identifier");
620 ep
.onStartTag
["function"] = (ElementParser ep
) {
621 ew("FIXME: Skipping enum func: ", name
, ".", ep
.tag().attr
["name"]);
622 // a gobject-introspection update made functions appear inside
623 // <enumeration> tags. We could handle this by using struct
624 // namespaces, but let's ignore these "functions" for now.
627 ep
.onEndTag
["function"] = (in Element e
) {
633 override string
toString() {
634 // Do we care about the basetype (ie do 31bits+ values happen?)
635 string r
= comment_to_D() ~ indent
~ "enum " ~ name
;
637 r
~= " /* Version" ~ ver
~ " */";
641 foreach (i
, m
; members
) {
642 long v
= to
!long(m
.value
);
644 m
.value
= "cast(uint)" ~ m
.value
;
645 r
~= indent
~ shortenconst(m
.name
, m
.identifier
) ~ " = " ~ m
.value
~
646 (i
!=members
.length
-1 ?
"," : "") ~ "\n";
649 return r
~ --indent
~ "}";
653 x_field gerrfield
; // pseudo field; used by functions that "throw" in gtk-speak.
655 gerrfield
= new x_field("GError");
656 gerrfield
.type
= "GLib2.Error**";
657 gerrfield
.name
= "error=null";
658 gerrfield
.cname
= "error";
661 final class x_field
: x_elem
{
662 string type
, name
, cname
;
663 string value
; // for constants
664 string identifier
; // for bitfields
666 bool varargs
; // true if this parameter is "..."
670 x_struct struct_
; // embedded struct/union;
671 x_function callback
; // a callback, ie embedded function pointer.
672 x_elem docparent
; // parent element, where we place our doc.
673 int bitfield
; // if >0 then we need to handle it specially...
675 this(string s
, x_elem parent
=null) { super(s
); docparent
=parent
;}
677 override string
toString() {
678 string r
= comment_to_D();
680 if (auto p
= name
in delete_symbol
)
681 return "\n" ~ indent
~ "// " ~ tagname
~ " \"" ~ name
~ "\" removed: " ~ *p
~ "\n";
684 return r
~ struct_
.toString();
690 r
~= indent
~ "enum " ~ name
~ " = \"" ~ escdquote(value
) ~ "\";";
693 string contype
= Dtype(type
, ctype
);
695 // "int MUTEX_DEBUG_MAGIC = cast(int)4175530711;" needs the cast...
697 if (contype
=="int") {
698 long conval
= to
!long(value
);
699 if (conval
>int.max
&& conval
<=uint.max
)
700 concast
= "cast(int)";
703 // Clutter has constants with *names* starting with a digit...
704 auto validname
= validDSym(name
);
706 r
~= indent
~ "enum " ~ contype
~ " " ~ validname
~ " = " ~ concast
~ value
~ ";";
710 string dtype
= Dtype(type
, ctype
);
711 // We're renaming types, and that can cause problems, as in Gtk2.Type case.
714 r
~= indent
~ "alias " ~ dtype
~ " " ~ name
~ ";" /*~ "\t// " ~ ctype*/;
718 // case "field": /* Fields in classes/structs are handled in containers. */
720 enforce(0, "Unknown field type: " ~ tagname
);
725 void parse(ElementParser ep
) {
726 trace("FIELD", tagname
, ep
.tag().toString());
727 SkipEndTag(ep
, tagname
);
730 auto field_attr
= tag
.attr
;
731 aassign(name
, field_attr
, "name");
732 aassign(value
, field_attr
, "value"); // Constants.
735 docparent
.register_doc_parser(ep
, name ?
"<" ~ name
~ ">": "PARAM");
737 register_doc_parser(ep
);
739 if (auto p
= "private" in field_attr
)
742 if (auto p
= "bits" in field_attr
) // Bitfields need special treatment.
743 bitfield
= to
!int(*p
);
745 ep
.onStartTag
["type"] = (ElementParser ep
) {};
746 ep
.onEndTag
["type"] = (in Element e
) {
748 aassign(ctype
, a
, "c:type");
749 if (tagname
=="alias" || tagname
=="parameter") {
750 /* GLib.List etc have extra info in child, ignored for now */
751 auto p
= "name" in a
;
752 if ( !p ||
( *p
!="GLib.List" && *p
!="GLib.SList" && *p
!="GLib.HashTable" ) )
753 enforce(e
.tag
.type
==TagType
.EMPTY
, e
.tag
.toString());
757 type
= get_name_attr(e
);
761 onEmptyTag(ep
, "attribute", (in Element e
) {
763 enforce(tag
.attr
.length
==2);
764 enforce(tag
.attr
["name"]=="c:identifier");
765 identifier
= tag
.attr
["value"];
768 if (tagname
=="parameter") {
769 if (auto p
= "allow-none" in field_attr
) {
773 if (auto p
= "direction" in field_attr
)
774 direction
= "/*" ~ *p
~ "*/ ";
776 parsearray(ep
, type
, ctype
);
777 ep
.onStartTag
["varargs"] = (ElementParser ep
) {
780 SkipEndTag(ep
, "varargs");
785 if (tagname
=="field") {
786 ep
.onStartTag
["array"] = (ElementParser ep
) {};
787 ep
.onEndTag
["array"] = (in Element e
) {
788 trace("FIELDARR", tagname
, e
.tag
.toString());
790 aassign(size
, e
.tag
.attr
, "fixed-size");
791 if (auto p
= "readable" in field_attr
) {
792 if (!size
&& *p
=="0")
793 // Generates zero sized array, but as long as all instances
794 // are private and not followed by public fields - harmless.
798 // Ugh, eg GTK.InputDialogClass contains embedded arrays w/o size.
799 // Not usuable anyway, so we only care about it being syntactically correct.
804 size
= "/*GIR: -1*/ 0"; // Hmm, does this really belong in GIR...
806 // Using D syntax for arrays.
807 type
~= "[" ~ size
~ "]";
808 ctype
~= "[" ~ size
~ "]";
812 ep
.onStartTag
["callback"] = (ElementParser ep
) {
813 callback
= new x_function("functionp");
821 bool methodtemplates
= 1;
823 final class x_function
: x_elem
{
825 x_struct obj
; // object if this is a method.
826 string rettype
, crettype
, retanno
;
827 string throws
; // actually gerrors, oh well.
831 this(string s
, x_struct pobj
=null) { super(s
); obj
= pobj
; }
833 string
c_func(bool proto
, bool skipthis
, scope const(x_field
)[] args
) {
839 r
~= validDSym(cname
) ~ "(";
841 foreach(i
, a
; args
) {
843 r
~= "..."; // Variadic - have to be aliased (avoids having to deal w/ varargs).
846 r
~= a
.direction
~ Dtype(a
.type
, a
.ctype
) ~ " ";
847 r
~= a
.cname ?
validDSym(a
.cname
) : validDSym(a
.name
);
850 if (i
!=args
.length
-1)
859 override string
toString() {
860 string Drettype
= Dtype(rettype
, crettype
);
861 string r
= comment_to_D() ~ indent
;
865 if (auto p
= cname
in delete_symbol
) {
866 r
~= "\n" ~ indent
~ "// " ~ tagname
~ " \"" ~ name
~ "\" removed: " ~ *p
~ "\n";
870 auto pos
= std
.string
.indexOf(cname
, '_');
871 name
= "/*NAME MISSING IN GIR*/ ";
873 name
~= "missing_" ~ cname
;
875 name
~= cname
[pos
+1..$];
878 bool is_funcp
= tagname
=="callback" ||
879 tagname
=="functionp" ||
880 tagname
=="virtual-method";
882 // Fix up the argument list.
887 auto t
= new x_field("this_");
888 t
.type
= obj
.name
~ "*";
891 if (tagname
!="constructor") {
892 // Methods have a this pointer as the first arg.
893 allargs
= t
~ allargs
;
895 if (tagname
!="function")
899 // Constructors return pointers to new objects, not some base type.
900 Drettype
= obj
.name
~ "*";
905 allargs
~= gerrfield
;
907 foreach(i
, a
; allargs
) {
908 // Fix up any missing arg names.
910 a
.name
= "arg_" ~ cast(char)('a'+i
);
911 // Gtk2 has out params w/o a ctype and "gint" type - we'll do our best to fix it...
912 if (a
.direction
=="/*out*/ " && !a
.ctype
/*&& a.type[$-1]!='*'*/) {
913 a
.ctype
= a
.type
~ "*";
914 a
.direction
~= "/*POINTER*/ ";
916 // Fix up default params.
917 if (a
.def_init
=="=null")
918 switch (Dtype(a
.type
, a
.ctype
)) {
919 case "int": a
.def_init
= "=0"; break;
920 default: /**/; break;
924 // Remove default initializers from args that are followed by ones w/o defaults.
926 bool nondefarg_seen
= 0;
927 foreach_reverse(a
; allargs
) {
928 if (!a
.def_init
&& !a
.varargs
)
935 if (auto p
= cname
in delete_symbol
)
936 goto add_cproto
; // Don't emit anything, but the C prototype.
938 if (tagname
=="glib:signal") {
939 Drettype
= Dtype(rettype
, crettype
, 1);
941 // Signals have an extra 'user_data' pointer as the last arg.
942 auto udata
= new x_field("user_data");
943 udata
.type
= "void*";
944 udata
.name
= "user_data=null"; // "=null" JIC previous args were optional.
945 allargs
= allargs
~ udata
;
950 r
~= Drettype
~ " " ~ retanno
;
953 foreach(i
, a
; allargs
) {
954 r
~= a
.direction
~ Dtype(a
.type
, a
.ctype
, 1) ~ " " ~ validDSym(a
.name
) ~ a
.def_init
;
955 if (i
!=allargs
.length
-1)
960 r
~= " signal_" ~ validDSym(name
);
963 // Add some convenience templates:
965 if (!obj
.emitted("signal_connect")) {
967 r
~= indent
~ "ulong signal_connect(string name, CB)(CB cb, void* data=null, ConnectFlags cf=cast(ConnectFlags)0) {\n";
968 r
~= ++indent
~ "return super_.signal_connect!name(cb, data, cf);\n";
969 r
~= --indent
~ "}\n\n";
972 // Callback /type/ not checked, as doing it results in weird errors
973 // and very unhelpful template instantiation error messages, like:
975 // Error: template instance Clutter.Actor.signal_connect!(name,int function(Actor* stage, Event* event, void* data)) error instantiating
976 // instantiated from here: signal_connect!("button-press-event",int function(Actor* stage, Event* event, void* data))
977 // Error: template instance Clutter.Stage.signal_connect!("button-press-event",int function(Actor* stage, Event* event, void* data)) error instantiating
978 r
~= indent
~ `ulong signal_connect(string name:"` ~ name
~ `", `;
979 r
~= `CB/*:signal_` ~ validDSym(name
) ~ "*/)(CB cb, void* data=null, ConnectFlags cf=cast(ConnectFlags)0) {\n";
980 r
~= ++indent
~ `return signal_connect_data` ~
981 (methodtemplates ?
"!()" : "") ~
982 `(&this, cast(char*)"` ~ name
~ "\",\n";
983 r
~= indent
~ "cast(GObject2.Callback)cb, data, null, cf);\n";
984 r
~= --indent
~ "}\n";
990 bool unsupported
= tagname
!="function" &&
991 tagname
!="constructor";
993 r
~= "/+ Not available -- variadic " ~
995 "methods unsupported - use the C function directly.\n"
997 "functions with non-D linkage must have >1 parameter.\n" );
1001 r
~= "alias " ~ cname
~ " " ~ validDSym(name
) ~ "; // Variadic\n";
1003 r
~= --indent
~ "+/\n";
1008 bool hasUpperCase(string s
) {
1016 string refDrettype
= Drettype
;
1017 if (reftypes
&& Drettype
[$-1]=='*' && hasUpperCase(Drettype
)) {
1018 refDrettype
= Drettype
[0..$-1];
1024 if (tagname
=="callback")
1026 if (tagname
=="constructor" || tagname
=="function")
1030 r
~= "Ref!(" ~ refDrettype
~ ")";
1039 r
~= validDSym(name
) ~ (methodtemplates ?
"()(" : "(");
1041 auto args
= allargs
;
1046 foreach(i
, a
; args
) {
1050 r
~= a
.direction
~ Dtype(a
.type
, a
.ctype
) ~ " " ~ validDSym(a
.name
) ~ a
.def_init
;
1051 if (i
!=args
.length
-1)
1059 r
~= " " ~ validDSym(name
);
1061 if (tagname
=="functionp")
1064 if (tagname
=="callback" || tagname
=="virtual-method") {
1069 r
~= " {\n" ~ ++indent
;
1071 if (Drettype
!="void")
1075 r
~= "Ref!(" ~ refDrettype
~ ")(";
1079 oname
= allargs
[0].name
;
1080 allargs
[0].name
= "&this";
1081 // If we know that the object implements something, then
1082 // the following cast should be safe.
1083 if (obj
.tagname
=="interface")
1084 allargs
[0].name
= "cast(" ~ obj
.name
~ "*)&this";
1086 r
~= c_func(0, 0, allargs
);
1089 r
~= ";\n" ~ --indent
~ "}\n";
1091 allargs
[0].name
= oname
;
1094 if (!is_funcp
/*&& tagname=="function" || obj*/)
1095 cprotos
~= Drettype
~ " " ~ retanno
~ c_func(1, 0, allargs
) ~ ";";
1100 void parse(ElementParser ep
) {
1101 trace("FUNC", tagname
~ (obj ?
": " ~ obj
.name
: ""), ep
.tag().toString());
1102 // "callbacks" in structs are actually pointers.
1103 SkipEndTag(ep
, tagname
!="functionp" ? tagname
: "callback");
1104 register_doc_parser(ep
);
1105 auto tag
= ep
.tag();
1107 name
= tag
.attr
["name"];
1108 aassign(cname
, tag
.attr
, "c:identifier");
1109 aassign(throws
, tag
.attr
, "throws");
1111 auto p
= "introspectable" in tag
.attr
;
1113 doc
~= "Unintrospectable " ~ tagname
~ ": " ~ name
~ "() / " ~ cname
~ "()\n";
1115 p
= "version" in tag
.attr
;
1117 doc
~= "VERSION: " ~ *p
~ "\n";
1119 p
= "deprecated" in tag
.attr
;
1122 if (auto p1
= "deprecated-version" in tag
.attr
)
1123 dv
= "(v" ~ *p1
~ ") ";
1124 doc
~= "DEPRECATED " ~ dv
~ tagname
~ ": " ~ name
~ " - " ~ *p
~ "\n";
1127 p
= "moved-to" in tag
.attr
;
1129 doc
~= "MOVED TO: " ~ *p
~ "\n";
1131 ep
.onStartTag
["return-value"] = (ElementParser ep
) {
1132 SkipEndTag(ep
, "return-value");
1133 register_doc_parser(ep
, "RETURNS");
1135 auto tag
= ep
.tag();
1137 if (auto p
= "transfer-ownership" in tag
.attr
) {
1139 case "none": retanno
= ""; break;
1140 case "full": retanno
= "/*new*/ "; break;
1141 case "container": retanno
= "/*new container*/ "; break;
1142 default: enforce(0, "Unknown transfer-ownership: " ~ *p
);
1146 parsearray(ep
, rettype
, crettype
);
1148 ep
.onStartTag
["type"] = (ElementParser ep
) {};
1149 ep
.onEndTag
["type"] = (in Element e
) {
1151 aassign(crettype
, tag
.attr
, "c:type");
1152 aassign(rettype
, tag
.attr
, "name"/*, "func retval typename"*/);
1153 // If we have a ctype, but no type, try using the ctype.
1154 if (!rettype
&& crettype
) {
1155 crettype
= "/*CTYPE*/ " ~ crettype
;
1158 enforce(rettype
, "Missing func retval typename" );
1163 // Note the trick is there can be multiple nested <type>s;
1164 // once we get here we should have filled in the types properly.
1165 // Keep in mind we only care about the outermost type, that's
1166 // why parsing the _end_ </type> tag works for us.
1168 // Unfortunately c:type is often missing, so this check is too strict.
1169 //enforce(crettype, "Missing func retval c:type" ~ to!string(this));
1172 ep
.onStartTag
["parameters"] = (ElementParser pa
) {
1173 trace("FUNCPARM", tagname
, ep
.tag().toString());
1174 SkipEndTag(pa
, "parameters");
1175 pa
.onStartTag
["parameter"] = (ElementParser pa
) {
1176 SkipEndTag(pa
, "parameter");
1177 auto arg
= new x_field("parameter", this); args
~= arg
; arg
.parse(pa
);
1188 final class x_struct
: x_elem
{
1189 string name
, parent
;
1190 x_struct embedded_in
;
1192 x_function
[] methods
;
1193 string
[] implements
;
1194 bool[string
] emitted_
;
1196 this(string tn
, x_struct emb
=null) {
1200 case "record": break;
1201 case "class": break;
1202 case "union": break;
1203 case "interface": break;
1204 default: enforce(0, "Unknown struct type: " ~ tagname
);
1208 private bool emitted(string s
) {
1209 if (auto p
= s
in emitted_
)
1215 private string
basetypename(scope string type
) {
1216 auto i
= lastIndexOf(type
, '.');
1217 return i
==-1 ? type
: type
[i
+1..$];
1220 override string
toString() {
1221 // Everything looks like a struct to us. Except unions.
1222 string r
= comment_to_D() ~ indent
~
1223 ((tagname
=="union") ?
"union " : "struct ") ~ subDKeyword(name
);
1225 if (tagname
=="interface") {
1226 enforce(fields
.length
==0, "Interface field overflow: " ~ name
);
1227 r
~= " /* Interface */";
1230 r
~= " /* : " ~ parent
~ " */";
1232 r
~= " /* Version" ~ ver
~ " */";
1237 if (tagname
=="interface") {
1238 r
~= indent
++ ~ "mixin template __interface__() {";
1241 foreach (iface
; implements
)
1242 r
~= indent
~ "mixin " ~ Dtype(iface
, null) ~ ".__interface__;\n";
1244 // "alias super this".
1246 // First, if this struct has a parent, but we don't know any of its
1247 // fields then add the parent as the only (sub)struct -- harmless as
1248 // we couldn't access the opaque empty struct anyway and this lets
1249 // the pseudo-inheritance work.
1250 if (parent
&& fields
.length
==0) {
1251 auto pf
= new x_field("record");
1254 pf
.name
= "method_parent";
1258 if (parent
&& fields
.length
>0 && (
1259 Dtype(fields
[0].type
, fields
[0].ctype
)==Dtype(parent
, null) ||
1260 Dtype(fields
[0].type
, fields
[0].ctype
)==parent
1262 auto fieldname
= validDSym(fields
[0].name
);
1264 r
~= indent
~ "alias " ~ fieldname
~ " this;\n";
1265 r
~= indent
~ "alias " ~ fieldname
~ " super_;\n";
1267 // Add an extra alias for easier upcasting.
1268 // NOTE: The name we add could clash with existing symbols;
1269 // this does not happen in practice, will be caught at
1270 // compiletime if it does, and can be addressed then.
1271 // Also "object" is not the safest symbol to add, but
1272 // works so far, let's wait until it causes problems
1273 // before renaming it to something like "gobject".
1274 auto partype
= fields
[0].type
;
1276 partype
= Dtype(null, fields
[0].ctype
);
1277 auto aname
= basetypename(validDSym(partype
));
1278 aname
= toLower(aname
);
1279 if (aname
!=fieldname
)
1280 r
~= indent
~ "alias " ~ fieldname
~ " " ~ aname
~ ";\n";
1282 // The parent cannot be private, so fix it:
1283 if (fields
[0].private_
)
1284 fields
[0].private_
= 0;
1287 // No bitfields in D.
1290 char bfcount
='A'; // More than one __dummyNN names? Must be different.
1296 // For now we'll assume all bitfields are at least 32bits wide (C int-sized)
1298 if (bfwidth
>0 && bfwidth
<=32)
1300 else if (bfwidth
>32 && bfwidth
<=64)
1302 enforce(rawwidth
, bmix
);
1304 // std.bitmaip reqs total width to be one of 8/16/32/64, so:
1305 if (rawwidth
!=bfwidth
) {
1306 if (rawwidth
<=8 && bfwidth
>0 && bfwidth
<8)
1307 bmix
~= ",\n" ~ indent
~ " uint, \"__dummy8"~bfcount
~"\", " ~ to
!string(8-bfwidth
);
1308 else if (rawwidth
<=16 && bfwidth
<16)
1309 bmix
~= ",\n" ~ indent
~ " uint, \"__dummy16"~bfcount
~"\", " ~ to
!string(16-bfwidth
);
1310 else if (rawwidth
<=32 && bfwidth
<32)
1311 bmix
~= ",\n" ~ indent
~ " uint, \"__dummy32"~bfcount
~"\", " ~ to
!string(32-bfwidth
);
1312 else if (bfwidth
<64)
1313 bmix
~= ",\n" ~ indent
~ " uint, \"__dummy64"~bfcount
~"\", " ~ to
!string(64-bfwidth
);
1314 else if (bfwidth
==64)
1325 // Unfortunately there are methods that end up using the same name
1326 // as struct fields. Build a list of all method names, and in the
1327 // code below check for clashes. Rename the fields as these are
1328 // less likely to be used by apps.
1329 bool[string
] m_names
;
1330 foreach (m
; methods
)
1331 m_names
[m
.name
] = 1;
1333 foreach (i
, f
; fields
) {
1334 string privstr
= f
.private_ ?
"private " : "";
1341 r
~= privstr
~ f
.callback
.toString();
1343 else if (f
.struct_
) {
1350 r
~= "\n" ~ privstr
~ f
.struct_
.toString();
1352 else if (f
.bitfield
) {
1356 bmix
= indent
~ " static import std.bitmanip;"
1357 ~ " mixin(std.bitmanip.bitfields!(\n" ~ indent
;
1360 bmix
~= ",\n" ~ indent
;
1361 string bftype
= Dtype(f
.type
, f
.ctype
);
1363 case "uint", "int", "c_uint", "c_int":
1364 string fname
= validDSym(f
.name
);
1365 if (fname
in m_names
)
1367 bmix
~= bftype
~ ", \"" ~ fname
~ "\", " ~ to
!string(f
.bitfield
);
1368 bfwidth
+= f
.bitfield
;
1371 enforce(0, "Unknown bitfield type " ~
1372 bftype
~ "[" ~ f
.type
~ "," ~ f
.ctype
~ "] " ~
1373 f
.name
~ ":" ~ to
!string(f
.bitfield
));
1378 string fname
= validDSym(f
.name
);
1379 if (fname
in m_names
)
1382 r
~= indent
~ privstr
~ Dtype(f
.type
, f
.ctype
) ~ " " ~ fname
;
1384 string prevtype
, currtype
;
1385 if (fields
[i
-1].type || fields
[i
-1].ctype
)
1386 prevtype
= Dtype(fields
[i
-1].type
, fields
[i
-1].ctype
);
1387 currtype
= Dtype(f
.type
, f
.ctype
);
1388 if ( (prevtype
== currtype
&& fields
[i
-1].type
==f
.type
)
1389 && (fields
[i
-1].private_
== f
.private_
)
1390 && !fields
[i
-1].bitfield
)
1393 r
~= ((!fields
[i
-1].struct_
) ?
";" : "");
1394 r
~= "\n"~ indent
~ privstr
~ currtype
~ " " ~ fname
;
1402 if (fields
.length
>0 && !fields
[$-1].struct_
)
1405 if (fields
.length
>0 && methods
.length
>0)
1408 foreach (f
; methods
)
1411 // Pull in the right mixin, if any:
1413 string mixname
= name
;
1415 for (auto ei
= embedded_in
; ei
; ei
= ei
.embedded_in
)
1416 mixname
= ei
.name
~ "_" ~ mixname
;
1418 r
~= load_mixin(mixname
);
1420 if (tagname
=="interface") {
1421 r
~= --indent
~ "}\n";
1422 r
~= indent
~ "mixin __interface__;\n";
1425 return r
~ --indent
~ "}\n";
1428 void parse(ElementParser ep
) {
1429 trace("STRUCT", tagname
, ep
.tag().toString());
1430 auto tag
= ep
.tag();
1431 name
= tag
.attr
["name"];
1432 aassign(parent
, tag
.attr
, "parent");
1434 register_doc_parser(ep
);
1435 ep
.onStartTag
["field"] = (ElementParser ep
) {
1436 auto nf
= new x_field("field"); fields
~= nf
; nf
.parse(ep
);
1438 ep
.onStartTag
["union"] = (ElementParser ep
) {
1439 auto nf
= new x_field("union"); fields
~= nf
;
1440 auto na
= new x_struct("union", this); nf
.struct_
= na
; na
.parse(ep
);
1442 ep
.onStartTag
["record"] = (ElementParser ep
) {
1443 auto nf
= new x_field("record"); fields
~= nf
;
1444 auto na
= new x_struct("record", this); nf
.struct_
= na
; na
.parse(ep
);
1446 ep
.onStartTag
["property"] = (ElementParser p
) {
1447 p
.parse(); // Drop them.
1449 ep
.onStartTag
["method"] = (ElementParser ep
) {
1450 auto nm
= new x_function("method", this); methods
~= nm
; nm
.parse(ep
);
1452 ep
.onStartTag
["virtual-method"] = (ElementParser p
) {
1453 p
.parse(); // Drop them.
1455 ep
.onStartTag
["function"] = (ElementParser ep
) {
1456 auto nm
= new x_function("function"); methods
~= nm
; nm
.parse(ep
);
1458 ep
.onStartTag
["constructor"] = (ElementParser ep
) {
1459 auto nm
= new x_function("constructor", this); methods
~= nm
; nm
.parse(ep
);
1461 ep
.onStartTag
["glib:signal"] = (ElementParser ep
) {
1462 auto nm
= new x_function("glib:signal", this); methods
~= nm
; nm
.parse(ep
);
1465 ep
.onStartTag
["implements"] = (ElementParser p
) { p
.parse(); };
1466 ep
.onEndTag
["implements"] = (in Element e
) {
1468 aassign(s
, e
.tag
.attr
, "name", "interface name");
1472 ep
.onStartTag
["prerequisite"] = (ElementParser ep
) { ep
.parse(); }; // Drop.
1473 ep
.onEndTag
["prerequisite"] = (in Element e
) {};
1479 void onUnknownTag(ElementParser ep
, string tagname
) {
1480 ep
.onStartTag
[null] = (ElementParser ep
) {
1481 enforce(0, "Unhandled <"~tagname
~"> start tag: "
1482 ~ ep
.tag
.name
~ "\n# " ~ ep
.tag().toString());
1484 ep
.onEndTag
[null] = (in Element e
) {
1485 enforce(0, "Unhandled <"~tagname
~"> end tag: " ~ "\n# " ~ e
.toString());
1489 void SkipEndTag(ElementParser p
, string tagname
) {
1490 onUnknownTag(p
, tagname
);
1491 p
.onEndTag
[tagname
] = (in Element e
) { };
1494 void onEmptyTag(ElementParser p
, string tagname
, void delegate(in Element e
) deleg
) {
1495 p
.onStartTag
[tagname
] = (ElementParser ep
) {
1496 /* do nothing; only tells the parser that we know about this tag */
1498 p
.onEndTag
[tagname
] = (in Element e
) {
1500 enforce(tag
.type
==TagType
.EMPTY
, tag
.toString());
1506 void onStartTag(ElementParser p
, string tagname
, void delegate(ElementParser ep
) deleg
) {
1507 p
.onStartTag
[tagname
] = (ElementParser ep
) {
1508 onUnknownTag(ep
, tagname
);
1509 ep
.onEndTag
[tagname
] = (in Element e
) {/*Reached our own end tag*/};
1512 p
.onEndTag
[tagname
] = (in Element e
) {
1513 /* Do nothing; only tells the parser that we know about this tag */
1514 /* Ugh. "<record name="AppLaunchContextPrivate" />" happens... */
1518 template OnTag(alias pa
, string Kind
, string xmlname
, string tagname
) {
1520 on"~Kind
~"Tag(" ~ pa
.stringof
~ ", \"" ~ xmlname
~ "\","
1521 " (" ~ ((Kind
=="Start") ?
"ElementParser e" : "in Element e") ~ ") {
1522 auto a = new x_"~tagname
~"(" ~ xmlname
.stringof
~ ");
1524 "`write(a.toString() ~"\n");`
1528 string delete_symbol
[string
]; // [name: reason]
1530 void load_delete() {
1531 string filename
= "mixin/" ~ thismodulename
~ "_" ~ "_DELETE_";
1535 s
= cast(string
)std
.file
.read(filename
);
1536 } catch (std
.file
.FileException
) {
1538 ew("DELETE Failed: " ~ filename
);
1542 foreach (line
; split(s
, "\n")) {
1543 auto arr
= split(line
, ":");
1547 delete_symbol
[line
] = "";
1549 delete_symbol
[arr
[0]] = strip(join(arr
[1..$], ":"));
1555 string
load_mixin(string id
) {
1556 string filename
= "mixin/" ~ thismodulename
~ "_" ~ id
~ ".d";
1559 static bool uniq_mix
[string
];
1561 enforce(filename
!in uniq_mix
, "Mixin collission: " ~ filename
);
1562 uniq_mix
[filename
] = 1;
1565 s
= cast(string
)std
.file
.read(filename
);
1566 } catch (std
.file
.FileException
) {
1568 ew("MIXIN Failed: " ~ filename
);
1573 ew("MIXIN Found: " ~ filename
);
1575 string r
= "\n" ~ indent
~ "// --- " ~ filename
~ " --->\n\n";
1577 foreach (sp
; split(s
, "\n"))
1578 r
~= indent
~ sp
~ "\n";
1580 r
~= indent
~ "// <--- " ~ filename
~ " ---\n";
1587 void main(string
[] args
) {
1588 string infilename
, outfilename
, modulename
;
1594 bool delegate(string arg
) eatarg
;
1596 cmdargloop
: foreach(arg
; args
[1..$]) {
1607 die("Unknown extra cmdline file name: " ~ arg
);
1610 die("Missing cmdline option: " ~ arg
);
1613 switch (arg
[2..$]) {
1615 case "trace": showtrace
= 1; break;
1617 case "trace-mixins": tracemixins
= 1; break;
1618 case "check": xmlcheck
= 1; break;
1619 case "reftypes": reftypes
= 1; break;
1620 case "no-reftypes": reftypes
= 0; break;
1621 default: die("Unknown long cmdline option: " ~ arg
);
1626 outfilename
= arg
[2..$];
1628 eatarg
= (string arg
) { outfilename
= arg
; return true; };
1629 continue cmdargloop
;
1632 outfilename
= arg
[2..$];
1634 eatarg
= (string arg
) { modulename
= arg
; return true; };
1635 continue cmdargloop
;
1637 die("Unknown cmdline option: " ~ arg
);
1642 die("Missing cmdline arg");
1644 string s
= cast(string
)std
.file
.read(infilename
);
1647 check(s
); // Running the checks increases the execution time from 0.22s to 1.74s...
1650 stdout
.open(outfilename
, "wt");
1652 writef("// *** DO NOT EDIT ***\n// Automatically generated from \"%s\"\n\n", infilename
);
1655 write("module " ~ modulename
~ ";\n");
1657 auto dp
= new DocumentParser(s
);
1659 void delegate (ElementParser ep
) epskip
= (ElementParser ep
) { ep
.parse(); };
1660 void delegate (ElementParser p
) epfixme
= (ElementParser p
)
1661 { p
.parse(); ew("FIXME: " ~ p
.toString()); };
1662 void delegate (in Element e
) efixme
= (in Element e
) { ew("FIXME: " ~ e
.toString()); };
1664 onUnknownTag(dp
, "Document");
1666 onEmptyTag(dp
, "package", (in Element e
) {
1667 auto name
= get_name_attr(e
);
1668 write("\n// package: \"" ~ name
~ "\";\n");
1671 onEmptyTag(dp
, "c:include", (in Element e
) {
1672 auto name
= get_name_attr(e
);
1673 write("// C header: \"" ~ name
~ "\";\n");
1676 mixin( OnTag
!(dp
, "Empty", "include", "include") );
1677 mixin( OnTag
!(dp
, "Start", "namespace", "namespace") );
1678 dp
.onEndTag
["namespace"] = (in Element e
) { }; // We're assuming one ns per file.
1680 dp
.onEndTag
["repository"] = (in Element e
) { }; // We're assuming one repo per file.
1682 mixin( OnTag
!(dp
, "Start", "class", "struct") );
1683 mixin( OnTag
!(dp
, "Start", "record", "struct") );
1684 mixin( OnTag
!(dp
, "Start", "union", "struct") );
1685 mixin( OnTag
!(dp
, "Start", "interface", "struct") );
1687 mixin( OnTag
!(dp
, "Start", "constant", "field") );
1688 mixin( OnTag
!(dp
, "Start", "alias", "field") );
1690 mixin( OnTag
!(dp
, "Start", "bitfield", "bitfield") );
1691 mixin( OnTag
!(dp
, "Start", "enumeration", "bitfield") );
1693 mixin( OnTag
!(dp
, "Start", "function", "function") );
1694 mixin( OnTag
!(dp
, "Start", "callback", "function") );
1696 dp
.onStartTag
["glib:boxed"] = epskip
;
1698 // Not handled yet. Can't eat them here, we need the types anyway.
1699 //dp.onStartTag["interface"] = epfixme;
1703 write(load_mixin("_MODULE"));
1705 write("\n// C prototypes:\n\nextern (C) {\n");
1706 // Some methods are also documented as functions and the D
1707 // compiler does not like duplicate prototypes; drop them.
1708 bool uniq_proto
[string
];
1709 foreach(s
; cprotos
) {
1710 if (s
!in uniq_proto
)
1717 catch (Exception e
) {
1721 remove(outfilename
);
1723 ew("WARNING: Unable to remove destination file.");
1728 void die(S
...)(S args
) {
1729 import core
.stdc
.stdlib
;