Category talk:Str

Revision as of 16:34, 23 May 2020 by PureFox (talk | contribs) (Added source code for new 'str' module.)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Source code

<lang ecmascript>/* Module "str.wren" */

/*

  Char contains routines to perform various operations on characters.
  For convenience a string containing more than one character can be passed 
  as an argument but the methods will only operate on the first character.
  • /

class Char {

   // Returns the codepoint of the first character of a string.
   static code(c) { (c is String && !c.isEmpty) ? c.codePoints[0] :
                     Fiber.abort("Argument must be a non-empty string.") }
   // Convenience method to return a character from its codepoint.
   static fromCode(c) { String.fromCodePoint(c) }
   // Checks if the first character of a string falls into a particular category.
   static isAscii(c)       { code(c) < 128 }
   static isSymbol(c)      { code(c) && "$+<=>^`|~".contains(c[0])  }
   static isControl(c)     { (c = code(c)) && (c <  32 || c == 127) }
   static isDigit(c)       { (c = code(c)) &&  c >= 48 && c <= 57   }
   static isLower(c)       { (c = code(c)) &&  c >= 97 && c <= 122  }
   static isUpper(c)       { (c = code(c)) &&  c >= 65 && c <= 90   }
   static isPrintable(c)   { (c = code(c)) &&  c >= 32 && c < 127   }
   static isSpace(c)       { (c = code(c)) && (c == 32 || c == 9 || c == 10 || c == 13) }
   static isWhitespace(c)  { (c = code(c)) && (c == 32 || (c >= 9 && c <= 13)) }
   /* Rather than use combinations of the above, these only call the 'code' nethod once. */
   static isLetter(c) {
       var d = code(c)
       return (d >= 65 && d <= 90) || (d >= 97 && d <= 122)
   }
   static isAlphanumeric(c) {
       var d = code(c)
       return (d >= 65 && d <= 90) || (d >= 97 && d <= 122) || (d >= 48 && d <= 57)
   }
   static isPunctuation(c) {
       var d = code(c)
       if (d < 33 || d > 126) return false
       if ((d >= 65 && d <= 90) || (d >= 97 && d <= 122) || (d >= 48 && d <= 57)) return false
       if ("$+<=>^`|~".contains(c[0])) return false
       return true
   }
   static category(c) {
       var d = code(c)
       return (d  < 32 || d == 127)      ? "control"   :
              (d == 32)                  ? "space"     :
              (d >= 48 && d <= 57)       ? "digit"     :
              (d >= 64 && d <= 90)       ? "upper"     :
              (d >= 97 && d <= 122)      ? "lower"     :
              (d >=128)                  ? "non-ascii" :
              "$+<=>^`|~".contains(c[0]) ? "symbol"    : "punctuation"
   }
   // Return the first character of a string converted to the appropriate case.
   static upper(c) { ((c = code(c)) && c >= 97 && c <= 122) ? fromCode(c-32) : fromCode(c) } 
   static lower(c) { ((c = code(c)) && c >= 65 && c <=  90) ? fromCode(c+32) : fromCode(c) }
   static swapCase(c) {
       var d = code(c)
       if (d >= 65 && d <=  90) return fromCode(d+32)
       if (d >= 97 && d <= 122) return fromCode(d-32)
       return c[0]
   }

}

