Category talk:Fmt: Difference between revisions

Content added Content deleted
(→‎Source code: Fixed some bugs, lots of improvements. Hopefully fairly stable now.)
(→‎Source code: Module reorganization - moving Str class to new 'str' module.)
Line 2: Line 2:


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

/* Str contains routines which manipulate strings in various ways. */
class Str {
// 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'.
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]
}

// 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()
}
}


/* Conv contains routines which do conversions between types. */
/* Conv contains routines which do conversions between types. */
Line 179: Line 25:
return ((neg) ? "-" : "") + res[-1..0]
return ((neg) ? "-" : "") + res[-1..0]
}
}

// Private helper function. Converts ASCII string to upper case.
static upper_(s) { s.bytes.map { |b|
return String.fromByte((b >= 97 && b <= 122) ? b - 32 : b)
}.join() }
// As itoa(n, b) but resulting digits are upper case.
// As itoa(n, b) but resulting digits are upper case.
static Itoa(n, b) { (b < 11) ? itoa(n, b) : Str.upper(itoa(n, b)) }
static Itoa(n, b) { (b < 11) ? itoa(n, b) : upper_(itoa(n, b)) }


// Converts a numeric ASCII string with a base between 2 and 36 to an integer.
// Converts a numeric ASCII string with a base between 2 and 36 to an integer.
Line 195: Line 46:
}
}
if (s == "") Fiber.abort("String must contain some digits.")
if (s == "") Fiber.abort("String must contain some digits.")
s = Str.lower(s)
s = upper_(s)
if ((s.startsWith("0b") && b != 2) || (s.startsWith("0o") && b != 8) || (s.startsWith("0x") && b != 16)) {
if ((s.startsWith("0B") && b != 2) || (s.startsWith("0O") && b != 8) || (s.startsWith("0X") && b != 16)) {
Fiber.abort("Inconsistent base specifier.")
Fiber.abort("Inconsistent base specifier.")
}
}
if (s.startsWith("0b") || s.startsWith("0o") || s.startsWith("0x")) {
if (s.startsWith("0B") || s.startsWith("0O") || s.startsWith("0X")) {
s = s[2..-1]
s = s[2..-1]
if (s == "") Fiber.abort("String after base specifier must contain some digits.")
if (s == "") Fiber.abort("String after base specifier must contain some digits.")
Line 301: Line 152:
}
}


// Private helper method for 'commatize' method.
// Checks whether argument is a numeric decimal string.
// Checks whether argument is a numeric decimal string.
static isDecimal(n) {
static isDecimal_(n) {
if (!(n is String && n != "" && "+- 0123456789".contains(n[0]))) return false
if (!(n is String && n != "" && "+- 0123456789".contains(n[0]))) return false
if ("-+ ".contains(n[0])) {
if ("-+ ".contains(n[0])) {
Line 340: Line 192:
static ordinalize(n) { commatize(n) + Conv.ord(n)[-2..-1] }
static ordinalize(n) { commatize(n) + Conv.ord(n)[-2..-1] }


// Private helper method for 'abbreviate' method.
static sub_(s, r) { s.toList[r].join() }
// Abbreviates a string 's' to a maximum number of characters 'w' (non-overlapping) at either end
// Abbreviates a string 's' to a maximum number of characters 'w' (non-overlapping) at either end
// or, if 'w' is negative from the front only, using 'sep' as the separator.
// or, if 'w' is negative from the front only, using 'sep' as the separator.
Line 350: Line 205:
if (c <= ((w < 0) ? -w : 2*w)) return s
if (c <= ((w < 0) ? -w : 2*w)) return s
var le = (w >= 0) ? w : -w
var le = (w >= 0) ? w : -w
return Str.sub(s, 0...le) + sep + ((w >= 0) ? Str.sub(s, -le..-1) : "")
return sub_(s, 0...le) + sep + ((w >= 0) ? sub_(s, -le..-1) : "")
}
}


Line 436: Line 291:


// Type aliases for classes in case of any name clashes with other modules.
// Type aliases for classes in case of any name clashes with other modules.
var Fmt_Str = Str
var Fmt_Conv = Conv
var Fmt_Conv = Conv
var Fmt_Fmt = Fmt</lang>
var Fmt_Fmt = Fmt</lang>