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
; }
66 private string
nodkeyword(scope string s
) {
67 // Associative array literals are non-const expressions, so...
69 case "double": return "double_";
70 case "string": return "string_";
71 case "union": return "union_";
72 case "module": return "module_";
73 case "int": return "int_";
74 case "in": return "in_";
75 case "out": return "out_";
76 case "ref": return "ref_";
77 case "function": return "function_";
78 case "foreach": return "foreach_";
79 case "delete": return "delete_";
80 case "continue": return "continue_";
81 case "new": return "new_";
82 case "export": return "export_";
83 case "body": return "body_";
84 case "version": return "version_";
85 case "scope": return "scope_";
86 case "break": return "break_";
87 case "false": return "false_";
88 case "true": return "true_";
89 case "alias": return "alias_";
90 case "main": return "main_";
93 // "signals" ie gtk callback functions have '-' both
94 // in function names and args...
95 s
= replace(s
, "-", "_");
97 // Unfortunately shortening the names of some enums leaves
98 // them with a leading digit, so turn "2BIG" into "_2BIG":
100 if (s
&& isDigit(s
[0]))
105 private string
escdquote(scope string s
) {
107 return replace(s
, "\\", "\\\\");
111 // From namespace tags:
112 string
[] ident_prefixes
; // eg. ["G"];
113 string
[] symbol_prefixes
; // eg. ["g", "glib"]
115 // Figure out the native D type from the gtktype and ctype.
116 // "signals" turns on heuristics, which isn't needed elsewhere,
117 // but the signal callback descriptions are lacking vital info...
118 string
Dtype(scope string gt
, scope string ct
, bool signals
=false) {
123 // Some gtk signals have /no/ info; at least don't break the build.
127 // If we're given an array, strip the "[n]" part, do the conversion,
128 // then append the "[n]" back and return the result. Keeps things simple.
129 if (gt
.length
>=1 && gt
[$-1]==']') {
131 auto spos
= std
.string
.indexOf(gt
, '[');
133 slen
= gt
.length
- spos
;
134 if (slen
>0 && gt
[$-slen
..$]==ct
[$-slen
..$]) {
135 string suf
= gt
[$-slen
..$];
136 return Dtype(gt
[0..$-slen
], ct
[0..$-slen
]) ~ suf
;
139 // Ditto for pointers; note - only the ctype has the right type
140 // (number of '*'). Keep in mind arrays of pointers exist.
141 if (ct
.length
>=1 && ct
[$-1]=='*' && gt
[$-1]!='*') {
143 auto apos
= std
.string
.indexOf(ct
, '*');
145 alen
= ct
.length
- apos
;
146 string suf
= ct
[$-alen
..$];
147 return Dtype(gt
, ct
[0..$-alen
]) ~ suf
;
150 // Obvious conversions first:
151 if (gt
=="gint8" && ct
=="gint8") return "byte";
152 if (gt
=="guint8" && ct
=="guint8") return "ubyte";
153 if (gt
=="guint8" && ct
=="gchar") return "ubyte";
154 if (gt
=="guint8" && ct
=="guchar") return "ubyte";
155 if (gt
=="gint16" && ct
=="gint16") return "short";
156 if (gt
=="guint16" && ct
=="guint16") return "ushort";
157 if (gt
=="gint32" && ct
=="gint32") return "int";
158 if (gt
=="guint32" && ct
=="guint32") return "uint";
159 if (gt
=="gint64" && ct
=="gint64") return "long";
160 if (gt
=="guint64" && ct
=="guint64") return "ulong";
163 if (gt
=="gshort" && ct
=="gshort") return "short";
164 if (gt
=="gshort" && ct
=="short") return "short";
165 if (gt
=="gushort" && ct
=="gushort") return "ushort";
168 if (gt
=="gint" && ct
=="signed") return "c_int";
169 if (gt
=="gint" && ct
=="int") return "c_int";
170 if (gt
=="gint" && ct
=="gint") return "c_int";
171 if (gt
=="gint" && !ct
) return "c_int"; // gtk2 needs it.
172 if (gt
=="guint" && ct
=="guint") return "c_uint";
174 // Assumes 32-bit wide target ints, but is far more readable...
175 if (gt
=="gint" && ct
=="signed") return "int";
176 if (gt
=="gint" && ct
=="int") return "int";
177 if (gt
=="gint" && ct
=="gint") return "int";
178 if (gt
=="gint" && !ct
) return "int"; // gtk2 needs it.
179 if (gt
=="guint" && ct
=="guint") return "uint";
181 if (gt
=="gfloat" && ct
=="gfloat") return "float";
182 if (gt
=="gdouble" && ct
=="double") return "double";
183 if (gt
=="gdouble" && ct
=="gdouble") return "double";
184 if (gt
=="long double" && ct
=="long double") return "real";
186 if (gt
=="none" && ct
=="void") return "void";
188 if (gt
=="gunichar" && ct
=="gunichar") return "dchar";
189 if (gt
=="guint16" && ct
=="gunichar2") return "wchar";
191 if (gt
=="glong" && ct
=="long") return "c_long";
192 if (gt
=="glong" && ct
=="glong") return "c_long";
193 if (gt
=="gulong" && ct
=="gulong") return "c_ulong";
195 if (gt
=="gsize" && ct
=="gsize") return "size_t";
196 if (gt
=="gssize" && ct
=="gssize") return "ssize_t";
197 if (gt
=="gint64" && ct
=="goffset") return "long"; // signed 64bit int, C99: off64_t
199 if (gt
=="gchar" && ct
=="gchar") return "char";
200 if (gt
=="gchar" && ct
=="char") return "char";
201 if (gt
=="gchar" && ct
=="guchar") return "ubyte";
203 if (gt
=="utf8" && ct
=="char") return "char";
204 if (gt
=="utf8" && ct
=="gchar") return "char";
206 if (gt
=="guint8" && ct
=="char") return "ubyte";
208 if (gt
=="gpointer" && ct
=="void") return "void";
209 if (gt
=="gpointer" && !ct
) return "void*"; // GTK2
210 if (gt
=="gpointer" && ct
=="gpointer") return "void*";
212 if (gt
=="guint8" && ct
=="void") return "ubyte"; // used for buffer *'s.
213 if (gt
=="guint8" && ct
=="gpointer") return "void*";
214 if (gt
=="gpointer" && ct
=="gconstpointer") return "const(void)*";
216 if (gt
=="gboolean" && ct
=="gboolean") return "int";
217 if (gt
=="glong" && ct
=="time_t") return "time_t"; // well...
219 if (gt
=="guint" && ct
=="uid_t") return "uint"; // Good enough?
222 if (gt
=="gpointer" && ct
=="GArray") return "Array";
223 if (gt
=="gpointer" && ct
=="GByteArray") return "ByteArray";
224 if (gt
=="gpointer" && ct
=="GPtrArray") return "PtrArray";
225 if (gt
=="gpointer" && ct
=="FILE") return "FILE";
227 // Not even the module names are used consistently...
229 void fixupmodnames(scope string from
, scope string to
) {
231 auto fl
= from
.length
+1; // '+1' cause of the trailing '.'.
232 if (gt
.length
>=fl
&& gt
[0..fl
]==from
~ ".") {
233 gt
= to
~ "." ~ gt
[fl
..$];
235 // Don't change ct for "GObject.ObjectClass" "GObjectClass"
236 if (ct
.length
>1 && orggt
[fl
..$]==ct
[1..$])
240 if (ct
.length
>fl
&& from
== ct
[0..fl
])
245 fixupmodnames("GLib", "GLib2");
246 fixupmodnames("GdkPixbuf", "GdkPixbuf2");
247 fixupmodnames("GObject", "GObject2");
248 fixupmodnames("Gio", "Gio2");
249 fixupmodnames("Gdk", "Gdk2");
251 if (gt
=="va_list" && ct
=="va_list") return "va_list";
254 if (gt
=="GType" && ct
=="GType") return "Type";
256 if (gt
=="GdkPixbuf2.Pixbuf" && ct
=="GdkPixbuf") return "GdkPixbuf2.Pixbuf"; // in gdk
258 if (gt
=="cairo.Surface" && ct
=="cairo_surface_t") return "cairo.Surface"; // in gdk
259 if (gt
=="cairo.Context" && ct
=="cairo_t") return "cairo.Context"; // in gdk
260 if (gt
=="cairo.FontOptions" && ct
=="cairo_font_options_t") return "cairo.FontOptions"; // in gdk
261 if (gt
=="cairo.Content" && ct
=="cairo_content_t") return "cairo.Content"; // in gdk
262 if (gt
=="cairo.Pattern" && ct
=="cairo_pattern_t") return "cairo.Pattern"; // in gdk
265 // "Pango.Font" "PangoFont*" => "Pango.Font*"; // in gdk
266 // "Pango.GlyphString" "PangoGlyphString*" => "Pango.GlyphString*"; // in gdk
267 // "Pango.Direction" "PangoDirection" => "Pango.Direction"
269 auto mlen
= countUntil(gt
, ".");
270 if (mlen
>0 && ct
.length
>mlen
&& gt
[0..mlen
]==ct
[0..mlen
]) {
271 auto ctapos
= countUntil(ct
, "*");
274 if (ctapos
>0 && gt
[mlen
+1..$]==ct
[mlen
..ctapos
] )
275 return gt
~ ct
[ctapos
..$];
279 // ATK needs this one, as the datatype is missing both name and definition; this lets it build.
280 if (gt
=="AtkPropertyValues*" && ct
=="AtkPropertyValues*") return "_PropertyValues*";
283 if (gt
=="GdkPixbuf2.PixbufAlphaMode" && ct
=="GdkPixbuf2AlphaMode") return "GdkPixbuf2.PixbufAlphaMode";
286 if (gt
=="filename" && ct
=="gchar") return "char"; // Probably const, but...
287 if (gt
=="filename" && !ct
) return "char*"; // Probably const, but...
289 if (gt
=="GdkPixbuf2.PixbufAnimation" && ct
=="GdkPixbuf2Animation") return "GdkPixbuf2.PixbufAnimation";
290 if (gt
=="GdkPixbuf2.PixbufAnimationIter" && ct
=="GdkPixbuf2AnimationIter") return "GdkPixbuf2.PixbufAnimationIter";
293 // In fact if this is for a signal and has no <ct> it probably should be pointer...
294 // We're gonna guess, and we'll be wrong sometimes. But we will be right in many
295 // cases and there isn't really much else that we could do...
296 if (signals
&& !ct
&& gt
[$-1]!='*') {
297 if (gt
=="GObject2.Error") return "GLib2.Error*";
299 if (std
.string
.indexOf(gt
,'.')!=-1) return gt
~ "*";
300 if (gt
[0]>='A' && gt
[0]<='Z') return gt
~ "*";
303 case "gfloat": return "float";
304 case "gdouble": return "double";
305 case "none": return "void";
306 case "utf8": return "char*";
307 case "gint": return "c_int";
308 case "guint": return "c_uint";
309 case "gboolean": return "c_int";
314 // Fix up some obviously broken types.
316 if (gt
=="Object") return "Object*";
319 version(all
) { // Disable this while debugging.
324 if (gt
&& (!ct || ct
==""))
325 return gt
; // what else?...
327 enforce(gt
&& ct
, "Missing type: GTK: \"" ~ gt
~ "\" C: \"" ~ ct
~ "\"");
329 foreach (prefix
; ident_prefixes
) {
330 auto l
= prefix
.length
;
331 if (ct
.length
>l
&& ct
[0..l
]==prefix
) {
332 // Try Quark" "GQuark -> "Quark"
333 string stripped_ct
= ct
[l
..$];
339 // Last try: if ctype starts with 'G' and last dot-delimited component
340 // of gtktype equals the post-G ctype, use that.
341 // Eg. "Gio.AppLaunchContext", "GAppLaunchContext" => "Gio.AppLaunchContext"
343 auto tail
= "." ~ ct
[1..$];
344 if(gt
.length
>=tail
.length
&& gt
[$-tail
.length
..$] == tail
)
348 enforce(0, "Unknown type: GTK: \"" ~ gt
~ "\" C: \"" ~ ct
~ "\"");
349 assert(0); // @noreturn
352 // Set string <dst> to <aa[index]>, but only if <aa> contains such value.
353 // if <req> is given assert if the value is missing.
354 void aassign(ref string dst
, in string
[string
] aa
, in string index
, string req
=null) {
355 if (auto p
= index
in aa
)
358 enforce(0, "Missing " ~ req
~ "; Available: " ~ to
!string(aa
));
360 string
get_name_attr(in Element e
) {
362 if (auto p
= "name" in tag
.attr
) {
363 // Debugging checks, harmless, can be left enabled.
364 if (*p
!="GLib.List" && *p
!="GLib.SList"&& *p
!="GLib.HashTable")
365 enforce(tag
.type
==TagType
.EMPTY
, tag
.toString());
366 // <alias>es trigger this one.
367 //enforce(tag.attr.length==1, to!string(tag.attr.length));
373 string
simplify_version(scope string v
) pure {
376 else if (v
[1..$]==".0")
382 string tagname
, doc
, dochead
, ver
;
384 this(string s
) { tagname
= s
; }
386 final void parse_doc_text(ElementParser ep
) {
387 ep
.onText
= (string s
) {
390 doc
~= "\n" ~ dochead
~ ": ";
393 SkipEndTag(ep
, "doc");
397 final void register_doc_parser(ElementParser ep
, string dochead
=null) {
398 ep
.onStartTag
["doc"] = &parse_doc_text
;
402 if (auto p
= "version" in tag
.attr
)
404 this.dochead
= dochead
;
406 final string
comment_to_D() {
410 while (doc
[$-1]=='\n')
414 r
= "\n// " ~ replace(doc
, "\n", "\n// ");
415 r
= replace(r
, "\n// ", "\n" ~ indent
~ "// ") ~ "\n";
416 // Remove the newline before one-line comments.
417 import std
.algorithm
;
418 if (count(r
, '\n')<=2)
424 final void parsearray(ElementParser p
, ref string _type
, ref string _ctype
,
425 bool makepointer
=0) {
426 p
.onStartTag
["array"] = (ElementParser ep
) {
427 aassign(_ctype
, ep
.tag().attr
, "c:type");
430 ep
.onStartTag
["type"] = (ElementParser ep
) { ep
.parse(); };
431 ep
.onEndTag
["type"] = (in Element e
) {
432 trace("ELEMARR", tagname
, e
.tag
.toString());
433 aassign(_type
, e
.tag
.attr
, "name");
440 final class x_include
: x_elem
{
443 this(string s
) { super(s
); }
445 void parse(in Element e
) {
447 enforce(tag
.attr
.length
==2);
448 name
= tag
.attr
["name"];
449 ver
= tag
.attr
["version"];
452 override string
toString() {
453 string modulebasename
= name
~ simplify_version(ver
);
454 string modulebasenamel
= toLower(modulebasename
);
455 // If we use "import GLib2" the compiler will fail to find the module
456 // while building the other ones.
457 // If we use "import gtk2.GLib2" the compiler will complain about
458 // missing Glib2.* types.
459 // NOTE: Imports are "public" so that apps don't have to import Gdk
460 // just for types only used for callback args etc.
461 return indent
~ "public import " ~ PACKAGE_NAME
~ "." ~ modulebasenamel
~ ";\n"
462 ~ indent
~ "alias " ~ PACKAGE_NAME
~ "." ~ modulebasenamel
~ " "
463 ~ modulebasename
~ ";";
467 string thismodulename
;
469 final class x_namespace
: x_elem
{
472 this(string s
) { super(s
); }
474 void parse(ElementParser ep
) {
475 auto attr
= ep
.tag().attr
;
477 ver
= attr
["version"];
481 if (auto p
= "c:symbol-prefixes" in attr
) {
482 symbol_prefixes
= csv2arr(*p
);
483 writeln("// c:symbol-prefixes: ", symbol_prefixes
);
485 if (auto p
= "c:identifier-prefixes" in attr
) {
486 ident_prefixes
= csv2arr(*p
);
487 write("// c:identifier-prefixes: ", ident_prefixes
, "\n\n");
490 // We do not parse further, ie no ep.parse() here.
493 override string
toString() {
494 thismodulename
= name
~ simplify_version(ver
);
495 string s
= indent
~ "// module " ~ thismodulename
~ ";\n";
496 // Per module fixups:
497 s
~= load_mixin("_MODULE_HEAD");
503 // "ALNUM" <= shortenconst("alnum", "G_ASCII_ALNUM")
504 string
shortenconst(scope string name
, scope string cname
) {
505 if (cname
.length
>=name
.length
) {
506 auto shortname
= cname
[($-name
.length
)..$];
507 if (shortname
==toUpper(name
))
508 return nodkeyword(shortname
);
513 final class x_bitfield
: x_elem
{
517 this(string s
) { super(s
); }
519 void parse(ElementParser ep
) {
520 trace("BITFIELD", tagname
, ep
.tag().toString());
522 name
= tag
.attr
["name"];
524 register_doc_parser(ep
);
525 ep
.onStartTag
["member"] = (ElementParser ep
) {};
526 ep
.onEndTag
["member"] = (in Element e
) {
527 enforce(e
.tag
.type
==TagType
.EMPTY
, e
.tag
.toString());
529 auto m
= new x_field("member");
532 auto mattr
= e
.tag
.attr
;
533 aassign(m
.name
, mattr
, "name", "bitfield name");
534 aassign(m
.value
, mattr
, "value", "bitfield value");
535 aassign(m
.identifier
, mattr
, "c:identifier", "bitfield c:identifier");
540 override string
toString() {
541 // Do we care about the basetype (ie do 31bits+ values happen?)
542 string r
= comment_to_D() ~ indent
~ "enum " ~ name
;
544 r
~= " /* Version" ~ ver
~ " */";
548 foreach (i
, m
; members
) {
549 long v
= to
!long(m
.value
);
551 m
.value
= "cast(uint)" ~ m
.value
;
552 r
~= indent
~ shortenconst(m
.name
, m
.identifier
) ~ " = " ~ m
.value
~
553 (i
!=members
.length
-1 ?
"," : "") ~ "\n";
556 return r
~ --indent
~ "}";
560 x_field gerrfield
; // pseudo field; used by functions that "throw" in gtk-speak.
562 gerrfield
= new x_field("GError");
563 gerrfield
.type
= "GLib2.Error**";
564 gerrfield
.name
= "error=null";
565 gerrfield
.cname
= "error";
568 final class x_field
: x_elem
{
569 string type
, name
, cname
;
570 string value
; // for constants
571 string identifier
; // for bitfields
573 bool varargs
; // true if this parameter is "..."
577 x_struct struct_
; // embedded struct/union;
578 x_function callback
; // a callback, ie embedded function pointer.
579 x_elem docparent
; // parent element, where we place our doc.
580 int bitfield
; // if >0 then we need to handle it specially...
582 this(string s
, x_elem parent
=null) { super(s
); docparent
=parent
;}
584 override string
toString() {
585 string r
= comment_to_D();
588 return r
~ struct_
.toString();
594 r
~= indent
~ "enum " ~ name
~ " = \"" ~ escdquote(value
) ~ "\";";
597 string contype
= Dtype(type
, ctype
);
599 // "int MUTEX_DEBUG_MAGIC = cast(int)4175530711;" needs the cast...
601 if (contype
=="int") {
602 long conval
= to
!long(value
);
603 if (conval
>int.max
&& conval
<=uint.max
)
604 concast
= "cast(int)";
606 r
~= indent
~ "enum " ~ contype
~ " " ~ name
~ " = " ~ concast
~ value
~ ";";
610 string dtype
= Dtype(type
, ctype
);
611 // We're renaming types, and that can cause problems, as in Gtk2.Type case.
614 r
~= indent
~ "alias " ~ dtype
~ " " ~ name
~ ";" /*~ "\t// " ~ ctype*/;
618 // case "field": /* Fields in classes/structs are handled in containers. */
620 enforce(0, "Unknown field type: " ~ tagname
);
625 void parse(ElementParser ep
) {
626 trace("FIELD", tagname
, ep
.tag().toString());
627 SkipEndTag(ep
, tagname
);
630 auto field_attr
= tag
.attr
;
631 aassign(name
, field_attr
, "name");
632 aassign(value
, field_attr
, "value"); // Constants.
635 docparent
.register_doc_parser(ep
, name ?
"<" ~ name
~ ">": "PARAM");
637 register_doc_parser(ep
);
639 if (auto p
= "private" in field_attr
)
642 if (auto p
= "bits" in field_attr
) // Bitfields need special treatment.
643 bitfield
= to
!int(*p
);
645 ep
.onStartTag
["type"] = (ElementParser ep
) {};
646 ep
.onEndTag
["type"] = (in Element e
) {
648 aassign(ctype
, a
, "c:type");
649 if (tagname
=="alias" || tagname
=="parameter") {
650 /* GLib.List etc have extra info in child, ignored for now */
651 auto p
= "name" in a
;
652 if ( !p ||
( *p
!="GLib.List" && *p
!="GLib.SList" && *p
!="GLib.HashTable" ) )
653 enforce(e
.tag
.type
==TagType
.EMPTY
, e
.tag
.toString());
657 type
= get_name_attr(e
);
661 onEmptyTag(ep
, "attribute", (in Element e
) {
663 enforce(tag
.attr
.length
==2);
664 enforce(tag
.attr
["name"]=="c:identifier");
665 identifier
= tag
.attr
["value"];
668 if (tagname
=="parameter") {
669 if (auto p
= "allow-none" in field_attr
) {
673 if (auto p
= "direction" in field_attr
)
674 direction
= "/*" ~ *p
~ "*/ ";
676 parsearray(ep
, type
, ctype
);
677 ep
.onStartTag
["varargs"] = (ElementParser ep
) {
680 SkipEndTag(ep
, "varargs");
685 if (tagname
=="field") {
686 ep
.onStartTag
["array"] = (ElementParser ep
) {};
687 ep
.onEndTag
["array"] = (in Element e
) {
688 trace("FIELDARR", tagname
, e
.tag
.toString());
690 aassign(size
, e
.tag
.attr
, "fixed-size");
691 if (auto p
= "readable" in field_attr
) {
692 if (!size
&& *p
=="0")
693 // Generates zero sized array, but as long as all instances
694 // are private and not followed by public fields - harmless.
698 // Ugh, eg GTK.InputDialogClass contains embedded arrays w/o size.
699 // Not usuable anyway, so we only care about it being syntactically correct.
703 // Using D syntax for arrays.
704 type
~= "[" ~ size
~ "]";
705 ctype
~= "[" ~ size
~ "]";
709 ep
.onStartTag
["callback"] = (ElementParser ep
) {
710 callback
= new x_function("functionp");
718 final class x_function
: x_elem
{
720 x_struct obj
; // object if this is a method.
721 string rettype
, crettype
, retanno
;
722 string throws
; // actually gerrors, oh well.
726 this(string s
, x_struct pobj
=null) { super(s
); obj
= pobj
; }
728 string
c_func(bool proto
, bool skipthis
, scope const(x_field
)[] args
) {
734 r
~= nodkeyword(cname
) ~ "(";
736 foreach(i
, a
; args
) {
738 r
~= "..."; // Variadic - have to be aliased (avoids having to deal w/ varargs).
741 r
~= a
.direction
~ Dtype(a
.type
, a
.ctype
) ~ " ";
742 r
~= a
.cname ?
nodkeyword(a
.cname
) : nodkeyword(a
.name
);
745 if (i
!=args
.length
-1)
754 override string
toString() {
755 string Drettype
= Dtype(rettype
, crettype
);
756 string r
= comment_to_D() ~ indent
;
760 // Ugh. Turning off -ffunction-sections causes link failures
761 // because if this missing glib function.
762 if (cname
=="glib_dummy_decl")
765 bool is_funcp
= tagname
=="callback" ||
766 tagname
=="functionp" ||
767 tagname
=="virtual-method";
769 // Fix up the argument list.
774 auto t
= new x_field("this_");
775 t
.type
= obj
.name
~ "*";
778 if (tagname
!="constructor") {
779 // Methods have a this pointer as the first arg.
780 allargs
= t
~ allargs
;
782 if (tagname
!="function")
786 // Constructors return pointers to new objects, not some base type.
787 Drettype
= obj
.name
~ "*";
792 allargs
~= gerrfield
;
794 foreach(i
, a
; allargs
) {
795 // Fix up any missing arg names.
797 a
.name
= "arg_" ~ cast(char)('a'+i
);
798 // Gtk2 has out params w/o a ctype and "gint" type - we'll do our best to fix it...
799 if (a
.direction
=="/*out*/ " && !a
.ctype
/*&& a.type[$-1]!='*'*/) {
800 a
.ctype
= a
.type
~ "*";
801 a
.direction
~= "/*POINTER*/ ";
803 // Fix up default params.
804 if (a
.def_init
=="=null")
805 switch (Dtype(a
.type
, a
.ctype
)) {
806 case "int": a
.def_init
= "=0"; break;
807 default: /**/; break;
811 // Remove default initializers from args that are followed by ones w/o defaults.
813 bool nondefarg_seen
= 0;
814 foreach_reverse(a
; allargs
) {
815 if (!a
.def_init
&& !a
.varargs
)
822 if (tagname
=="glib:signal") {
823 Drettype
= Dtype(rettype
, crettype
, 1);
825 // Signals have an extra 'user_data' pointer as the last arg.
826 auto udata
= new x_field("user_data");
827 udata
.type
= "void*";
828 udata
.name
= "user_data=null"; // "=null" JIC previous args were optional.
829 allargs
= allargs
~ udata
;
834 r
~= Drettype
~ " " ~ retanno
;
837 foreach(i
, a
; allargs
) {
838 r
~= a
.direction
~ Dtype(a
.type
, a
.ctype
, 1) ~ " " ~ nodkeyword(a
.name
) ~ a
.def_init
;
839 if (i
!=allargs
.length
-1)
844 r
~= " signal_" ~ nodkeyword(name
);
847 // Add some convenience templates:
849 if (!obj
.emitted("signal_connect")) {
851 r
~= indent
~ "ulong signal_connect(string name, CB)(CB cb, void* data=null, ConnectFlags cf=cast(ConnectFlags)0) {\n";
852 r
~= ++indent
~ "return super_.signal_connect!name(cb, data, cf);\n";
853 r
~= --indent
~ "}\n\n";
856 r
~= indent
~ `ulong signal_connect(string name:"` ~ name
~ `", `;
857 r
~= `CB:signal_` ~ nodkeyword(name
) ~ ")(CB cb, void* data=null, ConnectFlags cf=cast(ConnectFlags)0) {\n";
858 r
~= ++indent
~ `return signal_connect_data(&this, cast(char*)"` ~ name
~ "\",\n";
859 r
~= indent
~ "cast(GObject2.Callback)cb, data, null, cf);\n";
860 r
~= --indent
~ "}\n";
866 bool unsupported
= tagname
!="function" &&
867 tagname
!="constructor";
869 r
~= "/+ Not available -- variadic " ~
871 "methods unsupported - use the C function directly.\n"
873 "functions with non-D linkage must have >1 parameter.\n" );
877 r
~= "alias " ~ cname
~ " " ~ nodkeyword(name
) ~ "; // Variadic\n";
879 r
~= --indent
~ "+/\n";
884 bool hasUpperCase(string s
) {
892 string refDrettype
= Drettype
;
893 if (reftypes
&& Drettype
[$-1]=='*' && hasUpperCase(Drettype
)) {
894 refDrettype
= Drettype
[0..$-1];
900 if (tagname
=="callback")
902 if (tagname
=="constructor" || tagname
=="function")
906 r
~= "Ref!(" ~ refDrettype
~ ")";
915 r
~= nodkeyword(name
) ~ "(";
922 foreach(i
, a
; args
) {
926 r
~= a
.direction
~ Dtype(a
.type
, a
.ctype
) ~ " " ~ nodkeyword(a
.name
) ~ a
.def_init
;
927 if (i
!=args
.length
-1)
935 r
~= " " ~ nodkeyword(name
);
937 if (tagname
=="functionp")
940 if (tagname
=="callback" || tagname
=="virtual-method") {
945 r
~= " {\n" ~ ++indent
;
947 if (Drettype
!="void")
951 r
~= "Ref!(" ~ refDrettype
~ ")(";
955 oname
= allargs
[0].name
;
956 allargs
[0].name
= "&this";
958 r
~= c_func(0, 0, allargs
);
961 r
~= ";\n" ~ --indent
~ "}\n";
963 allargs
[0].name
= oname
;
966 if (!is_funcp
/*&& tagname=="function" || obj*/)
967 cprotos
~= Drettype
~ " " ~ retanno
~ c_func(1, 0, allargs
) ~ ";";
972 void parse(ElementParser ep
) {
973 trace("FUNC", tagname
~ (obj ?
": " ~ obj
.name
: ""), ep
.tag().toString());
974 // "callbacks" in structs are actually pointers.
975 SkipEndTag(ep
, tagname
!="functionp" ? tagname
: "callback");
976 register_doc_parser(ep
);
979 name
= tag
.attr
["name"];
980 aassign(cname
, tag
.attr
, "c:identifier");
981 aassign(throws
, tag
.attr
, "throws");
983 auto p
= "introspectable" in tag
.attr
;
985 doc
~= "Unintrospectable " ~ tagname
~ ": " ~ name
~ "() / " ~ cname
~ "()\n";
987 ep
.onStartTag
["return-value"] = (ElementParser ep
) {
988 SkipEndTag(ep
, "return-value");
989 register_doc_parser(ep
, "RETURNS");
993 if (auto p
= "transfer-ownership" in tag
.attr
) {
995 case "none": retanno
= ""; break;
996 case "full": retanno
= "/*new*/ "; break;
997 case "container": retanno
= "/*new container*/ "; break;
998 default: enforce(0, "Unknown transfer-ownership: " ~ *p
);
1002 parsearray(ep
, rettype
, crettype
);
1004 ep
.onStartTag
["type"] = (ElementParser ep
) {};
1005 ep
.onEndTag
["type"] = (in Element e
) {
1007 aassign(rettype
, tag
.attr
, "name", "func retval typename");
1008 aassign(crettype
, tag
.attr
, "c:type");
1013 // Note the trick is there can be multiple nested <type>s;
1014 // once we get here we should have filled in the types properly.
1015 // Keep in mind we only care about the outermost type, that's
1016 // why parsing the _end_ </type> tag works for us.
1018 // Unfortunately c:type is often missing, so this check is too strict.
1019 //enforce(crettype, "Missing func retval c:type" ~ to!string(this));
1022 ep
.onStartTag
["parameters"] = (ElementParser pa
) {
1023 trace("FUNCPARM", tagname
, ep
.tag().toString());
1024 SkipEndTag(pa
, "parameters");
1025 pa
.onStartTag
["parameter"] = (ElementParser pa
) {
1026 SkipEndTag(pa
, "parameter");
1027 auto arg
= new x_field("parameter", this); args
~= arg
; arg
.parse(pa
);
1038 final class x_struct
: x_elem
{
1039 string name
, parent
;
1040 x_struct embedded_in
;
1042 x_function
[] methods
;
1043 bool[string
] emitted_
;
1045 this(string tn
, x_struct emb
=null) {
1049 case "record": break;
1050 case "class": break;
1051 case "union": break;
1052 case "interface": break;
1053 default: enforce(0, "Unknown struct type: " ~ tagname
);
1057 private bool emitted(string s
) {
1058 if (auto p
= s
in emitted_
)
1064 private string
basetypename(scope string type
) {
1065 auto i
= lastIndexOf(type
, '.');
1066 return i
==-1 ? type
: type
[i
+1..$];
1069 override string
toString() {
1070 // Everything looks like struct to us. Except unions.
1071 string r
= comment_to_D() ~ indent
~
1072 ((tagname
=="union") ?
"union " : "struct ") ~ name
;
1075 r
~= " /* : " ~ parent
~ " */";
1077 r
~= " /* Version" ~ ver
~ " */";
1082 // "alias super this".
1084 // First, if this struct has a parent, but we don't know any of its
1085 // fields then add the parent as the only (sub)struct -- harmless as
1086 // we couldn't access the opaque empty struct anyway and this lets
1087 // the pseudo-inheritance work.
1088 if (parent
&& fields
.length
==0) {
1089 auto pf
= new x_field("record");
1092 pf
.name
= "method_parent";
1096 if (parent
&& fields
.length
>0 && (
1097 Dtype(fields
[0].type
, fields
[0].ctype
)==Dtype(parent
, null) ||
1098 Dtype(fields
[0].type
, fields
[0].ctype
)==parent
1100 auto fieldname
= nodkeyword(fields
[0].name
);
1102 r
~= indent
~ "alias " ~ fieldname
~ " this;\n";
1103 r
~= indent
~ "alias " ~ fieldname
~ " super_;\n";
1105 // Add an extra alias for easier upcasting.
1106 // NOTE: The name we add could clash with existing symbols;
1107 // this does not happen in practice, will be caught at
1108 // compiletime if it does, and can be addressed then.
1109 // Also "object" is not the safest symbol to add, but
1110 // works so far, let's wait until it causes problems
1111 // before renaming it to something like "gobject".
1112 auto aname
= basetypename(nodkeyword(fields
[0].type
));
1113 aname
= toLower(aname
);
1114 if (aname
!=fieldname
)
1115 r
~= indent
~ "alias " ~ fieldname
~ " " ~ aname
~ ";\n";
1117 // The parent cannot be private, so fix it:
1118 if (fields
[0].private_
)
1119 fields
[0].private_
= 0;
1122 // No bitfields in D.
1125 char bfcount
='A'; // More than one __dummyNN names? Must be different.
1131 // For now we'll assume all bitfields are at least 32bits wide (C int-sized)
1133 if (bfwidth
>0 && bfwidth
<=32)
1135 else if (bfwidth
>32 && bfwidth
<=64)
1137 enforce(rawwidth
, bmix
);
1139 // std.bitmaip reqs total width to be one of 8/16/32/64, so:
1140 if (rawwidth
!=bfwidth
) {
1141 if (rawwidth
<=8 && bfwidth
>0 && bfwidth
<8)
1142 bmix
~= ",\n" ~ indent
~ " uint, \"__dummy8"~bfcount
~"\", " ~ to
!string(8-bfwidth
);
1143 else if (rawwidth
<=16 && bfwidth
<16)
1144 bmix
~= ",\n" ~ indent
~ " uint, \"__dummy16"~bfcount
~"\", " ~ to
!string(16-bfwidth
);
1145 else if (rawwidth
<=32 && bfwidth
<32)
1146 bmix
~= ",\n" ~ indent
~ " uint, \"__dummy32"~bfcount
~"\", " ~ to
!string(32-bfwidth
);
1147 else if (bfwidth
<64)
1148 bmix
~= ",\n" ~ indent
~ " uint, \"__dummy64"~bfcount
~"\", " ~ to
!string(64-bfwidth
);
1149 else if (bfwidth
==64)
1160 // Unfortunately there are methods that end up using the same name
1161 // as struct fields. Build a list of all method names, and in the
1162 // code below check for clashes. Rename the fields as these are
1163 // less likely to be used by apps.
1164 bool[string
] m_names
;
1165 foreach (m
; methods
)
1166 m_names
[m
.name
] = 1;
1168 foreach (i
, f
; fields
) {
1169 string privstr
= f
.private_ ?
"private " : "";
1176 r
~= privstr
~ f
.callback
.toString();
1178 else if (f
.struct_
) {
1185 r
~= "\n" ~ privstr
~ f
.struct_
.toString();
1187 else if (f
.bitfield
) {
1191 bmix
= indent
~ " static import std.bitmanip;"
1192 ~ " mixin(std.bitmanip.bitfields!(\n" ~ indent
;
1195 bmix
~= ",\n" ~ indent
;
1196 string bftype
= Dtype(f
.type
, f
.ctype
);
1198 case "uint", "int", "c_uint", "c_int":
1199 bmix
~= bftype
~ ", \"" ~ f
.name
~"\", "~ to
!string(f
.bitfield
);
1200 bfwidth
+= f
.bitfield
;
1203 enforce(0, "Unknown bitfield type " ~
1204 bftype
~ "[" ~ f
.type
~ "," ~ f
.ctype
~ "] " ~
1205 f
.name
~ ":" ~ to
!string(f
.bitfield
));
1210 string fname
= nodkeyword(f
.name
);
1211 if (fname
in m_names
)
1214 r
~= indent
~ privstr
~ Dtype(f
.type
, f
.ctype
) ~ " " ~ fname
;
1216 string prevtype
, currtype
;
1217 if (fields
[i
-1].type || fields
[i
-1].ctype
)
1218 prevtype
= Dtype(fields
[i
-1].type
, fields
[i
-1].ctype
);
1219 currtype
= Dtype(f
.type
, f
.ctype
);
1220 if ( (prevtype
== currtype
&& fields
[i
-1].type
==f
.type
)
1221 && (fields
[i
-1].private_
== f
.private_
)
1222 && !fields
[i
-1].bitfield
)
1225 r
~= ((!fields
[i
-1].struct_
) ?
";" : "");
1226 r
~= "\n"~ indent
~ privstr
~ currtype
~ " " ~ fname
;
1234 if (fields
.length
>0 && !fields
[$-1].struct_
)
1237 if (fields
.length
>0 && methods
.length
>0)
1240 foreach (f
; methods
)
1243 // Pull in the right mixin, if any:
1245 string mixname
= name
;
1247 for (auto ei
= embedded_in
; ei
; ei
= ei
.embedded_in
)
1248 mixname
= ei
.name
~ "_" ~ mixname
;
1250 r
~= load_mixin(mixname
);
1252 return r
~ --indent
~ "}\n";
1255 void parse(ElementParser ep
) {
1256 trace("STRUCT", tagname
, ep
.tag().toString());
1257 auto tag
= ep
.tag();
1258 name
= tag
.attr
["name"];
1259 aassign(parent
, tag
.attr
, "parent");
1261 register_doc_parser(ep
);
1262 ep
.onStartTag
["field"] = (ElementParser ep
) {
1263 auto nf
= new x_field("field"); fields
~= nf
; nf
.parse(ep
);
1265 ep
.onStartTag
["union"] = (ElementParser ep
) {
1266 auto nf
= new x_field("union"); fields
~= nf
;
1267 auto na
= new x_struct("union", this); nf
.struct_
= na
; na
.parse(ep
);
1269 ep
.onStartTag
["record"] = (ElementParser ep
) {
1270 auto nf
= new x_field("record"); fields
~= nf
;
1271 auto na
= new x_struct("record", this); nf
.struct_
= na
; na
.parse(ep
);
1273 ep
.onStartTag
["property"] = (ElementParser p
) {
1274 p
.parse(); // Drop them.
1276 ep
.onStartTag
["method"] = (ElementParser ep
) {
1277 auto nm
= new x_function("method", this); methods
~= nm
; nm
.parse(ep
);
1279 ep
.onStartTag
["virtual-method"] = (ElementParser p
) {
1280 p
.parse(); // Drop them.
1282 ep
.onStartTag
["function"] = (ElementParser ep
) {
1283 auto nm
= new x_function("function"); methods
~= nm
; nm
.parse(ep
);
1285 ep
.onStartTag
["constructor"] = (ElementParser ep
) {
1286 auto nm
= new x_function("constructor", this); methods
~= nm
; nm
.parse(ep
);
1288 ep
.onStartTag
["glib:signal"] = (ElementParser ep
) {
1289 auto nm
= new x_function("glib:signal", this); methods
~= nm
; nm
.parse(ep
);
1292 ep
.onStartTag
["prerequisite"] = (ElementParser ep
) { ep
.parse(); }; // Drop.
1293 ep
.onEndTag
["prerequisite"] = (in Element e
) {};
1296 ep
.onStartTag
["implements"] = (ElementParser p
) { p
.parse(); };
1297 ep
.onEndTag
["implements"] = (in Element e
) {};
1303 void onUnknownTag(ElementParser ep
, string tagname
) {
1304 ep
.onStartTag
[null] = (ElementParser ep
) {
1305 enforce(0, "Unhandled <"~tagname
~"> start tag: "
1306 ~ ep
.tag
.name
~ "\n# " ~ ep
.tag().toString());
1308 ep
.onEndTag
[null] = (in Element e
) {
1309 enforce(0, "Unhandled <"~tagname
~"> end tag: " ~ "\n# " ~ e
.toString());
1313 void SkipEndTag(ElementParser p
, string tagname
) {
1314 onUnknownTag(p
, tagname
);
1315 p
.onEndTag
[tagname
] = (in Element e
) { };
1318 void onEmptyTag(ElementParser p
, string tagname
, void delegate(in Element e
) deleg
) {
1319 p
.onStartTag
[tagname
] = (ElementParser ep
) {
1320 /* do nothing; only tells the parser that we know about this tag */
1322 p
.onEndTag
[tagname
] = (in Element e
) {
1324 enforce(tag
.type
==TagType
.EMPTY
, tag
.toString());
1330 void onStartTag(ElementParser p
, string tagname
, void delegate(ElementParser ep
) deleg
) {
1331 p
.onStartTag
[tagname
] = (ElementParser ep
) {
1332 onUnknownTag(ep
, tagname
);
1333 ep
.onEndTag
[tagname
] = (in Element e
) {/*Reached our own end tag*/};
1336 p
.onEndTag
[tagname
] = (in Element e
) {
1337 /* Do nothing; only tells the parser that we know about this tag */
1338 /* Ugh. "<record name="AppLaunchContextPrivate" />" happens... */
1342 template OnTag(alias pa
, string Kind
, string xmlname
, string tagname
) {
1344 on"~Kind
~"Tag(" ~ pa
.stringof
~ ", \"" ~ xmlname
~ "\","
1345 " (" ~ ((Kind
=="Start") ?
"ElementParser e" : "in Element e") ~ ") {
1346 auto a = new x_"~tagname
~"(" ~ xmlname
.stringof
~ ");
1348 "`write(a.toString() ~"\n");`
1354 string
load_mixin(string id
) {
1355 string filename
= "mixin/" ~ thismodulename
~ "_" ~ id
~ ".d";
1358 static bool uniq_mix
[string
];
1360 enforce(filename
!in uniq_mix
, "Mixin collission: " ~ filename
);
1361 uniq_mix
[filename
] = 1;
1364 s
= cast(string
)std
.file
.read(filename
);
1365 } catch (std
.file
.FileException
) {
1367 ew("MIXIN Failed: " ~ filename
);
1372 ew("MIXIN Found: " ~ filename
);
1374 string r
= "\n" ~ indent
~ "// --- " ~ filename
~ " --->\n\n";
1376 foreach (sp
; split(s
, "\n"))
1377 r
~= indent
~ sp
~ "\n";
1379 r
~= indent
~ "// <--- " ~ filename
~ " ---\n";
1386 void main(string
[] args
) {
1387 string infilename
, outfilename
, modulename
;
1393 bool delegate(string arg
) eatarg
;
1395 cmdargloop
: foreach(arg
; args
[1..$]) {
1406 die("Unknown extra cmdline file name: " ~ arg
);
1409 die("Missing cmdline option: " ~ arg
);
1412 switch (arg
[2..$]) {
1414 case "trace": showtrace
= 1; break;
1416 case "trace-mixins": tracemixins
= 1; break;
1417 case "check": xmlcheck
= 1; break;
1418 case "reftypes": reftypes
= 1; break;
1419 case "no-reftypes": reftypes
= 0; break;
1420 default: die("Unknown long cmdline option: " ~ arg
);
1425 outfilename
= arg
[2..$];
1427 eatarg
= (string arg
) { outfilename
= arg
; return true; };
1428 continue cmdargloop
;
1431 outfilename
= arg
[2..$];
1433 eatarg
= (string arg
) { modulename
= arg
; return true; };
1434 continue cmdargloop
;
1436 die("Unknown cmdline option: " ~ arg
);
1441 die("Missing cmdline arg");
1443 string s
= cast(string
)std
.file
.read(infilename
);
1446 check(s
); // Running the checks increases the execution time from 0.22s to 1.74s...
1449 stdout
.open(outfilename
, "wt");
1451 writef("// *** DO NOT EDIT ***\n// Automatically generated from \"%s\"\n\n", infilename
);
1454 write("module " ~ modulename
~ ";\n");
1456 auto dp
= new DocumentParser(s
);
1458 void delegate (ElementParser ep
) epskip
= (ElementParser ep
) { ep
.parse(); };
1459 void delegate (ElementParser p
) epfixme
= (ElementParser p
)
1460 { p
.parse(); ew("FIXME: " ~ p
.toString()); };
1461 void delegate (in Element e
) efixme
= (in Element e
) { ew("FIXME: " ~ e
.toString()); };
1463 onUnknownTag(dp
, "Document");
1465 onEmptyTag(dp
, "package", (in Element e
) {
1466 auto name
= get_name_attr(e
);
1467 write("\n// package: \"" ~ name
~ "\";\n");
1470 onEmptyTag(dp
, "c:include", (in Element e
) {
1471 auto name
= get_name_attr(e
);
1472 write("// C header: \"" ~ name
~ "\";\n");
1475 mixin( OnTag
!(dp
, "Empty", "include", "include") );
1476 mixin( OnTag
!(dp
, "Start", "namespace", "namespace") );
1477 dp
.onEndTag
["namespace"] = (in Element e
) { }; // We're assuming one ns per file.
1479 dp
.onEndTag
["repository"] = (in Element e
) { }; // We're assuming one repo per file.
1481 mixin( OnTag
!(dp
, "Start", "class", "struct") );
1482 mixin( OnTag
!(dp
, "Start", "record", "struct") );
1483 mixin( OnTag
!(dp
, "Start", "union", "struct") );
1484 mixin( OnTag
!(dp
, "Start", "interface", "struct") );
1486 mixin( OnTag
!(dp
, "Start", "constant", "field") );
1487 mixin( OnTag
!(dp
, "Start", "alias", "field") );
1489 mixin( OnTag
!(dp
, "Start", "bitfield", "bitfield") );
1490 mixin( OnTag
!(dp
, "Start", "enumeration", "bitfield") );
1492 mixin( OnTag
!(dp
, "Start", "function", "function") );
1493 mixin( OnTag
!(dp
, "Start", "callback", "function") );
1495 dp
.onStartTag
["glib:boxed"] = epskip
;
1497 // Not handled yet. Can't eat them here, we need the types anyway.
1498 //dp.onStartTag["interface"] = epfixme;
1502 write(load_mixin("_MODULE"));
1504 write("\n// C prototypes:\n\nextern (C) {\n");
1510 catch (Exception e
) {
1514 remove(outfilename
);
1516 ew("WARNING: Unable to remove destination file.");
1521 void die(S
...)(S args
) {
1522 import core
.stdc
.stdlib
;