1 // Written in the D programming language.
2 
3 /++
4     Functions which operate on ASCII characters.
5 
6     All of the functions in std.ascii accept Unicode characters but
7     effectively ignore them if they're not ASCII. All `isX` functions return
8     `false` for non-ASCII characters, and all `toX` functions do nothing
9     to non-ASCII characters.
10 
11     For functions which operate on Unicode characters, see
12     $(MREF std, uni).
13 
14 $(SCRIPT inhibitQuickIndex = 1;)
15 $(DIVC quickindex,
16 $(BOOKTABLE,
17 $(TR $(TH Category) $(TH Functions))
18 $(TR $(TD Validation) $(TD
19         $(LREF isAlpha)
20         $(LREF isAlphaNum)
21         $(LREF isASCII)
22         $(LREF isControl)
23         $(LREF isDigit)
24         $(LREF isGraphical)
25         $(LREF isHexDigit)
26         $(LREF isOctalDigit)
27         $(LREF isPrintable)
28         $(LREF isPunctuation)
29         $(LREF isUpper)
30         $(LREF isWhite)
31 ))
32 $(TR $(TD Conversions) $(TD
33         $(LREF toLower)
34         $(LREF toUpper)
35 ))
36 $(TR $(TD Constants) $(TD
37         $(LREF digits)
38         $(LREF fullHexDigits)
39         $(LREF hexDigits)
40         $(LREF letters)
41         $(LREF lowercase)
42         $(LREF lowerHexDigits)
43         $(LREF newline)
44         $(LREF octalDigits)
45         $(LREF uppercase)
46         $(LREF whitespace)
47 ))
48 $(TR $(TD Enums) $(TD
49         $(LREF ControlChar)
50         $(LREF LetterCase)
51 ))
52 ))
53     References:
54         $(LINK2 http://www.digitalmars.com/d/ascii-table.html, ASCII Table),
55         $(HTTP en.wikipedia.org/wiki/Ascii, Wikipedia)
56 
57     License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
58     Authors:   $(HTTP digitalmars.com, Walter Bright) and
59                $(HTTP jmdavisprog.com, Jonathan M Davis)
60     Source:    $(PHOBOSSRC std/ascii.d)
61   +/
62 module std.ascii;
63 
64 immutable fullHexDigits  = "0123456789ABCDEFabcdef";     /// 0 .. 9A .. Fa .. f
65 immutable hexDigits      = fullHexDigits[0 .. 16];         /// 0 .. 9A .. F
66 immutable lowerHexDigits = "0123456789abcdef";           /// 0 .. 9a .. f
67 immutable digits         = hexDigits[0 .. 10];             /// 0 .. 9
68 immutable octalDigits    = digits[0 .. 8];                 /// 0 .. 7
69 immutable letters        = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; /// A .. Za .. z
70 immutable uppercase      = letters[0 .. 26];               /// A .. Z
71 immutable lowercase      = letters[26 .. 52];              /// a .. z
72 immutable whitespace     = " \t\v\r\n\f";                /// ASCII _whitespace
73 
74 /++
75     Letter case specifier.
76   +/
77 enum LetterCase : bool
78 {
79     upper, /// Upper case letters
80     lower  /// Lower case letters
81 }
82 
83 ///
84 @safe unittest
85 {
86     import std.conv : to;
87 
88     assert(42.to!string(16, LetterCase.upper) == "2A");
89     assert(42.to!string(16, LetterCase.lower) == "2a");
90 }
91 
92 ///
93 @safe unittest
94 {
95     import std.digest.hmac : hmac;
96     import std.digest : toHexString;
97     import std.digest.sha : SHA1;
98     import std..string : representation;
99 
100     const sha1HMAC = "A very long phrase".representation
101         .hmac!SHA1("secret".representation)
102         .toHexString!(LetterCase.lower);
103     assert(sha1HMAC == "49f2073c7bf58577e8c9ae59fe8cfd37c9ab94e5");
104 }
105 
106 /++
107     All control characters in the ASCII table ($(HTTPS www.asciitable.com, source)).
108 +/
109 enum ControlChar : char
110 {
111     nul = '\x00', /// Null
112     soh = '\x01', /// Start of heading
113     stx = '\x02', /// Start of text
114     etx = '\x03', /// End of text
115     eot = '\x04', /// End of transmission
116     enq = '\x05', /// Enquiry
117     ack = '\x06', /// Acknowledge
118     bel = '\x07', /// Bell
119     bs  = '\x08', /// Backspace
120     tab = '\x09', /// Horizontal tab
121     lf  = '\x0A', /// NL line feed, new line
122     vt  = '\x0B', /// Vertical tab
123     ff  = '\x0C', /// NP form feed, new page
124     cr  = '\x0D', /// Carriage return
125     so  = '\x0E', /// Shift out
126     si  = '\x0F', /// Shift in
127     dle = '\x10', /// Data link escape
128     dc1 = '\x11', /// Device control 1
129     dc2 = '\x12', /// Device control 2
130     dc3 = '\x13', /// Device control 3
131     dc4 = '\x14', /// Device control 4
132     nak = '\x15', /// Negative acknowledge
133     syn = '\x16', /// Synchronous idle
134     etb = '\x17', /// End of transmission block
135     can = '\x18', /// Cancel
136     em  = '\x19', /// End of medium
137     sub = '\x1A', /// Substitute
138     esc = '\x1B', /// Escape
139     fs  = '\x1C', /// File separator
140     gs  = '\x1D', /// Group separator
141     rs  = '\x1E', /// Record separator
142     us  = '\x1F', /// Unit separator
143     del = '\x7F' /// Delete
144 }
145 
146 ///
147 @safe pure nothrow @nogc unittest
148 {
149     import std.algorithm.comparison, std.algorithm.searching, std.range, std.traits;
150 
151     // Because all ASCII characters fit in char, so do these
152     static assert(ControlChar.ack.sizeof == 1);
153 
154     // All control characters except del are in row starting from 0
155     static assert(EnumMembers!ControlChar.only.until(ControlChar.del).equal(iota(32)));
156 
157     static assert(ControlChar.nul == '\0');
158     static assert(ControlChar.bel == '\a');
159     static assert(ControlChar.bs  == '\b');
160     static assert(ControlChar.ff  == '\f');
161     static assert(ControlChar.lf  == '\n');
162     static assert(ControlChar.cr  == '\r');
163     static assert(ControlChar.tab == '\t');
164     static assert(ControlChar.vt  == '\v');
165 }
166 
167 ///
168 @safe pure nothrow unittest
169 {
170     import std.conv;
171     //Control character table can be used in place of hexcodes.
172     with (ControlChar) assert(text("Phobos", us, "Deimos", us, "Tango", rs) == "Phobos\x1FDeimos\x1FTango\x1E");
173 }
174 
175 /// Newline sequence for this system.
176 version (Windows)
177     immutable newline = "\r\n";
178 else version (Posix)
179     immutable newline = "\n";
180 else
181     static assert(0, "Unsupported OS");
182 
183 
184 /++
185     Params: c = The character to test.
186     Returns: Whether `c` is a letter or a number (0 .. 9, a .. z, A .. Z).
187   +/
188 bool isAlphaNum(dchar c) @safe pure nothrow @nogc
189 {
190     return c <= 'z' && c >= '0' && (c <= '9' || c >= 'a' || (c >= 'A' && c <= 'Z'));
191 }
192 
193 ///
194 @safe pure nothrow @nogc unittest
195 {
196     assert( isAlphaNum('A'));
197     assert( isAlphaNum('1'));
198     assert(!isAlphaNum('#'));
199 
200     // N.B.: does not return true for non-ASCII Unicode alphanumerics:
201     assert(!isAlphaNum('á'));
202 }
203 
204 @safe unittest
205 {
206     import std.range;
207     foreach (c; chain(digits, octalDigits, fullHexDigits, letters, lowercase, uppercase))
208         assert(isAlphaNum(c));
209 
210     foreach (c; whitespace)
211         assert(!isAlphaNum(c));
212 }
213 
214 
215 /++
216     Params: c = The character to test.
217     Returns: Whether `c` is an ASCII letter (A .. Z, a .. z).
218   +/
219 bool isAlpha(dchar c) @safe pure nothrow @nogc
220 {
221     // Optimizer can turn this into a bitmask operation on 64 bit code
222     return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
223 }
224 
225 ///
226 @safe pure nothrow @nogc unittest
227 {
228     assert( isAlpha('A'));
229     assert(!isAlpha('1'));
230     assert(!isAlpha('#'));
231 
232     // N.B.: does not return true for non-ASCII Unicode alphabetic characters:
233     assert(!isAlpha('á'));
234 }
235 
236 @safe unittest
237 {
238     import std.range;
239     foreach (c; chain(letters, lowercase, uppercase))
240         assert(isAlpha(c));
241 
242     foreach (c; chain(digits, octalDigits, whitespace))
243         assert(!isAlpha(c));
244 }
245 
246 
247 /++
248     Params: c = The character to test.
249     Returns: Whether `c` is a lowercase ASCII letter (a .. z).
250   +/
251 bool isLower(dchar c) @safe pure nothrow @nogc
252 {
253     return c >= 'a' && c <= 'z';
254 }
255 
256 ///
257 @safe pure nothrow @nogc unittest
258 {
259     assert( isLower('a'));
260     assert(!isLower('A'));
261     assert(!isLower('#'));
262 
263     // N.B.: does not return true for non-ASCII Unicode lowercase letters
264     assert(!isLower('á'));
265     assert(!isLower('Á'));
266 }
267 
268 @safe unittest
269 {
270     import std.range;
271     foreach (c; lowercase)
272         assert(isLower(c));
273 
274     foreach (c; chain(digits, uppercase, whitespace))
275         assert(!isLower(c));
276 }
277 
278 
279 /++
280     Params: c = The character to test.
281     Returns: Whether `c` is an uppercase ASCII letter (A .. Z).
282   +/
283 bool isUpper(dchar c) @safe pure nothrow @nogc
284 {
285     return c <= 'Z' && 'A' <= c;
286 }
287 
288 ///
289 @safe pure nothrow @nogc unittest
290 {
291     assert( isUpper('A'));
292     assert(!isUpper('a'));
293     assert(!isUpper('#'));
294 
295     // N.B.: does not return true for non-ASCII Unicode uppercase letters
296     assert(!isUpper('á'));
297     assert(!isUpper('Á'));
298 }
299 
300 @safe unittest
301 {
302     import std.range;
303     foreach (c; uppercase)
304         assert(isUpper(c));
305 
306     foreach (c; chain(digits, lowercase, whitespace))
307         assert(!isUpper(c));
308 }
309 
310 
311 /++
312     Params: c = The character to test.
313     Returns: Whether `c` is a digit (0 .. 9).
314   +/
315 bool isDigit(dchar c) @safe pure nothrow @nogc
316 {
317     return '0' <= c && c <= '9';
318 }
319 
320 ///
321 @safe pure nothrow @nogc unittest
322 {
323     assert( isDigit('3'));
324     assert( isDigit('8'));
325     assert(!isDigit('B'));
326     assert(!isDigit('#'));
327 
328     // N.B.: does not return true for non-ASCII Unicode numbers
329     assert(!isDigit('0')); // full-width digit zero (U+FF10)
330     assert(!isDigit('4')); // full-width digit four (U+FF14)
331 }
332 
333 @safe unittest
334 {
335     import std.range;
336     foreach (c; digits)
337         assert(isDigit(c));
338 
339     foreach (c; chain(letters, whitespace))
340         assert(!isDigit(c));
341 }
342 
343 
344 /++
345     Params: c = The character to test.
346     Returns: Whether `c` is a digit in base 8 (0 .. 7).
347   +/
348 bool isOctalDigit(dchar c) @safe pure nothrow @nogc
349 {
350     return c >= '0' && c <= '7';
351 }
352 
353 ///
354 @safe pure nothrow @nogc unittest
355 {
356     assert( isOctalDigit('0'));
357     assert( isOctalDigit('7'));
358     assert(!isOctalDigit('8'));
359     assert(!isOctalDigit('A'));
360     assert(!isOctalDigit('#'));
361 }
362 
363 @safe unittest
364 {
365     import std.range;
366     foreach (c; octalDigits)
367         assert(isOctalDigit(c));
368 
369     foreach (c; chain(letters, ['8', '9'], whitespace))
370         assert(!isOctalDigit(c));
371 }
372 
373 
374 /++
375     Params: c = The character to test.
376     Returns: Whether `c` is a digit in base 16 (0 .. 9, A .. F, a .. f).
377   +/
378 bool isHexDigit(dchar c) @safe pure nothrow @nogc
379 {
380     return c <= 'f' && c >= '0' && (c <= '9' || c >= 'a' || (c >= 'A' && c <= 'F'));
381 }
382 
383 ///
384 @safe pure nothrow @nogc unittest
385 {
386     assert( isHexDigit('0'));
387     assert( isHexDigit('A'));
388     assert( isHexDigit('f')); // lowercase hex digits are accepted
389     assert(!isHexDigit('g'));
390     assert(!isHexDigit('G'));
391     assert(!isHexDigit('#'));
392 }
393 
394 @safe unittest
395 {
396     import std.range;
397     foreach (c; fullHexDigits)
398         assert(isHexDigit(c));
399 
400     foreach (c; chain(lowercase[6 .. $], uppercase[6 .. $], whitespace))
401         assert(!isHexDigit(c));
402 }
403 
404 
405 /++
406     Params: c = The character to test.
407     Returns: Whether or not `c` is a whitespace character. That includes the
408     space, tab, vertical tab, form feed, carriage return, and linefeed
409     characters.
410   +/
411 bool isWhite(dchar c) @safe pure nothrow @nogc
412 {
413     return c == ' ' || (c >= 0x09 && c <= 0x0D);
414 }
415 
416 ///
417 @safe pure nothrow @nogc unittest
418 {
419     assert( isWhite(' '));
420     assert( isWhite('\t'));
421     assert( isWhite('\n'));
422     assert(!isWhite('1'));
423     assert(!isWhite('a'));
424     assert(!isWhite('#'));
425 
426     // N.B.: Does not return true for non-ASCII Unicode whitespace characters.
427     static import std.uni;
428     assert(std.uni.isWhite('\u00A0'));
429     assert(!isWhite('\u00A0')); // std.ascii.isWhite
430 }
431 
432 @safe unittest
433 {
434     import std.range;
435     foreach (c; whitespace)
436         assert(isWhite(c));
437 
438     foreach (c; chain(digits, letters))
439         assert(!isWhite(c));
440 }
441 
442 
443 /++
444     Params: c = The character to test.
445     Returns: Whether `c` is a control character.
446   +/
447 bool isControl(dchar c) @safe pure nothrow @nogc
448 {
449     return c < 0x20 || c == 0x7F;
450 }
451 
452 ///
453 @safe pure nothrow @nogc unittest
454 {
455     assert( isControl('\0'));
456     assert( isControl('\022'));
457     assert( isControl('\n')); // newline is both whitespace and control
458     assert(!isControl(' '));
459     assert(!isControl('1'));
460     assert(!isControl('a'));
461     assert(!isControl('#'));
462 
463     // N.B.: non-ASCII Unicode control characters are not recognized:
464     assert(!isControl('\u0080'));
465     assert(!isControl('\u2028'));
466     assert(!isControl('\u2029'));
467 }
468 
469 @safe unittest
470 {
471     import std.range;
472     foreach (dchar c; 0 .. 32)
473         assert(isControl(c));
474     assert(isControl(127));
475 
476     foreach (c; chain(digits, letters, [' ']))
477         assert(!isControl(c));
478 }
479 
480 
481 /++
482     Params: c = The character to test.
483     Returns: Whether or not `c` is a punctuation character. That includes
484     all ASCII characters which are not control characters, letters, digits, or
485     whitespace.
486   +/
487 bool isPunctuation(dchar c) @safe pure nothrow @nogc
488 {
489     return c <= '~' && c >= '!' && !isAlphaNum(c);
490 }
491 
492 ///
493 @safe pure nothrow @nogc unittest
494 {
495     assert( isPunctuation('.'));
496     assert( isPunctuation(','));
497     assert( isPunctuation(':'));
498     assert( isPunctuation('!'));
499     assert( isPunctuation('#'));
500     assert( isPunctuation('~'));
501     assert( isPunctuation('+'));
502     assert( isPunctuation('_'));
503 
504     assert(!isPunctuation('1'));
505     assert(!isPunctuation('a'));
506     assert(!isPunctuation(' '));
507     assert(!isPunctuation('\n'));
508     assert(!isPunctuation('\0'));
509 
510     // N.B.: Non-ASCII Unicode punctuation characters are not recognized.
511     assert(!isPunctuation('\u2012')); // (U+2012 = en-dash)
512 }
513 
514 @safe unittest
515 {
516     foreach (dchar c; 0 .. 128)
517     {
518         if (isControl(c) || isAlphaNum(c) || c == ' ')
519             assert(!isPunctuation(c));
520         else
521             assert(isPunctuation(c));
522     }
523 }
524 
525 
526 /++
527     Params: c = The character to test.
528     Returns: Whether or not `c` is a printable character other than the
529     space character.
530   +/
531 bool isGraphical(dchar c) @safe pure nothrow @nogc
532 {
533     return '!' <= c && c <= '~';
534 }
535 
536 ///
537 @safe pure nothrow @nogc unittest
538 {
539     assert( isGraphical('1'));
540     assert( isGraphical('a'));
541     assert( isGraphical('#'));
542     assert(!isGraphical(' ')); // whitespace is not graphical
543     assert(!isGraphical('\n'));
544     assert(!isGraphical('\0'));
545 
546     // N.B.: Unicode graphical characters are not regarded as such.
547     assert(!isGraphical('á'));
548 }
549 
550 @safe unittest
551 {
552     foreach (dchar c; 0 .. 128)
553     {
554         if (isControl(c) || c == ' ')
555             assert(!isGraphical(c));
556         else
557             assert(isGraphical(c));
558     }
559 }
560 
561 
562 /++
563     Params: c = The character to test.
564     Returns: Whether or not `c` is a printable character - including the
565     space character.
566   +/
567 bool isPrintable(dchar c) @safe pure nothrow @nogc
568 {
569     return c >= ' ' && c <= '~';
570 }
571 
572 ///
573 @safe pure nothrow @nogc unittest
574 {
575     assert( isPrintable(' '));  // whitespace is printable
576     assert( isPrintable('1'));
577     assert( isPrintable('a'));
578     assert( isPrintable('#'));
579     assert(!isPrintable('\0')); // control characters are not printable
580 
581     // N.B.: Printable non-ASCII Unicode characters are not recognized.
582     assert(!isPrintable('á'));
583 }
584 
585 @safe unittest
586 {
587     foreach (dchar c; 0 .. 128)
588     {
589         if (isControl(c))
590             assert(!isPrintable(c));
591         else
592             assert(isPrintable(c));
593     }
594 }
595 
596 
597 /++
598     Params: c = The character to test.
599     Returns: Whether or not `c` is in the ASCII character set - i.e. in the
600     range 0 .. 0x7F.
601   +/
602 pragma(inline, true)
603 bool isASCII(dchar c) @safe pure nothrow @nogc
604 {
605     return c <= 0x7F;
606 }
607 
608 ///
609 @safe pure nothrow @nogc unittest
610 {
611     assert( isASCII('a'));
612     assert(!isASCII('á'));
613 }
614 
615 @safe unittest
616 {
617     foreach (dchar c; 0 .. 128)
618         assert(isASCII(c));
619 
620     assert(!isASCII(128));
621 }
622 
623 
624 /++
625     Converts an ASCII letter to lowercase.
626 
627     Params: c = A character of any type that implicitly converts to `dchar`.
628     In the case where it's a built-in type, or an enum of a built-in type,
629     `Unqual!(OriginalType!C)` is returned, whereas if it's a user-defined
630     type, `dchar` is returned.
631 
632     Returns: The corresponding lowercase letter, if `c` is an uppercase
633     ASCII character, otherwise `c` itself.
634   +/
635 auto toLower(C)(C c)
636 if (is(C : dchar))
637 {
638     import std.traits : OriginalType;
639 
640     static if (!__traits(isScalar, C))
641         alias R = dchar;
642     else static if (is(immutable OriginalType!C == immutable OC, OC))
643         alias R = OC;
644 
645     return isUpper(c) ? cast(R)(cast(R) c + 'a' - 'A') : cast(R) c;
646 }
647 
648 ///
649 @safe pure nothrow @nogc unittest
650 {
651     assert(toLower('a') == 'a');
652     assert(toLower('A') == 'a');
653     assert(toLower('#') == '#');
654 
655     // N.B.: Non-ASCII Unicode uppercase letters are not converted.
656     assert(toLower('Á') == 'Á');
657 }
658 
659 @safe pure nothrow unittest
660 {
661 
662     import std.meta;
663     static foreach (C; AliasSeq!(char, wchar, dchar, immutable char, ubyte))
664     {
665         foreach (i, c; uppercase)
666             assert(toLower(cast(C) c) == lowercase[i]);
667 
668         foreach (C c; 0 .. 128)
669         {
670             if (c < 'A' || c > 'Z')
671                 assert(toLower(c) == c);
672             else
673                 assert(toLower(c) != c);
674         }
675 
676         foreach (C c; 128 .. C.max)
677             assert(toLower(c) == c);
678 
679         //CTFE
680         static assert(toLower(cast(C)'a') == 'a');
681         static assert(toLower(cast(C)'A') == 'a');
682     }
683 }
684 
685 
686 /++
687     Converts an ASCII letter to uppercase.
688 
689     Params: c = Any type which implicitly converts to `dchar`. In the case
690     where it's a built-in type, or an enum of a built-in type,
691     `Unqual!(OriginalType!C)` is returned, whereas if it's a user-defined
692     type, `dchar` is returned.
693 
694     Returns: The corresponding uppercase letter, if `c` is a lowercase ASCII
695     character, otherwise `c` itself.
696   +/
697 auto toUpper(C)(C c)
698 if (is(C : dchar))
699 {
700     import std.traits : OriginalType;
701 
702     static if (!__traits(isScalar, C))
703         alias R = dchar;
704     else static if (is(immutable OriginalType!C == immutable OC, OC))
705         alias R = OC;
706 
707     return isLower(c) ? cast(R)(cast(R) c - ('a' - 'A')) : cast(R) c;
708 }
709 
710 ///
711 @safe pure nothrow @nogc unittest
712 {
713     assert(toUpper('a') == 'A');
714     assert(toUpper('A') == 'A');
715     assert(toUpper('#') == '#');
716 
717     // N.B.: Non-ASCII Unicode lowercase letters are not converted.
718     assert(toUpper('á') == 'á');
719 }
720 
721 @safe pure nothrow unittest
722 {
723     import std.meta;
724     static foreach (C; AliasSeq!(char, wchar, dchar, immutable char, ubyte))
725     {
726         foreach (i, c; lowercase)
727             assert(toUpper(cast(C) c) == uppercase[i]);
728 
729         foreach (C c; 0 .. 128)
730         {
731             if (c < 'a' || c > 'z')
732                 assert(toUpper(c) == c);
733             else
734                 assert(toUpper(c) != c);
735         }
736 
737         foreach (C c; 128 .. C.max)
738             assert(toUpper(c) == c);
739 
740         //CTFE
741         static assert(toUpper(cast(C)'a') == 'A');
742         static assert(toUpper(cast(C)'A') == 'A');
743     }
744 }
745 
746 
747 @safe unittest //Test both toUpper and toLower with non-builtin
748 {
749     import std.meta;
750     import std.traits;
751 
752     //User Defined [Char|Wchar|Dchar]
753     static struct UDC {  char c; alias c this; }
754     static struct UDW { wchar c; alias c this; }
755     static struct UDD { dchar c; alias c this; }
756     //[Char|Wchar|Dchar] Enum
757     enum CE :  char {a = 'a', A = 'A'}
758     enum WE : wchar {a = 'a', A = 'A'}
759     enum DE : dchar {a = 'a', A = 'A'}
760     //User Defined [Char|Wchar|Dchar] Enum
761     enum UDCE : UDC {a = UDC('a'), A = UDC('A')}
762     enum UDWE : UDW {a = UDW('a'), A = UDW('A')}
763     enum UDDE : UDD {a = UDD('a'), A = UDD('A')}
764 
765     //User defined types with implicit cast to dchar test.
766     static foreach (Char; AliasSeq!(UDC, UDW, UDD))
767     {
768         assert(toLower(Char('a')) == 'a');
769         assert(toLower(Char('A')) == 'a');
770         static assert(toLower(Char('a')) == 'a');
771         static assert(toLower(Char('A')) == 'a');
772         static assert(toUpper(Char('a')) == 'A');
773         static assert(toUpper(Char('A')) == 'A');
774     }
775 
776     //Various enum tests.
777     static foreach (Enum; AliasSeq!(CE, WE, DE, UDCE, UDWE, UDDE))
778     {
779         assert(toLower(Enum.a) == 'a');
780         assert(toLower(Enum.A) == 'a');
781         assert(toUpper(Enum.a) == 'A');
782         assert(toUpper(Enum.A) == 'A');
783         static assert(toLower(Enum.a) == 'a');
784         static assert(toLower(Enum.A) == 'a');
785         static assert(toUpper(Enum.a) == 'A');
786         static assert(toUpper(Enum.A) == 'A');
787     }
788 
789     //Return value type tests for enum of non-UDT. These should be the original type.
790     static foreach (T; AliasSeq!(CE, WE, DE))
791     {{
792         alias C = OriginalType!T;
793         static assert(is(typeof(toLower(T.init)) == C));
794         static assert(is(typeof(toUpper(T.init)) == C));
795     }}
796 
797     //Return value tests for UDT and enum of UDT. These should be dchar
798     static foreach (T; AliasSeq!(UDC, UDW, UDD, UDCE, UDWE, UDDE))
799     {
800         static assert(is(typeof(toLower(T.init)) == dchar));
801         static assert(is(typeof(toUpper(T.init)) == dchar));
802     }
803 }
Suggestion Box / Bug Report