/* Str supplements the String class with various other operations on strings. */ class Str {

   // Mimics the comparison operators <, <=, >, >=
   // not supported by the String class.
   static lt(s1, s2) { compare(s1, s2) <  0 }
   static le(s1, s2) { compare(s1, s2) <= 0 }
   static gt(s1, s2) { compare(s1, s2) >  0 }
   static ge(s1, s2) { compare(s1, s2) >= 0 }
   // Compares two strings lexicographically by codepoint.
   // Returns -1, 0 or +1 depending on whether
   // s1 < s2, s1 == s2 or s1 > s2 respectively.
   static compare(s1, s2)  {
       if (s1 == s2) return 0
       var cp1 = s1.codePoints
       var cp2 = s2.codePoints
       var len = (cp1.count <= cp2.count) ? cp1.count : cp2.count
       for (i in 0...len) {
           if (cp1[i] < cp2[i]) return -1
           if (cp1[i] > cp2[i]) return 1
       }
       return (cp1.count < cp2.count) ? -1 : 1
   }
   // Checks if a string falls into a particular category.
   static allAscii(s)      { s.codePoints.all { |c| c < 128                        } }
   static allDigits(s)     { s.codePoints.all { |c| c >= 48 && c <= 57             } }
   static allLower(s)      { s.codePoints.all { |c| c >= 97 && c <= 122            } }
   static allUpper(s)      { s.codePoints.all { |c| c >= 65 && c <= 90             } }
   static allPrintable(s)  { s.codePoints.all { |c| c >= 32 && c < 127             } }
   static allWhitespace(s) { s.codePoints.all { |c| c == 32 || (c >= 9 && c <= 13) } }
   static allLetters(s) { s.codePoints.all { |c|
       return (c >= 65 && c <= 90) || (c >= 97 && c <= 122)
   } }
   static allAlphanumeric(s) { s.codepoints.all { |c|
       return (c >= 65 && c <=  90) || (c >= 97 && c <= 122) || (c >= 48 && c <= 57)
   } }
   // Checks whether a string can be parsed to a number, an integer or a non-integer (float).
   static isNumeric(s)  { Num.fromString(s)                  }
   static isIntegral(s) { (s = isNumeric(s)) && s.isInteger  }
   static isFloat(s)    { (s = isNumeric(s)) && !s.isInteger }
   // Converts a string to lower case.
   static lower(s) {
       if (!(s is String)) s = "%(s)"
       if (s == "") return s
       var cps = s.codePoints.toList
       for (i in 0...cps.count) {
           var c = cps[i]
           if (c >= 65 && c <= 90) cps[i] = c + 32
       }
       return cps.reduce("") { |acc, c| acc + String.fromCodePoint(c) }
   }
   // Converts a string to upper case.
   static upper(s) {
       if (!(s is String)) s = "%(s)"
       if (s == "") return s
       var cps = s.codePoints.toList
       for (i in 0...cps.count) {
           var c = cps[i]
           if (c >= 97 && c <= 122) cps[i] = c - 32
       }
       return cps.reduce("") { |acc, c| acc + String.fromCodePoint(c) }
   }
   // Swaps the case of each character in a string.
   static swapCase(s) {
       if (!(s is String)) s = "%(s)"
       if (s == "") return s
       var cps = s.codePoints.toList
       for (i in 0...cps.count) {
           var c = cps[i]
           if (c >= 65 && c <= 90) {
               cps[i] = c + 32
           } else if (c >= 97 && c <= 122) {
               cps[i] = c - 32
           }
       }
       return cps.reduce("") { |acc, c| acc + String.fromCodePoint(c) }
   }
   // Capitalizes the first character of a string.
   static capitalize(s) {
       if (!(s is String)) s = "%(s)"
       if (s == "") return s
       var cps = s.codePoints.toList
       var start = (s.startsWith("[") && cps.count > 1) ? 1 : 0
       var c = cps[start]
       if (c >= 97 && c <= 122) {
           cps[start] = c - 32
           return cps.reduce("") { |acc, c| acc + String.fromCodePoint(c) }
       }
       return s
   }
   // Capitalizes the first character of each word of a string.
   static title(s) {
       if (!(s is String)) s = "%(s)"
       if (s == "") return s
       var words = s.split(" ")
       return words.map { |w| capitalize(w) }.join(" ")
   }
   // Reverses the characters (not necessarily single bytes) of a string.
   static reverse(s) {
       if (!(s is String)) s = "%(s)"
       return (s != "") ? s[-1..0] : s
   }
   // Performs a circular shift of the characters of 's' one place to the left.
   static lshift(s) {
       if (!(s is String)) s = "%(s)"
       var chars = s.toList
       var count = chars.count
       if (count < 2) return s
       var t = chars[0]
       for (i in 0..count-2) chars[i] = chars[i+1]
       chars[-1] = t
       return chars.join()
   }
   // Performs a circular shift of the characters of 's' one place to the right.
   static rshift(s) {
       if (!(s is String)) s = "%(s)"
       var chars = s.toList
       var count = chars.count
       if (count < 2) return s
       var t = chars[-1]
       for (i in count-2..0) chars[i+1] = chars[i]
       chars[0] = t
       return chars.join()
   }
   /* The indices (or ranges thereof) for all the following functions are measured in codepoints (not bytes).
      As with core library methods, the indices must be within bounds or errors will be generated. */
   // Extracts the sub-string of 's' over the range 'r'.
   static sub(s, r) {
       if (!(r is Range)) Fiber.abort("Second argument must be a range.")
       if (!(s is String)) s = "%(s)"
       return s.toList[r].join()
   }
   // Gets the character of 's' at index 'i'. Throws an error if 'i is out of bounds.
   static get(s, i) {
       if (!(i is Num && i.isInteger && i >= 0)) Fiber.abort("Index must be a non-negative integer.")
       if (!(s is String)) s = "%(s)"
       return s.toList[i]
   }
   // Gets the character of 's' at index 'i'. Returns null if 'i is out of bounds.
   static getOrNull(s, i) {
       if (!(i is Num && i.isInteger)) Fiber.abort("Index must be an integer.")
       if (!(s is String)) s = "%(s)"
       return (i >= 0 && i < s.count) ? s.toList[i] : null
   }
   // Changes the character of 's' at index 'i' to the string 't'.
   static change(s, i, t) {
       if (!(i is Num && i.isInteger && i >= 0)) Fiber.abort("Index must be a non-negative integer.")
       if (!(t is String)) Fiber.abort("Replacment must be a string.")
       if (!(s is String)) s = "%(s)"
       var chars = s.toList
       chars[i] = t
       return chars.join()
   }
   // Inserts at index 'i' of 's' the string 't'.
   static insert(s, i, t) {
       if (!(i is Num && i.isInteger && i >= 0)) Fiber.abort("Index must be a non-negative integer.")
       if (!(t is String)) Fiber.abort("Insertion must be a string.")
       if (!(s is String)) s = "%(s)"
       var chars = s.toList
       chars.insert(i, t)
       return chars.join()
   }
   // Deletes the character of 's' at index 'i'.
   static delete(s, i) {
       if (!(i is Num && i.isInteger && i >= 0)) Fiber.abort("Index must be a non-negative integer.")
       if (!(s is String)) s = "%(s)"
       var chars = s.toList
       chars.removeAt(i)
       return chars.join()
   }
   // Exchanges the characters of 's' at indices 'i' and 'j'
   static exchange(s, i, j) {
       if (!(i is Num && i.isInteger && i >= 0)) Fiber.abort("First index must be a non-negative integer.")
       if (!(j is Num && j.isInteger && j >= 0)) Fiber.abort("Second index must be a non-negative integer.")
       if (!(s is String)) s = "%(s)"
       if (i == j) return s
       var chars = s.toList
       var t = chars[i]
       chars[i] = chars[j]
       chars[j] = t
       return chars.join()
   }
   // Splits a string 's' into chunks of not more than 'size' characters.
   // Returns a list of these chunks, preserving order.
   static chunks(s, size) {
       if (!(size is Num && size.isInteger && size > 0)) {
           Fiber.abort("Size must be a positive integer.")
       }
       if (!(s is String)) s = "%(s)"
       var c = s.count
       if (size >= c) return [s]
       var res = []
       var n = (c/size).floor
       var final = c % size
       var first = 0
       var last  = first + size - 1
       for (i in 0...n) {
           res.add(sub(s, first..last))
           first = last + 1
           last  = first + size - 1
       }
       if (final > 0) res.add(sub(s, first..-1))
       return res
   }

}

// Type aliases for classes in case of any name clashes with other modules. var Fmt_Char = Char var Fmt_Str = Str</lang>

Return to "Str" page.