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
~ "_";
108 private string
escdquote(scope string s
) {
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) {
126 // Some gtk signals have /no/ info; at least don't break the build.
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]==']') {
134 auto spos
= std
.string
.indexOf(gt
, '[');
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]!='*') {
146 auto apos
= std
.string
.indexOf(ct
, '*');
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";
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";
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)*";
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?
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
) {
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..$])
263 if (ct
.length
>fl
&& from
== ct
[0..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";
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
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";
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) {
306 if (gt
[$-l
-1..$]==("."~ct
))
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";
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";
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
, "*");
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
, '.');
339 gt
= gt
[0..lastpi
+1] ~ subDKeyword(gt
[lastpi
+1..$]);
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*";
349 if (gt
=="GdkPixbuf2.PixbufAlphaMode" && ct
=="GdkPixbuf2AlphaMode") return "GdkPixbuf2.PixbufAlphaMode";
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";
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
~ "*";
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";
380 // Fix up some obviously broken types.
382 if (gt
=="Object") return "Object*";
385 version(all
) { // Disable this while debugging.
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
..$];
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"
409 auto tail
= "." ~ ct
[1..$];
410 if(gt
.length
>=tail
.length
&& gt
[$-tail
.length
..$] == tail
)
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
)
424 enforce(0, "Missing " ~ req
~ "; Available: " ~ to
!string(aa
));
426 string
get_name_attr(in Element e
) {
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));
439 string
simplify_version(scope string v
, scope string modname
=null) pure {
443 else if (v
[1..$]==".0")
445 if (modname
&& result
.length
==1 && modname
[$-1]==result
[0])
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
) {
459 doc
~= "\n" ~ dochead
~ ": ";
462 SkipEndTag(ep
, "doc");
466 final void register_doc_parser(ElementParser ep
, string dochead
=null) {
467 ep
.onStartTag
["doc"] = &parse_doc_text
;
471 if (auto p
= "version" in tag
.attr
)
473 this.dochead
= dochead
;
475 final string
comment_to_D() {
479 while (doc
[$-1]=='\n')
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)
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");
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");
509 final class x_include
: x_elem
{
512 this(string s
) { super(s
); }
514 void parse(in Element e
) {
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
{
541 this(string s
) { super(s
); }
543 void parse(ElementParser ep
) {
544 auto attr
= ep
.tag().attr
;
546 ver
= attr
["version"];
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");
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
);
583 final class x_bitfield
: x_elem
{
587 this(string s
) { super(s
); }
589 void parse(ElementParser ep
) {
590 trace("BITFIELD", tagname
, ep
.tag().toString());
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");
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.
615 ep
.onEndTag
["function"] = (in Element e
) {
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
;
625 r
~= " /* Version" ~ ver
~ " */";
629 foreach (i
, m
; members
) {
630 long v
= to
!long(m
.value
);
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.
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
654 bool varargs
; // true if this parameter is "..."
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";
672 return r
~ struct_
.toString();
678 r
~= indent
~ "enum " ~ name
~ " = \"" ~ escdquote(value
) ~ "\";";
681 string contype
= Dtype(type
, ctype
);
683 // "int MUTEX_DEBUG_MAGIC = cast(int)4175530711;" needs the cast...
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
~ ";";
698 string dtype
= Dtype(type
, ctype
);
699 // We're renaming types, and that can cause problems, as in Gtk2.Type case.
702 r
~= indent
~ "alias " ~ dtype
~ " " ~ name
~ ";" /*~ "\t// " ~ ctype*/;
706 // case "field": /* Fields in classes/structs are handled in containers. */
708 enforce(0, "Unknown field type: " ~ tagname
);
713 void parse(ElementParser ep
) {
714 trace("FIELD", tagname
, ep
.tag().toString());
715 SkipEndTag(ep
, tagname
);
718 auto field_attr
= tag
.attr
;
719 aassign(name
, field_attr
, "name");
720 aassign(value
, field_attr
, "value"); // Constants.
723 docparent
.register_doc_parser(ep
, name ?
"<" ~ name
~ ">": "PARAM");
725 register_doc_parser(ep
);
727 if (auto p
= "private" in field_attr
)
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
) {
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());
745 type
= get_name_attr(e
);
749 onEmptyTag(ep
, "attribute", (in Element e
) {
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
) {
761 if (auto p
= "direction" in field_attr
)
762 direction
= "/*" ~ *p
~ "*/ ";
764 parsearray(ep
, type
, ctype
);
765 ep
.onStartTag
["varargs"] = (ElementParser ep
) {
768 SkipEndTag(ep
, "varargs");
773 if (tagname
=="field") {
774 ep
.onStartTag
["array"] = (ElementParser ep
) {};
775 ep
.onEndTag
["array"] = (in Element e
) {
776 trace("FIELDARR", tagname
, e
.tag
.toString());
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.
786 // Ugh, eg GTK.InputDialogClass contains embedded arrays w/o size.
787 // Not usuable anyway, so we only care about it being syntactically correct.
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");
809 final class x_function
: x_elem
{
811 x_struct obj
; // object if this is a method.
812 string rettype
, crettype
, retanno
;
813 string throws
; // actually gerrors, oh well.
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
) {
825 r
~= validDSym(cname
) ~ "(";
827 foreach(i
, a
; args
) {
829 r
~= "..."; // Variadic - have to be aliased (avoids having to deal w/ varargs).
832 r
~= a
.direction
~ Dtype(a
.type
, a
.ctype
) ~ " ";
833 r
~= a
.cname ?
validDSym(a
.cname
) : validDSym(a
.name
);
836 if (i
!=args
.length
-1)
845 override string
toString() {
846 string Drettype
= Dtype(rettype
, crettype
);
847 string r
= comment_to_D() ~ indent
;
851 if (auto p
= cname
in delete_symbol
)
852 return "\n" ~ indent
~ "// " ~ tagname
~ " \"" ~ name
~ "\" removed: " ~ *p
~ "\n";
855 auto pos
= std
.string
.indexOf(cname
, '_');
856 name
= "/*NAME MISSING IN GIR*/ ";
858 name
~= "missing_" ~ cname
;
860 name
~= cname
[pos
+1..$];
863 bool is_funcp
= tagname
=="callback" ||
864 tagname
=="functionp" ||
865 tagname
=="virtual-method";
867 // Fix up the argument list.
872 auto t
= new x_field("this_");
873 t
.type
= obj
.name
~ "*";
876 if (tagname
!="constructor") {
877 // Methods have a this pointer as the first arg.
878 allargs
= t
~ allargs
;
880 if (tagname
!="function")
884 // Constructors return pointers to new objects, not some base type.
885 Drettype
= obj
.name
~ "*";
890 allargs
~= gerrfield
;
892 foreach(i
, a
; allargs
) {
893 // Fix up any missing arg names.
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
)
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
;
932 r
~= Drettype
~ " " ~ retanno
;
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)
942 r
~= " signal_" ~ validDSym(name
);
945 // Add some convenience templates:
947 if (!obj
.emitted("signal_connect")) {
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";
970 bool unsupported
= tagname
!="function" &&
971 tagname
!="constructor";
973 r
~= "/+ Not available -- variadic " ~
975 "methods unsupported - use the C function directly.\n"
977 "functions with non-D linkage must have >1 parameter.\n" );
981 r
~= "alias " ~ cname
~ " " ~ validDSym(name
) ~ "; // Variadic\n";
983 r
~= --indent
~ "+/\n";
988 bool hasUpperCase(string s
) {
996 string refDrettype
= Drettype
;
997 if (reftypes
&& Drettype
[$-1]=='*' && hasUpperCase(Drettype
)) {
998 refDrettype
= Drettype
[0..$-1];
1004 if (tagname
=="callback")
1006 if (tagname
=="constructor" || tagname
=="function")
1010 r
~= "Ref!(" ~ refDrettype
~ ")";
1019 r
~= validDSym(name
) ~ "(";
1021 auto args
= allargs
;
1026 foreach(i
, a
; args
) {
1030 r
~= a
.direction
~ Dtype(a
.type
, a
.ctype
) ~ " " ~ validDSym(a
.name
) ~ a
.def_init
;
1031 if (i
!=args
.length
-1)
1039 r
~= " " ~ validDSym(name
);
1041 if (tagname
=="functionp")
1044 if (tagname
=="callback" || tagname
=="virtual-method") {
1049 r
~= " {\n" ~ ++indent
;
1051 if (Drettype
!="void")
1055 r
~= "Ref!(" ~ refDrettype
~ ")(";
1059 oname
= allargs
[0].name
;
1060 allargs
[0].name
= "&this";
1062 r
~= c_func(0, 0, allargs
);
1065 r
~= ";\n" ~ --indent
~ "}\n";
1067 allargs
[0].name
= oname
;
1070 if (!is_funcp
/*&& tagname=="function" || obj*/)
1071 cprotos
~= Drettype
~ " " ~ retanno
~ c_func(1, 0, allargs
) ~ ";";
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
;
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
) {
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
) {
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
;
1118 enforce(rettype
, "Missing func retval typename" );
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
);
1148 final class x_struct
: x_elem
{
1149 string name
, parent
;
1150 x_struct embedded_in
;
1152 x_function
[] methods
;
1153 bool[string
] emitted_
;
1155 this(string tn
, x_struct emb
=null) {
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_
)
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
);
1185 r
~= " /* : " ~ parent
~ " */";
1187 r
~= " /* Version" ~ ver
~ " */";
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");
1202 pf
.name
= "method_parent";
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
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
;
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.
1238 char bfcount
='A'; // More than one __dummyNN names? Must be different.
1244 // For now we'll assume all bitfields are at least 32bits wide (C int-sized)
1246 if (bfwidth
>0 && bfwidth
<=32)
1248 else if (bfwidth
>32 && bfwidth
<=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)
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 " : "";
1289 r
~= privstr
~ f
.callback
.toString();
1291 else if (f
.struct_
) {
1298 r
~= "\n" ~ privstr
~ f
.struct_
.toString();
1300 else if (f
.bitfield
) {
1304 bmix
= indent
~ " static import std.bitmanip;"
1305 ~ " mixin(std.bitmanip.bitfields!(\n" ~ indent
;
1308 bmix
~= ",\n" ~ indent
;
1309 string bftype
= Dtype(f
.type
, f
.ctype
);
1311 case "uint", "int", "c_uint", "c_int":
1312 bmix
~= bftype
~ ", \"" ~ f
.name
~"\", "~ to
!string(f
.bitfield
);
1313 bfwidth
+= f
.bitfield
;
1316 enforce(0, "Unknown bitfield type " ~
1317 bftype
~ "[" ~ f
.type
~ "," ~ f
.ctype
~ "] " ~
1318 f
.name
~ ":" ~ to
!string(f
.bitfield
));
1323 string fname
= validDSym(f
.name
);
1324 if (fname
in m_names
)
1327 r
~= indent
~ privstr
~ Dtype(f
.type
, f
.ctype
) ~ " " ~ fname
;
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
)
1338 r
~= ((!fields
[i
-1].struct_
) ?
";" : "");
1339 r
~= "\n"~ indent
~ privstr
~ currtype
~ " " ~ fname
;
1347 if (fields
.length
>0 && !fields
[$-1].struct_
)
1350 if (fields
.length
>0 && methods
.length
>0)
1353 foreach (f
; methods
)
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
) {};
1409 ep
.onStartTag
["implements"] = (ElementParser p
) { p
.parse(); };
1410 ep
.onEndTag
["implements"] = (in Element e
) {};
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
) {
1437 enforce(tag
.type
==TagType
.EMPTY
, tag
.toString());
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*/};
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
) {
1457 on"~Kind
~"Tag(" ~ pa
.stringof
~ ", \"" ~ xmlname
~ "\","
1458 " (" ~ ((Kind
=="Start") ?
"ElementParser e" : "in Element e") ~ ") {
1459 auto a = new x_"~tagname
~"(" ~ xmlname
.stringof
~ ");
1461 "`write(a.toString() ~"\n");`
1465 string delete_symbol
[string
]; // [name: reason]
1467 void load_delete() {
1468 string filename
= "mixin/" ~ thismodulename
~ "_" ~ "_DELETE_";
1472 s
= cast(string
)std
.file
.read(filename
);
1473 } catch (std
.file
.FileException
) {
1475 ew("DELETE Failed: " ~ filename
);
1479 foreach (line
; split(s
, "\n")) {
1480 auto arr
= split(line
, ":");
1484 delete_symbol
[line
] = "";
1486 delete_symbol
[arr
[0]] = strip(join(arr
[1..$], ":"));
1492 string
load_mixin(string id
) {
1493 string filename
= "mixin/" ~ thismodulename
~ "_" ~ id
~ ".d";
1496 static bool uniq_mix
[string
];
1498 enforce(filename
!in uniq_mix
, "Mixin collission: " ~ filename
);
1499 uniq_mix
[filename
] = 1;
1502 s
= cast(string
)std
.file
.read(filename
);
1503 } catch (std
.file
.FileException
) {
1505 ew("MIXIN Failed: " ~ filename
);
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";
1524 void main(string
[] args
) {
1525 string infilename
, outfilename
, modulename
;
1531 bool delegate(string arg
) eatarg
;
1533 cmdargloop
: foreach(arg
; args
[1..$]) {
1544 die("Unknown extra cmdline file name: " ~ arg
);
1547 die("Missing cmdline option: " ~ arg
);
1550 switch (arg
[2..$]) {
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
);
1563 outfilename
= arg
[2..$];
1565 eatarg
= (string arg
) { outfilename
= arg
; return true; };
1566 continue cmdargloop
;
1569 outfilename
= arg
[2..$];
1571 eatarg
= (string arg
) { modulename
= arg
; return true; };
1572 continue cmdargloop
;
1574 die("Unknown cmdline option: " ~ arg
);
1579 die("Missing cmdline arg");
1581 string s
= cast(string
)std
.file
.read(infilename
);
1584 check(s
); // Running the checks increases the execution time from 0.22s to 1.74s...
1587 stdout
.open(outfilename
, "wt");
1589 writef("// *** DO NOT EDIT ***\n// Automatically generated from \"%s\"\n\n", infilename
);
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");
1608 onEmptyTag(dp
, "c:include", (in Element e
) {
1609 auto name
= get_name_attr(e
);
1610 write("// C header: \"" ~ name
~ "\";\n");
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;
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
)
1654 catch (Exception e
) {
1658 remove(outfilename
);
1660 ew("WARNING: Unable to remove destination file.");
1665 void die(S
...)(S args
) {
1666 import core
.stdc
.stdlib
;