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, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 /* very simple compile-time format writer
19 * understands [+|-]width[.maxlen]
20 * negative width: add spaces to right
21 * negative maxlen: get right part
23 * 's': use to!string to write argument
24 * note that writer can print strings, bools and integrals without allocation
25 * 'S': print asciiz C string
26 * 'x': write integer as hex
27 * 'X': write integer as HEX
28 * '|': write all arguments that's left with "%s"
29 * '@': go to argument with number 'width' (use sign to relative goto)
30 * argument count starts with '1'
31 * '!': skip all arguments that's left, no width allowed
32 * '%': just a percent sign, no width allowed
33 * '$': go to argument with number 'width' (use sign to relative goto), continue parsing
34 * argument count starts with '1'
35 * options (must immediately follow '%'):
36 * '/': center string; negative width means "add extra space (if any) to the right"
37 * '~': fill with the following char instead of space
38 * second '~': right filling char for 'center'
39 * '\0'...'\0': separator string for '%|'
41 module console
is aliced
;
44 private import std
.traits
: isBoolean
, isIntegral
, isPointer
;
45 static if (!is(typeof(usize
))) private alias usize
= size_t
;
46 private alias StripTypedef(T
) = T
;
49 __gshared
void delegate (scope const(char[]), scope int fd
=1) @trusted nothrow @nogc conwriter
;
51 public @property auto ConWriter () @trusted nothrow @nogc { return conwriter
; }
52 public @property auto ConWriter (typeof(conwriter
) cv
) @trusted nothrow @nogc { auto res
= conwriter
; conwriter
= cv
; return res
; }
55 shared static this () {
56 conwriter
= (scope str, scope fd
) @trusted nothrow @nogc {
57 import core
.sys
.posix
.unistd
: STDOUT_FILENO
, STDERR_FILENO
, write
;
59 if (fd
== 0 || fd
== 1) fd
= STDOUT_FILENO
;
60 else if (fd
== 2) fd
= STDERR_FILENO
;
61 if (str.length
> 0) write(fd
, str.ptr
, str.length
);
68 // width == int.min: no width specified
69 // maxlen == int.min: no maxlen specified
70 private void wrWriteWidth(char lfill
=' ', char rfill
=' ')
76 bool leftIsMinus
=false) {
77 static immutable char[64] spacesl
= () { char[64] r
; foreach (immutable p
; 0..64) r
[p
] = lfill
; return r
; }();
78 static immutable char[64] spacesr
= () { char[64] r
; foreach (immutable p
; 0..64) r
[p
] = rfill
; return r
; }();
81 if (maxlen
!= int.min
) {
84 if (maxlen
> s
.length
) {
85 maxlen
= cast(int)s
.length
;
87 stpos
= s
.length
-maxlen
;
89 } else if (maxlen
> 0) {
90 if (maxlen
> s
.length
) maxlen
= cast(int)s
.length
;
93 // no maxlen specified
94 maxlen
= cast(int)s
.length
;
97 if (maxlen
< 0) maxlen
= 666;
99 if (width
== int.min
) {
100 // no width specified, defaults to visible string width
101 width
= cast(int)(s
.length
-stpos
);
103 if (width
< 0) width
= 666;
106 if (center
&& ((width
> 0 && width
> maxlen
) ||
(width
< 0 && -width
> maxlen
))) {
108 int wdt
= (width
> 0 ? width
: -width
)-maxlen
;
109 int spleft
= wdt
/2+(width
> 0 && wdt
%2);
110 int spright
= wdt
-spleft
;
112 if (spleft
> spacesl
.length
) {
113 conwriter(spacesl
, fd
);
114 spleft
-= spacesl
.length
;
117 conwriter(spacesl
[0..spleft
], fd
);
121 if (maxlen
> 0) conwriter(s
[stpos
..stpos
+maxlen
], fd
);
122 while (spright
> 0) {
123 if (spright
> spacesr
.length
) {
124 conwriter(spacesr
, fd
);
125 spright
-= spacesr
.length
;
128 conwriter(spacesr
[0..spright
], fd
);
136 // right padding, write string
138 if (maxlen
> 0) conwriter(s
[stpos
..stpos
+maxlen
], fd
);
141 if (maxlen
< width
) {
143 if (writeS
&& stpos
== 0 && leftIsMinus
&& width
> 0) {
150 if (width
> spacesl
.length
) {
151 if (writeS
) conwriter(spacesl
, fd
); else conwriter(spacesr
, fd
);
152 width
-= spacesl
.length
;
154 if (writeS
) conwriter(spacesl
[0..width
], fd
); else conwriter(spacesr
[0..width
], fd
);
159 if (writeS
&& maxlen
> 0) conwriter(s
[stpos
..stpos
+maxlen
], fd
);
163 // width<0: pad right
164 // width == int.min: no width specified
165 // maxlen == int.min: no maxlen specified
166 private void wrWriteWidthStrZ(char lfill
=' ', char rfill
=' ')
172 bool leftIsMinus
=false) {
174 while (s
[end
]) ++end
;
175 wrWriteWidth
!(lfill
, rfill
)(fd
, width
, maxlen
, center
, s
[0..end
], leftIsMinus
);
179 private void wrWriteWidthHex(char lfill
=' ', char rfill
=' ', T
)
188 import std
.traits
: isSigned
, isMutable
, Unqual
;
189 static if (isMutable
!T
) alias num
= numm
; else Unqual
!T num
= cast(Unqual
!T
)numm
;
190 char[18] hstr
= void;
191 auto pos
= hstr
.length
;
192 static if (isSigned
!T
) {
193 static if (T
.sizeof
== 8) {
194 if (num
== 0x8000_0000_0000_0000uL) { wrWriteWidth
!(lfill
, rfill
)(fd
, width
, maxlen
, center
, "-8000000000000000", (lfill
== '0')); return; }
195 } else static if (T
.sizeof
== 4) {
196 if (num
== 0x8000_0000uL) { wrWriteWidth
!(lfill
, rfill
)(fd
, width
, maxlen
, center
, "-80000000", (lfill
== '0')); return; }
197 } else static if (T
.sizeof
== 2) {
198 if (num
== 0x8000uL
) { wrWriteWidth
!(lfill
, rfill
)(fd
, width
, maxlen
, center
, "-8000", (lfill
== '0')); return; }
199 } else static if (T
.sizeof
== 1) {
200 if (num
== 0x80uL
) { wrWriteWidth
!(lfill
, rfill
)(fd
, width
, maxlen
, center
, "-80", (lfill
== '0')); return; }
202 bool neg = (num
< 0);
210 hstr
[--pos
] = cast(char)('0'+b
);
212 hstr
[--pos
] = cast(char)('A'+b
-10);
214 hstr
[--pos
] = cast(char)('a'+b
-10);
217 static if (isSigned
!T
) {
222 wrWriteWidth
!(lfill
, rfill
)(fd
, width
, maxlen
, center
, hstr
[pos
..$], (neg && lfill
== '0'));
224 wrWriteWidth
!(lfill
, rfill
)(fd
, width
, maxlen
, center
, hstr
[pos
..$]);
229 // 2**64: 18446744073709551616 (20 chars)
230 // 2**64: 0x1_0000_0000_0000_0000
231 // width<0: pad right
232 private void wrWriteWidthInt(char lfill
=' ', char rfill
=' ', T
)
240 import std
.traits
: isSigned
, isMutable
, Unqual
;
241 static if (isMutable
!T
) alias num
= numm
; else Unqual
!T num
= cast(Unqual
!T
)numm
;
243 auto pos
= hstr
.length
;
244 static if (isSigned
!T
) {
245 static if (T
.sizeof
== 8) {
246 if (num
== 0x8000_0000_0000_0000uL) { wrWriteWidth
!(lfill
, rfill
)(fd
, width
, maxlen
, center
, "-9223372036854775808", (lfill
== '0')); return; }
247 } else static if (T
.sizeof
== 4) {
248 if (num
== 0x8000_0000uL) { wrWriteWidth
!(lfill
, rfill
)(fd
, width
, maxlen
, center
, "-2147483648", (lfill
== '0')); return; }
249 } else static if (T
.sizeof
== 2) {
250 if (num
== 0x8000uL
) { wrWriteWidth
!(lfill
, rfill
)(fd
, width
, maxlen
, center
, "-32768", (lfill
== '0')); return; }
251 } else static if (T
.sizeof
== 1) {
252 if (num
== 0x80uL
) { wrWriteWidth
!(lfill
, rfill
)(fd
, width
, maxlen
, center
, "-128", (lfill
== '0')); return; }
254 bool neg = (num
< 0);
259 ubyte b
= cast(ubyte)(num
%10);
261 hstr
[--pos
] = cast(char)('0'+b
);
263 static if (isSigned
!T
) {
268 wrWriteWidth
!(lfill
, rfill
)(fd
, width
, maxlen
, center
, hstr
[pos
..$], (neg && lfill
== '0'));
270 wrWriteWidth
!(lfill
, rfill
)(fd
, width
, maxlen
, center
, hstr
[pos
..$]);
275 private void wrWriteWidthBool(char lfill
=' ', char rfill
=' ', T
)
283 wrWriteWidth
!(lfill
, rfill
)(fd
, width
, maxlen
, center
, (v ?
"true" : "false"));
287 import std
.traits
: Unqual
;
288 private void wrWriteWidthChar(char lfill
=' ', char rfill
=' ', T
)
294 if (is(Unqual
!T
== char))
297 wrWriteWidth
!(lfill
, rfill
)(fd
, width
, maxlen
, center
, s
);
301 ////////////////////////////////////////////////////////////////////////////////
302 private auto WrData (int fd
, int alen
) {
304 string fd
; // just to pass to writers; string, 'cause we will concat it with other strings
305 int aidx
; // current arg index
306 int alen
; // number of args
308 int width
= int.min
; // this means 'not specified'
309 char widthSign
= ' '; // '+', '-', '*' (no sign), ' ' (absent)
310 bool widthZeroStarted
;
312 int maxlen
= int.min
; // this means 'not specified'
313 char maxlenSign
= ' '; // '+', '-', '*' (no sign), ' ' (absent)
314 bool maxlenZeroStarted
;
315 bool maxlenWasDigits
;
316 bool optCenter
; // center string?
317 char lfchar
= ' '; // "left fill"
318 char rfchar
= ' '; // "right fill"
319 int fillhcharIdx
; // 0: next will be lfchar, 1: next will be rfchar; 2: no more fills
320 string wsep
; // separator string for "%|"
324 this (int afd
, usize aalen
) {
325 import std
.conv
: to
;
327 if (aalen
>= 1024) assert(0, "too many arguments for writer");
328 alen
= cast(int)aalen
;
332 auto set(string name
, T
) (in T value
) if (__traits(hasMember
, this, name
)) {
333 __traits(getMember
, this, name
) = value
;
337 // increment current index
340 if (aidx
> alen
) aidx
= alen
;
344 // prepare for next formatted output (reset all format params)
346 // trick with saving necessary fields
358 auto setFillChar (char ch
) {
359 switch (fillhcharIdx
) {
360 case 0: lfchar
= ch
; break;
361 case 1: rfchar
= ch
; break;
368 // prepare to parse integer field
369 auto initInt(string name
) (char sign
) if (__traits(hasMember
, this, name
)) {
370 __traits(getMember
, this, name
) = (sign
== '-' ?
-1 : 0);
371 __traits(getMember
, this, name
~"Sign") = sign
;
372 __traits(getMember
, this, name
~"ZeroStarted") = false;
373 __traits(getMember
, this, name
~"WasDigits") = false;
377 // integer field parsing: process next char
378 auto putIntChar(string name
) (char ch
) if (__traits(hasMember
, this, name
)) {
379 bool wd
= __traits(getMember
, this, name
~"WasDigits");
381 __traits(getMember
, this, name
~"ZeroStarted") = (ch
== '0');
382 __traits(getMember
, this, name
~"WasDigits") = true;
384 int n
= __traits(getMember
, this, name
);
385 if (n
== int.min
) n
= 0;
388 immutable nn
= n
*10+ch
-'0';
389 if (nn
< n || nn
== int.max
) assert(0, "integer overflow");
392 immutable nn
= n
*10+ch
-'0';
393 if (nn
< n || nn
== int.max
) assert(0, "integer overflow");
396 __traits(getMember
, this, name
) = n
;
400 //TODO: do more checks on getInt, getBool, etc.
401 auto getInt(string name
) () if (__traits(hasMember
, this, name
)) {
403 immutable n
= __traits(getMember
, this, name
);
404 static if (isSigned
!(typeof(n
))) {
405 return (n
< 0 && n
!= n
.min ? n
+1 : n
);
411 auto getIntDef(string name
) () if (__traits(hasMember
, this, name
)) {
413 immutable n
= __traits(getMember
, this, name
);
414 static if (isSigned
!(typeof(n
))) {
415 if (n
== n
.min
) return 0;
416 else if (n
< 0) return n
+1;
423 string
getIntStr(string name
) () if (__traits(hasMember
, this, name
)) {
424 import std
.conv
: to
;
425 return to
!string(getInt
!name());
428 string
getBoolStr(string name
) () if (__traits(hasMember
, this, name
)) {
429 return (__traits(getMember
, this, name
) ?
"true" : "false");
432 // set fillchar according to width flags
433 auto fixWidthFill () {
434 if (fillhcharIdx
== 0 && widthZeroStarted
) {
442 return Data(fd
, alen
);
446 ////////////////////////////////////////////////////////////////////////////////
447 // parse (possibly signed) number
448 template conwritefImpl(string state
, string field
, string fmt
, alias data
, AA
...)
449 if (state
== "parse-int")
451 static assert(fmt
.length
> 0, "invalid format string");
452 static if (fmt
[0] == '-' || fmt
[0] == '+') {
453 static assert(fmt
.length
> 1 && fmt
[1] >= '0' && fmt
[1] <= '9', "invalid number for '"~field
~"'");
454 enum conwritefImpl
= conwritefImpl
!("parse-digits", field
, fmt
[1..$], data
.initInt
!field(fmt
[0]), AA
);
455 } else static if (fmt
[0] >= '0' && fmt
[0] <= '9') {
456 enum conwritefImpl
= conwritefImpl
!("parse-digits", field
, fmt
, data
.initInt
!field('*'), AA
);
458 enum conwritefImpl
= conwritefImpl
!("got-"~field
, fmt
, data
.initInt
!field(' '), AA
);
463 // parse integer digits
464 template conwritefImpl(string state
, string field
, string fmt
, alias data
, AA
...)
465 if (state
== "parse-digits")
467 static assert(fmt
.length
> 0, "invalid format string");
468 static if (fmt
[0] >= '0' && fmt
[0] <= '9') {
469 enum conwritefImpl
= conwritefImpl
!(state
, field
, fmt
[1..$], data
.putIntChar
!field(fmt
[0]), AA
);
471 enum conwritefImpl
= conwritefImpl
!("got-"~field
, fmt
, data
, AA
);
476 ////////////////////////////////////////////////////////////////////////////////
477 // got maxlen, done with width parsing
478 template conwritefImpl(string state
, string fmt
, alias data
, AA
...)
479 if (state
== "parse-format")
481 static assert(fmt
.length
> 0, "invalid format string");
482 static assert(fmt
[0] == '%', "internal error");
483 enum conwritefImpl
= conwritefImpl
!("parse-options", fmt
[1..$], data
, AA
);
488 template conwritefImpl(string state
, string fmt
, alias data
, AA
...)
489 if (state
== "parse-options")
491 import std
.string
: indexOf
;
492 static if (fmt
[0] == '/') {
493 enum conwritefImpl
= conwritefImpl
!(state
, fmt
[1..$], data
.set
!"optCenter"(true), AA
);
494 } else static if (fmt
[0] == '~') {
495 static assert(fmt
.length
> 1, "invalid format option: '~'");
496 enum conwritefImpl
= conwritefImpl
!(state
, fmt
[2..$], data
.setFillChar(fmt
[1]), AA
);
497 } else static if (fmt
[0] == '\0') {
498 enum epos
= fmt
.indexOf('\0', 1);
499 static assert(epos
> 0, "unterminated separator option");
500 static assert(fmt
[epos
] == '\0');
501 enum conwritefImpl
= conwritefImpl
!(state
, fmt
[epos
+1..$], data
.set
!"wsep"(fmt
[1..epos
]), AA
);
503 enum conwritefImpl
= conwritefImpl
!("parse-int", "width", fmt
, data
, AA
);
508 // got width, try maxlen
509 template conwritefImpl(string state
, string fmt
, alias data
, AA
...)
510 if (state
== "got-width")
512 static assert(fmt
.length
> 0, "invalid format string");
513 static if (fmt
[0] == '.') {
514 // got maxlen, parse it
515 enum conwritefImpl
= conwritefImpl
!("parse-int", "maxlen", fmt
[1..$], data
.fixWidthFill(), AA
);
517 enum conwritefImpl
= conwritefImpl
!("got-maxlen", fmt
, data
.fixWidthFill(), AA
);
522 // got maxlen, done with width parsing
523 template conwritefImpl(string state
, string fmt
, alias data
, AA
...)
524 if (state
== "got-maxlen")
526 static assert(fmt
.length
> 0, "invalid format string");
527 enum conwritefImpl
= conwritefImpl
!("format-spec", fmt
, data
, AA
);
531 ////////////////////////////////////////////////////////////////////////////////
532 static template isStaticNarrowString(T
) {
533 import std
.traits
: isStaticArray
;
534 static if (isStaticArray
!T
) {
535 import std
.traits
: Unqual
;
536 static alias ArrayElementType(T
: T
[]) = Unqual
!T
;
537 enum isStaticNarrowString
= is(ArrayElementType
!T
== char);
539 enum isStaticNarrowString
= false;
543 template conwritefImpl(string state
, alias data
, AA
...)
544 if (state
== "write-argument-s")
546 import std
.traits
: Unqual
;
547 import std
.conv
: to
;
548 static assert(data
.aidx
>= 0 && data
.aidx
< data
.alen
, "argument index out of range");
549 enum aidx
= data
.aidx
;
550 alias aatype
= StripTypedef
!(AA
[aidx
]);
551 //pragma(msg, "TYPE: ", Unqual!aatype);
552 static if (is(Unqual
!aatype
== char[]) ||
553 is(Unqual
!aatype
== const(char)[]) ||
554 is(aatype
== string
) ||
555 isStaticNarrowString
!aatype
) {
556 //pragma(msg, "STRING!");
557 enum callFunc
= "wrWriteWidth";
559 } else static if (isIntegral
!aatype
) {
560 enum callFunc
= "wrWriteWidthInt";
562 } else static if (isBoolean
!aatype
) {
563 enum callFunc
= "wrWriteWidthBool";
565 } else static if (is(Unqual
!aatype
== char)) {
566 enum callFunc
= "wrWriteWidthChar";
569 // this may allocate!
570 enum callFunc
= "wrWriteWidth";
571 enum func
= "to!string";
573 enum lfchar
= data
.lfchar
;
574 enum rfchar
= data
.rfchar
;
576 callFunc
~"!("~lfchar
.stringof
~","~rfchar
.stringof
~")("~
578 data
.getIntStr
!"width"()~","~
579 data
.getIntStr
!"maxlen"()~","~
580 data
.getBoolStr
!"optCenter"()~","~
581 func
~"(args["~to
!string(aidx
)~"]));\n";
585 template conwritefImpl(string state
, alias data
, AA
...)
586 if (state
== "write-argument-S")
588 import std
.traits
: Unqual
;
589 import std
.conv
: to
;
590 static assert(data
.aidx
>= 0 && data
.aidx
< data
.alen
, "argument index out of range");
591 enum aidx
= data
.aidx
;
592 alias aatype
= StripTypedef
!(AA
[aidx
]);
593 //pragma(msg, "TYPE: ", Unqual!aatype);
594 static if (is(Unqual
!aatype
== char*) ||
595 is(Unqual
!aatype
== const(char)*) ||
596 is(Unqual
!aatype
== immutable(char)*) ||
597 is(Unqual
!aatype
== const(char*)) ||
598 is(Unqual
!aatype
== immutable(char*))) {
599 enum lfchar
= data
.lfchar
;
600 enum rfchar
= data
.rfchar
;
602 "wrWriteWidthStrZ!("~lfchar
.stringof
~","~rfchar
.stringof
~")("~
604 data
.getIntStr
!"width"()~","~
605 data
.getIntStr
!"maxlen"()~","~
606 data
.getBoolStr
!"optCenter"()~","~
607 "(cast(const char*)args["~to
!string(aidx
)~"]));\n";
609 enum conwritefImpl
= conwritefImpl
!"write-argument-s"(state
, data
, AA
);
614 template conwritefImpl(string state
, bool upcase
, alias data
, AA
...)
615 if (state
== "write-argument-xx")
617 import std
.traits
: Unqual
;
618 import std
.conv
: to
;
619 static assert(data
.aidx
>= 0 && data
.aidx
< data
.alen
, "argument index out of range");
620 enum aidx
= data
.aidx
;
621 private alias TTA
= StripTypedef
!(AA
[aidx
]);
622 static assert(isIntegral
!TTA || isPointer
!TTA
, "'x' expects integer or pointer argument");
623 enum lfchar
= data
.lfchar
;
624 enum rfchar
= data
.rfchar
;
626 "wrWriteWidthHex!("~lfchar
.stringof
~","~rfchar
.stringof
~")("~
628 data
.getIntStr
!"width"()~","~
629 data
.getIntStr
!"maxlen"()~","~
630 data
.getBoolStr
!"optCenter"()~","~
631 (upcase ?
"true," : "false,")~
632 (isPointer
!TTA ?
"cast(usize)" : "cast("~TTA
.stringof
~")")~
633 "(args["~to
!string(aidx
)~"]));\n";
637 template conwritefImpl(string state
, alias data
, AA
...)
638 if (state
== "write-argument-x")
640 enum conwritefImpl
= conwritefImpl
!("write-argument-xx", false, data
, AA
);
644 template conwritefImpl(string state
, alias data
, AA
...)
645 if (state
== "write-argument-X")
647 enum conwritefImpl
= conwritefImpl
!("write-argument-xx", true, data
, AA
);
651 template conwritefImpl(string state
, string field
, alias data
)
652 if (state
== "write-field")
654 enum fld = __traits(getMember
, data
, field
);
655 static if (fld.length
> 0) {
656 enum conwritefImpl
= "conwriter("~fld.stringof
~", "~data
.fd
~");\n";
658 enum conwritefImpl
= "";
663 template conwritefImpl(string state
, string
str, alias data
)
664 if (state
== "write-strlit")
666 static if (str.length
> 0) {
667 enum conwritefImpl
= "conwriter("~str.stringof
~", "~data
.fd
~");\n";
669 enum conwritefImpl
= "";
674 ////////////////////////////////////////////////////////////////////////////////
675 template conwritefImpl(string state
, string fmt
, alias data
, AA
...)
676 if (state
== "format-spec")
678 static assert(fmt
.length
> 0, "invalid format string");
679 static if (fmt
[0] == 's' || fmt
[0] == 'x' || fmt
[0] == 'X' || fmt
[0] == 'S') {
682 conwritefImpl
!("write-argument-"~fmt
[0], data
, AA
)~
683 conwritefImpl
!("main", fmt
[1..$], data
.incAIdx(), AA
);
684 } else static if (fmt
[0] == '|') {
685 // write all unprocessed arguments
686 static if (data
.aidx
< data
.alen
) {
687 // has argument to process
688 static if (data
.aidx
+1 < data
.alen
&& data
.wsep
.length
> 0) {
691 conwritefImpl
!("write-argument-s", data
, AA
)~
692 conwritefImpl
!("write-field", "wsep", data
)~
693 conwritefImpl
!(state
, fmt
, data
.incAIdx(), AA
);
697 conwritefImpl
!("write-argument-s", data
, AA
)~
698 conwritefImpl
!(state
, fmt
, data
.incAIdx(), AA
);
702 enum conwritefImpl
= conwritefImpl
!("main", fmt
[1..$], data
, AA
);
704 } else static if (fmt
[0] == '@' || fmt
[0] == '$') {
705 // set current argument index
706 // we must have no maxlen here
707 static assert(data
.maxlenSign
== ' ', "invalid position for '@'");
708 static if (data
.widthSign
== '+' || data
.widthSign
== '-')
709 enum newpos
= data
.aidx
+data
.getIntDef
!"width"()+1;
711 enum newpos
= data
.getIntDef
!"width"();
712 static assert(newpos
>= 1 && newpos
<= data
.alen
+1, "position out of range for '"~fmt
[0]~"'");
713 static if (fmt
[0] == '@' ||
(fmt
.length
> 1 && fmt
[1] == '%')) {
714 enum conwritefImpl
= conwritefImpl
!("main", fmt
[1..$], data
.set
!"aidx"(newpos
-1), AA
);
716 enum conwritefImpl
= conwritefImpl
!("main", "%"~fmt
[1..$], data
.set
!"aidx"(newpos
-1), AA
);
719 static assert(0, "invalid format specifier: '"~fmt
[0]~"'");
724 ////////////////////////////////////////////////////////////////////////////////
725 template conwritefImpl(string state
, string accum
, string fmt
, alias data
, AA
...)
726 if (state
== "main-with-accum")
728 static if (fmt
.length
== 0) {
729 static assert(data
.aidx
== data
.alen
, "too many arguments to writer");
730 enum conwritefImpl
= conwritefImpl
!("write-strlit", accum
, data
);
731 } else static if (fmt
[0] == '%') {
732 static assert (fmt
.length
> 1, "invalid format string");
733 static if (fmt
[1] == '%') {
735 enum conwritefImpl
= conwritefImpl
!(state
, accum
~"%", fmt
[2..$], data
, AA
);
736 } else static if (fmt
[1] == '!') {
738 enum conwritefImpl
= conwritefImpl
!(state
, accum
, fmt
[2..$], data
.set
!"aidx"(data
.alen
), AA
);
740 // other format specifiers
742 conwritefImpl
!("write-strlit", accum
, data
)~
743 conwritefImpl
!("parse-format", fmt
, data
, AA
);
746 import std
.string
: indexOf
;
747 enum ppos
= fmt
.indexOf('%');
748 static if (ppos
< 0) {
749 // no format specifiers
750 enum conwritefImpl
= conwritefImpl
!("write-strlit", accum
~fmt
, data
);
752 enum conwritefImpl
= conwritefImpl
!(state
, accum
~fmt
[0..ppos
], fmt
[ppos
..$], data
, AA
);
758 ////////////////////////////////////////////////////////////////////////////////
759 template conwritefImpl(string state
, string fmt
, alias data
, AA
...)
762 enum conwritefImpl
= conwritefImpl
!("main-with-accum", "", fmt
, data
.resetFmt(), AA
);
766 ////////////////////////////////////////////////////////////////////////////////
767 void fdwritef(int fd
, string fmt
, AA
...) (AA args
) {
768 import std
.string
: indexOf
;
769 static if (fmt
.indexOf('%') < 0) {
772 import std
.conv
: to
;
773 enum mixstr
= conwritefImpl
!("main", fmt
, WrData(fd
, AA
.length
), AA
);
774 //pragma(msg, "-------\n"~mixstr~"-------");
781 ////////////////////////////////////////////////////////////////////////////////
784 //void fdwritef(int fd, string fmt, A...) (A args) { fdwritef!(fd, fmt)(args); }
785 void fdwrite(int fd
, A
...) (A args
) { fdwritef
!(fd
, "%|")(args
); }
786 void fdwriteln(int fd
, A
...) (A args
) { fdwritef
!(fd
, "%|\n")(args
); }
788 void conwritef(string fmt
, A
...) (A args
) { fdwritef
!(1, fmt
)(args
); }
789 void errwritef(string fmt
, A
...) (A args
) { fdwritef
!(2, fmt
)(args
); }
791 void conwritefln(string fmt
, A
...) (A args
) { fdwritef
!(1, fmt
~"\n")(args
); }
792 void errwritefln(string fmt
, A
...) (A args
) { fdwritef
!(2, fmt
~"\n")(args
); }
794 void conwrite(A
...) (A args
) { fdwritef
!(1, "%|")(args
); }
795 void errwrite(A
...) (A args
) { fdwritef
!(2, "%|")(args
); }
797 void conwriteln(A
...) (A args
) { fdwritef
!(1, "%|\n")(args
); }
798 void errwriteln(A
...) (A args
) { fdwritef
!(2, "%|\n")(args
); }
801 ////////////////////////////////////////////////////////////////////////////////
802 version(conwriter_test
)
805 override string
toString () const { return "{A}"; }
808 char[] n
= ['x', 'y', 'z'];
809 char[3] t
= "def";//['d', 'e', 'f'];
810 conwriter("========================\n");
811 conwritef
!"`%%`\n"();
812 conwritef
!"`%-3s`\n"(42);
813 conwritef
!"<`%3s`%%{str=%s}%|>\n"(cast(int)42, "[a]", new A(), n
, t
[]);
814 conwritefln
!"<`%3@%3s`>%!"(cast(int)42, "[a]", new A(), n
, t
);
815 errwriteln("stderr");
816 conwritefln
!"`%-3s`"(42);
817 conwritefln
!"`%!z%-2@%-3s`%!"(69, 42, 666);
818 conwritefln
!"`%!%1@%-3s%!`"(69, 42, 666);
819 conwritefln
!"`%!%-1@%+0@%-3s%!`"(69, 42, 666);
820 conwritefln
!"`%3.5s`"("a");
821 conwritefln
!"`%7.5s`"("abcdefgh");
822 conwritef
!"%|\n"(42, 666);
823 conwritefln
!"`%/10.5s`"("abcdefgh");
824 conwritefln
!"`%/-10.-5s`"("abcdefgh");
825 conwritefln
!"`%/~+-10.-5s`"("abcdefgh");
826 conwritefln
!"`%/~+~:-10.-5s`"("abcdefgh");
827 conwritef
!"%\0<>\0|\n"(42, 666, 999);
828 conwritef
!"%\0\t\0|\n"(42, 666, 999);
829 conwritefln
!"`%~*05s %~.5s`"(42, 666);
830 conwritef
!"`%s`\n"(t
);
831 conwritef
!"`%08s`\n"("alice");
832 conwritefln
!"#%08x"(16396);
833 conwritefln
!"#%08X"(-16396);
834 conwritefln
!"#%02X"(-16385);
835 conwritefln
!"[%06s]"(-666);
836 conwritefln
!"[%06s]"(cast(long)0x8000_0000_0000_0000uL);
837 conwritefln
!"[%06x]"(cast(long)0x8000_0000_0000_0000uL);
840 enum TypedefTestStr
= q
{
842 typedef MyString
= string
;
845 MyString ms
= cast(MyString
)"hurry";
846 conwritefln
!"%s"(mi
);
847 conwritefln
!"%x"(mi
);
848 conwritefln
!"%s"(ms
);
850 void testBool () @nogc {
851 conwritefln
!"%s"(true);
852 conwritefln
!"%s"(false);
856 conwritefln
!"Hello, %2$s, I'm %1$s."("Alice", "Miriel");
857 conwritef
!"%2$7s|\n%1$%7s|\n%||\n"("Alice", "Miriel");
859 //mixin(TypedefTestStr);
862 immutable char *strz
= "stringz\0s";
863 conwritefln
!"[%S]"(strz
);
867 ////////////////////////////////////////////////////////////////////////////////
868 mixin template condump (Names
...) {
870 import console
: conwrite
;
871 foreach (auto i
, auto name
; Names
) conwrite(name
, " = ", mixin(name
), (i
< Names
.length
-1 ?
", " : "\n"));
881 mixin condump
!("x", "y"); // x = 5, y = 3
882 mixin condump
!("z"); // z = 15
883 mixin condump
!("x+y"); // x+y = 8
884 mixin condump
!("x+y < z"); // x+y < z = true