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
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*/;
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
;
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
);
67 // width == int.min: no width specified
68 // maxlen == int.min: no maxlen specified
69 private void wrWriteWidth(char lfill
=' ', char rfill
=' ')
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
; }();
80 if (maxlen
!= int.min
) {
83 if (maxlen
> s
.length
) {
84 maxlen
= cast(int)s
.length
;
86 stpos
= s
.length
-maxlen
;
88 } else if (maxlen
> 0) {
89 if (maxlen
> s
.length
) maxlen
= cast(int)s
.length
;
92 // no maxlen specified
93 maxlen
= cast(int)s
.length
;
96 if (maxlen
< 0) maxlen
= 666;
98 if (width
== int.min
) {
99 // no width specified, defaults to visible string width
100 width
= cast(int)(s
.length
-stpos
);
102 if (width
< 0) width
= 666;
105 if (center
&& ((width
> 0 && width
> maxlen
) ||
(width
< 0 && -width
> maxlen
))) {
107 int wdt
= (width
> 0 ? width
: -width
)-maxlen
;
108 int spleft
= wdt
/2+(width
> 0 && wdt
%2);
109 int spright
= wdt
-spleft
;
111 if (spleft
> spacesl
.length
) {
112 wrwriter(spacesl
, fd
);
113 spleft
-= spacesl
.length
;
116 wrwriter(spacesl
[0..spleft
], fd
);
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
;
127 wrwriter(spacesr
[0..spright
], fd
);
135 // right padding, write string
137 if (maxlen
> 0) wrwriter(s
[stpos
..stpos
+maxlen
], fd
);
140 if (maxlen
< width
) {
142 if (writeS
&& stpos
== 0 && leftIsMinus
&& width
> 0) {
149 if (width
> spacesl
.length
) {
150 if (writeS
) wrwriter(spacesl
, fd
); else wrwriter(spacesr
, fd
);
151 width
-= spacesl
.length
;
153 if (writeS
) wrwriter(spacesl
[0..width
], fd
); else wrwriter(spacesr
[0..width
], fd
);
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
=' ')
171 bool leftIsMinus
=false) {
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
)
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);
209 hstr
[--pos
] = cast(char)('0'+b
);
211 hstr
[--pos
] = cast(char)('A'+b
-10);
213 hstr
[--pos
] = cast(char)('a'+b
-10);
216 static if (isSigned
!T
) {
221 wrWriteWidth
!(lfill
, rfill
)(fd
, width
, maxlen
, center
, hstr
[pos
..$], (neg && lfill
== '0'));
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
)
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);
258 ubyte b
= cast(ubyte)(num
%10);
260 hstr
[--pos
] = cast(char)('0'+b
);
262 static if (isSigned
!T
) {
267 wrWriteWidth
!(lfill
, rfill
)(fd
, width
, maxlen
, center
, hstr
[pos
..$], (neg && lfill
== '0'));
269 wrWriteWidth
!(lfill
, rfill
)(fd
, width
, maxlen
, center
, hstr
[pos
..$]);
273 private void wrWriteWidthBool(char lfill
=' ', char rfill
=' ', 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
)
292 if (is(Unqual
!T
== char))
295 wrWriteWidth
!(lfill
, rfill
)(fd
, width
, maxlen
, center
, s
);
298 private void wrWriteWidthFloat(char lfill
=' ', char rfill
=' ', T
)
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
) {
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
320 int width
= int.min
; // this means 'not specified'
321 char widthSign
= ' '; // '+', '-', '*' (no sign), ' ' (absent)
322 bool widthZeroStarted
;
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 "%|"
336 this (int afd
, usize aalen
) {
337 import std
.conv
: to
;
339 if (aalen
>= 1024) assert(0, "too many arguments for writer");
340 alen
= cast(int)aalen
;
344 auto set(string name
, T
) (in T value
) if (__traits(hasMember
, this, name
)) {
345 __traits(getMember
, this, name
) = value
;
349 // increment current index
352 if (aidx
> alen
) aidx
= alen
;
356 // prepare for next formatted output (reset all format params)
358 // trick with saving necessary fields
370 auto setFillChar (char ch
) {
371 switch (fillhcharIdx
) {
372 case 0: lfchar
= ch
; break;
373 case 1: rfchar
= ch
; break;
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;
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");
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;
400 immutable nn
= n
*10+ch
-'0';
401 if (nn
< n || nn
== int.max
) assert(0, "integer overflow");
404 immutable nn
= n
*10+ch
-'0';
405 if (nn
< n || nn
== int.max
) assert(0, "integer overflow");
408 __traits(getMember
, this, name
) = n
;
412 //TODO: do more checks on getInt, getBool, etc.
413 auto getInt(string name
) () if (__traits(hasMember
, this, name
)) {
415 immutable n
= __traits(getMember
, this, name
);
416 static if (isSigned
!(typeof(n
))) {
417 return (n
< 0 && n
!= n
.min ? n
+1 : n
);
423 auto getIntDef(string name
) () if (__traits(hasMember
, this, name
)) {
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;
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
) {
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
);
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
);
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
);
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
);
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
);
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);
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";
571 } else static if (isIntegral
!aatype
) {
572 enum callFunc
= "wrWriteWidthInt";
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";
577 } else static if (isBoolean
!aatype
) {
578 enum callFunc
= "wrWriteWidthBool";
580 } else static if (is(Unqual
!aatype
== char)) {
581 enum callFunc
= "wrWriteWidthChar";
584 // this may allocate!
585 enum callFunc
= "wrWriteWidth";
586 enum func
= "to!string";
588 enum lfchar
= data
.lfchar
;
589 enum rfchar
= data
.rfchar
;
591 callFunc
~"!("~lfchar
.stringof
~","~rfchar
.stringof
~")("~
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
;
617 "wrWriteWidthStrZ!("~lfchar
.stringof
~","~rfchar
.stringof
~")("~
619 data
.getIntStr
!"width"()~","~
620 data
.getIntStr
!"maxlen"()~","~
621 data
.getBoolStr
!"optCenter"()~","~
622 "(cast(const char*)args["~to
!string(aidx
)~"]));\n";
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
;
641 "wrWriteWidthHex!("~lfchar
.stringof
~","~rfchar
.stringof
~")("~
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";
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";
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') {
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) {
706 writefImpl
!("write-argument-s", data
, AA
)~
707 writefImpl
!("write-field", "wsep", data
)~
708 writefImpl
!(state
, fmt
, data
.incAIdx(), AA
);
712 writefImpl
!("write-argument-s", data
, AA
)~
713 writefImpl
!(state
, fmt
, data
.incAIdx(), AA
);
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;
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
);
731 enum writefImpl
= writefImpl
!("main", "%"~fmt
[1..$], data
.set
!"aidx"(newpos
-1), AA
);
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] == '%') {
750 enum writefImpl
= writefImpl
!(state
, accum
~"%", fmt
[2..$], data
, AA
);
751 } else static if (fmt
[1] == '!') {
753 enum writefImpl
= writefImpl
!(state
, accum
, fmt
[2..$], data
.set
!"aidx"(data
.alen
), AA
);
755 // other format specifiers
757 writefImpl
!("write-strlit", accum
, data
)~
758 writefImpl
!("parse-format", fmt
, data
, AA
);
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
);
767 enum writefImpl
= writefImpl
!(state
, accum
~fmt
[0..ppos
], fmt
[ppos
..$], data
, AA
);
773 ////////////////////////////////////////////////////////////////////////////////
774 template writefImpl(string state
, string fmt
, alias data
, AA
...)
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) {
787 import std
.conv
: to
;
788 enum mixstr
= writefImpl
!("main", fmt
, WrData(fd
, AA
.length
), AA
);
789 //pragma(msg, "-------\n"~mixstr~"-------");
796 ////////////////////////////////////////////////////////////////////////////////
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 ////////////////////////////////////////////////////////////////////////////////
820 override string
toString () const { return "{A}"; }
823 char[] n
= ['x', 'y', 'z'];
824 char[3] t
= "def";//['d', 'e', 'f'];
825 wrwriter("========================\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);
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);
855 enum TypedefTestStr
= q
{
857 typedef MyString
= string
;
860 MyString ms
= cast(MyString
)"hurry";
865 void testBool () @nogc {
867 writefln
!"%s"(false);
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 {
879 writeln(cast(double)42.666);
883 immutable char *strz
= "stringz\0s";
884 writefln
!"[%S]"(strz
);
888 ////////////////////////////////////////////////////////////////////////////////
889 mixin template writedump (Names
...) {
891 import iv
.writer
: write
;
892 foreach (immutable i
, immutable name
; Names
) write(name
, " = ", mixin(name
), (i
< Names
.length
-1 ?
", " : "\n"));
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