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