Skip to content

Commit

Permalink
Add 'char' library by cherrypicking from #631 (#664)
Browse files Browse the repository at this point in the history
Quick backport (forward-port?) of the 'char' library from #631 by @marzipankaiser plus:
- doc comments,
- using the library in the `wordcount` example, (resolves #632)
- and _very_ basic tests.

One big limitation is that it's ASCII only which we should remedy in the future...
  • Loading branch information
jiribenes authored Nov 4, 2024
1 parent eae3bbe commit f9e1968
Show file tree
Hide file tree
Showing 16 changed files with 256 additions and 8 deletions.
8 changes: 8 additions & 0 deletions examples/stdlib/char/ascii_isalphabetic.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
true
true

false
false
false
false
false
14 changes: 14 additions & 0 deletions examples/stdlib/char/ascii_isalphabetic.effekt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import char

def main() = {
println(isAlphabetic('a'))
println(isAlphabetic('Z'))

println("")

println(isAlphabetic('5'))
println(isAlphabetic('@'))
println(isAlphabetic('\u80'))
println(isAlphabetic('\uFF'))
println(isAlphabetic('\u039E'))
}
9 changes: 9 additions & 0 deletions examples/stdlib/char/ascii_isalphanumeric.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
true
true
true
true

false
false
false
false
15 changes: 15 additions & 0 deletions examples/stdlib/char/ascii_isalphanumeric.effekt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import char

def main() = {
println(isAlphanumeric('5'))
println(isAlphanumeric('Z'))
println(isAlphanumeric('a'))
println(isAlphanumeric('z'))

println("")

println(isAlphanumeric('?'))
println(isAlphanumeric('\u80'))
println(isAlphanumeric('\uFF'))
println(isAlphanumeric('\u039E'))
}
16 changes: 16 additions & 0 deletions examples/stdlib/char/ascii_isdigit.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
true
true
true
true
true
true

false
false
false
false
false
false
false
false
false
22 changes: 22 additions & 0 deletions examples/stdlib/char/ascii_isdigit.effekt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import char

def main() = {
println(isDigit('0'))
println(isDigit('9'))
println(isDigit('A', 16))
println(isDigit('0', 16))
println(isDigit('0', 2))
println(isDigit('1', 2))

println("")

println(isDigit('A'))
println(isDigit('A', 2))
println(isDigit('A', 8))
println(isDigit('A', 10))
println(isDigit('3', 2))
println(isDigit('~'))
println(isDigit('\u80'))
println(isDigit('\uFF'))
println(isDigit('\u039E'))
}
10 changes: 10 additions & 0 deletions examples/stdlib/char/ascii_islower.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
true
true

false
false
false
false
false
false
false
16 changes: 16 additions & 0 deletions examples/stdlib/char/ascii_islower.effekt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import char

def main() = {
println(isLower('a'))
println(isLower('z'))

println("")

println(isLower('A'))
println(isLower('Z'))
println(isLower('0'))
println(isLower('!'))
println(isLower('\u80'))
println(isLower('\uFF'))
println(isLower('\u039E'))
}
10 changes: 10 additions & 0 deletions examples/stdlib/char/ascii_isupper.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
true
true

false
false
false
false
false
false
false
16 changes: 16 additions & 0 deletions examples/stdlib/char/ascii_isupper.effekt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import char

def main() = {
println(isUpper('A'))
println(isUpper('Z'))

println("")

println(isUpper('a'))
println(isUpper('z'))
println(isUpper('0'))
println(isUpper('!'))
println(isUpper('\u80'))
println(isUpper('\uFF'))
println(isUpper('\u039E'))
}
15 changes: 15 additions & 0 deletions examples/stdlib/char/ascii_iswhitespace.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
true
true
true
true
true

false
false
false
false
false
false
false
false

20 changes: 20 additions & 0 deletions examples/stdlib/char/ascii_iswhitespace.effekt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import char

def main() = {
println(isWhitespace(' '))
println(isWhitespace('\t'))
println(isWhitespace('\r'))
println(isWhitespace('\n'))
println(isWhitespace('\u0C'))

println("")

println(isWhitespace('Z'))
println(isWhitespace('A'))
println(isWhitespace('!'))
println(isWhitespace('.'))
println(isWhitespace('\u1F'))
println(isWhitespace('\u80'))
println(isWhitespace('\uFF'))
println(isWhitespace('\u039E'))
}
9 changes: 9 additions & 0 deletions examples/stdlib/char/isascii.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
true
true
true
true
true

false
false
false
15 changes: 15 additions & 0 deletions examples/stdlib/char/isascii.effekt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import char

def main() = {
println(isASCII('A'))
println(isASCII('1'))
println(isASCII('Z'))
println(isASCII('\n'))
println(isASCII('!'))

println("")

println(isASCII('\u80'))
println(isASCII('\uFF'))
println(isASCII('\u039E'))
}
11 changes: 3 additions & 8 deletions examples/stdlib/io/filesystem/wordcount.effekt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module wordcount

import char
import io
import io/error
import io/filesystem
Expand All @@ -9,12 +10,6 @@ record Output(chars: Int, words: Int, lines: Int)
def formatWith(output: Output, filename: String): String =
output.lines.show ++ " " ++ output.words.show ++ " " ++ output.chars.show ++ " " ++ filename

def isSpace(c: Char) =
c == ' ' || c.toInt == 9 || c.toInt == 10 || c.toInt == 11 || c.toInt == 12 || c.toInt == 13
// \n \r \t \v \f

def isNewline(c: Char) = c.toInt == 10 // \n

def countWords(input: String): Output = {
val len = input.length

Expand All @@ -25,15 +20,15 @@ def countWords(input: String): Output = {

each(0, len) { i =>
val c = input.unsafeCharAt(i)
val currentIsSpace = isSpace(c)
val currentIsSpace = char::isWhitespace(c)

chars = chars + 1

if (wasSpace && not(currentIsSpace)) {
words = words + 1
}

if (isNewline(c)) {
if (c == '\n') {
lines = lines + 1
}

Expand Down
58 changes: 58 additions & 0 deletions libraries/common/char.effekt
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/// Warning: This library currently only works with ASCII characters, **not** unicode!
module char

import exception


/// Checks if the given character is an ASCII whitespace
def isWhitespace(c: Char): Bool = c match {
case ' ' => true
case '\n' => true
case '\r' => true
case '\t' => true
case '\u0B' => true // vertical tab
case '\u0C' => true // form feed
case _ => false
}

/// Checks if the given character is an ASCII digit in base 10
/// Use `digitValue(c: Char)` to get the numeric value out.
def isDigit(c: Char): Bool = { c >= '0' && c <= '9' }

/// Checks if the given character is an ASCII digit in the given base
/// Use `digitValue(c: Char, base: Int)` to get the numeric value out.
def isDigit(c: Char, base: Int): Bool = {
with on[WrongFormat].default { false }
val _ = digitValue(c, base)
true
}

/// Gets the value of a given ASCII digit in base 10
def digitValue(c: Char): Int / Exception[WrongFormat] =
digitValue(c, 10)

/// Gets the value of a given ASCII digit in the given base
def digitValue(c: Char, base: Int): Int / Exception[WrongFormat] = {
val v = c match {
case c and c >= '0' && c <= '9' => (c.toInt - '0'.toInt)
case c and c >= 'a' && c <= 'z' => (c.toInt - 'a'.toInt) + 10
case c and c >= 'A' && c <= 'Z' => (c.toInt - 'A'.toInt) + 10
case _ => wrongFormat("'" ++ c.toString ++ "' is not a digit")
}
if (v >= base) { wrongFormat(c.toString ++ " is not a valid digit in base " ++ base.show) } else { v }
}

/// Checks if a given character is a 7-bit ASCII character
def isASCII(c: Char): Bool = { c.toInt < 128 }

/// Checks if a given character is an ASCII lower alphabetic character
def isLower(c: Char): Bool = { c >= 'a' && c <= 'z' }

/// Checks if a given character is an ASCII upper alphabetic character
def isUpper(c: Char): Bool = { c >= 'A' && c <= 'Z' }

/// Checks if a given character is an ASCII alphabetic or numeric character
def isAlphanumeric(c: Char): Bool = isDigit(c) || isLower(c) || isUpper(c)

/// Checks if a given character is an ASCII alphabetic character
def isAlphabetic(c: Char): Bool = isLower(c) || isUpper(c)

0 comments on commit f9e1968

Please sign in to comment.