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 +/
isAlphaNum(dchar c)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 }
804