egra: some X11 hacks
[iv.d.git] / _obsolete_dont_use / writer.d
blobdf9be1bf1c2244c8db98d5a4e1689980c0f72a44
1 /* Invisible Vector Library
2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3 of the License ONLY.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 /* very simple compile-time format writer
18 * understands [+|-]width[.maxlen]
19 * negative width: add spaces to right
20 * negative maxlen: get right part
21 * specifiers:
22 * 's': use to!string to write argument
23 * note that writer can print strings, bools and integrals without allocation
24 * 'S': print asciiz C string
25 * 'x': write integer as hex
26 * 'X': write integer as HEX
27 * '|': write all arguments that's left with "%s"
28 * '@': go to argument with number 'width' (use sign to relative goto)
29 * argument count starts with '1'
30 * '!': skip all arguments that's left, no width allowed
31 * '%': just a percent sign, no width allowed
32 * '$': go to argument with number 'width' (use sign to relative goto), continue parsing
33 * argument count starts with '1'
34 * options (must immediately follow '%'):
35 * '/': center string; negative width means "add extra space (if any) to the right"
36 * '~': fill with the following char instead of space
37 * second '~': right filling char for 'center'
38 * '\0'...'\0': separator string for '%|'
40 module iv.writer /*is aliced*/;
41 private:
42 import iv.alice;
44 private import std.traits : isBoolean, isIntegral, isPointer;
45 private alias StripTypedef(T) = T;
48 __gshared void delegate (scope const(char[]), scope int fd=1) @trusted nothrow @nogc wrwriter;
50 public @property auto WrWriter () @trusted nothrow @nogc { return wrwriter; }
51 public @property auto WrWriter (typeof(wrwriter) cv) @trusted nothrow @nogc { auto res = wrwriter; wrwriter = cv; return res; }
54 shared static this () {
55 wrwriter = (scope str, scope fd) @trusted nothrow @nogc {
56 import core.sys.posix.unistd : STDOUT_FILENO, STDERR_FILENO, write;
57 if (fd >= 0) {
58 if (fd == 0 || fd == 1) fd = STDOUT_FILENO;
59 else if (fd == 2) fd = STDERR_FILENO;
60 if (str.length > 0) write(fd, str.ptr, str.length);
66 // width<0: pad right
67 // width == int.min: no width specified
68 // maxlen == int.min: no maxlen specified
69 private void wrWriteWidth(char lfill=' ', char rfill=' ')
70 (int fd,
71 int width,
72 int maxlen,
73 bool center,
74 const(char[]) s,
75 bool leftIsMinus=false) {
76 static immutable char[64] spacesl = () { char[64] r; foreach (immutable p; 0..64) r[p] = lfill; return r; }();
77 static immutable char[64] spacesr = () { char[64] r; foreach (immutable p; 0..64) r[p] = rfill; return r; }();
78 usize stpos = 0;
79 // fix maxlen
80 if (maxlen != int.min) {
81 if (maxlen < 0) {
82 maxlen = -maxlen;
83 if (maxlen > s.length) {
84 maxlen = cast(int)s.length;
85 } else {
86 stpos = s.length-maxlen;
88 } else if (maxlen > 0) {
89 if (maxlen > s.length) maxlen = cast(int)s.length;
91 } else {
92 // no maxlen specified
93 maxlen = cast(int)s.length;
95 // fuck overflows
96 if (maxlen < 0) maxlen = 666;
97 // fix width
98 if (width == int.min) {
99 // no width specified, defaults to visible string width
100 width = cast(int)(s.length-stpos);
101 // fuck overflows
102 if (width < 0) width = 666;
104 // centering?
105 if (center && ((width > 0 && width > maxlen) || (width < 0 && -width > maxlen))) {
106 // center string
107 int wdt = (width > 0 ? width : -width)-maxlen;
108 int spleft = wdt/2+(width > 0 && wdt%2);
109 int spright = wdt-spleft;
110 while (spleft > 0) {
111 if (spleft > spacesl.length) {
112 wrwriter(spacesl, fd);
113 spleft -= spacesl.length;
114 continue;
115 } else {
116 wrwriter(spacesl[0..spleft], fd);
117 break;
120 if (maxlen > 0) wrwriter(s[stpos..stpos+maxlen], fd);
121 while (spright > 0) {
122 if (spright > spacesr.length) {
123 wrwriter(spacesr, fd);
124 spright -= spacesr.length;
125 continue;
126 } else {
127 wrwriter(spacesr[0..spright], fd);
128 break;
131 } else {
132 // pad string
133 bool writeS = true;
134 if (width < 0) {
135 // right padding, write string
136 width = -width;
137 if (maxlen > 0) wrwriter(s[stpos..stpos+maxlen], fd);
138 writeS = false;
140 if (maxlen < width) {
141 width -= maxlen;
142 if (writeS && stpos == 0 && leftIsMinus && width > 0) {
143 wrwriter("-", fd);
144 // remove '-'
145 ++stpos;
146 --maxlen;
148 for (;;) {
149 if (width > spacesl.length) {
150 if (writeS) wrwriter(spacesl, fd); else wrwriter(spacesr, fd);
151 width -= spacesl.length;
152 } else {
153 if (writeS) wrwriter(spacesl[0..width], fd); else wrwriter(spacesr[0..width], fd);
154 break;
158 if (writeS && maxlen > 0) wrwriter(s[stpos..stpos+maxlen], fd);
162 // width<0: pad right
163 // width == int.min: no width specified
164 // maxlen == int.min: no maxlen specified
165 private void wrWriteWidthStrZ(char lfill=' ', char rfill=' ')
166 (int fd,
167 int width,
168 int maxlen,
169 bool center,
170 const(char)* s,
171 bool leftIsMinus=false) {
172 usize end = 0;
173 while (s[end]) ++end;
174 wrWriteWidth!(lfill, rfill)(fd, width, maxlen, center, s[0..end], leftIsMinus);
178 private void wrWriteWidthHex(char lfill=' ', char rfill=' ', T)
179 (int fd,
180 int width,
181 int maxlen,
182 bool center,
183 bool upcase,
184 T numm)
185 if (isIntegral!T)
187 import std.traits : isSigned, isMutable, Unqual;
188 static if (isMutable!T) alias num = numm; else Unqual!T num = cast(Unqual!T)numm;
189 char[18] hstr = void;
190 auto pos = hstr.length;
191 static if (isSigned!T) {
192 static if (T.sizeof == 8) {
193 if (num == 0x8000_0000_0000_0000uL) { wrWriteWidth!(lfill, rfill)(fd, width, maxlen, center, "-8000000000000000", (lfill == '0')); return; }
194 } else static if (T.sizeof == 4) {
195 if (num == 0x8000_0000uL) { wrWriteWidth!(lfill, rfill)(fd, width, maxlen, center, "-80000000", (lfill == '0')); return; }
196 } else static if (T.sizeof == 2) {
197 if (num == 0x8000uL) { wrWriteWidth!(lfill, rfill)(fd, width, maxlen, center, "-8000", (lfill == '0')); return; }
198 } else static if (T.sizeof == 1) {
199 if (num == 0x80uL) { wrWriteWidth!(lfill, rfill)(fd, width, maxlen, center, "-80", (lfill == '0')); return; }
201 bool neg = (num < 0);
202 if (neg) num = -num;
204 do {
205 assert(pos > 0);
206 ubyte b = num&0x0f;
207 num >>= 4;
208 if (b < 10) {
209 hstr[--pos] = cast(char)('0'+b);
210 } else if (upcase) {
211 hstr[--pos] = cast(char)('A'+b-10);
212 } else {
213 hstr[--pos] = cast(char)('a'+b-10);
215 } while (num);
216 static if (isSigned!T) {
217 if (neg) {
218 assert(pos > 0);
219 hstr[--pos] = '-';
221 wrWriteWidth!(lfill, rfill)(fd, width, maxlen, center, hstr[pos..$], (neg && lfill == '0'));
222 } else {
223 wrWriteWidth!(lfill, rfill)(fd, width, maxlen, center, hstr[pos..$]);
228 // 2**64: 18446744073709551616 (20 chars)
229 // 2**64: 0x1_0000_0000_0000_0000
230 // width<0: pad right
231 private void wrWriteWidthInt(char lfill=' ', char rfill=' ', T)
232 (int fd,
233 int width,
234 int maxlen,
235 bool center,
236 T numm)
237 if (isIntegral!T)
239 import std.traits : isSigned, isMutable, Unqual;
240 static if (isMutable!T) alias num = numm; else Unqual!T num = cast(Unqual!T)numm;
241 char[22] hstr = void;
242 auto pos = hstr.length;
243 static if (isSigned!T) {
244 static if (T.sizeof == 8) {
245 if (num == 0x8000_0000_0000_0000uL) { wrWriteWidth!(lfill, rfill)(fd, width, maxlen, center, "-9223372036854775808", (lfill == '0')); return; }
246 } else static if (T.sizeof == 4) {
247 if (num == 0x8000_0000uL) { wrWriteWidth!(lfill, rfill)(fd, width, maxlen, center, "-2147483648", (lfill == '0')); return; }
248 } else static if (T.sizeof == 2) {
249 if (num == 0x8000uL) { wrWriteWidth!(lfill, rfill)(fd, width, maxlen, center, "-32768", (lfill == '0')); return; }
250 } else static if (T.sizeof == 1) {
251 if (num == 0x80uL) { wrWriteWidth!(lfill, rfill)(fd, width, maxlen, center, "-128", (lfill == '0')); return; }
253 bool neg = (num < 0);
254 if (neg) num = -num;
256 do {
257 assert(pos > 0);
258 ubyte b = cast(ubyte)(num%10);
259 num /= 10;
260 hstr[--pos] = cast(char)('0'+b);
261 } while (num);
262 static if (isSigned!T) {
263 if (neg) {
264 assert(pos > 0);
265 hstr[--pos] = '-';
267 wrWriteWidth!(lfill, rfill)(fd, width, maxlen, center, hstr[pos..$], (neg && lfill == '0'));
268 } else {
269 wrWriteWidth!(lfill, rfill)(fd, width, maxlen, center, hstr[pos..$]);
273 private void wrWriteWidthBool(char lfill=' ', char rfill=' ', T)
274 (int fd,
275 int width,
276 int maxlen,
277 bool center,
278 T v)
279 if (isBoolean!T)
281 wrWriteWidth!(lfill, rfill)(fd, width, maxlen, center, (v ? "true" : "false"));
285 import std.traits : Unqual;
286 private void wrWriteWidthChar(char lfill=' ', char rfill=' ', T)
287 (int fd,
288 int width,
289 int maxlen,
290 bool center,
291 T v)
292 if (is(Unqual!T == char))
294 char[1] s = v;
295 wrWriteWidth!(lfill, rfill)(fd, width, maxlen, center, s);
298 private void wrWriteWidthFloat(char lfill=' ', char rfill=' ', T)
299 (int fd,
300 int width,
301 int maxlen,
302 bool center,
303 T numm) nothrow @trusted @nogc
304 if (is(T == float) || is(T == double) || is(T == const float) || is(T == const double) || is(T == immutable float) || is(T == immutable double))
306 import core.stdc.stdio : snprintf;
307 char[256] hstr = void;
308 auto len = snprintf(hstr.ptr, hstr.length, "%g", cast(double)numm);
309 wrWriteWidth!(lfill, rfill)(fd, width, maxlen, center, hstr[0..len], (numm < 0 && lfill == '0'));
313 ////////////////////////////////////////////////////////////////////////////////
314 private auto WrData (int fd, int alen) {
315 static struct Data {
316 string fd; // just to pass to writers; string, 'cause we will concat it with other strings
317 int aidx; // current arg index
318 int alen; // number of args
319 // changeable
320 int width = int.min; // this means 'not specified'
321 char widthSign = ' '; // '+', '-', '*' (no sign), ' ' (absent)
322 bool widthZeroStarted;
323 bool widthWasDigits;
324 int maxlen = int.min; // this means 'not specified'
325 char maxlenSign = ' '; // '+', '-', '*' (no sign), ' ' (absent)
326 bool maxlenZeroStarted;
327 bool maxlenWasDigits;
328 bool optCenter; // center string?
329 char lfchar = ' '; // "left fill"
330 char rfchar = ' '; // "right fill"
331 int fillhcharIdx; // 0: next will be lfchar, 1: next will be rfchar; 2: no more fills
332 string wsep; // separator string for "%|"
334 @disable this ();
336 this (int afd, usize aalen) {
337 import std.conv : to;
338 fd = to!string(afd);
339 if (aalen >= 1024) assert(0, "too many arguments for writer");
340 alen = cast(int)aalen;
343 // set named field
344 auto set(string name, T) (in T value) if (__traits(hasMember, this, name)) {
345 __traits(getMember, this, name) = value;
346 return this;
349 // increment current index
350 auto incAIdx () {
351 ++aidx;
352 if (aidx > alen) aidx = alen;
353 return this;
356 // prepare for next formatted output (reset all format params)
357 auto resetFmt () {
358 // trick with saving necessary fields
359 auto sfd = fd;
360 auto saidx = aidx;
361 auto salen = alen;
362 this = this.init;
363 fd = sfd;
364 aidx = saidx;
365 alen = salen;
366 return this;
369 // set filling char
370 auto setFillChar (char ch) {
371 switch (fillhcharIdx) {
372 case 0: lfchar = ch; break;
373 case 1: rfchar = ch; break;
374 default:
376 ++fillhcharIdx;
377 return this;
380 // prepare to parse integer field
381 auto initInt(string name) (char sign) if (__traits(hasMember, this, name)) {
382 __traits(getMember, this, name) = (sign == '-' ? -1 : 0);
383 __traits(getMember, this, name~"Sign") = sign;
384 __traits(getMember, this, name~"ZeroStarted") = false;
385 __traits(getMember, this, name~"WasDigits") = false;
386 return this;
389 // integer field parsing: process next char
390 auto putIntChar(string name) (char ch) if (__traits(hasMember, this, name)) {
391 bool wd = __traits(getMember, this, name~"WasDigits");
392 if (!wd) {
393 __traits(getMember, this, name~"ZeroStarted") = (ch == '0');
394 __traits(getMember, this, name~"WasDigits") = true;
396 int n = __traits(getMember, this, name);
397 if (n == int.min) n = 0;
398 if (n < 0) {
399 n = -(n+1);
400 immutable nn = n*10+ch-'0';
401 if (nn < n || nn == int.max) assert(0, "integer overflow");
402 n = (-nn)-1;
403 } else {
404 immutable nn = n*10+ch-'0';
405 if (nn < n || nn == int.max) assert(0, "integer overflow");
406 n = nn;
408 __traits(getMember, this, name) = n;
409 return this;
412 //TODO: do more checks on getInt, getBool, etc.
413 auto getInt(string name) () if (__traits(hasMember, this, name)) {
414 import std.traits;
415 immutable n = __traits(getMember, this, name);
416 static if (isSigned!(typeof(n))) {
417 return (n < 0 && n != n.min ? n+1 : n);
418 } else {
419 return n;
423 auto getIntDef(string name) () if (__traits(hasMember, this, name)) {
424 import std.traits;
425 immutable n = __traits(getMember, this, name);
426 static if (isSigned!(typeof(n))) {
427 if (n == n.min) return 0;
428 else if (n < 0) return n+1;
429 else return n;
430 } else {
431 return n;
435 string getIntStr(string name) () if (__traits(hasMember, this, name)) {
436 import std.conv : to;
437 return to!string(getInt!name());
440 string getBoolStr(string name) () if (__traits(hasMember, this, name)) {
441 return (__traits(getMember, this, name) ? "true" : "false");
444 // set fillchar according to width flags
445 auto fixWidthFill () {
446 if (fillhcharIdx == 0 && widthZeroStarted) {
447 lfchar = '0';
448 fillhcharIdx = 1;
450 return this;
454 return Data(fd, alen);
458 ////////////////////////////////////////////////////////////////////////////////
459 // parse (possibly signed) number
460 template writefImpl(string state, string field, string fmt, alias data, AA...)
461 if (state == "parse-int")
463 static assert(fmt.length > 0, "invalid format string");
464 static if (fmt[0] == '-' || fmt[0] == '+') {
465 static assert(fmt.length > 1 && fmt[1] >= '0' && fmt[1] <= '9', "invalid number for '"~field~"'");
466 enum writefImpl = writefImpl!("parse-digits", field, fmt[1..$], data.initInt!field(fmt[0]), AA);
467 } else static if (fmt[0] >= '0' && fmt[0] <= '9') {
468 enum writefImpl = writefImpl!("parse-digits", field, fmt, data.initInt!field('*'), AA);
469 } else {
470 enum writefImpl = writefImpl!("got-"~field, fmt, data.initInt!field(' '), AA);
475 // parse integer digits
476 template writefImpl(string state, string field, string fmt, alias data, AA...)
477 if (state == "parse-digits")
479 static assert(fmt.length > 0, "invalid format string");
480 static if (fmt[0] >= '0' && fmt[0] <= '9') {
481 enum writefImpl = writefImpl!(state, field, fmt[1..$], data.putIntChar!field(fmt[0]), AA);
482 } else {
483 enum writefImpl = writefImpl!("got-"~field, fmt, data, AA);
488 ////////////////////////////////////////////////////////////////////////////////
489 // got maxlen, done with width parsing
490 template writefImpl(string state, string fmt, alias data, AA...)
491 if (state == "parse-format")
493 static assert(fmt.length > 0, "invalid format string");
494 static assert(fmt[0] == '%', "internal error");
495 enum writefImpl = writefImpl!("parse-options", fmt[1..$], data, AA);
499 // parse options
500 template writefImpl(string state, string fmt, alias data, AA...)
501 if (state == "parse-options")
503 import std.string : indexOf;
504 static if (fmt[0] == '/') {
505 enum writefImpl = writefImpl!(state, fmt[1..$], data.set!"optCenter"(true), AA);
506 } else static if (fmt[0] == '~') {
507 static assert(fmt.length > 1, "invalid format option: '~'");
508 enum writefImpl = writefImpl!(state, fmt[2..$], data.setFillChar(fmt[1]), AA);
509 } else static if (fmt[0] == '\0') {
510 enum epos = fmt.indexOf('\0', 1);
511 static assert(epos > 0, "unterminated separator option");
512 static assert(fmt[epos] == '\0');
513 enum writefImpl = writefImpl!(state, fmt[epos+1..$], data.set!"wsep"(fmt[1..epos]), AA);
514 } else {
515 enum writefImpl = writefImpl!("parse-int", "width", fmt, data, AA);
520 // got width, try maxlen
521 template writefImpl(string state, string fmt, alias data, AA...)
522 if (state == "got-width")
524 static assert(fmt.length > 0, "invalid format string");
525 static if (fmt[0] == '.') {
526 // got maxlen, parse it
527 enum writefImpl = writefImpl!("parse-int", "maxlen", fmt[1..$], data.fixWidthFill(), AA);
528 } else {
529 enum writefImpl = writefImpl!("got-maxlen", fmt, data.fixWidthFill(), AA);
534 // got maxlen, done with width parsing
535 template writefImpl(string state, string fmt, alias data, AA...)
536 if (state == "got-maxlen")
538 static assert(fmt.length > 0, "invalid format string");
539 enum writefImpl = writefImpl!("format-spec", fmt, data, AA);
543 ////////////////////////////////////////////////////////////////////////////////
544 static template isStaticNarrowString(T) {
545 import std.traits : isStaticArray;
546 static if (isStaticArray!T) {
547 import std.traits : Unqual;
548 static alias ArrayElementType(T: T[]) = Unqual!T;
549 enum isStaticNarrowString = is(ArrayElementType!T == char);
550 } else {
551 enum isStaticNarrowString = false;
555 template writefImpl(string state, alias data, AA...)
556 if (state == "write-argument-s")
558 import std.traits : Unqual;
559 import std.conv : to;
560 static assert(data.aidx >= 0 && data.aidx < data.alen, "argument index out of range");
561 enum aidx = data.aidx;
562 alias aatype = StripTypedef!(AA[aidx]);
563 //pragma(msg, "TYPE: ", Unqual!aatype);
564 static if (is(Unqual!aatype == char[]) ||
565 is(Unqual!aatype == const(char)[]) ||
566 is(aatype == string) ||
567 isStaticNarrowString!aatype) {
568 //pragma(msg, "STRING!");
569 enum callFunc = "wrWriteWidth";
570 enum func = "";
571 } else static if (isIntegral!aatype) {
572 enum callFunc = "wrWriteWidthInt";
573 enum func = "";
574 } else static if (is(aatype == float) || is(aatype == double) || is(aatype == const float) || is(aatype == const double) || is(aatype == immutable float) || is(aatype == immutable double)) {
575 enum callFunc = "wrWriteWidthFloat";
576 enum func = "";
577 } else static if (isBoolean!aatype) {
578 enum callFunc = "wrWriteWidthBool";
579 enum func = "";
580 } else static if (is(Unqual!aatype == char)) {
581 enum callFunc = "wrWriteWidthChar";
582 enum func = "";
583 } else {
584 // this may allocate!
585 enum callFunc = "wrWriteWidth";
586 enum func = "to!string";
588 enum lfchar = data.lfchar;
589 enum rfchar = data.rfchar;
590 enum writefImpl =
591 callFunc~"!("~lfchar.stringof~","~rfchar.stringof~")("~
592 data.fd~","~
593 data.getIntStr!"width"()~","~
594 data.getIntStr!"maxlen"()~","~
595 data.getBoolStr!"optCenter"()~","~
596 func~"(args["~to!string(aidx)~"]));\n";
600 template writefImpl(string state, alias data, AA...)
601 if (state == "write-argument-S")
603 import std.traits : Unqual;
604 import std.conv : to;
605 static assert(data.aidx >= 0 && data.aidx < data.alen, "argument index out of range");
606 enum aidx = data.aidx;
607 alias aatype = StripTypedef!(AA[aidx]);
608 //pragma(msg, "TYPE: ", Unqual!aatype);
609 static if (is(Unqual!aatype == char*) ||
610 is(Unqual!aatype == const(char)*) ||
611 is(Unqual!aatype == immutable(char)*) ||
612 is(Unqual!aatype == const(char*)) ||
613 is(Unqual!aatype == immutable(char*))) {
614 enum lfchar = data.lfchar;
615 enum rfchar = data.rfchar;
616 enum writefImpl =
617 "wrWriteWidthStrZ!("~lfchar.stringof~","~rfchar.stringof~")("~
618 data.fd~","~
619 data.getIntStr!"width"()~","~
620 data.getIntStr!"maxlen"()~","~
621 data.getBoolStr!"optCenter"()~","~
622 "(cast(const char*)args["~to!string(aidx)~"]));\n";
623 } else {
624 enum writefImpl = writefImpl!"write-argument-s"(state, data, AA);
629 template writefImpl(string state, bool upcase, alias data, AA...)
630 if (state == "write-argument-xx")
632 import std.traits : Unqual;
633 import std.conv : to;
634 static assert(data.aidx >= 0 && data.aidx < data.alen, "argument index out of range");
635 enum aidx = data.aidx;
636 private alias TTA = StripTypedef!(AA[aidx]);
637 static assert(isIntegral!TTA || isPointer!TTA, "'x' expects integer or pointer argument");
638 enum lfchar = data.lfchar;
639 enum rfchar = data.rfchar;
640 enum writefImpl =
641 "wrWriteWidthHex!("~lfchar.stringof~","~rfchar.stringof~")("~
642 data.fd~","~
643 data.getIntStr!"width"()~","~
644 data.getIntStr!"maxlen"()~","~
645 data.getBoolStr!"optCenter"()~","~
646 (upcase ? "true," : "false,")~
647 (isPointer!TTA ? "cast(usize)" : "cast("~TTA.stringof~")")~
648 "(args["~to!string(aidx)~"]));\n";
652 template writefImpl(string state, alias data, AA...)
653 if (state == "write-argument-x")
655 enum writefImpl = writefImpl!("write-argument-xx", false, data, AA);
659 template writefImpl(string state, alias data, AA...)
660 if (state == "write-argument-X")
662 enum writefImpl = writefImpl!("write-argument-xx", true, data, AA);
666 template writefImpl(string state, string field, alias data)
667 if (state == "write-field")
669 enum fld = __traits(getMember, data, field);
670 static if (fld.length > 0) {
671 enum writefImpl = "wrwriter("~fld.stringof~", "~data.fd~");\n";
672 } else {
673 enum writefImpl = "";
678 template writefImpl(string state, string str, alias data)
679 if (state == "write-strlit")
681 static if (str.length > 0) {
682 enum writefImpl = "wrwriter("~str.stringof~", "~data.fd~");\n";
683 } else {
684 enum writefImpl = "";
689 ////////////////////////////////////////////////////////////////////////////////
690 template writefImpl(string state, string fmt, alias data, AA...)
691 if (state == "format-spec")
693 static assert(fmt.length > 0, "invalid format string");
694 static if (fmt[0] == 's' || fmt[0] == 'x' || fmt[0] == 'X' || fmt[0] == 'S') {
695 // known specs
696 enum writefImpl =
697 writefImpl!("write-argument-"~fmt[0], data, AA)~
698 writefImpl!("main", fmt[1..$], data.incAIdx(), AA);
699 } else static if (fmt[0] == '|') {
700 // write all unprocessed arguments
701 static if (data.aidx < data.alen) {
702 // has argument to process
703 static if (data.aidx+1 < data.alen && data.wsep.length > 0) {
704 // has separator
705 enum writefImpl =
706 writefImpl!("write-argument-s", data, AA)~
707 writefImpl!("write-field", "wsep", data)~
708 writefImpl!(state, fmt, data.incAIdx(), AA);
709 } else {
710 // has no separator
711 enum writefImpl =
712 writefImpl!("write-argument-s", data, AA)~
713 writefImpl!(state, fmt, data.incAIdx(), AA);
715 } else {
716 // no more arguments
717 enum writefImpl = writefImpl!("main", fmt[1..$], data, AA);
719 } else static if (fmt[0] == '@' || fmt[0] == '$') {
720 // set current argument index
721 // we must have no maxlen here
722 static assert(data.maxlenSign == ' ', "invalid position for '@'");
723 static if (data.widthSign == '+' || data.widthSign == '-')
724 enum newpos = data.aidx+data.getIntDef!"width"()+1;
725 else
726 enum newpos = data.getIntDef!"width"();
727 static assert(newpos >= 1 && newpos <= data.alen+1, "position out of range for '"~fmt[0]~"'");
728 static if (fmt[0] == '@' || (fmt.length > 1 && fmt[1] == '%')) {
729 enum writefImpl = writefImpl!("main", fmt[1..$], data.set!"aidx"(newpos-1), AA);
730 } else {
731 enum writefImpl = writefImpl!("main", "%"~fmt[1..$], data.set!"aidx"(newpos-1), AA);
733 } else {
734 static assert(0, "invalid format specifier: '"~fmt[0]~"'");
739 ////////////////////////////////////////////////////////////////////////////////
740 template writefImpl(string state, string accum, string fmt, alias data, AA...)
741 if (state == "main-with-accum")
743 static if (fmt.length == 0) {
744 static assert(data.aidx == data.alen, "too many arguments to writer");
745 enum writefImpl = writefImpl!("write-strlit", accum, data);
746 } else static if (fmt[0] == '%') {
747 static assert (fmt.length > 1, "invalid format string");
748 static if (fmt[1] == '%') {
749 // '%%'
750 enum writefImpl = writefImpl!(state, accum~"%", fmt[2..$], data, AA);
751 } else static if (fmt[1] == '!') {
752 // '%!'
753 enum writefImpl = writefImpl!(state, accum, fmt[2..$], data.set!"aidx"(data.alen), AA);
754 } else {
755 // other format specifiers
756 enum writefImpl =
757 writefImpl!("write-strlit", accum, data)~
758 writefImpl!("parse-format", fmt, data, AA);
760 } else {
761 import std.string : indexOf;
762 enum ppos = fmt.indexOf('%');
763 static if (ppos < 0) {
764 // no format specifiers
765 enum writefImpl = writefImpl!("write-strlit", accum~fmt, data);
766 } else {
767 enum writefImpl = writefImpl!(state, accum~fmt[0..ppos], fmt[ppos..$], data, AA);
773 ////////////////////////////////////////////////////////////////////////////////
774 template writefImpl(string state, string fmt, alias data, AA...)
775 if (state == "main")
777 enum writefImpl = writefImpl!("main-with-accum", "", fmt, data.resetFmt(), AA);
781 ////////////////////////////////////////////////////////////////////////////////
782 void wrwritef(int fd, string fmt, AA...) (AA args) {
783 import std.string : indexOf;
784 static if (fmt.indexOf('%') < 0) {
785 wrwriter(fmt, fd);
786 } else {
787 import std.conv : to;
788 enum mixstr = writefImpl!("main", fmt, WrData(fd, AA.length), AA);
789 //pragma(msg, "-------\n"~mixstr~"-------");
790 mixin(mixstr);
792 wrwriter(null, fd);
796 ////////////////////////////////////////////////////////////////////////////////
797 public:
799 void fdwritef(int fd, string fmt, A...) (A args) { wrwritef!(fd, fmt)(args); }
800 void fdwrite(int fd, A...) (A args) { wrwritef!(fd, "%|")(args); }
801 void fdwriteln(int fd, A...) (A args) { wrwritef!(fd, "%|\n")(args); }
803 void writef(string fmt, A...) (A args) { wrwritef!(1, fmt)(args); }
804 void errwritef(string fmt, A...) (A args) { wrwritef!(2, fmt)(args); }
806 void writefln(string fmt, A...) (A args) { wrwritef!(1, fmt~"\n")(args); }
807 void errwritefln(string fmt, A...) (A args) { wrwritef!(2, fmt~"\n")(args); }
809 void write(A...) (A args) { wrwritef!(1, "%|")(args); }
810 void errwrite(A...) (A args) { wrwritef!(2, "%|")(args); }
812 void writeln(A...) (A args) { wrwritef!(1, "%|\n")(args); }
813 void errwriteln(A...) (A args) { wrwritef!(2, "%|\n")(args); }
816 ////////////////////////////////////////////////////////////////////////////////
817 version(writer_test)
818 unittest {
819 class A {
820 override string toString () const { return "{A}"; }
823 char[] n = ['x', 'y', 'z'];
824 char[3] t = "def";//['d', 'e', 'f'];
825 wrwriter("========================\n");
826 writef!"`%%`\n"();
827 writef!"`%-3s`\n"(42);
828 writef!"<`%3s`%%{str=%s}%|>\n"(cast(int)42, "[a]", new A(), n, t[]);
829 writefln!"<`%3@%3s`>%!"(cast(int)42, "[a]", new A(), n, t);
830 errwriteln("stderr");
831 writefln!"`%-3s`"(42);
832 writefln!"`%!z%-2@%-3s`%!"(69, 42, 666);
833 writefln!"`%!%1@%-3s%!`"(69, 42, 666);
834 writefln!"`%!%-1@%+0@%-3s%!`"(69, 42, 666);
835 writefln!"`%3.5s`"("a");
836 writefln!"`%7.5s`"("abcdefgh");
837 writef!"%|\n"(42, 666);
838 writefln!"`%/10.5s`"("abcdefgh");
839 writefln!"`%/-10.-5s`"("abcdefgh");
840 writefln!"`%/~+-10.-5s`"("abcdefgh");
841 writefln!"`%/~+~:-10.-5s`"("abcdefgh");
842 writef!"%\0<>\0|\n"(42, 666, 999);
843 writef!"%\0\t\0|\n"(42, 666, 999);
844 writefln!"`%~*05s %~.5s`"(42, 666);
845 writef!"`%s`\n"(t);
846 writef!"`%08s`\n"("alice");
847 writefln!"#%08x"(16396);
848 writefln!"#%08X"(-16396);
849 writefln!"#%02X"(-16385);
850 writefln!"[%06s]"(-666);
851 writefln!"[%06s]"(cast(long)0x8000_0000_0000_0000uL);
852 writefln!"[%06x]"(cast(long)0x8000_0000_0000_0000uL);
854 version(aliced) {
855 enum TypedefTestStr = q{
856 typedef MyInt = int;
857 typedef MyString = string;
859 MyInt mi = 42;
860 MyString ms = cast(MyString)"hurry";
861 writefln!"%s"(mi);
862 writefln!"%x"(mi);
863 writefln!"%s"(ms);
865 void testBool () @nogc {
866 writefln!"%s"(true);
867 writefln!"%s"(false);
869 testBool();
871 writefln!"Hello, %2$s, I'm %1$s."("Alice", "Miriel");
872 writef!"%2$7s|\n%1$%7s|\n%||\n"("Alice", "Miriel");
874 //mixin(TypedefTestStr);
877 void wrflt () nothrow @nogc {
878 writeln(42.666f);
879 writeln(cast(double)42.666);
881 wrflt();
883 immutable char *strz = "stringz\0s";
884 writefln!"[%S]"(strz);
888 ////////////////////////////////////////////////////////////////////////////////
889 mixin template writedump (Names...) {
890 auto _xdump_tmp_ = {
891 import iv.writer : write;
892 foreach (immutable i, immutable name; Names) write(name, " = ", mixin(name), (i < Names.length-1 ? ", " : "\n"));
893 return false;
894 }();
897 version(writer_test)
898 unittest {
899 int x = 5;
900 int y = 3;
901 int z = 15;
903 mixin writedump!("x", "y"); // x = 5, y = 3
904 mixin writedump!("z"); // z = 15
905 mixin writedump!("x+y"); // x+y = 8
906 mixin writedump!("x+y < z"); // x+y < z = true