Category talk:Wren-fmt: Difference between revisions

→‎Source code: Added 'jslwrite' and family methods.
(→‎Source code: Added 'numbers to names' routines, plus several other minor improvements.)
(→‎Source code: Added 'jslwrite' and family methods.)
 
(12 intermediate revisions by the same user not shown)
Line 1:
===Source code===
 
<syntaxhighlight lang="ecmascriptwren">/* Module "fmt.wren" */
 
/* Conv contains routines which do conversions between types. */
Line 115:
"7": "⁷", "8": "⁸", "9": "⁹", "-": "⁻", "+": "⁺", "e": "ᵉ"
}
//return n.toString.map { |d| ss.containsKey(d) ? ss[d] : d }.join()
return Fmt.i(0, n).map { |d| ss.containsKey(d) ? ss[d] : d }.join()
}
Line 141 ⟶ 140:
var frac = "%(n)/%(d)"
return fracs.containsKey(frac) ? fracs[frac] : frac
}
 
// Converts 's' to the plural form 'p' if and only if the absolute value of 'n' is not equal to 1.
static plural(n, s, p) { (n.abs != 1) ? p : s }
 
// Convenience version of the above method which forms the plural by adding 's' to the singular.
static plural(n, s) { (n.abs != 1) ? s + "s" : s }
 
// Makes a UTF-8 or byte string printable by replacing non-printable characters
// with escaped ones. If 'asBytes' is true, not just control characters
// but all bytes > 127 are regarded as non-printable.
static printable(s, asBytes) {
var res = ""
var chars = asBytes ? s.bytes : s.codePoints
var lets = "abtnvfr"
for (c in chars) {
if (c == -1) {
res = res + "\ufffd"
} else if (c == 0) {
res = res + "\\0"
} else if (c >= 7 && c <= 13) {
res = res + "\\" + lets[c - 7]
} else if (c == 27) {
res = res + "\\e"
} else if (c < 32 || c == 127 || (asBytes && c > 127)) {
res = res + "\\x" + Fmt.swrite("$02x", c)
} else if (!asBytes && c >= 128 && c < 160) {
res = res + esc + "\\u00" + Fmt.swrite("$02x", c)
} else {
res = res + String.fromCodePoint(c)
}
}
return res
}
 
Line 361 ⟶ 393:
// Convenience version of the above which uses double quotes as the embedding characters.
static q(v) { "\"%(v)\"" }
 
// Maps the boolean value 'b' to "yes" if true or "no" if false
// and then applies the 's' format to the result.
static y(w, b) { Fmt.s(w, b ? "yes" : "no") }
 
// Formats a number 'n' (using 'h' format) to a maximum precision of 14 decimal places.
Line 395 ⟶ 431:
 
// Works like 'e' except that the exponent symbol 'e' is replaced by upper case 'E'.
static E(w, n, p) { Fmt.e(w, n, p).replace("e", "E") }
 
// Applies the 's' format to the name of a number 'n'.
Line 408 ⟶ 444:
// Applies the 's' format to the the inferior (or subscript) version of a number 'n'.
static I(w, n) { Fmt.s(w, Conv.subscript(n)) }
 
// Applies the 's' format to a fraction f[0] / f[1 where 'f' is a two element list.
static F(w, f) { Fmt.s(w, Conv.fraction(f[0], f[1])) }
 
// Applies the 's' format to the plural of l[1] depending on l[0] and, if present, l[2] where 'l' is a
// two or three element list. Forms the plural by just adding "s" to l[1] if 'l' is a two element list.
static P(w, l) { Fmt.s(w, Conv.plural(l[0], l[1], l.count == 2 ? l[1] + "s" : l[2])) }
 
// Makes a byte string printable and applies the 's' format to the result.
static B(w, v) { Fmt.s(w, Conv.printable(v, true)) }
 
// Makes a UTF-8 string printable and applies the 's' format to the result.
static U(w, v) { Fmt.s(w, Conv.printable(v, false)) }
 
