1 /* DooM2D: Midnight on the Firing Line
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 conwrt
is aliced
;
45 private import std
.traits
: isBoolean
, isIntegral
, isPointer
;
46 static if (!is(typeof(usize
))) private alias usize
= size_t
;
47 private alias StripTypedef(T
) = T
;
50 __gshared
void delegate (scope const(char[])) @trusted nothrow @nogc conwriter
;
52 public @property auto ConWriter () @trusted nothrow @nogc { return conwriter
; }
53 public @property auto ConWriter (typeof(conwriter
) cv
) @trusted nothrow @nogc { auto res
= conwriter
; conwriter
= cv
; return res
; }
56 shared static this () {
57 conwriter
= (scope str) @trusted nothrow @nogc {
59 import core
.sys
.posix
.unistd
: STDOUT_FILENO
, write
;
60 write(STDOUT_FILENO
, 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
=' ')
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
) {
113 spleft
-= spacesl
.length
;
116 conwriter(spacesl
[0..spleft
]);
120 if (maxlen
> 0) conwriter(s
[stpos
..stpos
+maxlen
]);
121 while (spright
> 0) {
122 if (spright
> spacesr
.length
) {
124 spright
-= spacesr
.length
;
127 conwriter(spacesr
[0..spright
]);
135 // right padding, write string
137 if (maxlen
> 0) conwriter(s
[stpos
..stpos
+maxlen
]);
140 if (maxlen
< width
) {
142 if (writeS
&& stpos
== 0 && leftIsMinus
&& width
> 0) {
149 if (width
> spacesl
.length
) {
150 if (writeS
) conwriter(spacesl
); else conwriter(spacesr
);
151 width
-= spacesl
.length
;
153 if (writeS
) conwriter(spacesl
[0..width
]); else conwriter(spacesr
[0..width
]);
158 if (writeS
&& maxlen
> 0) conwriter(s
[stpos
..stpos
+maxlen
]);
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
=' ')
170 bool leftIsMinus
=false) {
172 while (s
[end
]) ++end
;
173 wrWriteWidth
!(lfill
, rfill
)(width
, maxlen
, center
, s
[0..end
], leftIsMinus
);
177 private void wrWriteWidthHex(char lfill
=' ', char rfill
=' ', T
)
185 import std
.traits
: isSigned
, isMutable
, Unqual
;
186 static if (isMutable
!T
) alias num
= numm
; else Unqual
!T num
= cast(Unqual
!T
)numm
;
187 char[18] hstr
= void;
188 auto pos
= hstr
.length
;
189 static if (isSigned
!T
) {
190 static if (T
.sizeof
== 8) {
191 if (num
== 0x8000_0000_0000_0000uL) { wrWriteWidth
!(lfill
, rfill
)(width
, maxlen
, center
, "-8000000000000000", (lfill
== '0')); return; }
192 } else static if (T
.sizeof
== 4) {
193 if (num
== 0x8000_0000uL) { wrWriteWidth
!(lfill
, rfill
)(width
, maxlen
, center
, "-80000000", (lfill
== '0')); return; }
194 } else static if (T
.sizeof
== 2) {
195 if (num
== 0x8000uL
) { wrWriteWidth
!(lfill
, rfill
)(width
, maxlen
, center
, "-8000", (lfill
== '0')); return; }
196 } else static if (T
.sizeof
== 1) {
197 if (num
== 0x80uL
) { wrWriteWidth
!(lfill
, rfill
)(width
, maxlen
, center
, "-80", (lfill
== '0')); return; }
199 bool neg = (num
< 0);
207 hstr
[--pos
] = cast(char)('0'+b
);
209 hstr
[--pos
] = cast(char)('A'+b
-10);
211 hstr
[--pos
] = cast(char)('a'+b
-10);
214 static if (isSigned
!T
) {
219 wrWriteWidth
!(lfill
, rfill
)(width
, maxlen
, center
, hstr
[pos
..$], (neg && lfill
== '0'));
221 wrWriteWidth
!(lfill
, rfill
)(width
, maxlen
, center
, hstr
[pos
..$]);
226 // 2**64: 18446744073709551616 (20 chars)
227 // 2**64: 0x1_0000_0000_0000_0000
228 // width<0: pad right
229 private void wrWriteWidthInt(char lfill
=' ', char rfill
=' ', T
)
236 import std
.traits
: isSigned
, isMutable
, Unqual
;
237 static if (isMutable
!T
) alias num
= numm
; else Unqual
!T num
= cast(Unqual
!T
)numm
;
239 auto pos
= hstr
.length
;
240 static if (isSigned
!T
) {
241 static if (T
.sizeof
== 8) {
242 if (num
== 0x8000_0000_0000_0000uL) { wrWriteWidth
!(lfill
, rfill
)(width
, maxlen
, center
, "-9223372036854775808", (lfill
== '0')); return; }
243 } else static if (T
.sizeof
== 4) {
244 if (num
== 0x8000_0000uL) { wrWriteWidth
!(lfill
, rfill
)(width
, maxlen
, center
, "-2147483648", (lfill
== '0')); return; }
245 } else static if (T
.sizeof
== 2) {
246 if (num
== 0x8000uL
) { wrWriteWidth
!(lfill
, rfill
)(width
, maxlen
, center
, "-32768", (lfill
== '0')); return; }
247 } else static if (T
.sizeof
== 1) {
248 if (num
== 0x80uL
) { wrWriteWidth
!(lfill
, rfill
)(width
, maxlen
, center
, "-128", (lfill
== '0')); return; }
250 bool neg = (num
< 0);
255 ubyte b
= cast(ubyte)(num
%10);
257 hstr
[--pos
] = cast(char)('0'+b
);
259 static if (isSigned
!T
) {
264 wrWriteWidth
!(lfill
, rfill
)(width
, maxlen
, center
, hstr
[pos
..$], (neg && lfill
== '0'));
266 wrWriteWidth
!(lfill
, rfill
)(width
, maxlen
, center
, hstr
[pos
..$]);
271 private void wrWriteWidthBool(char lfill
=' ', char rfill
=' ', T
)
278 wrWriteWidth
!(lfill
, rfill
)(width
, maxlen
, center
, (v ?
"true" : "false"));
282 import std
.traits
: Unqual
;
283 private void wrWriteWidthChar(char lfill
=' ', char rfill
=' ', T
)
288 if (is(Unqual
!T
== char))
291 wrWriteWidth
!(lfill
, rfill
)(width
, maxlen
, center
, s
);
295 ////////////////////////////////////////////////////////////////////////////////
296 private auto WrData (int alen
) {
298 int aidx
; // current arg index
299 int alen
; // number of args
301 int width
= int.min
; // this means 'not specified'
302 char widthSign
= ' '; // '+', '-', '*' (no sign), ' ' (absent)
303 bool widthZeroStarted
;
305 int maxlen
= int.min
; // this means 'not specified'
306 char maxlenSign
= ' '; // '+', '-', '*' (no sign), ' ' (absent)
307 bool maxlenZeroStarted
;
308 bool maxlenWasDigits
;
309 bool optCenter
; // center string?
310 char lfchar
= ' '; // "left fill"
311 char rfchar
= ' '; // "right fill"
312 int fillhcharIdx
; // 0: next will be lfchar, 1: next will be rfchar; 2: no more fills
313 string wsep
; // separator string for "%|"
318 if (aalen
>= 1024) assert(0, "too many arguments for writer");
319 alen
= cast(int)aalen
;
323 auto set(string name
, T
) (in T value
) if (__traits(hasMember
, this, name
)) {
324 __traits(getMember
, this, name
) = value
;
328 // increment current index
331 if (aidx
> alen
) aidx
= alen
;
335 // prepare for next formatted output (reset all format params)
337 // trick with saving necessary fields
347 auto setFillChar (char ch
) {
348 switch (fillhcharIdx
) {
349 case 0: lfchar
= ch
; break;
350 case 1: rfchar
= ch
; break;
357 // prepare to parse integer field
358 auto initInt(string name
) (char sign
) if (__traits(hasMember
, this, name
)) {
359 __traits(getMember
, this, name
) = (sign
== '-' ?
-1 : 0);
360 __traits(getMember
, this, name
~"Sign") = sign
;
361 __traits(getMember
, this, name
~"ZeroStarted") = false;
362 __traits(getMember
, this, name
~"WasDigits") = false;
366 // integer field parsing: process next char
367 auto putIntChar(string name
) (char ch
) if (__traits(hasMember
, this, name
)) {
368 bool wd
= __traits(getMember
, this, name
~"WasDigits");
370 __traits(getMember
, this, name
~"ZeroStarted") = (ch
== '0');
371 __traits(getMember
, this, name
~"WasDigits") = true;
373 int n
= __traits(getMember
, this, name
);
374 if (n
== int.min
) n
= 0;
377 immutable nn
= n
*10+ch
-'0';
378 if (nn
< n || nn
== int.max
) assert(0, "integer overflow");
381 immutable nn
= n
*10+ch
-'0';
382 if (nn
< n || nn
== int.max
) assert(0, "integer overflow");
385 __traits(getMember
, this, name
) = n
;
389 //TODO: do more checks on getInt, getBool, etc.
390 auto getInt(string name
) () if (__traits(hasMember
, this, name
)) {
392 immutable n
= __traits(getMember
, this, name
);
393 static if (isSigned
!(typeof(n
))) {
394 return (n
< 0 && n
!= n
.min ? n
+1 : n
);
400 auto getIntDef(string name
) () if (__traits(hasMember
, this, name
)) {
402 immutable n
= __traits(getMember
, this, name
);
403 static if (isSigned
!(typeof(n
))) {
404 if (n
== n
.min
) return 0;
405 else if (n
< 0) return n
+1;
412 string
getIntStr(string name
) () if (__traits(hasMember
, this, name
)) {
413 import std
.conv
: to
;
414 return to
!string(getInt
!name());
417 string
getBoolStr(string name
) () if (__traits(hasMember
, this, name
)) {
418 return (__traits(getMember
, this, name
) ?
"true" : "false");
421 // set fillchar according to width flags
422 auto fixWidthFill () {
423 if (fillhcharIdx
== 0 && widthZeroStarted
) {
435 ////////////////////////////////////////////////////////////////////////////////
436 // parse (possibly signed) number
437 template conwritefImpl(string state
, string field
, string fmt
, alias data
, AA
...)
438 if (state
== "parse-int")
440 static assert(fmt
.length
> 0, "invalid format string");
441 static if (fmt
[0] == '-' || fmt
[0] == '+') {
442 static assert(fmt
.length
> 1 && fmt
[1] >= '0' && fmt
[1] <= '9', "invalid number for '"~field
~"'");
443 enum conwritefImpl
= conwritefImpl
!("parse-digits", field
, fmt
[1..$], data
.initInt
!field(fmt
[0]), AA
);
444 } else static if (fmt
[0] >= '0' && fmt
[0] <= '9') {
445 enum conwritefImpl
= conwritefImpl
!("parse-digits", field
, fmt
, data
.initInt
!field('*'), AA
);
447 enum conwritefImpl
= conwritefImpl
!("got-"~field
, fmt
, data
.initInt
!field(' '), AA
);
452 // parse integer digits
453 template conwritefImpl(string state
, string field
, string fmt
, alias data
, AA
...)
454 if (state
== "parse-digits")
456 static assert(fmt
.length
> 0, "invalid format string");
457 static if (fmt
[0] >= '0' && fmt
[0] <= '9') {
458 enum conwritefImpl
= conwritefImpl
!(state
, field
, fmt
[1..$], data
.putIntChar
!field(fmt
[0]), AA
);
460 enum conwritefImpl
= conwritefImpl
!("got-"~field
, fmt
, data
, AA
);
465 ////////////////////////////////////////////////////////////////////////////////
466 // got maxlen, done with width parsing
467 template conwritefImpl(string state
, string fmt
, alias data
, AA
...)
468 if (state
== "parse-format")
470 static assert(fmt
.length
> 0, "invalid format string");
471 static assert(fmt
[0] == '%', "internal error");
472 enum conwritefImpl
= conwritefImpl
!("parse-options", fmt
[1..$], data
, AA
);
477 template conwritefImpl(string state
, string fmt
, alias data
, AA
...)
478 if (state
== "parse-options")
480 import std
.string
: indexOf
;
481 static if (fmt
[0] == '/') {
482 enum conwritefImpl
= conwritefImpl
!(state
, fmt
[1..$], data
.set
!"optCenter"(true), AA
);
483 } else static if (fmt
[0] == '~') {
484 static assert(fmt
.length
> 1, "invalid format option: '~'");
485 enum conwritefImpl
= conwritefImpl
!(state
, fmt
[2..$], data
.setFillChar(fmt
[1]), AA
);
486 } else static if (fmt
[0] == '\0') {
487 enum epos
= fmt
.indexOf('\0', 1);
488 static assert(epos
> 0, "unterminated separator option");
489 static assert(fmt
[epos
] == '\0');
490 enum conwritefImpl
= conwritefImpl
!(state
, fmt
[epos
+1..$], data
.set
!"wsep"(fmt
[1..epos
]), AA
);
492 enum conwritefImpl
= conwritefImpl
!("parse-int", "width", fmt
, data
, AA
);
497 // got width, try maxlen
498 template conwritefImpl(string state
, string fmt
, alias data
, AA
...)
499 if (state
== "got-width")
501 static assert(fmt
.length
> 0, "invalid format string");
502 static if (fmt
[0] == '.') {
503 // got maxlen, parse it
504 enum conwritefImpl
= conwritefImpl
!("parse-int", "maxlen", fmt
[1..$], data
.fixWidthFill(), AA
);
506 enum conwritefImpl
= conwritefImpl
!("got-maxlen", fmt
, data
.fixWidthFill(), AA
);
511 // got maxlen, done with width parsing
512 template conwritefImpl(string state
, string fmt
, alias data
, AA
...)
513 if (state
== "got-maxlen")
515 static assert(fmt
.length
> 0, "invalid format string");
516 enum conwritefImpl
= conwritefImpl
!("format-spec", fmt
, data
, AA
);
520 ////////////////////////////////////////////////////////////////////////////////
521 static template isStaticNarrowString(T
) {
522 import std
.traits
: isStaticArray
;
523 static if (isStaticArray
!T
) {
524 import std
.traits
: Unqual
;
525 static alias ArrayElementType(T
: T
[]) = Unqual
!T
;
526 enum isStaticNarrowString
= is(ArrayElementType
!T
== char);
528 enum isStaticNarrowString
= false;
532 template conwritefImpl(string state
, alias data
, AA
...)
533 if (state
== "write-argument-s")
535 import std
.traits
: Unqual
;
536 import std
.conv
: to
;
537 static assert(data
.aidx
>= 0 && data
.aidx
< data
.alen
, "argument index out of range");
538 enum aidx
= data
.aidx
;
539 alias aatype
= StripTypedef
!(AA
[aidx
]);
540 //pragma(msg, "TYPE: ", Unqual!aatype);
541 static if (is(Unqual
!aatype
== char[]) ||
542 is(Unqual
!aatype
== const(char)[]) ||
543 is(aatype
== string
) ||
544 isStaticNarrowString
!aatype
) {
545 //pragma(msg, "STRING!");
546 enum callFunc
= "wrWriteWidth";
548 } else static if (isIntegral
!aatype
) {
549 enum callFunc
= "wrWriteWidthInt";
551 } else static if (isBoolean
!aatype
) {
552 enum callFunc
= "wrWriteWidthBool";
554 } else static if (is(Unqual
!aatype
== char)) {
555 enum callFunc
= "wrWriteWidthChar";
558 // this may allocate!
559 enum callFunc
= "wrWriteWidth";
560 enum func
= "to!string";
562 enum lfchar
= data
.lfchar
;
563 enum rfchar
= data
.rfchar
;
565 callFunc
~"!("~lfchar
.stringof
~","~rfchar
.stringof
~")("~
566 data
.getIntStr
!"width"()~","~
567 data
.getIntStr
!"maxlen"()~","~
568 data
.getBoolStr
!"optCenter"()~","~
569 func
~"(args["~to
!string(aidx
)~"]));\n";
573 template conwritefImpl(string state
, alias data
, AA
...)
574 if (state
== "write-argument-S")
576 import std
.traits
: Unqual
;
577 import std
.conv
: to
;
578 static assert(data
.aidx
>= 0 && data
.aidx
< data
.alen
, "argument index out of range");
579 enum aidx
= data
.aidx
;
580 alias aatype
= StripTypedef
!(AA
[aidx
]);
581 //pragma(msg, "TYPE: ", Unqual!aatype);
582 static if (is(Unqual
!aatype
== char*) ||
583 is(Unqual
!aatype
== const(char)*) ||
584 is(Unqual
!aatype
== immutable(char)*) ||
585 is(Unqual
!aatype
== const(char*)) ||
586 is(Unqual
!aatype
== immutable(char*))) {
587 enum lfchar
= data
.lfchar
;
588 enum rfchar
= data
.rfchar
;
590 "wrWriteWidthStrZ!("~lfchar
.stringof
~","~rfchar
.stringof
~")("~
591 data
.getIntStr
!"width"()~","~
592 data
.getIntStr
!"maxlen"()~","~
593 data
.getBoolStr
!"optCenter"()~","~
594 "(cast(const char*)args["~to
!string(aidx
)~"]));\n";
596 enum conwritefImpl
= conwritefImpl
!"write-argument-s"(state
, data
, AA
);
601 template conwritefImpl(string state
, bool upcase
, alias data
, AA
...)
602 if (state
== "write-argument-xx")
604 import std
.traits
: Unqual
;
605 import std
.conv
: to
;
606 static assert(data
.aidx
>= 0 && data
.aidx
< data
.alen
, "argument index out of range");
607 enum aidx
= data
.aidx
;
608 private alias TTA
= StripTypedef
!(AA
[aidx
]);
609 static assert(isIntegral
!TTA || isPointer
!TTA
, "'x' expects integer or pointer argument");
610 enum lfchar
= data
.lfchar
;
611 enum rfchar
= data
.rfchar
;
613 "wrWriteWidthHex!("~lfchar
.stringof
~","~rfchar
.stringof
~")("~
614 data
.getIntStr
!"width"()~","~
615 data
.getIntStr
!"maxlen"()~","~
616 data
.getBoolStr
!"optCenter"()~","~
617 (upcase ?
"true," : "false,")~
618 (isPointer
!TTA ?
"cast(usize)" : "cast("~TTA
.stringof
~")")~
619 "(args["~to
!string(aidx
)~"]));\n";
623 template conwritefImpl(string state
, alias data
, AA
...)
624 if (state
== "write-argument-x")
626 enum conwritefImpl
= conwritefImpl
!("write-argument-xx", false, data
, AA
);
630 template conwritefImpl(string state
, alias data
, AA
...)
631 if (state
== "write-argument-X")
633 enum conwritefImpl
= conwritefImpl
!("write-argument-xx", true, data
, AA
);
637 template conwritefImpl(string state
, string field
, alias data
)
638 if (state
== "write-field")
640 enum fld = __traits(getMember
, data
, field
);
641 static if (fld.length
> 0) {
642 enum conwritefImpl
= "conwriter("~fld.stringof
~");\n";
644 enum conwritefImpl
= "";
649 template conwritefImpl(string state
, string
str, alias data
)
650 if (state
== "write-strlit")
652 static if (str.length
> 0) {
653 enum conwritefImpl
= "conwriter("~str.stringof
~");\n";
655 enum conwritefImpl
= "";
660 ////////////////////////////////////////////////////////////////////////////////
661 template conwritefImpl(string state
, string fmt
, alias data
, AA
...)
662 if (state
== "format-spec")
664 static assert(fmt
.length
> 0, "invalid format string");
665 static if (fmt
[0] == 's' || fmt
[0] == 'x' || fmt
[0] == 'X' || fmt
[0] == 'S') {
668 conwritefImpl
!("write-argument-"~fmt
[0], data
, AA
)~
669 conwritefImpl
!("main", fmt
[1..$], data
.incAIdx(), AA
);
670 } else static if (fmt
[0] == '|') {
671 // write all unprocessed arguments
672 static if (data
.aidx
< data
.alen
) {
673 // has argument to process
674 static if (data
.aidx
+1 < data
.alen
&& data
.wsep
.length
> 0) {
677 conwritefImpl
!("write-argument-s", data
, AA
)~
678 conwritefImpl
!("write-field", "wsep", data
)~
679 conwritefImpl
!(state
, fmt
, data
.incAIdx(), AA
);
683 conwritefImpl
!("write-argument-s", data
, AA
)~
684 conwritefImpl
!(state
, fmt
, data
.incAIdx(), AA
);
688 enum conwritefImpl
= conwritefImpl
!("main", fmt
[1..$], data
, AA
);
690 } else static if (fmt
[0] == '@' || fmt
[0] == '$') {
691 // set current argument index
692 // we must have no maxlen here
693 static assert(data
.maxlenSign
== ' ', "invalid position for '@'");
694 static if (data
.widthSign
== '+' || data
.widthSign
== '-')
695 enum newpos
= data
.aidx
+data
.getIntDef
!"width"()+1;
697 enum newpos
= data
.getIntDef
!"width"();
698 static assert(newpos
>= 1 && newpos
<= data
.alen
+1, "position out of range for '"~fmt
[0]~"'");
699 static if (fmt
[0] == '@' ||
(fmt
.length
> 1 && fmt
[1] == '%')) {
700 enum conwritefImpl
= conwritefImpl
!("main", fmt
[1..$], data
.set
!"aidx"(newpos
-1), AA
);
702 enum conwritefImpl
= conwritefImpl
!("main", "%"~fmt
[1..$], data
.set
!"aidx"(newpos
-1), AA
);
705 static assert(0, "invalid format specifier: '"~fmt
[0]~"'");
710 ////////////////////////////////////////////////////////////////////////////////
711 template conwritefImpl(string state
, string accum
, string fmt
, alias data
, AA
...)
712 if (state
== "main-with-accum")
714 static if (fmt
.length
== 0) {
715 static assert(data
.aidx
== data
.alen
, "too many arguments to writer");
716 enum conwritefImpl
= conwritefImpl
!("write-strlit", accum
, data
);
717 } else static if (fmt
[0] == '%') {
718 static assert (fmt
.length
> 1, "invalid format string");
719 static if (fmt
[1] == '%') {
721 enum conwritefImpl
= conwritefImpl
!(state
, accum
~"%", fmt
[2..$], data
, AA
);
722 } else static if (fmt
[1] == '!') {
724 enum conwritefImpl
= conwritefImpl
!(state
, accum
, fmt
[2..$], data
.set
!"aidx"(data
.alen
), AA
);
726 // other format specifiers
728 conwritefImpl
!("write-strlit", accum
, data
)~
729 conwritefImpl
!("parse-format", fmt
, data
, AA
);
732 import std
.string
: indexOf
;
733 enum ppos
= fmt
.indexOf('%');
734 static if (ppos
< 0) {
735 // no format specifiers
736 enum conwritefImpl
= conwritefImpl
!("write-strlit", accum
~fmt
, data
);
738 enum conwritefImpl
= conwritefImpl
!(state
, accum
~fmt
[0..ppos
], fmt
[ppos
..$], data
, AA
);
744 ////////////////////////////////////////////////////////////////////////////////
745 template conwritefImpl(string state
, string fmt
, alias data
, AA
...)
748 enum conwritefImpl
= conwritefImpl
!("main-with-accum", "", fmt
, data
.resetFmt(), AA
);
752 ////////////////////////////////////////////////////////////////////////////////
753 void fdwritef(string fmt
, AA
...) (AA args
) {
754 import std
.string
: indexOf
;
755 static if (fmt
.indexOf('%') < 0) {
758 import std
.conv
: to
;
759 enum mixstr
= conwritefImpl
!("main", fmt
, WrData(AA
.length
), AA
);
760 //pragma(msg, "-------\n"~mixstr~"-------");
767 ////////////////////////////////////////////////////////////////////////////////
770 void conwritef(string fmt
, A
...) (A args
) { fdwritef
!(fmt
)(args
); }
771 void conwritefln(string fmt
, A
...) (A args
) { fdwritef
!(fmt
~"\n")(args
); }
772 void conwrite(A
...) (A args
) { fdwritef
!("%|")(args
); }
773 void conwriteln(A
...) (A args
) { fdwritef
!("%|\n")(args
); }
776 ////////////////////////////////////////////////////////////////////////////////
777 version(conwriter_test
)
780 override string
toString () const { return "{A}"; }
783 char[] n
= ['x', 'y', 'z'];
784 char[3] t
= "def";//['d', 'e', 'f'];
785 conwriter("========================\n");
786 conwritef
!"`%%`\n"();
787 conwritef
!"`%-3s`\n"(42);
788 conwritef
!"<`%3s`%%{str=%s}%|>\n"(cast(int)42, "[a]", new A(), n
, t
[]);
789 conwritefln
!"<`%3@%3s`>%!"(cast(int)42, "[a]", new A(), n
, t
);
790 //errwriteln("stderr");
791 conwritefln
!"`%-3s`"(42);
792 conwritefln
!"`%!z%-2@%-3s`%!"(69, 42, 666);
793 conwritefln
!"`%!%1@%-3s%!`"(69, 42, 666);
794 conwritefln
!"`%!%-1@%+0@%-3s%!`"(69, 42, 666);
795 conwritefln
!"`%3.5s`"("a");
796 conwritefln
!"`%7.5s`"("abcdefgh");
797 conwritef
!"%|\n"(42, 666);
798 conwritefln
!"`%/10.5s`"("abcdefgh");
799 conwritefln
!"`%/-10.-5s`"("abcdefgh");
800 conwritefln
!"`%/~+-10.-5s`"("abcdefgh");
801 conwritefln
!"`%/~+~:-10.-5s`"("abcdefgh");
802 conwritef
!"%\0<>\0|\n"(42, 666, 999);
803 conwritef
!"%\0\t\0|\n"(42, 666, 999);
804 conwritefln
!"`%~*05s %~.5s`"(42, 666);
805 conwritef
!"`%s`\n"(t
);
806 conwritef
!"`%08s`\n"("alice");
807 conwritefln
!"#%08x"(16396);
808 conwritefln
!"#%08X"(-16396);
809 conwritefln
!"#%02X"(-16385);
810 conwritefln
!"[%06s]"(-666);
811 conwritefln
!"[%06s]"(cast(long)0x8000_0000_0000_0000uL);
812 conwritefln
!"[%06x]"(cast(long)0x8000_0000_0000_0000uL);
815 enum TypedefTestStr
= q
{
817 typedef MyString
= string
;
820 MyString ms
= cast(MyString
)"hurry";
821 conwritefln
!"%s"(mi
);
822 conwritefln
!"%x"(mi
);
823 conwritefln
!"%s"(ms
);
825 void testBool () @nogc {
826 conwritefln
!"%s"(true);
827 conwritefln
!"%s"(false);
831 conwritefln
!"Hello, %2$s, I'm %1$s."("Alice", "Miriel");
832 conwritef
!"%2$7s|\n%1$%7s|\n%||\n"("Alice", "Miriel");
834 //mixin(TypedefTestStr);
837 immutable char *strz
= "stringz\0s";
838 conwritefln
!"[%S]"(strz
);
842 ////////////////////////////////////////////////////////////////////////////////
843 mixin template condump (Names
...) {
845 import conwrt
: conwrite
;
846 foreach (auto i
, auto name
; Names
) conwrite(name
, " = ", mixin(name
), (i
< Names
.length
-1 ?
", " : "\n"));
851 version(conwriter_test
)
857 mixin condump
!("x", "y"); // x = 5, y = 3
858 mixin condump
!("z"); // z = 15
859 mixin condump
!("x+y"); // x+y = 8
860 mixin condump
!("x+y < z"); // x+y < z = true