Skip to content

Commit 328cf01

Browse files
feat(stdlib)!: Add an Ascii submodule to Char and move isAscii, toUppercase, toLowercase (#2178)
Co-authored-by: Oscar Spencer <oscar.spen@gmail.com>
1 parent 72cc978 commit 328cf01

File tree

5 files changed

+555
-135
lines changed

5 files changed

+555
-135
lines changed

compiler/test/stdlib/char.test.gr

Lines changed: 70 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -87,25 +87,73 @@ assert !('a' >= 'b')
8787
assert 'a' >= 'a'
8888
assert 'B' >= 'B'
8989

90-
// isAsciiDigit
91-
assert Char.isAsciiDigit('1')
92-
assert !Char.isAsciiDigit('a')
93-
assert !Char.isAsciiDigit('🌾')
94-
95-
// isAsciiAlpha
96-
assert !Char.isAsciiAlpha('1')
97-
assert Char.isAsciiAlpha('a')
98-
assert Char.isAsciiAlpha('Z')
99-
assert !Char.isAsciiAlpha('λ')
100-
101-
// toAsciiLowercase
102-
assert Char.toAsciiLowercase('A') == 'a'
103-
assert Char.toAsciiLowercase('a') == 'a'
104-
assert Char.toAsciiLowercase('1') == '1'
105-
assert Char.toAsciiLowercase('λ') == 'λ'
106-
107-
// toAsciiUppercase
108-
assert Char.toAsciiUppercase('a') == 'A'
109-
assert Char.toAsciiUppercase('A') == 'A'
110-
assert Char.toAsciiUppercase('1') == '1'
111-
assert Char.toAsciiUppercase('λ') == 'λ'
90+
// Char.Ascii
91+
module AsciiTest {
92+
use Char.{ module Ascii }
93+
94+
// isValid
95+
assert Ascii.isValid('1')
96+
assert Ascii.isValid('a')
97+
assert Ascii.isValid(';')
98+
assert Ascii.isValid(' ')
99+
assert Ascii.isValid('\n')
100+
assert !Ascii.isValid('🌾')
101+
102+
// isDigit
103+
assert Ascii.isDigit('1')
104+
assert !Ascii.isDigit('a')
105+
assert !Ascii.isDigit('🌾')
106+
107+
// isAlpha
108+
assert !Ascii.isAlpha('1')
109+
assert Ascii.isAlpha('a')
110+
assert Ascii.isAlpha('Z')
111+
assert !Ascii.isAlpha('λ')
112+
113+
// isControl
114+
assert Ascii.isControl('\n')
115+
assert Ascii.isControl('\t')
116+
assert Ascii.isControl('\u{007F}')
117+
assert !Ascii.isControl(' ')
118+
assert !Ascii.isControl('a')
119+
assert !Ascii.isControl('🌾')
120+
121+
// isWhitespace
122+
assert Ascii.isWhitespace(' ')
123+
assert Ascii.isWhitespace('\t')
124+
assert Ascii.isWhitespace('\n')
125+
assert Ascii.isWhitespace('\r')
126+
assert Ascii.isWhitespace('\x0C')
127+
assert !Ascii.isWhitespace('a')
128+
assert !Ascii.isWhitespace('1')
129+
assert !Ascii.isWhitespace('🌾')
130+
131+
// isPunctuation
132+
assert Ascii.isPunctuation('!')
133+
assert Ascii.isPunctuation('?')
134+
assert Ascii.isPunctuation('.')
135+
assert Ascii.isPunctuation(',')
136+
assert !Ascii.isPunctuation('1')
137+
assert !Ascii.isPunctuation('a')
138+
assert !Ascii.isPunctuation('🌾')
139+
140+
// isGraphic
141+
assert Ascii.isGraphic('1')
142+
assert Ascii.isGraphic('a')
143+
assert Ascii.isGraphic('!')
144+
assert !Ascii.isGraphic('\n')
145+
assert !Ascii.isGraphic('\t')
146+
assert !Ascii.isGraphic('🌾')
147+
148+
// toLowercase
149+
assert Ascii.toLowercase('A') == 'a'
150+
assert Ascii.toLowercase('a') == 'a'
151+
assert Ascii.toLowercase('1') == '1'
152+
assert Ascii.toLowercase('λ') == 'λ'
153+
154+
// toUppercase
155+
assert Ascii.toUppercase('a') == 'A'
156+
assert Ascii.toUppercase('A') == 'A'
157+
assert Ascii.toUppercase('1') == '1'
158+
assert Ascii.toUppercase('λ') == 'λ'
159+
}

stdlib/char.gr

Lines changed: 158 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -315,64 +315,170 @@ provide let (>=) = (x: Char, y: Char) => {
315315
}
316316

317317
/**
318-
* Checks if the character is an ASCII digit.
318+
* Utilities for working with ASCII characters.
319319
*
320-
* @param char: The character to check
321-
* @returns `true` if the character is an ASCII digit or `false` otherwise
322-
*
323-
* @example assert Char.isAsciiDigit('1')
324-
* @example assert !Char.isAsciiDigit('a')
320+
* @example Char.Ascii.isAscii('1')
325321
*
326-
* @since v0.6.0
322+
* @since v0.7.0
327323
*/
328-
provide let isAsciiDigit = char => char >= '0' && char <= '9'
324+
provide module Ascii {
325+
/**
326+
* The minimum valid ASCII character code.
327+
*
328+
* @since v0.7.0
329+
*/
330+
provide let min = 0x00
329331

330-
/**
331-
* Checks if the character is an ASCII alphabetical character.
332-
*
333-
* @param char: The character to check
334-
* @returns `true` if the character is an ASCII alphabetical or `false` otherwise
335-
*
336-
* @example assert Char.isAsciiAlpha('a')
337-
* @example assert !Char.isAsciiAlpha('1')
338-
*
339-
* @since v0.6.0
340-
*/
341-
provide let isAsciiAlpha = char =>
342-
char >= 'a' && char <= 'z' || char >= 'A' && char <= 'Z'
332+
/**
333+
* The maximum valid ASCII character code.
334+
*
335+
* @since v0.7.0
336+
*/
337+
provide let max = 0x7F
343338

344-
/**
345-
* Converts the character to ASCII lowercase if it is an ASCII uppercase character.
346-
*
347-
* @param char: The character to convert
348-
* @returns The lowercased character
349-
*
350-
* @example assert Char.toAsciiLowercase('B') == 'b'
351-
*
352-
* @since v0.6.0
353-
*/
354-
provide let toAsciiLowercase = char => {
355-
if (char >= 'A' && char <= 'Z') {
356-
fromCode(code(char) + 0x20)
357-
} else {
358-
char
339+
/**
340+
* Checks if the character is a valid ASCII character.
341+
*
342+
* @param char: The character to check
343+
* @returns `true` if the character is an ASCII character or `false` otherwise
344+
*
345+
* @example assert Char.Ascii.isValid('1')
346+
* @example assert Char.Ascii.isValid('a')
347+
* @example assert !Char.Ascii.isValid('🌾')
348+
*
349+
* @since v0.7.0
350+
*/
351+
provide let isValid = char => char <= '\u{007F}'
352+
353+
/**
354+
* Checks if the character is an ASCII digit.
355+
*
356+
* @param char: The character to check
357+
* @returns `true` if the character is an ASCII digit or `false` otherwise
358+
*
359+
* @example assert Char.Ascii.isDigit('1')
360+
* @example assert !Char.Ascii.isDigit('a')
361+
*
362+
* @since v0.7.0
363+
* @history v0.6.0: Originally `Char.isAsciiDigit`
364+
*/
365+
provide let isDigit = char => char >= '0' && char <= '9'
366+
367+
/**
368+
* Checks if the character is an ASCII alphabetical character.
369+
*
370+
* @param char: The character to check
371+
* @returns `true` if the character is an ASCII alphabetical or `false` otherwise
372+
*
373+
* @example assert Char.Ascii.isAlpha('a')
374+
* @example assert !Char.Ascii.isAlpha('1')
375+
*
376+
* @since v0.7.0
377+
* @history v0.6.0: Originally `Char.isAsciiAlpha`
378+
*/
379+
provide let isAlpha = char =>
380+
char >= 'a' && char <= 'z' || char >= 'A' && char <= 'Z'
381+
382+
/**
383+
* Checks if the character is an ASCII control character.
384+
*
385+
* @param char: The character to check
386+
* @returns `true` if the character is an ASCII control character or `false` otherwise
387+
*
388+
* @example assert Char.Ascii.isControl('\t')
389+
* @example assert Char.Ascii.isControl('\n')
390+
* @example assert !Char.Ascii.isControl('1')
391+
* @example assert !Char.Ascii.isControl('a')
392+
*
393+
* @since v0.7.0
394+
*/
395+
provide let isControl = char => char <= '\u{001F}' || char == '\u{007F}'
396+
397+
/**
398+
* Checks if the character is an ASCII whitespace character.
399+
*
400+
* @param char: The character to check
401+
* @returns `true` if the character is an ASCII whitespace character or `false` otherwise
402+
*
403+
* @example assert Char.isWhitespace('\t')
404+
* @example assert Char.isWhitespace('\n')
405+
* @example assert !Char.isWhitespace('1')
406+
* @example assert !Char.isWhitespace('a')
407+
*
408+
* @since v0.7.0
409+
*/
410+
provide let isWhitespace = char => {
411+
match (char) {
412+
'\t' | '\n' | '\x0C' | '\r' | ' ' => true,
413+
_ => false,
414+
}
359415
}
360-
}
361416

362-
/**
363-
* Converts the character to ASCII uppercase if it is an ASCII lowercase character.
364-
*
365-
* @param char: The character to convert
366-
* @returns The uppercased character
367-
*
368-
* @example assert Char.toAsciiUppercase('b') == 'B'
369-
*
370-
* @since v0.6.0
371-
*/
372-
provide let toAsciiUppercase = char => {
373-
if (char >= 'a' && char <= 'z') {
374-
fromCode(code(char) - 0x20)
375-
} else {
376-
char
417+
/**
418+
* Checks if the character is an ASCII punctuation character.
419+
*
420+
* @param char: The character to check
421+
* @returns `true` if the character is an ASCII punctuation character or `false` otherwise
422+
*
423+
* @example assert Char.Ascii.isPunctuation('!')
424+
* @example assert !Char.Ascii.isPunctuation('1')
425+
*
426+
* @since v0.7.0
427+
*/
428+
provide let isPunctuation = char =>
429+
char >= '!' && char <= '/' ||
430+
char >= ':' && char <= '@' ||
431+
char >= '[' && char <= '`' ||
432+
char >= '{' && char <= '~'
433+
434+
/**
435+
* Checks if the character is an ASCII graphic character.
436+
*
437+
* @param char: The character to check
438+
* @returns `true` if the character is an ASCII graphic character or `false` otherwise
439+
*
440+
* @example assert Char.Ascii.isGraphic('!')
441+
* @example assert !Char.Ascii.isGraphic('\t')
442+
*
443+
* @since v0.7.0
444+
*/
445+
provide let isGraphic = char => char >= '!' && char <= '~'
446+
447+
/**
448+
* Converts the character to ASCII lowercase if it is an ASCII uppercase character.
449+
*
450+
* @param char: The character to convert
451+
* @returns The lowercased character
452+
*
453+
* @example assert Char.Ascii.toLowercase('B') == 'b'
454+
*
455+
* @since v0.7.0
456+
* @history v0.6.0: Originally `Char.toAsciiLowercase`
457+
*/
458+
provide let toLowercase = char => {
459+
if (char >= 'A' && char <= 'Z') {
460+
fromCode(code(char) + 0x20)
461+
} else {
462+
char
463+
}
464+
}
465+
466+
/**
467+
* Converts the character to ASCII uppercase if it is an ASCII lowercase character.
468+
*
469+
* @param char: The character to convert
470+
* @returns The uppercased character
471+
*
472+
* @example assert Char.Ascii.toUppercase('b') == 'B'
473+
*
474+
* @since v0.7.0
475+
* @history v0.6.0: Originally `Char.toAsciiUppercase`
476+
*/
477+
provide let toUppercase = char => {
478+
if (char >= 'a' && char <= 'z') {
479+
fromCode(code(char) - 0x20)
480+
} else {
481+
char
482+
}
377483
}
378484
}

0 commit comments

Comments
 (0)