// Repeats 'v', a string or value, 'n' times.
static R(n, v) { (v is String) ? v * n : v.toString * n }
 
// Pads a number 'n' with leading spaces to a minimum width 'w' and a precision of 'p' decimal places.
Line 445 ⟶ 497:
static g(w, n, p) {
var f = f(w, n, p)
if (f.contains("e")) return f
if (f.contains(".") && (f[-1] == "0" || f[-1] == " ")) {
var l1 = f.count
Line 458 ⟶ 511:
static h(w, n, p) {
var f = f(w, n, p)
if (f.contains("e")) return f
if (f.contains(".") && (f[-1] == "0" || f[-1] == " ")) {
var l1 = f.count
Line 466 ⟶ 520:
return f
}
 
// Works like 'h' except, if right justified, removes any trailing spaces before rejustifying.
static j(w, n, p) { (w >= 0) ? rjust(w, h(w, n, p).trimEnd()) : h(w, n, p) }
 
// Works like 'j' except derives the number to be formatted from its name.
static l(w, name, p) { j(w, Name.toNum(name), p) }
 
// As above but pads with leading zeros instead of spaces.
Line 509 ⟶ 569:
static gc(w, n, p) {
var f = fc(w, n, p)
if (f.contains("e")) return f
if (f.contains(".") && (f[-1] == "0" || f[-1] == " ")) {
var l1 = f.count
Line 522 ⟶ 583:
static hc(w, n, p) {
var f = fc(w, n, p)
if (f.contains("e")) return f
if (f.contains(".") && (f[-1] == "0" || f[-1] == " ")) {
var l1 = f.count
Line 530 ⟶ 592:
return f
}
 
// Works like 'hc' except, if right justified, removes any trailing spaces before rejustifying.
static jc(w, n, p) { (w >= 0) ? rjust(w, hc(w, n, p).trimEnd()) : hc(w, n, p) }
 
// Works like 'jc' except derives the number to be formatted from its name.
static lc(w, name, p) { jc(w, Name.toNum(name), p) }
 
// Applies the 'f' format to each component, x and y, of a complex number 'n'
Line 537 ⟶ 605:
if (n.type.toString != "Complex") Fiber.abort("Argument must be a complex or real number.")
var real = f(w, n.real, p)
var sign = (n.imag >= 0) ? " + " : " - "
var imag = f(w, n.imag.abs, p)
if (w < 0) imag = imag.trimEnd(" ")
return real + sign + imag + "i"
}
 
// Applies the 'fp' and 'f' formats respectively to each component, x and y, of a
// complex number 'n' before joining them together in the form x ± yi.
static zp(w, n, p) {
if (n is Num) return fp(w, n, p)
if (n.type.toString != "Complex") Fiber.abort("Argument must be a complex or real number.")
var real = fp(w, n.real, p)
var sign = (n.imag >= 0) ? " + " : " - "
var imag = f(w, n.imag.abs, p)
Line 561 ⟶ 641:
static g(w, n) { g(w, n, precision) }
static h(w, n) { h(w, n, precision) }
static j(w, n) { j(w, n, precision) }
static l(w, n) { l(w, n, precision) }
static z(w, n) { z(w, n, precision) }
static fz(w, n) { fz(w, n, precision) }
Line 568 ⟶ 650:
static gp(w, n) { gp(w, n, precision) }
static hp(w, n) { hp(w, n, precision) }
static zp(w, n) { zp(w, n, precision) }
static fm(w, n) { fm(w, n, precision) }
static gm(w, n) { gm(w, n, precision) }
Line 574 ⟶ 657:
static fc(w, n) { fc(w, n, precision) }
static gc(w, n) { gc(w, n, precision) }
static hc(w, n) { hc(w, n, precision) }
static jc(w, n) { jc(w, n, precision) }
 
static lc(w, n) { lc(w, n, precision) }
// Private worker method which calls a 'short name' method and returns its result.
static callFn_(fn, w, v, p) {
Line 594 ⟶ 679:
(fn == "u") ? u(w, v) :
(fn == "q") ? q(v) :
(fn == "y") ? y(w, v) :
(fn == "e") ? e(w, v, p) :
(fn == "E") ? Fmt.E(w, v, p) :
Line 600 ⟶ 686:
(fn == "S") ? Fmt.S(w, v) :
(fn == "I") ? Fmt.I(w, v) :
(fn == "F") ? Fmt.F(w, v) :
(fn == "P") ? Fmt.P(w, v) :
(fn == "B") ? Fmt.B(w, v) :
(fn == "U") ? Fmt.U(w, v) :
(fn == "R") ? Fmt.R(w, v) :
(fn == "f") ? f(w, v, p) :
(fn == "g") ? g(w, v, p) :
(fn == "h") ? h(w, v, p) :
(fn == "j") ? j(w, v, p) :
(fn == "l") ? l(w, v, p) :
(fn == "z") ? z(w, v, p) :
(fn == "dz") ? dz(w, v) :
Line 618 ⟶ 711:
(fn == "gp") ? gp(w, v, p) :
(fn == "hp") ? hp(w, v, p) :
(fn == "zp") ? zp(w, v, p) :
(fn == "fm") ? fm(w, v, p) :
(fn == "gm") ? gm(w, v, p) :
Line 630 ⟶ 724:
(fn == "fc") ? fc(w, v, p) :
(fn == "gc") ? gc(w, v, p) :
(fn == "hc") ? hc(w, v, p) : Fiber.abort("Method not recognized.")
(fn == "jc") ? jc(w, v, p) :
(fn == "lc") ? lc(w, v, p) : Fiber.abort("Method not recognized.")
}
 
Line 636 ⟶ 732:
// The method to be applied is specified (as a string) in 'fn'.
// The parameters to be passed to the method are specified in 'w' and 'p'
// 'p' is needed for 'e', 'E', 'f', 'g', 'h', 'j', 'l', 'z', 'fz', 'gz', 'hz', 'fp', 'gp', 'hp',
// 'hp', 'zp', 'fm', 'gm', 'hm', 'zm', 'fc', 'gc', 'hc', 'jc' or 'hclc' but is ignored otherwise.
// The resulting strings are then joined together using the separator 'sep'.
// having first applied the 'q' method, with parameter 'cc', to each of them.
Line 713 ⟶ 809:
// $[flag][width][.precision][letter] of which all bracketed items except [letter] are optional.
// The letter must be one of the 'short' methods:
// a, b, B, c, d, e, E, f, F, g, h, i, I, j, k, l, m, n, N, o, O, P, q, r, R, s, S, t, u, U, x, X, y or z.
// If present, the flag (there can only be one) must be one of the following:
// + always prints a + or - sign ('dp', 'fp', 'gp', 'hp' or 'hpzp' methods)
// (space) leaves a space for the sign but only prints minus ('dm', 'fm', 'gm', 'hm' or 'zm' methods)
// , commatizes the following number ('dc', 'rc', 'sc', 'ic', 'fc', 'gc, 'hc', 'jc' or 'hclc' methods)
// # adds the appropriate prefix for the number formats: b, t, o, d, x and X
// * reads the width from the argument before the one to be formatted
Line 725 ⟶ 821:
// It doesn't include any '#' flag prefix. If [width] is absent, a width of one is passed.
// If present, the precision is the number of decimal places to be passed to the appropriate
// 'e', 'E', 'f', 'g', 'h', 'j', 'l' or 'z' style method. If absent, the default precision is passed.
// Where any optional item is inappropriate to the method being used it is simply ignored.
// Where one of the arguments is a sequence (other than a string) this method senses it
Line 783 ⟶ 879:
var fn = ""
var ds = ""
if ("abcdeEfghiIkmnNoOqrsStuxXzabBcdeEfFghiIjklmnNoOPqrRsStuUxXyz".codePoints.contains(cp)) { // format letter
fn = Conv.itoc(cp)
} else if (cp == 42) { // star
Line 818 ⟶ 914:
 
if (fn == "") {
if (!"abcdeEfghiIkmnNoOqrsStuxXzabBcdeEfFghiIjklmnNoOPqrRsStuUxXyz".codePoints.contains(cp)) {
Fiber.abort("Unrecognized character in format string.")
}
Line 831 ⟶ 927:
fn = "dc"
}
} else if ((fn == "f" || fn == "g" || fn == "h" || fn == "z") && plus) {
fn = fn + "p"
} else if ((fn == "f" || fn == "g" || fn == "h" || fn == "z") && space) {
fn = fn + "m"
} else if ((fn == "r" || fn == "s" || fn == "i" || fn == "f" ||
fn == "g" || fn == "h" || fn == "j" || fn == "l") && comma) {
fn = fn + "c"
}
Line 847 ⟶ 943:
if (next < a.count) {
var e = a[next]
if ((e is Sequence) && !(e is String) && fn != "nnuFPR" && .contains(fn != "u")) {
if (hash && "btodxX".contains(fn[0])) {
var rr = []
Line 910 ⟶ 1,006:
static print(fmt, a1) { System.print(slwrite(fmt, [a1])) }
static lprint(fmt, a) { System.print(slwrite(fmt, a)) }
 
// Synomyms of corresponding methods in System class - useful for aligning code.
static write(object) { System.write(object) }
static writeAll(sequence) { System.writeAll(sequence) }
static print() { System.print() }
static print(object) { System.print(object) }
static printAll(sequence) { System.printAll(sequence) }
 
// Gets or sets the separator for the 'jslwrite' method. The default is a single space.
static separator { (__separator != null) ? __separator : " " }
static separator=(s) { __separator = (s is String) ? s : s.toString }
 
// Returns a string formed from joining together the string representation of
// the elements of the list or sequence 'a' using the current separator.
static jslwrite(a) { a.join(separator) }
 
// Convenience versions of the 'jslwrite' method which allow from 2 to 6 arguments
// to be passed individually rather than in a list or sequence
static jswrite(a1, a2, a3, a4, a5, a6) { jsl.write([a1, a2, a3, a4, a5, a6]) }
static jswrite(a1, a2, a3, a4, a5) { jsl.write([a1, a2, a3, a4, a5]) }
static jswrite(a1, a2, a3, a4) { jsl.write([a1, a2, a3, a4]) }
static jswrite(a1, a2, a3) { jsl.write([a1, a2, a3]) }
static jswrite(a1, a2) { jsl.write([a1, a2]) }
 
// Applies jslwrite to the arguments and then 'writes' it (no following \n) to stdout.
static jwrite(a1, a2, a3, a4, a5, a6) { System.write(jslwrite([a1, a2, a3, a4, a5, a6])) }
static jwrite(a1, a2, a3, a4, a5) { System.write(jslwrite([a1, a2, a3, a4, a5])) }
static jwrite(a1, a2, a3, a4) { System.write(jslwrite([a1, a2, a3, a4])) }
static jwrite(a1, a2, a3) { System.write(jslwrite([a1, a2, a3])) }
static jwrite(a1, a2) { System.write(jslwrite([a1, a2])) }
static jlwrite(a) { System.write(jslwrite(a)) }
 
// Applies jslwrite to the arguments and then 'prints' it (with a following \n) to stdout.
static jprint(a1, a2, a3, a4, a5, a6) { System.print(jslwrite([a1, a2, a3, a4, a5, a6])) }
static jprint(a1, a2, a3, a4, a5) { System.print(jslwrite([a1, a2, a3, a4, a5])) }
static jprint(a1, a2, a3, a4) { System.print(jslwrite([a1, a2, a3, a4])) }
static jprint(a1, a2, a3) { System.print(jslwrite([a1, a2, a3])) }
static jprint(a1, a2) { System.print(jslwrite([a1, a2])) }
static jlprint(a) { System.print(jslwrite(a)) }
 
// Prints (with a following \n) a sequence 'a' to stdout in tabular form
Line 1,039 ⟶ 1,174:
p = p + Fmt.swrite("$s$s$d", variable, symbol, pow)
} else {
p = p + Fmt.swrite("$s$sS", variable, Conv.superscript(pow))
}
} else if (pow == 1) {
Line 1,090 ⟶ 1,225:
"twelve": "twelfth"
}
 
__revIrregulars = {}
for (me in __irregularOrdinals) __revIrregulars[me.value] = me.key
 
__names = {}
for (i in 0..19) __names[__small[i]] = i
for (i in 2..9) __names[__tens[i]] = i * 10
__names["hundred"] = 100
for (i in 1..11) __names[__illions[i][1..-1]] = 10.pow(i * 3)
__names["nought"] = 0
 
__zeros = ["zero", "nought", "nil", "none", "nothing"]
 
__points = ["point", "dot", "spot"]
}
 
// Private helper function. Converts ASCII string to lower case.
// Gets or sets precision for 'f(w, n)' style convenience methods.
static precision { lower_( __precision != nulls) ?{ __precisions.bytes.map : 6{ }|b|
static precision=(p) { __precision =return String.fromByte((pb is>= Num)65 && p.isIntegerb && p ><= 090) ? pb + 32 : __precision }b)
}.join() }
 
// Gets or sets whether names are to expressed in UK English i.e. 'and' is used to connect
Line 1,186 ⟶ 1,336:
return s[0...i] + s[i..-1] + "th"
}
}
 
// Translates the name of a number or ordinal integer into that number and returns it.
// Ignores case and custom settings but recognizes all suggested alternatives and some others.
static toNum(name) {
if (!(name is String) || name == "") Fiber.abort("'name' must be a non-empty string.")
name = lower_(name).replace(",", " ").replace("-", " ").replace(" and", "")
var words = name.split(" ").where { |w| w != "" }.toList
var isNegative = words[0] == "minus" || words[0] == "negative"
if (isNegative || words[0] == "plus") words = words[1..-1]
if (words[0] == "a") words[0] = "one"
if (words[-1].endsWith("ieth")) {
words[-1] = words[-1][0..-5] + "y"
} else if(__revIrregulars.containsKey(words[-1])) {
words[-1] = __revIrregulars[words[-1]]
} else if (words[-1].endsWith("th")) {
words[-1] = words[-1][0..-3]
}
var size = words.count
if (size == 1 && __zeros.contains(words[0])) return 0
if (size == 1 && words[0] == "nan") return Num.nan
if (size == 1 && words[0] == "infinity") return isNegative ? -Num.infinity : Num.infinity
var ix = -1
for (p in __points) {
if ((ix = words.indexOf(p)) >= 0) break
}
if (ix == 0) {
words.insert(0, "zero")
ix = 1
}
var dec = ""
if (ix > 0) {
for (word in words[ix+1..-1]) dec = dec + __names[word].toString
size = ix
}
var multiplier = 1
var lastNum = -1
var sum = 0
for (i in size-1..0) {
var num = __names[words[i]]
if (!num) Fiber.abort("'%(words[i])' is not a valid cardinal number.")
if (num == lastNum) Fiber.abort("'%(name)' is not a well formed numeric string.")
if (num >= 1000) {
if (lastNum >= 100) {
Fiber.abort("'%(name)' is not a well formed numeric string.")
}
multiplier = num
if (i == 0) sum = sum + multiplier
} else if (num >= 100) {
multiplier = multiplier * 100
if (i == 0) sum = sum + multiplier
} else if (num >= 20) {
if (lastNum >= 10 && lastNum <= 90) {
Fiber.abort("'%(name)' is not a well formed numeric string.")
}
sum = sum + num*multiplier
} else {
if (lastNum >= 1 && lastNum <= 90) {
Fiber.abort("'%(name)' is not a well formed numeric string.")
}
sum = sum + num*multiplier
}
lastNum = num
}
if (dec != "") sum = sum + Num.fromString("0." + dec)
return (isNegative) ? -sum : sum
}
}
9,485

edits