The OpenD Programming Language

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 version (LDC) // WebAssembly etc.
181     immutable newline = "\n";
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     const hc = c | 0x20;
191     return ('0' <= c && c <= '9') || ('a' <= hc && hc <= 'z');
192 }
193 
194 ///
195 @safe pure nothrow @nogc unittest
196 {
197     assert( isAlphaNum('A'));
198     assert( isAlphaNum('1'));
199     assert(!isAlphaNum('#'));
200 
201     // N.B.: does not return true for non-ASCII Unicode alphanumerics:
202     assert(!isAlphaNum('á'));
203 }
204 
205 @safe unittest
206 {
207     import std.range;
208     foreach (c; chain(digits, octalDigits, fullHexDigits, letters, lowercase, uppercase))
209         assert(isAlphaNum(c));
210 
211     foreach (c; whitespace)
212         assert(!isAlphaNum(c));
213 }
214 
215 
216 /++
217     Params: c = The character to test.
218     Returns: Whether `c` is an ASCII letter (A .. Z, a .. z).
219   +/
220 bool isAlpha(dchar c) @safe pure nothrow @nogc
221 {
222     // Optimizer can turn this into a bitmask operation on 64 bit code
223     return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
224 }
225 
226 ///
227 @safe pure nothrow @nogc unittest
228 {
229     assert( isAlpha('A'));
230     assert(!isAlpha('1'));
231     assert(!isAlpha('#'));
232 
233     // N.B.: does not return true for non-ASCII Unicode alphabetic characters:
234     assert(!isAlpha('á'));
235 }
236 
237 @safe unittest
238 {
239     import std.range;
240     foreach (c; chain(letters, lowercase, uppercase))
241         assert(isAlpha(c));
242 
243     foreach (c; chain(digits, octalDigits, whitespace))
244         assert(!isAlpha(c));
245 }
246 
247 
248 /++
249     Params: c = The character to test.
250     Returns: Whether `c` is a lowercase ASCII letter (a .. z).
251   +/
252 bool isLower(dchar c) @safe pure nothrow @nogc
253 {
254     return c >= 'a' && c <= 'z';
255 }
256 
257 ///
258 @safe pure nothrow @nogc unittest
259 {
260     assert( isLower('a'));
261     assert(!isLower('A'));
262     assert(!isLower('#'));
263 
264     // N.B.: does not return true for non-ASCII Unicode lowercase letters
265     assert(!isLower('á'));
266     assert(!isLower('Á'));
267 }
268 
269 @safe unittest
270 {
271     import std.range;
272     foreach (c; lowercase)
273         assert(isLower(c));
274 
275     foreach (c; chain(digits, uppercase, whitespace))
276         assert(!isLower(c));
277 }
278 
279 
280 /++
281     Params: c = The character to test.
282     Returns: Whether `c` is an uppercase ASCII letter (A .. Z).
283   +/
284 bool isUpper(dchar c) @safe pure nothrow @nogc
285 {
286     return c <= 'Z' && 'A' <= c;
287 }
288 
289 ///
290 @safe pure nothrow @nogc unittest
291 {
292     assert( isUpper('A'));
293     assert(!isUpper('a'));
294     assert(!isUpper('#'));
295 
296     // N.B.: does not return true for non-ASCII Unicode uppercase letters
297     assert(!isUpper('á'));
298     assert(!isUpper('Á'));
299 }
300 
301 @safe unittest
302 {
303     import std.range;
304     foreach (c; uppercase)
305         assert(isUpper(c));
306 
307     foreach (c; chain(digits, lowercase, whitespace))
308         assert(!isUpper(c));
309 }
310 
311 
312 /++
313     Params: c = The character to test.
314     Returns: Whether `c` is a digit (0 .. 9).
315   +/
316 bool isDigit(dchar c) @safe pure nothrow @nogc
317 {
318     return '0' <= c && c <= '9';
319 }
320 
321 ///
322 @safe pure nothrow @nogc unittest
323 {
324     assert( isDigit('3'));
325     assert( isDigit('8'));
326     assert(!isDigit('B'));
327     assert(!isDigit('#'));
328 
329     // N.B.: does not return true for non-ASCII Unicode numbers
330     assert(!isDigit('0')); // full-width digit zero (U+FF10)
331     assert(!isDigit('4')); // full-width digit four (U+FF14)
332 }
333 
334 @safe unittest
335 {
336     import std.range;
337     foreach (c; digits)
338         assert(isDigit(c));
339 
340     foreach (c; chain(letters, whitespace))
341         assert(!isDigit(c));
342 }
343 
344 
345 /++
346     Params: c = The character to test.
347     Returns: Whether `c` is a digit in base 8 (0 .. 7).
348   +/
349 bool isOctalDigit(dchar c) @safe pure nothrow @nogc
350 {
351     return c >= '0' && c <= '7';
352 }
353 
354 ///
355 @safe pure nothrow @nogc unittest
356 {
357     assert( isOctalDigit('0'));
358     assert( isOctalDigit('7'));
359     assert(!isOctalDigit('8'));
360     assert(!isOctalDigit('A'));
361     assert(!isOctalDigit('#'));
362 }
363 
364 @safe unittest
365 {
366     import std.range;
367     foreach (c; octalDigits)
368         assert(isOctalDigit(c));
369 
370     foreach (c; chain(letters, ['8', '9'], whitespace))
371         assert(!isOctalDigit(c));
372 }
373 
374 
375 /++
376     Params: c = The character to test.
377     Returns: Whether `c` is a digit in base 16 (0 .. 9, A .. F, a .. f).
378   +/
379 bool isHexDigit(dchar c) @safe pure nothrow @nogc
380 {
381     const hc = c | 0x20;
382     return ('0' <= c && c <= '9') || ('a' <= hc && hc <= 'f');
383 }
384 
385 ///
386 @safe pure nothrow @nogc unittest
387 {
388     assert( isHexDigit('0'));
389     assert( isHexDigit('A'));
390     assert( isHexDigit('f')); // lowercase hex digits are accepted
391     assert(!isHexDigit('g'));
392     assert(!isHexDigit('G'));
393     assert(!isHexDigit('#'));
394 }
395 
396 @safe unittest
397 {
398     import std.range;
399     foreach (c; fullHexDigits)
400         assert(isHexDigit(c));
401 
402     foreach (c; chain(lowercase[6 .. $], uppercase[6 .. $], whitespace))
403         assert(!isHexDigit(c));
404 }
405 
406 
407 /++
408     Params: c = The character to test.
409     Returns: Whether or not `c` is a whitespace character. That includes the
410     space, tab, vertical tab, form feed, carriage return, and linefeed
411     characters.
412   +/
413 bool isWhite(dchar c) @safe pure nothrow @nogc
414 {
415     return c == ' ' || (c >= 0x09 && c <= 0x0D);
416 }
417 
418 ///
419 @safe pure nothrow @nogc unittest
420 {
421     assert( isWhite(' '));
422     assert( isWhite('\t'));
423     assert( isWhite('\n'));
424     assert(!isWhite('1'));
425     assert(!isWhite('a'));
426     assert(!isWhite('#'));
427 
428     // N.B.: Does not return true for non-ASCII Unicode whitespace characters.
429     static import std.uni;
430     assert(std.uni.isWhite('\u00A0'));
431     assert(!isWhite('\u00A0')); // std.ascii.isWhite
432 }
433 
434 @safe unittest
435 {
436     import std.range;
437     foreach (c; whitespace)
438         assert(isWhite(c));
439 
440     foreach (c; chain(digits, letters))
441         assert(!isWhite(c));
442 }
443 
444 
445 /++
446     Params: c = The character to test.
447     Returns: Whether `c` is a control character.
448   +/
449 bool isControl(dchar c) @safe pure nothrow @nogc
450 {
451     return c < 0x20 || c == 0x7F;
452 }
453 
454 ///
455 @safe pure nothrow @nogc unittest
456 {
457     assert( isControl('\0'));
458     assert( isControl('\022'));
459     assert( isControl('\n')); // newline is both whitespace and control
460     assert(!isControl(' '));
461     assert(!isControl('1'));
462     assert(!isControl('a'));
463     assert(!isControl('#'));
464 
465     // N.B.: non-ASCII Unicode control characters are not recognized:
466     assert(!isControl('\u0080'));
467     assert(!isControl('\u2028'));
468     assert(!isControl('\u2029'));
469 }
470 
471 @safe unittest
472 {
473     import std.range;
474     foreach (dchar c; 0 .. 32)
475         assert(isControl(c));
476     assert(isControl(127));
477 
478     foreach (c; chain(digits, letters, [' ']))
479         assert(!isControl(c));
480 }
481 
482 
483 /++
484     Params: c = The character to test.
485     Returns: Whether or not `c` is a punctuation character. That includes
486     all ASCII characters which are not control characters, letters, digits, or
487     whitespace.
488   +/
489 bool isPunctuation(dchar c) @safe pure nothrow @nogc
490 {
491     return c <= '~' && c >= '!' && !isAlphaNum(c);
492 }
493 
494 ///
495 @safe pure nothrow @nogc unittest
496 {
497     assert( isPunctuation('.'));
498     assert( isPunctuation(','));
499     assert( isPunctuation(':'));
500     assert( isPunctuation('!'));
501     assert( isPunctuation('#'));
502     assert( isPunctuation('~'));
503     assert( isPunctuation('+'));
504     assert( isPunctuation('_'));
505 
506     assert(!isPunctuation('1'));
507     assert(!isPunctuation('a'));
508     assert(!isPunctuation(' '));
509     assert(!isPunctuation('\n'));
510     assert(!isPunctuation('\0'));
511 
512     // N.B.: Non-ASCII Unicode punctuation characters are not recognized.
513     assert(!isPunctuation('\u2012')); // (U+2012 = en-dash)
514 }
515 
516 @safe unittest
517 {
518     foreach (dchar c; 0 .. 128)
519     {
520         if (isControl(c) || isAlphaNum(c) || c == ' ')
521             assert(!isPunctuation(c));
522         else
523             assert(isPunctuation(c));
524     }
525 }
526 
527 
528 /++
529     Params: c = The character to test.
530     Returns: Whether or not `c` is a printable character other than the
531     space character.
532   +/
533 bool isGraphical(dchar c) @safe pure nothrow @nogc
534 {
535     return '!' <= c && c <= '~';
536 }
537 
538 ///
539 @safe pure nothrow @nogc unittest
540 {
541     assert( isGraphical('1'));
542     assert( isGraphical('a'));
543     assert( isGraphical('#'));
544     assert(!isGraphical(' ')); // whitespace is not graphical
545     assert(!isGraphical('\n'));
546     assert(!isGraphical('\0'));
547 
548     // N.B.: Unicode graphical characters are not regarded as such.
549     assert(!isGraphical('á'));
550 }
551 
552 @safe unittest
553 {
554     foreach (dchar c; 0 .. 128)
555     {
556         if (isControl(c) || c == ' ')
557             assert(!isGraphical(c));
558         else
559             assert(isGraphical(c));
560     }
561 }
562 
563 
564 /++
565     Params: c = The character to test.
566     Returns: Whether or not `c` is a printable character - including the
567     space character.
568   +/
569 bool isPrintable(dchar c) @safe pure nothrow @nogc
570 {
571     return c >= ' ' && c <= '~';
572 }
573 
574 ///
575 @safe pure nothrow @nogc unittest
576 {
577     assert( isPrintable(' '));  // whitespace is printable
578     assert( isPrintable('1'));
579     assert( isPrintable('a'));
580     assert( isPrintable('#'));
581     assert(!isPrintable('\0')); // control characters are not printable
582 
583     // N.B.: Printable non-ASCII Unicode characters are not recognized.
584     assert(!isPrintable('á'));
585 }
586 
587 @safe unittest
588 {
589     foreach (dchar c; 0 .. 128)
590     {
591         if (isControl(c))
592             assert(!isPrintable(c));
593         else
594             assert(isPrintable(c));
595     }
596 }
597 
598 
599 /++
600     Params: c = The character to test.
601     Returns: Whether or not `c` is in the ASCII character set - i.e. in the
602     range 0 .. 0x7F.
603   +/
604 pragma(inline, true)
605 bool isASCII(dchar c) @safe pure nothrow @nogc
606 {
607     return c <= 0x7F;
608 }
609 
610 ///
611 @safe pure nothrow @nogc unittest
612 {
613     assert( isASCII('a'));
614     assert(!isASCII('á'));
615 }
616 
617 @safe unittest
618 {
619     foreach (dchar c; 0 .. 128)
620         assert(isASCII(c));
621 
622     assert(!isASCII(128));
623 }
624 
625 
626 /++
627     Converts an ASCII letter to lowercase.
628 
629     Params: c = A character of any type that implicitly converts to `dchar`.
630     In the case where it's a built-in type, or an enum of a built-in type,
631     `Unqual!(OriginalType!C)` is returned, whereas if it's a user-defined
632     type, `dchar` is returned.
633 
634     Returns: The corresponding lowercase letter, if `c` is an uppercase
635     ASCII character, otherwise `c` itself.
636   +/
637 auto toLower(C)(C c)
638 if (is(C : dchar))
639 {
640     import std.traits : OriginalType;
641 
642     static if (!__traits(isScalar, C))
643         alias R = dchar;
644     else static if (is(immutable OriginalType!C == immutable OC, OC))
645         alias R = OC;
646 
647     return isUpper(c) ? cast(R)(cast(R) c + 'a' - 'A') : cast(R) c;
648 }
649 
650 ///
651 @safe pure nothrow @nogc unittest
652 {
653     assert(toLower('a') == 'a');
654     assert(toLower('A') == 'a');
655     assert(toLower('#') == '#');
656 
657     // N.B.: Non-ASCII Unicode uppercase letters are not converted.
658     assert(toLower('Á') == 'Á');
659 }
660 
661 @safe pure nothrow unittest
662 {
663 
664     import std.meta;
665     static foreach (C; AliasSeq!(char, wchar, dchar, immutable char, ubyte))
666     {
667         foreach (i, c; uppercase)
668             assert(toLower(cast(C) c) == lowercase[i]);
669 
670         foreach (C c; 0 .. 128)
671         {
672             if (c < 'A' || c > 'Z')
673                 assert(toLower(c) == c);
674             else
675                 assert(toLower(c) != c);
676         }
677 
678         foreach (C c; 128 .. C.max)
679             assert(toLower(c) == c);
680 
681         //CTFE
682         static assert(toLower(cast(C)'a') == 'a');
683         static assert(toLower(cast(C)'A') == 'a');
684     }
685 }
686 
687 
688 /++
689     Converts an ASCII letter to uppercase.
690 
691     Params: c = Any type which implicitly converts to `dchar`. In the case
692     where it's a built-in type, or an enum of a built-in type,
693     `Unqual!(OriginalType!C)` is returned, whereas if it's a user-defined
694     type, `dchar` is returned.
695 
696     Returns: The corresponding uppercase letter, if `c` is a lowercase ASCII
697     character, otherwise `c` itself.
698   +/
699 auto toUpper(C)(C c)
700 if (is(C : dchar))
701 {
702     import std.traits : OriginalType;
703 
704     static if (!__traits(isScalar, C))
705         alias R = dchar;
706     else static if (is(immutable OriginalType!C == immutable OC, OC))
707         alias R = OC;
708 
709     return isLower(c) ? cast(R)(cast(R) c - ('a' - 'A')) : cast(R) c;
710 }
711 
712 ///
713 @safe pure nothrow @nogc unittest
714 {
715     assert(toUpper('a') == 'A');
716     assert(toUpper('A') == 'A');
717     assert(toUpper('#') == '#');
718 
719     // N.B.: Non-ASCII Unicode lowercase letters are not converted.
720     assert(toUpper('á') == 'á');
721 }
722 
723 @safe pure nothrow unittest
724 {
725     import std.meta;
726     static foreach (C; AliasSeq!(char, wchar, dchar, immutable char, ubyte))
727     {
728         foreach (i, c; lowercase)
729             assert(toUpper(cast(C) c) == uppercase[i]);
730 
731         foreach (C c; 0 .. 128)
732         {
733             if (c < 'a' || c > 'z')
734                 assert(toUpper(c) == c);
735             else
736                 assert(toUpper(c) != c);
737         }
738 
739         foreach (C c; 128 .. C.max)
740             assert(toUpper(c) == c);
741 
742         //CTFE
743         static assert(toUpper(cast(C)'a') == 'A');
744         static assert(toUpper(cast(C)'A') == 'A');
745     }
746 }
747 
748 
749 @safe unittest //Test both toUpper and toLower with non-builtin
750 {
751     import std.meta;
752     import std.traits;
753 
754     //User Defined [Char|Wchar|Dchar]
755     static struct UDC {  char c; alias c this; }
756     static struct UDW { wchar c; alias c this; }
757     static struct UDD { dchar c; alias c this; }
758     //[Char|Wchar|Dchar] Enum
759     enum CE :  char {a = 'a', A = 'A'}
760     enum WE : wchar {a = 'a', A = 'A'}
761     enum DE : dchar {a = 'a', A = 'A'}
762     //User Defined [Char|Wchar|Dchar] Enum
763     enum UDCE : UDC {a = UDC('a'), A = UDC('A')}
764     enum UDWE : UDW {a = UDW('a'), A = UDW('A')}
765     enum UDDE : UDD {a = UDD('a'), A = UDD('A')}
766 
767     //User defined types with implicit cast to dchar test.
768     static foreach (Char; AliasSeq!(UDC, UDW, UDD))
769     {
770         assert(toLower(Char('a')) == 'a');
771         assert(toLower(Char('A')) == 'a');
772         static assert(toLower(Char('a')) == 'a');
773         static assert(toLower(Char('A')) == 'a');
774         static assert(toUpper(Char('a')) == 'A');
775         static assert(toUpper(Char('A')) == 'A');
776     }
777 
778     //Various enum tests.
779     static foreach (Enum; AliasSeq!(CE, WE, DE, UDCE, UDWE, UDDE))
780     {
781         assert(toLower(Enum.a) == 'a');
782         assert(toLower(Enum.A) == 'a');
783         assert(toUpper(Enum.a) == 'A');
784         assert(toUpper(Enum.A) == 'A');
785         static assert(toLower(Enum.a) == 'a');
786         static assert(toLower(Enum.A) == 'a');
787         static assert(toUpper(Enum.a) == 'A');
788         static assert(toUpper(Enum.A) == 'A');
789     }
790 
791     //Return value type tests for enum of non-UDT. These should be the original type.
792     static foreach (T; AliasSeq!(CE, WE, DE))
793     {{
794         alias C = OriginalType!T;
795         static assert(is(typeof(toLower(T.init)) == C));
796         static assert(is(typeof(toUpper(T.init)) == C));
797     }}
798 
799     //Return value tests for UDT and enum of UDT. These should be dchar
800     static foreach (T; AliasSeq!(UDC, UDW, UDD, UDCE, UDWE, UDDE))
801     {
802         static assert(is(typeof(toLower(T.init)) == dchar));
803         static assert(is(typeof(toUpper(T.init)) == dchar));
804     }
805 }