Experimental GTK structs-as-references support. Not enabled by default.
[girtod.git] / girtod.d
blobe1d8e4417f67c42a9404a43cc306128cda7690f8
2 // Copyright Artur Skawina 2012.
3 // Distributed under the Boost Software License, Version 1.0.
4 // (See accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
8 private:
10 import std.array;
11 import std.string;
12 import std.stdio;
13 import std.xml;
14 import std.conv;
15 import std.algorithm;
16 import std.exception;
18 // Compile in tracing support? (also configurable at runtime)
19 debug = TRACE;
21 ////////////////////////////////////////////////////////////
23 enum PACKAGE_NAME = "gtk2";
24 // Module naming notes:
25 // - The filenames of modules needs to be lowercase, to work with
26 // broken filesystems.
27 // - D's module system is primitive, so this was designed to keep things
28 // as simple as possible (eg not using absolute module names can result
29 // in problems, either in the module files or apps that import them in
30 // various ways.
31 // - The original mixed-caps module names should be used in the modules.
34 string[] cprotos; // All seen functions and C "methods".
36 auto ew(S...)(S args) { return stderr.writeln(args); }
38 debug(TRACE) {
39 bool showtrace;
40 void trace(string cl, string c, string m) {
41 // Note: this intentionally writes to stdout - we want
42 // to see the trace in context of the generated output
43 if (showtrace)
44 writefln("//TRACE:%s(%s) %s", cl, c, m);
47 static if (!is(typeof(trace))) {
48 void trace(string cl, string c, string m) { }
51 // ["ab", "cd", "ef", "gh", "ij"] <= "ab,cd, ef, gh , ij"
52 auto csv2arr(scope string s) { return array( map!strip( split(s, ",") ) ); }
54 struct Indent {
55 enum by = 3;
56 private int level;
57 private static immutable spaces =
58 " ";
59 @property string s() { return spaces[0..level]; }
60 alias s this;
61 string opUnary(string op:"++")() { level += by; return s; }
62 string opUnary(string op:"--")() { level -= by; assert (level>=0); return s; }
64 Indent indent;
66 private string nodkeyword(scope string s) {
67 // Associative array literals are non-const expressions, so...
68 switch (s) {
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_";
91 default:
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":
99 import std.ascii;
100 if (s && isDigit(s[0]))
101 return "_" ~ s;
102 return s;
105 private string escdquote(scope string s) {
106 import std.array;
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) {
120 if (!gt && ct)
121 gt = ct;
123 // Some gtk signals have /no/ info; at least don't break the build.
124 if (signals && !gt)
125 return "void*";
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]==']') {
130 size_t slen;
131 auto spos = std.string.indexOf(gt, '[');
132 if (spos>1)
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]!='*') {
142 size_t alen;
143 auto apos = std.string.indexOf(ct, '*');
144 if (apos>=0)
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";
167 version (None) {
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";
173 } else {
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?
221 // GLib:
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) {
230 auto orggt = gt;
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..$])
237 return;
239 fl--;
240 if (ct.length>fl && from == ct[0..fl])
241 ct = to ~ ct[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";
253 // gobject
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
264 // Ugh:
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, "*");
272 if (ctapos==-1)
273 ctapos = ct.length;
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*";
282 // GDK:
283 if (gt=="GdkPixbuf2.PixbufAlphaMode" && ct=="GdkPixbuf2AlphaMode") return "GdkPixbuf2.PixbufAlphaMode";
285 // GTK:
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";
292 // Signals:
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 ~ "*";
302 switch (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";
310 default:
314 // Fix up some obviously broken types.
315 if (!ct) {
316 if (gt=="Object") return "Object*";
319 version(all) { // Disable this while debugging.
320 if (gt==ct)
321 return gt;
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..$];
334 if (stripped_ct==gt)
335 return stripped_ct;
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"
342 if (ct[0]=='G') {
343 auto tail = "." ~ ct[1..$];
344 if(gt.length>=tail.length && gt[$-tail.length..$] == tail)
345 return gt;
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)
356 dst = *p;
357 else if (req)
358 enforce(0, "Missing " ~ req ~ "; Available: " ~ to!string(aa));
360 string get_name_attr(in Element e) {
361 auto tag = e.tag;
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));
368 return *p;
370 return null;
373 string simplify_version(scope string v) pure {
374 if (v=="1.0")
375 return "";
376 else if (v[1..$]==".0")
377 return v[0..1];
378 return v;
381 class x_elem {
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) {
388 trace("DOC", "", s);
389 if (dochead)
390 doc ~= "\n" ~ dochead ~ ": ";
391 doc ~= s;
393 SkipEndTag(ep, "doc");
394 ep.parse();
397 final void register_doc_parser(ElementParser ep, string dochead=null) {
398 ep.onStartTag["doc"] = &parse_doc_text;
400 auto tag = ep.tag();
402 if (auto p = "version" in tag.attr)
403 ver = " " ~ *p;
404 this.dochead = dochead;
406 final string comment_to_D() {
407 import std.array;
408 string r;
409 if (doc) {
410 while (doc[$-1]=='\n')
411 doc = doc[0..$-1];
412 while (doc[0]=='\n')
413 doc = doc[1..$];
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)
419 r = r[1..$];
421 return r;
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");
428 if (makepointer)
429 _ctype ~= "*";
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");
435 ep.parse();
440 final class x_include : x_elem {
441 string name, ver;
443 this(string s) { super(s); }
445 void parse(in Element e) {
446 auto tag = e.tag;
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 {
470 string name, ver;
472 this(string s) { super(s); }
474 void parse(ElementParser ep) {
475 auto attr = ep.tag().attr;
476 name = attr["name"];
477 ver = attr["version"];
479 writeln();
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");
499 return s;
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);
510 return cname;
513 final class x_bitfield : x_elem {
514 string name;
515 x_field[] members;
517 this(string s) { super(s); }
519 void parse(ElementParser ep) {
520 trace("BITFIELD", tagname, ep.tag().toString());
521 auto tag = ep.tag();
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");
530 members ~= m;
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");
537 ep.parse();
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;
543 if (ver)
544 r ~= " /* Version" ~ ver ~ " */";
545 r ~= " {\n";
546 indent++;
548 foreach (i, m; members) {
549 long v = to!long(m.value);
550 if (v==0x8000_0000L)
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.
561 static this() {
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
572 string ctype;
573 bool varargs; // true if this parameter is "..."
574 bool private_;
575 string def_init;
576 string direction;
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();
587 if (struct_)
588 return r ~ struct_.toString();
590 switch (tagname) {
591 case "constant":
592 switch (type) {
593 case "utf8":
594 r ~= indent ~ "enum " ~ name ~ " = \"" ~ escdquote(value) ~ "\";";
595 break;
596 default:
597 string contype = Dtype(type, ctype);
599 // "int MUTEX_DEBUG_MAGIC = cast(int)4175530711;" needs the cast...
600 string concast;
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 ~ ";";
608 break;
609 case "alias":
610 string dtype = Dtype(type, ctype);
611 // We're renaming types, and that can cause problems, as in Gtk2.Type case.
612 if (dtype==name)
613 r ~= "/* ";
614 r ~= indent ~ "alias " ~ dtype ~ " " ~ name ~ ";" /*~ "\t// " ~ ctype*/;
615 if (dtype==name)
616 r ~= " */";
617 break;
618 // case "field": /* Fields in classes/structs are handled in containers. */
619 default:
620 enforce(0, "Unknown field type: " ~ tagname);
622 return r;
625 void parse(ElementParser ep) {
626 trace("FIELD", tagname, ep.tag().toString());
627 SkipEndTag(ep, tagname);
628 auto tag = ep.tag();
630 auto field_attr = tag.attr;
631 aassign(name, field_attr, "name");
632 aassign(value, field_attr, "value"); // Constants.
634 if (docparent)
635 docparent.register_doc_parser(ep, name ? "<" ~ name ~ ">": "PARAM");
636 else
637 register_doc_parser(ep);
639 if (auto p = "private" in field_attr)
640 private_ = *p=="1";
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) {
647 auto a = e.tag.attr;
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());
655 switch (tagname) {
656 default:
657 type = get_name_attr(e);
661 onEmptyTag(ep, "attribute", (in Element e) {
662 auto tag = e.tag;
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) {
670 if (*p=="1")
671 def_init = "=null";
673 if (auto p = "direction" in field_attr)
674 direction = "/*" ~ *p ~ "*/ ";
676 parsearray(ep, type, ctype);
677 ep.onStartTag["varargs"] = (ElementParser ep) {
678 name = "...";
679 varargs = 1;
680 SkipEndTag(ep, "varargs");
681 ep.parse();
685 if (tagname=="field") {
686 ep.onStartTag["array"] = (ElementParser ep) {};
687 ep.onEndTag["array"] = (in Element e) {
688 trace("FIELDARR", tagname, e.tag.toString());
689 string size;
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.
695 size = "0";
697 //enforce(size);
698 // Ugh, eg GTK.InputDialogClass contains embedded arrays w/o size.
699 // Not usuable anyway, so we only care about it being syntactically correct.
700 if (!size)
701 size = "666";
703 // Using D syntax for arrays.
704 type ~= "[" ~ size ~ "]";
705 ctype ~= "[" ~ size ~ "]";
709 ep.onStartTag["callback"] = (ElementParser ep) {
710 callback = new x_function("functionp");
711 callback.parse(ep);
714 ep.parse();
718 final class x_function : x_elem {
719 string name, cname;
720 x_struct obj; // object if this is a method.
721 string rettype, crettype, retanno;
722 string throws; // actually gerrors, oh well.
723 bool is_variadic;
724 x_field[] args;
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) {
729 string r;
731 if (skipthis)
732 args = args[1..$];
734 r ~= nodkeyword(cname) ~ "(";
736 foreach(i, a; args) {
737 if (a.varargs)
738 r ~= "..."; // Variadic - have to be aliased (avoids having to deal w/ varargs).
739 else {
740 if (proto)
741 r ~= a.direction ~ Dtype(a.type, a.ctype) ~ " ";
742 r ~= a.cname ? nodkeyword(a.cname) : nodkeyword(a.name);
743 if (proto)
744 r ~= a.def_init;
745 if (i!=args.length-1)
746 r ~= ", ";
750 r ~= ")";
751 return r;
754 override string toString() {
755 string Drettype = Dtype(rettype, crettype);
756 string r = comment_to_D() ~ indent;
757 bool hasthis = 0;
758 bool refret;
760 // Ugh. Turning off -ffunction-sections causes link failures
761 // because if this missing glib function.
762 if (cname=="glib_dummy_decl")
763 return "";
765 bool is_funcp = tagname=="callback" ||
766 tagname=="functionp" ||
767 tagname=="virtual-method";
769 // Fix up the argument list.
771 auto allargs = args;
773 if (obj) {
774 auto t = new x_field("this_");
775 t.type = obj.name ~ "*";
776 t.name = "this_";
778 if (tagname!="constructor") {
779 // Methods have a this pointer as the first arg.
780 allargs = t ~ allargs;
782 if (tagname!="function")
783 hasthis = 1;
785 else {
786 // Constructors return pointers to new objects, not some base type.
787 Drettype = obj.name ~ "*";
791 if (throws)
792 allargs ~= gerrfield;
794 foreach(i, a; allargs) {
795 // Fix up any missing arg names.
796 if (!a.name)
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)
816 nondefarg_seen = 1;
817 if (nondefarg_seen)
818 a.def_init = null;
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;
831 r ~= "extern (C) ";
832 r ~= "alias ";
833 r ~= "static ";
834 r ~= Drettype ~ " " ~ retanno;
835 r ~= "function (";
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)
840 r ~= ", ";
843 r ~= ")";
844 r ~= " signal_" ~ nodkeyword(name);
845 r ~= ";\n";
847 // Add some convenience templates:
849 if (!obj.emitted("signal_connect")) {
850 r ~= "\n";
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";
862 return r;
865 if (is_variadic) {
866 bool unsupported = tagname!="function" &&
867 tagname!="constructor";
868 if (unsupported) {
869 r ~= "/+ Not available -- variadic " ~
870 ( hasthis ?
871 "methods unsupported - use the C function directly.\n"
873 "functions with non-D linkage must have >1 parameter.\n" );
874 r ~= ++indent;
877 r ~= "alias " ~ cname ~ " " ~ nodkeyword(name) ~ "; // Variadic\n";
878 if (unsupported)
879 r ~= --indent ~ "+/\n";
881 goto add_cproto;
884 bool hasUpperCase(string s) {
885 import std.uni;
886 foreach(c; s)
887 if (isUpper(c))
888 return 1;
889 return 0;
892 string refDrettype = Drettype;
893 if (reftypes && Drettype[$-1]=='*' && hasUpperCase(Drettype)) {
894 refDrettype = Drettype[0..$-1];
895 refret = 1;
898 if (is_funcp)
899 r ~= "extern (C) ";
900 if (tagname=="callback")
901 r ~= "alias ";
902 if (tagname=="constructor" || tagname=="function")
903 r ~= "static ";
905 if (refret)
906 r ~= "Ref!(" ~ refDrettype ~ ")";
907 else
908 r ~= Drettype;
910 r ~= " " ~ retanno;
912 if (is_funcp)
913 r ~= "function (";
914 else
915 r ~= nodkeyword(name) ~ "(";
917 auto args = allargs;
919 if (hasthis)
920 args = args[1..$];
922 foreach(i, a; args) {
923 if (a.varargs)
924 r ~= "...";
925 else {
926 r ~= a.direction ~ Dtype(a.type, a.ctype) ~ " " ~ nodkeyword(a.name) ~ a.def_init;
927 if (i!=args.length-1)
928 r ~= ", ";
932 r ~= ")";
934 if (is_funcp)
935 r ~= " " ~ nodkeyword(name);
937 if (tagname=="functionp")
938 return r;
940 if (tagname=="callback" || tagname=="virtual-method") {
941 r ~= ";\n";
942 return r;
945 r ~= " {\n" ~ ++indent;
947 if (Drettype!="void")
948 r ~= "return ";
950 if (refret)
951 r ~= "Ref!(" ~ refDrettype ~ ")(";
953 string oname;
954 if (hasthis) {
955 oname = allargs[0].name;
956 allargs[0].name = "&this";
958 r ~= c_func(0, 0, allargs);
959 if (refret)
960 r ~= ")";
961 r ~= ";\n" ~ --indent ~ "}\n";
962 if (hasthis)
963 allargs[0].name = oname;
965 add_cproto:
966 if (!is_funcp /*&& tagname=="function" || obj*/)
967 cprotos ~= Drettype ~ " " ~ retanno ~ c_func(1, 0, allargs) ~ ";";
969 return r;
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);
977 auto tag = ep.tag();
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;
984 if (p && *p=="0")
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");
991 auto tag = ep.tag();
993 if (auto p = "transfer-ownership" in tag.attr) {
994 switch (*p) {
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) {
1006 auto tag = e.tag;
1007 aassign(rettype, tag.attr, "name", "func retval typename");
1008 aassign(crettype, tag.attr, "c:type");
1011 ep.parse();
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);
1028 if (arg.varargs)
1029 is_variadic = 1;
1031 pa.parse();
1034 ep.parse();
1038 final class x_struct : x_elem {
1039 string name, parent;
1040 x_struct embedded_in;
1041 x_field[] fields;
1042 x_function[] methods;
1043 bool[string] emitted_;
1045 this(string tn, x_struct emb=null) {
1046 super(tn);
1047 embedded_in = emb;
1048 switch (tagname) {
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_)
1059 return 1;
1060 emitted_[s] = 1;
1061 return 0;
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;
1074 if (parent)
1075 r ~= " /* : " ~ parent ~ " */";
1076 if (ver)
1077 r ~= " /* Version" ~ ver~ " */";
1078 r ~= " {\n";
1080 indent++;
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");
1090 pf.type = parent;
1091 pf.ctype = parent;
1092 pf.name = "method_parent";
1093 fields ~= pf;
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
1099 )) {
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.
1123 string bmix;
1124 int bfwidth;
1125 char bfcount='A'; // More than one __dummyNN names? Must be different.
1127 void eofbf() {
1128 if (!bmix)
1129 return;
1131 // For now we'll assume all bitfields are at least 32bits wide (C int-sized)
1132 int rawwidth;
1133 if (bfwidth>0 && bfwidth<=32)
1134 rawwidth = 32;
1135 else if (bfwidth>32 && bfwidth<=64)
1136 rawwidth = 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)
1151 else
1152 enforce(0);
1154 r ~= bmix ~ "))";
1155 bmix = null;
1156 bfwidth = 0;
1157 bfcount++;
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 " : "";
1171 if (f.callback) {
1172 eofbf();
1173 if (i!=0)
1174 r ~= ";\n";
1176 r ~= privstr ~ f.callback.toString();
1178 else if (f.struct_) {
1179 eofbf();
1180 if (i!=0)
1181 r ~= ";\n";
1182 else
1183 r ~= "\n";
1185 r ~= "\n" ~ privstr ~ f.struct_.toString();
1187 else if (f.bitfield) {
1188 if (!bmix) {
1189 if (i!=0)
1190 r ~= ";\n";
1191 bmix = indent ~ " static import std.bitmanip;"
1192 ~ " mixin(std.bitmanip.bitfields!(\n" ~ indent;
1194 else
1195 bmix ~= ",\n" ~ indent;
1196 string bftype = Dtype(f.type, f.ctype);
1197 switch (bftype) {
1198 case "uint", "int", "c_uint", "c_int":
1199 bmix ~= bftype ~ ", \"" ~ f.name~"\", "~ to!string(f.bitfield);
1200 bfwidth += f.bitfield;
1201 break;
1202 default:
1203 enforce(0, "Unknown bitfield type " ~
1204 bftype ~ "[" ~ f.type ~ "," ~ f.ctype ~ "] " ~
1205 f.name ~ ":" ~ to!string(f.bitfield));
1208 else {
1209 eofbf();
1210 string fname = nodkeyword(f.name);
1211 if (fname in m_names)
1212 fname ~= "_";
1213 if (i==0)
1214 r ~= indent ~ privstr ~ Dtype(f.type, f.ctype) ~ " " ~ fname;
1215 else {
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 )
1223 r ~= ", " ~ fname;
1224 else {
1225 r ~= ((!fields[i-1].struct_) ? ";" : "");
1226 r ~= "\n"~ indent ~ privstr ~ currtype ~ " " ~ fname;
1232 eofbf();
1234 if (fields.length>0 && !fields[$-1].struct_)
1235 r ~= ";\n";
1237 if (fields.length>0 && methods.length>0)
1238 r ~= "\n";
1240 foreach (f; methods)
1241 r ~= f.toString();
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) {};
1295 // Drop them.
1296 ep.onStartTag["implements"] = (ElementParser p) { p.parse(); };
1297 ep.onEndTag["implements"] = (in Element e) {};
1299 ep.parse();
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) {
1323 auto tag = e.tag;
1324 enforce(tag.type==TagType.EMPTY, tag.toString());
1326 deleg(e);
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*/};
1334 deleg(ep);
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) {
1343 enum OnTag = "
1344 on"~Kind~"Tag(" ~ pa.stringof ~ ", \"" ~ xmlname ~ "\","
1345 " (" ~ ((Kind=="Start") ? "ElementParser e" : "in Element e") ~ ") {
1346 auto a = new x_"~tagname~"(" ~ xmlname.stringof ~ ");
1347 a.parse(e);
1348 "`write(a.toString() ~"\n");`
1349 "} );";
1352 bool tracemixins;
1354 string load_mixin(string id) {
1355 string filename = "mixin/" ~ thismodulename ~ "_" ~ id ~ ".d";
1356 string s;
1358 static bool uniq_mix[string];
1360 enforce(filename !in uniq_mix, "Mixin collission: " ~ filename);
1361 uniq_mix[filename] = 1;
1363 try {
1364 s = cast(string)std.file.read(filename);
1365 } catch (std.file.FileException) {
1366 if (tracemixins)
1367 ew("MIXIN Failed: " ~ filename);
1368 return null;
1371 if (tracemixins)
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";
1381 return r;
1384 bool reftypes = 0;
1386 void main(string[] args) {
1387 string infilename, outfilename, modulename;
1388 bool xmlcheck;
1390 try {
1391 // Parse cmd line:
1393 bool delegate(string arg) eatarg;
1395 cmdargloop: foreach(arg; args[1..$]) {
1396 if (eatarg) {
1397 if (eatarg(arg))
1398 eatarg = null;
1399 continue;
1401 if (arg[0]!='-') {
1402 if (!infilename) {
1403 infilename = arg;
1404 continue;
1406 die("Unknown extra cmdline file name: " ~ arg);
1408 if (arg.length<2)
1409 die("Missing cmdline option: " ~ arg);
1410 switch (arg[1]) {
1411 case '-':
1412 switch (arg[2..$]) {
1413 debug(TRACE) {
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);
1422 break;
1423 case 'o':
1424 if (arg.length>2)
1425 outfilename = arg[2..$];
1426 else
1427 eatarg = (string arg) { outfilename = arg; return true; };
1428 continue cmdargloop;
1429 case 'M':
1430 if (arg.length>2)
1431 outfilename = arg[2..$];
1432 else
1433 eatarg = (string arg) { modulename = arg; return true; };
1434 continue cmdargloop;
1435 default:
1436 die("Unknown cmdline option: " ~ arg);
1440 if (eatarg)
1441 die("Missing cmdline arg");
1443 string s = cast(string)std.file.read(infilename);
1445 if (xmlcheck)
1446 check(s); // Running the checks increases the execution time from 0.22s to 1.74s...
1448 if (outfilename)
1449 stdout.open(outfilename, "wt");
1451 writef("// *** DO NOT EDIT ***\n// Automatically generated from \"%s\"\n\n", infilename);
1453 if (modulename)
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");
1468 } );
1470 onEmptyTag(dp, "c:include", (in Element e) {
1471 auto name = get_name_attr(e);
1472 write("// C header: \"" ~ name ~ "\";\n");
1473 } );
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;
1500 dp.parse();
1502 write(load_mixin("_MODULE"));
1504 write("\n// C prototypes:\n\nextern (C) {\n");
1505 foreach(s; cprotos)
1506 writeln(s);
1507 write("}\n");
1510 catch (Exception e) {
1511 import std.file;
1512 ew("ERROR: ", e);
1513 if (outfilename)
1514 remove(outfilename);
1515 else
1516 ew("WARNING: Unable to remove destination file.");
1517 die();
1521 void die(S...)(S args) {
1522 import core.stdc.stdlib;
1523 ew(args);
1524 exit(1);
1525 assert(0);