Category talk:Wren-fmt: Difference between revisions

→‎Source code: Added 'jslwrite' and family methods.
(→‎Source code: Added a verb to enable a string to be commatized directly.)
(→‎Source code: Added 'jslwrite' and family methods.)
 
(37 intermediate revisions by the same user not shown)
Line 1:
===Source code===
 
<lang ecmascript>/* Module "fmt.wren" */
<syntaxhighlight lang="wren">/* Module "fmt.wren" */
 
/* Conv contains routines which do conversions between types. */
Line 40 ⟶ 41:
 
// Converts a numeric ASCII string with a base between 2 and 36 to an integer.
// The string can optionally begin with a base specifier provided it is consistent with the base.
static atoi(s, b) {
if (!(s is String && s != "" && s.count == s.bytes.count)) Fiber.abort("Argument must be an ASCII string.")
Line 52 ⟶ 54:
if (s == "") Fiber.abort("String must contain some digits.")
s = upper_(s)
if ((s.startsWith("0B") && b !== 2) || (s.startsWith("0T") && b !== 3) ||
(s.startsWith("0O") && b !== 8) || (s.startsWith("0X") && b !== 16)) {
Fiber.abort("Inconsistent base specifier.")
}
if (s.startsWith("0B") || s.startsWith("0T") || s.startsWith("0O") || s.startsWith("0X")) {
s = s[2..-1]
if (s == "") Fiber.abort("String after base specifier must contain some digits.")
Line 108 ⟶ 107:
return "%(n)%(suffix)"
}
 
// Converts an integer to its unicode superscript equivalent.
static superscript(n) {
if (!(n is Num && n.isInteger)) Fiber.abort("Argument must be an integer.")
var ss = {
"0": "⁰", "1": "¹", "2": "²", "3": "³", "4": "⁴", "5": "⁵", "6": "⁶",
"7": "⁷", "8": "⁸", "9": "⁹", "-": "⁻", "+": "⁺", "e": "ᵉ"
}
return Fmt.i(0, n).map { |d| ss.containsKey(d) ? ss[d] : d }.join()
}
 
// Converts an integer to its unicode subscript equivalent.
static subscript(n) {
if (!(n is Num && n.isInteger)) Fiber.abort("Argument must be an integer.")
var ss = {
"0": "₀", "1": "₁", "2": "₂", "3": "₃", "4": "₄", "5": "₅", "6": "₆",
"7": "₇", "8": "₈", "9": "₉", "-": "₋", "+": "₊", "e": "ₑ"
}
return Fmt.i(0, n).map { |d| ss.containsKey(d) ? ss[d] : d }.join()
}
 
// Converts a numerator and denominator to their unicode fraction equivalent.
// If there is none, returns a string of the form "n/d".
static fraction(n, d) {
if (!(n is Num && n.isInteger && n > 0)) Fiber.abort("n must be a positive integer.")
if (!(d is Num && d.isInteger && d > 0)) Fiber.abort("d must be a positive integer.")
var fracs = {
"1/4": "¼", "1/2": "½", "3/4": "¾", "1/7": "⅐", "1/9": "⅑", "1/10": "⅒",
"1/3": "⅓", "2/3": "⅔", "1/5": "⅕", "2/5": "⅖", "3/5": "⅗", "4/5": "⅘",
"1/6": "⅙", "5/6": "⅚", "1/8": "⅛", "3/8": "⅜", "5/8": "⅝", "7/8": "⅞"
}
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
}
 
// Returns the unicode infinity symbol.
static infinity { "∞" }
}
 
Line 196 ⟶ 265:
static commatize(n) { commatize(n, ",") }
 
// ConvenienceAdds method'thousand' whichseparators' commatizesto an ordinal number using a comma as the separator.
static ordinalize(n, c) { commatize(n, c) + Conv.ord(n)[-2..-1] }
 
// Convenience version of the above method which uses a comma as the separator.
static ordinalize(n) { ordinalize(n, ",") }
 
// Private helper method for 'abbreviate' method.
Line 206 ⟶ 278:
// Doesn't abbreviate a string unless at least one character would need to be suppressed.
static abbreviate(w, s, sep) {
if (!(w is Num && w.isInteger && w.abs >= 1)) Fiber.abort("Maximum width must be a non-zeropositive integer.")
if (!(sep is String)) Fiber.abort("Separator must be a string.")
if (!(s is String)) s = "%(s)"
Line 255 ⟶ 327:
// Ranks a non-negative integer 'n' i.e. expresses it in ordinal form, space padded.
static r(w, n) { (w >= 0) ? rjust(w, Conv.ord(n)) : ljust(-w, Conv.ord(n)) }
 
// As the above method but commatizes the ordinal number, using ',' as the separator.
static rc(w, n) { (w >= 0) ? rjust(w, ordinalize(n)) : ljust(-w, ordinalize(n)) }
 
// Pads a character (equivalent to the codepoint 'n') with spaces to a minimum width of 'w'.
Line 263 ⟶ 338:
// Negative 'w' left justifies, non-negative 'w' right justifies.
static s(w, v) { (w >= 0) ? rjust(w, v) : ljust(-w, v) }
 
// As 's' above but pads with leading zeros instead of spaces.
// Any minus sign will be placed before the padding.
// When used with negative 'w' behaves the same as the above method.
static sz(w, v) { (w >= 0) ? zfill(w, v) : ljust(-w, v) }
 
// Formats a string or value 'v' in commatized form, space padded, using ',' as the separator.
Line 269 ⟶ 349:
return (w >= 0) ? rjust(w, commatize(v)): ljust(-w, commatize(v))
}
 
// These methods use the appropriate 'd' format if 'v' is a safe integer or the 's' format otherwise.
static i(w, v) { (v is Num && v.isInteger && v.abs <= Conv.maxSafeInt) ? d (w, v) : s (w, v) }
static iz(w, v) { (v is Num && v.isInteger && v.abs <= Conv.maxSafeInt) ? dz(w, v) : sz(w, v) }
static ic(w, v) { (v is Num && v.isInteger && v.abs <= Conv.maxSafeInt) ? dc(w, v) : sc(w, v) }
 
// Middles a string or value 'v' within a field of minimum width 'w'. Pads with spaces.
static m(w, v) { cjust(w, v) }
 
// 'Short name' synonym for abbreviate(w, s) method except doesn't abbreviate (rather than throwing
// an error) if a width of '0' is passed.
static a(w, v) { (w != 0) ? abbreviate(w, v) : s(0, v) }
 
// Enables a value to be printed in its 'normal' form (i.e. by applying the 'toString' method),
// within a space-padded minimum field of width 'w', notwithstanding any special formatting
// that would otherwise be applied by 'short name' methods.
static n(w, v) { s(w, v.toString) }
 
// Applies the 's' format to the kind (i.e. type) of 'v'.
static k(w, v) { s(w, v.type) }
 
// Converts a list of Unicode code points to a string and then applies the 's' format to it.
static u(w, v) {
if (!(v is List)) Fiber.abort("Second argument must be list of code points.")
var str = ""
for (c in v) str = str + String.fromCodePoint(c)
return s(w, str)
}
 
// Embeds a string or value 'v' in 'cc', a string with no more than two characters.
Line 288 ⟶ 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.
// It then converts it to exponential format and formats the mantissa to 'p' decimal places.
// The result is then padded with spaces to a minimum width 'w'.
// Negative 'w' left justifies, non-negative 'w' right justifies.
static e(w, n, p) {
var f = Fmt.h(w, n, 14).trim()
if (f.contains("e") || n.isInfinity || n.isNan) return Fmt.s(w, n) // use 'normal' representation
var dix = f.indexOf(".")
if (dix >= 0) {
f = f.replace(".", "")
} else {
dix = f.count
}
// look for index of first non-zero digit if there is one
var nzix = -1
var i = (f[0] == "-") ? 1 : 0
while (i < f.count) {
if (f[i] != "0") {
nzix = i
break
}
i = i + 1
}
if (nzix == -1) return "0e00"
var delta = dix - nzix
f = (nzix+1<f.count) ? f[nzix] + "." + f[nzix+1..-1] : f[nzix]
if (n < 0) f = "-" + f
f = Fmt.h(p+2, Num.fromString(f), p).trim()
var exp = (delta >= 0) ? Fmt.dz(2, delta-1) : Fmt.dz(3, delta-1)
return Fmt.s(w, "%(f)e%(exp)")
}
 
// 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'.
static N(w, n) { Fmt.s(w, Name.fromNum(n)) }
 
// Applies the 's' format to the ordinal name of an integer 'i'.
static O(w, i) { Fmt.s(w, Name.ordinal(i)) }
 
// Applies the 's' format to the superior (or superscript) version of a number 'n'.
static S(w, n) { Fmt.s(w, Conv.superscript(n)) }
 
// 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 325 ⟶ 497:
static g(w, n, p) {
var f = f(w, n, p)
if (f.contains(".e")) &&return f[-1] == "0") {
if (f.contains(".") && (f[-1] == "0" || f[-1] == " ")) {
var l1 = f.count
f = f.trimEnd("0 ")
if (f[-1] == ".") f = f + "0"
f = f + (" " * (l1 - f.count))
Line 333 ⟶ 506:
return f
}
 
// Works like 'f' except replaces any trailing zeros after the decimal point with spaces.
// If the resulting string would end with a decimal point, that is also replaced with a space.
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
f = f.trimEnd("0 ")
if (f[-1] == ".") f = f[0..-2]
f = f + (" " * (l1 - f.count))
}
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 339 ⟶ 532:
static fz(w, n, p) { (w >= 0) ? zfill(w, f(w, n, p).trimStart()) : f(w, n, p) }
static gz(w, n, p) { (w >= 0) ? zfill(w, g(w, n, p).trimStart()) : g(w, n, p) }
static hz(w, n, p) { (w >= 0) ? zfill(w, h(w, n, p).trimStart()) : h(w, n, p) }
 
// As above but prepends non-negative numbers with a '+' sign.
static fp(w, n, p) { signFloat_("f", w, n, p, "+") }
static gp(w, n, p) { signFloat_("g", w, n, p, "+") }
static hp(w, n, p) { signFloat_("h", w, n, p, "+") }
 
// As above but prepends non-negative numbers with a space.
static fm(w, n, p) { signFloat_("f", w, n, p, " ") }
static gm(w, n, p) { signFloat_("g", w, n, p, " ") }
static hm(w, n, p) { signFloat_("h", w, n, p, " ") }
 
// Private helper method for signing floating point numbers.
static signFloat_(fn, w, n, p, ch) {
var fmt = "$%(w).%(p)%(fn)"
if (n < 0) return swrite(fmt, n)
if (n > 0) return swrite(fmt, -n).replace("-", ch)
return swrite(fmt, -1).replace("-1", "%(ch)0")
}
 
// Formats the integer part of 'n' in commatized form, space padded,
Line 357 ⟶ 569:
static gc(w, n, p) {
var f = fc(w, n, p)
if (f.contains(".e")) &&return f[-1] == "0") {
if (f.contains(".") && (f[-1] == "0" || f[-1] == " ")) {
var l1 = f.count
f = f.trimEnd("0 ")
if (f[-1] == ".") f = f + "0"
f = f + (" " * (l1 - f.count))
Line 366 ⟶ 579:
}
 
// Works like 'fc' except replaces any trailing zeros after the decimal point with spaces.
// Convenience versions of the above methods which use the default precision.
// If the resulting string would end with a decimal point, that is also replaced with a space.
static f(w, n) { f(w, n, precision) }
static ghc(w, n) { g(w, n, precisionp) }{
static fz(w, n) { fzvar f = fc(w, n, precisionp) }
static gz(w, n) { gz(w,if n, precision(f.contains("e")) return }f
if (f.contains(".") && (f[-1] == "0" || f[-1] == " ")) {
static fc(w, n) { fc(w, n, precision) }
var l1 = f.count
static gc(w, n) { gc(w, n, precision) }
f = f.trimEnd("0 ")
if (f[-1] == ".") f = f[0..-2]
f = f + (" " * (l1 - f.count))
}
return f
}
 
// Works like 'hc' except, if right justified, removes any trailing spaces before rejustifying.
// Private worker method which calls a'short name' method and returns its result.
static callFn_jc(fnw, n, p) { (w >= 0) ? rjust(w, ehc(w, n, p).trimEnd()) {: hc(w, n, p) }
 
return (fn == "d") ? d(w, e) :
// Works like 'jc' except derives the number to be formatted from its name.
(fn == "b") ? b(w, e) :
static lc(fnw, ==name, "t"p) { ? tjc(w, eName.toNum(name), p) :}
 
(fn == "o") ? o(w, e) :
// Applies the 'f' format to each component, (fn == "x") and ? x(wy, e)of a complex number 'n' :
// before joining them together in the form x ± yi. (fn == "X") ? Fmt.X(w, e) :
static (fn == "r") ? rz(w, en, p) :{
if (fnn ==is "c"Num) return ? cf(w, en, p) :
if (fnn.type.toString =!= "sComplex") ? sFiber.abort(w,"Argument e)must be a complex or real :number.")
var real (fn == "m") ? mf(w, en.real, p) :
var sign = (fnn.imag =>= "q"0) ? q(e)" + " : " - :"
var imag (fn == "f") ? f(w, en.imag.abs, p) :
if (fnw ==< "g"0) imag ?= gimag.trimEnd(w," e, p") :
return real + sign + imag (fn ==+ "dzi") ? dz(w, e) :
}
(fn == "bz") ? bz(w, e) :
 
(fn == "tz") ? tz(w, e) :
// Applies the 'fp' and 'f' formats respectively to each (fncomponent, ==x "oz")and ? oz(wy, e) of :a
// complex number 'n' before joining them together in the form (fnx ==± "xz")yi. ? xz(w, e) :
static zp(fnw, == "Xz") ? Fmt.Xz(wn, ep) :{
if (fnn ==is "fz"Num) ?return fzfp(w, en, p) :
if (fnn.type.toString =!= "gzComplex") ? gzFiber.abort(w,"Argument e,must p)be a complex :or real number.")
var real (fn == "dp") ? dpfp(w, en.real, p) :
var sign = (fnn.imag =>= "dm"0) ? dm(w," e)+ " : " - :"
var imag (fn == "dc") ? dcf(w, en.imag.abs, p) :
if (w < 0) imag = imag.trimEnd(fn ==" "sc") ? sc(w, e) :
return real + sign + imag (fn ==+ "fci") ? fc(w, e, p) :
}
(fn == "gc") ? gc(w, e, p) : Fiber.abort("Method not recognized.")
 
// Applies the 'fm' 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 zm(w, n, p) {
if (n is Num) return fm(w, n, p)
if (n.type.toString != "Complex") Fiber.abort("Argument must be a complex or real number.")
var real = fm(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"
}
 
// Convenience versions of the above methods which use the default precision.
static e(w, n) { e(w, n, precision) }
static E(w, n) { Fmt.E(w, n, precision) }
static f(w, n) { f(w, n, precision) }
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) }
static gz(w, n) { gz(w, n, precision) }
static hz(w, n) { hz(w, n, precision) }
static fp(w, n) { fp(w, n, precision) }
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) }
static hm(w, n) { hm(w, n, precision) }
static zm(w, n) { zm(w, n, precision) }
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) {
return (fn == "d") ? d(w, v) :
(fn == "b") ? b(w, v) :
(fn == "t") ? t(w, v) :
(fn == "o") ? o(w, v) :
(fn == "x") ? x(w, v) :
(fn == "X") ? Fmt.X(w, v) :
(fn == "r") ? r(w, v) :
(fn == "c") ? c(w, v) :
(fn == "s") ? s(w, v) :
(fn == "i") ? i(w, v) :
(fn == "m") ? m(w, v) :
(fn == "a") ? a(w, v) :
(fn == "n") ? n(w, v) :
(fn == "k") ? k(w, v) :
(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) :
(fn == "N") ? Fmt.N(w, v) :
(fn == "O") ? Fmt.O(w, v) :
(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) :
(fn == "bz") ? bz(w, v) :
(fn == "tz") ? tz(w, v) :
(fn == "oz") ? oz(w, v) :
(fn == "xz") ? xz(w, v) :
(fn == "Xz") ? Fmt.Xz(w, v) :
(fn == "sz") ? sz(w, v) :
(fn == "iz") ? iz(w, v) :
(fn == "fz") ? fz(w, v, p) :
(fn == "gz") ? gz(w, v, p) :
(fn == "hz") ? hz(w, v, p) :
(fn == "fp") ? fp(w, v, p) :
(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) :
(fn == "hm") ? hm(w, v, p) :
(fn == "zm") ? zm(w, v, p) :
(fn == "dp") ? dp(w, v) :
(fn == "dm") ? dm(w, v) :
(fn == "dc") ? dc(w, v) :
(fn == "rc") ? rc(w, v) :
(fn == "sc") ? sc(w, v) :
(fn == "ic") ? ic(w, v) :
(fn == "fc") ? fc(w, v, p) :
(fn == "gc") ? gc(w, v, p) :
(fn == "hc") ? hc(w, v, p) :
(fn == "jc") ? jc(w, v, p) :
(fn == "lc") ? lc(w, v, p) : Fiber.abort("Method not recognized.")
}
 
Line 408 ⟶ 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', 'fzh', 'gzj', 'fcl', or'z', 'gcfz', but'gz', is'hz', ignored'fp', otherwise.'gp',
// 'hp', 'zp', 'fm', 'gm', 'hm', 'zm', 'fc', 'gc', 'hc', 'jc' or 'lc' 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 426 ⟶ 751:
// for some parameters.
static v(fn, w, seq, p, sep, bb) { v(fn, w, seq, p, sep, bb, "") }
static v(fn, w, seq, p, sep) { v(fn, w, seq, p, sep, "[]", "") }
static v(fn, w, seq, p) { v(fn, w, seq, p, ", ", "[]", "") }
static v(fn, w, seq) { v(fn, w, seq, precision, ", ", "[]", "") }
 
// As the 'v' method but abridges 'seq' to a maximum number
// of elements 'n' (non-overlapping) at either end or, if 'n' is negative,
// from the front only, using 'aa' as the separator. Doesn't abridge 'seq'
// unless at least one element would need to be suppressed.
static va(fn, w, seq, p, sep, bb, cc, n, aa) {
if (!(n is Num && n.isInteger && n.abs >= 1)) Fiber.abort("'n' must be a non-zero integer.")
if (!(aa is String)) Fiber.abort("'aa' must be a string.")
var c = seq.count
if (c <= ((n < 0) ? -n : 2*n)) return Fmt.v(fn, w, seq, p, sep, bb, cc)
var left = (seq is List) ? seq[0...n.abs] : seq.take(n.abs)
var sleft = Fmt.v(fn, w, left, p, sep, "", cc) + sep
var sright = ""
if (n > 0) {
var right = (seq is List) ? seq[-n..-1] : seq.skip(c - n)
sright = sep + Fmt.v(fn, w, right, p, sep, "", cc)
}
var res = sleft + aa + sright
if (bb != "") res = Fmt.q(res, bb)
return res
}
 
// Applies a 'short' formatting method to each element of a two-dimensional
// list or sequence 'm'.
// A Matrix or CMatrix object is automatically converted to a 2D list of numbers.
// The parameters: 'fn', 'w', 'p', 'sep', 'bb' and 'cc'
// are applied using the 'v' method to each row of 'm'.
// The rows are then joined together using the separator 'ss'.
static v2(fn, w, m, p, sep, bb, cc, ss) {
var s = m.type.toString
if (s == "Matrix" || s == "CMatrix") m = m.toList
var nr = m.count
if (nr == 0) return ""
var l = List.filled(nr, "")
var i = 0
for (row in m) {
l[i] = v(fn, w, row, p, sep, bb, cc)
i = i + 1
}
return l.join(ss)
}
 
// Convenience versions of the above method which use default values
// for some parameters.
static v2(fn, w, m, p, sep, bb, cc) { v(fn, w, m, p, sep, bb, cc, "\n") }
static v2(fn, w, m, p, sep, bb) { v(fn, w, m, p, sep, bb, "", "\n") }
static v2(fn, w, m, p, sep) { v(fn, w, m, p, sep, "|", "", "\n") }
static v2(fn, w, m, p) { v(fn, w, m, p, " ", "|", "", "\n") }
static v2(fn, w, m) { v(fn, w, m, precision, " ", "|", "", "\n") }
 
// Provides a 'sprintf' style method where the arguments are passed in a separate list and
Line 433 ⟶ 808:
// it is an error to provide insufficient arguments. Verbs must be given in this form:
// $[flag][width][.precision][letter] of which all bracketed items except [letter] are optional.
// The letter must be one of the 'short' methods: b, c, d, f, g, m, o, q, r, s, t, v, x or X.
// 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', method'fp', 'gp', 'hp' or 'zp' methods)
// (space) leaves a space for the sign but only prints minus ('dm', method'fm', 'gm', 'hm' or 'zm' methods)
// , commatizes the following number ('dc', 'rc', 'sc', 'ic', 'fc', 'gc, 'hc', 'jc' or 'gclc' 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
// 0 when followed by an explicit width, pads with leading zeros rather than spaces:
// ('dz', 'bz', 'tz', 'oz', 'xz, 'Xz', 'sz', iz', 'fz', 'gz' and 'hz' methods)
// If present, the width is the minimum width (+/-) to be passed to the appropriate method.
// It doesn't include any '#' flag prefix. If [width] is absent, a width of zeroone 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 'gz' 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
// and applies the 'v' method to it. However, the 'sep' parameter is always a single space
// and the 'bb' and 'cc' parameters are always empty strings. TheFinally, '#'to flagprint hasa no effect.literal
// dollar symbol use $$.
static slwrite(fmt, a) {
if (!(fmt is String)) Fiber.abort("First argument must be a string.")
Line 460 ⟶ 839:
 
// Gets the next numeric string from the format.
var getNumber = Fn.new { |minusAllowed|
i = i + 1
if (i == le) Fiber.abort("Invalid format string.")
cp = cps[i]
var ns = ""
if (cp == 45) {
if (!minusAllowed) Fiber.abort("Invalid format string")
ns = "-"
i = i + 1
if (i == le) Fiber.abort("Invalid format string.")
cp = cps[i]
}
while (cp >= 48 && cp <= 57) {
ns = ns + Conv.itoc(cp)
Line 470 ⟶ 856:
if (i == le) Fiber.abort("Invalid format string.")
cp = cps[i]
}
if (ns == "-") Fiber.abort("Invalid format string.")
return ns
}
Line 482 ⟶ 869:
i = i + 1
} else {
var ns = getNumber.call(true)
if (ns != "" && "*+,- #".codePoints.contains(cp)) {
Fiber.abort("Invalid format string.")
}
var plus = false
var comma = false
var minus = false
var space = false
var hash = false
var fn = ""
var ds = ""
if ("bcdfgmoqrstxXabBcdeEfFghiIjklmnNoOPqrRsStuUxXyz".codePoints.contains(cp)) { // format letter
fn = Conv.itoc(cp)
} else if (cp == 42) { // star
Line 504 ⟶ 890:
i = i + 1
cp = cps[i]
if (cp == 46) ds = getNumber.call(false)
} else if (cp == 43) { // plus sign
plus = true
ns = getNumber.call(true)
if (cp == 46) ds = getNumber.call(false)
} else if (cp == 44) { // comma
comma = true
ns = getNumber.call(true)
if (cp == 46) ds = getNumber.call(false)
} else if (cp == 45) { // minus sign
minus = true
ns = getNumber.call()
if (cp == 46) ds = getNumber.call()
} else if (cp == 46) { // dot
ds = getNumber.call(false)
} else if (cp == 32) { // space
space = true
ns = getNumber.call(true)
if (cp == 46) ds = getNumber.call(false)
} else if (cp == 35) { // hash
hash = true
ns = getNumber.call(true)
if (cp == 46) ds = getNumber.call(false)
} else {
Fiber.abort("Unrecognized character in format string.")
Line 532 ⟶ 914:
 
if (fn == "") {
if (!"bcdfgmoqrstxXabBcdeEfFghiIjklmnNoOPqrRsStuUxXyz".codePoints.contains(cp)) {
Fiber.abort("Unrecognized character in format string.")
}
Line 545 ⟶ 927:
fn = "dc"
}
} else if ((fn == "sf" || fn == "fg" || fn == "gh" || fn == "z") && commaplus) {
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"
}
if (ns == "") ns = "1"
if (ns[0] == "0" && ns.count > 1 && "dbtoxXfgdbtoxXsifgh".contains(fn[0])) {
fn = fn[0] + "z"
}
var nw = Num.fromString(ns)
var w = minus ? -n : n
var p = (ds != "") ? Num.fromString(ds) : precision
if (next < a.count) {
var e = a[next]
if ((e is Sequence) && !(e is String) && !"nuFPR".contains(fn)) {
sif =(hash s&& + Fmt"btodxX".vcontains(fn,[0])) w, e, p, " ", "", ""){
var rr = []
for (ee in e) {
var r = callFn_(fn, w, ee, p)
if (r[0] == "-") {
r = "-" + Conv.prefixes[fn[0]] + r[1..-1]
} else {
r = Conv.prefixes[fn[0]] + r
}
rr.add(r)
}
s = s + rr.join(" ")
} else {
s = s + Fmt.v(fn, w, e, p, " ", "", "")
}
} else {
var r = callFn_(fn, w, e, p)
Line 580 ⟶ 980:
}
 
// Convenience versions of the 'slwrite' method which allow up to 56 arguments
// to be passed individually rather than in a list.
static swrite(fmt, a1, a2, a3, a4, a5), a6) { slwrite(fmt, [a1, a2, a3, a4, a5, a6]) }
static swrite(fmt, a1, a2, a3, a4, a5) { slwrtieslwrite(fmt, [a1, a2, a3, a4, a5]) }
static swrite(fmt, a1, a2, a3, a4) { slwrite(fmt, [a1, a2, a3, a4]) }
static swrite(fmt, a1, a2, a3) { slwrite(fmt, [a1, a2, a3]) }
static swrite(fmt, a1, a2) { slwrite(fmt, [a1, a2]) }
static swrite(fmt, a1) { slwrite(fmt, [a1]) }
 
// Applies slwrite to the arguments and then 'writes' it (no following \n) to stdout.
static write(fmt, a1, a2, a3, a4, a5, a6) { System.write(slwrite(fmt, [a1, a2, a3, a4, a5, a6])) }
static write(fmt, a1, a2, a3, a4, a5) { System.write(slwrite(fmt, [a1, a2, a3, a4, a5])) }
static write(fmt, a1, a2, a3, a4) { System.write(slwrite(fmt, [a1, a2, a3, a4])) }
static write(fmt, a1, a2, a3) { System.write(slwrite(fmt, [a1, a2, a3])) }
static write(fmt, a1, a2) { System.write(slwrite(fmt, [a1, a2])) }
static lwritewrite(fmt, aa1) { System.write(slwrite(fmt, a[a1])) }
static lwrite(fmt, a) { System.write(slwrite(fmt, a)) }
 
// Applies slwrite to the arguments and then 'prints' it (with a following \n) to stdout.
static print(fmt, a1, a2, a3, a4, a5, a6) { System.print(slwrite(fmt, [a1, a2, a3, a4, a5, a6])) }
static print(fmt, a1, a2, a3, a4, a5) { System.print(slwrite(fmt, [a1, a2, a3, a4, a5])) }
static print(fmt, a1, a2, a3, a4) { System.print(slwrite(fmt, [a1, a2, a3, a4])) }
static print(fmt, a1, a2, a3) { System.print(slwrite(fmt, [a1, a2, a3])) }
static print(fmt, a1, a2) { System.print(slwrite(fmt, [a1, a2])) }
static lprintprint(fmt, aa1) { System.print(slwrite(fmt, a[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
// with a maximum of 'rowSize' elements per row. 'fmt' is applied individually
// to each element and formatted elements are separated by 'sep'.
static tprint(fmt, a, rowSize, sep) {
if (!(fmt is String)) Fiber.abort("First argument must be a string.")
if (!(a is Sequence)) Fiber.abort("Second argument must be a sequence.")
if (!((rowSize is Num) && rowSize.isInteger && rowSize > 0)) {
Fiber.abort("Third argument must be a positive integer.")
}
var ac = a.count
var count = 0
for (e in a) {
Fmt.write(fmt, e)
count = count + 1
if (count % rowSize == 0 || count == ac) {
System.print()
} else System.write(sep)
}
}
 
// Prints (with a following \n) a sequence 'a' to stdout in columnar form
// with a maximum of 'colSize' elements per column. 'fmt' is applied individually
// to each element and formatted elements are separated by 'sep'.
static cprint(fmt, a, colSize, sep) {
if (!(fmt is String)) Fiber.abort("First argument must be a string.")
if (!(a is Sequence)) Fiber.abort("Second argument must be a sequence.")
if (!((colSize is Num) && colSize.isInteger && colSize > 0)) {
Fiber.abort("Third argument must be a positive integer.")
}
if (!(a is List)) a = a.toList
var ac = a.count
for (i in 0...colSize) {
var j = i
while (true) {
Fmt.write(fmt, a[j])
j = j + colSize
if (j >= ac) break
System.write(sep)
}
System.print()
}
}
 
// Convenience versions of the above methods which use a single space for the separator.
static tprint(fmt, a, rowSize) { tprint(fmt, a, rowSize, " ") }
static cprint(fmt, a, colSize) { cprint(fmt, a, colSize, " ") }
 
// Prints (with a following \n) an array 'a' to stdout using a typical layout.
// An 'array' for this purpose is a list or sequence of objects.
// The parameters: 'w', 'p' and 'bb' are applied using the 'v' method to 'a'.
// The settings for the other parameters are:
// 'fn' = "f" for numbers, "z" for complex numbers,"s" otherwise
// ('p' is ignored for latter) 'sep' = " ", 'cc' = "".
// The 'rns' parameter, if true, leaves a space for the sign of a real number but only prints minus.
static aprint(a, w, p, bb, rns) {
if (!(a is Sequence)) Fiber.abort("Second argument must be a sequence.")
if (!(a is List)) a = a.toList
var fn = (a.count > 0 && (a[0] is Num)) ? "f" :
(a.count > 0 && (a[0].type.toString == "Complex")) ? "z" : "s"
if (rns && fn != "s") fn = fn + "m"
System.print(Fmt.v(fn, w, a, p, " ", bb, ""))
}
 
// Convenience versions of the above method which use default values for
// some parameters.
static aprint(a, w, p, bb) { aprint(a, w, p, bb, false) }
static aprint(a, w, p) { aprint(a, w, p, "[]", false) }
static aprint(a, w) { aprint(a, w, precision, "[]", false) }
static aprint(a) { aprint(a, 0, precision, "[]", false) }
 
// Prints (with a following \n) a matrix 'm' to stdout using a typical layout.
// A 'matrix' for this purpose is a two-dimensional list or sequence of objects.
// A Matrix or CMatrix object is automatically converted to a 2D list of numbers.
// The parameters: 'w', 'p' and 'bb' are applied using the 'v2' method to 'm'.
// The settings for the other parameters are:
// 'fn' = "f" for numbers, "z" for complex numbers, "s" otherwise
// ('p' is ignored for latter) 'sep' = " ", 'cc' = "", 'ss' = "\n".
// The rns' parameter, if true, leaves a space for the sign of a real number but only prints minus.
static mprint(m, w, p, bb, rns) {
if (!(m is List)) m = m.toList
var fn = (m.count > 0 && m[0].count > 0 && (m[0][0] is Num)) ? "f" :
(m.count > 0 && m[0].count > 0 && (m[0][0].type.toString == "Complex")) ? "z" : "s"
if (rns && fn != "s") fn = fn + "m"
System.print(Fmt.v2(fn, w, m, p, " ", bb, "", "\n"))
}
 
// Convenience versions of the above method which use default values for
// some parameters.
static mprint(m, w, p, bb) { mprint(m, w, p, bb, false) }
static mprint(m, w, p) { mprint(m, w, p, "|", false) }
static mprint(m, w) { mprint(m, w, precision, "|", false) }
static mprint(m) { mprint(m, 0, precision, "|", false) }
 
// Formats a polynomial as a string.
// Polynomials are represented by an ordered list of coefficients
// from the term with the highest degree down to the constant term.
// Unless there is only one term, terms with zero coefficents are suppressed.
// Unless it's the constant term, the '1' in coefficients of exactly ± 1 is also suppressed.
// 'fmt' is applied to each coefficient, 'symbol' is the exponentiation
// symbol (e.g. "^") and 'variable' (e.g. "x") is the variable name.
// If symbol = "", unicode superscript characters are used for the degree.
static spwrite(fmt, coefs, symbol, variable) {
if (!(fmt is String)) Fiber.abort("First argument must be a string.")
if (!(coefs is List) || coefs.count == 0 || !(coefs[0] is Num)) {
Fiber.abort("Second argument must be a non-empty ordered list of numbers.")
}
if (!(symbol is String)) Fiber.abort("Third argument must be a string.")
if (!(variable is String)) Fiber.abort("Fourth argument must be a string.")
var degree = coefs.count - 1
if (degree == 0 || coefs.all { |c| c == 0 }) return Fmt.swrite(fmt, coefs[0])
var p = ""
for (i in 0..degree) {
var coef = coefs[i]
var pow = degree - i
if (coef == 0) continue
if (coef > 0) {
var t = Fmt.swrite(fmt, coef)
if (pow > 0 && coef == 1 && t[-1] == "1") t = t[0...-1]
p = p + " + " + t
} else {
var t = Fmt.swrite(fmt, -coef)
if (pow > 0 && coef == -1 && t[-1] == "1") t = t[0...-1]
p = p + " - " + t
}
if (pow > 1) {
if (symbol != "") {
p = p + Fmt.swrite("$s$s$d", variable, symbol, pow)
} else {
p = p + Fmt.swrite("$s$S", variable, pow)
}
} else if (pow == 1) {
p = p + variable
}
}
p = p.startsWith(" + ") ? p[3..-1] : "-" + p[3..-1]
return p
}
 
// Prints a polynomial without (pwrite) or with (pprint) a following \n to stdout.
static pwrite(fmt, coefs, symbol, variable) {
System.write(spwrite(fmt, coefs, symbol, variable))
}
 
static pprint(fmt, coefs, symbol, variable) {
System.print(spwrite(fmt, coefs, symbol, variable))
}
}
 
/* Name contains infrastructure and routines for converting numbers to their English names. */
class Name {
// Private method to initialize static fields.
static init_() {
__uk = false
__neg = "minus"
__point = "point"
__zero = "zero"
__small = [
"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven",
"twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"
]
 
__tens = ["", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"]
 
__illions = [
"", " thousand", " million", " billion"," trillion", " quadrillion", " quintillion",
" sextillion", " septillion", " octillion", " nonillion", " decillion"
]
__irregularOrdinals = {
"nought": "noughth",
"one": "first",
"two": "second",
"three": "third",
"five": "fifth",
"eight": "eighth",
"nine": "ninth",
"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.
static lower_(s) { s.bytes.map { |b|
return String.fromByte((b >= 65 && b <= 90) ? b + 32 : b)
}.join() }
 
// Gets or sets whether names are to expressed in UK English i.e. 'and' is used to connect
// double or single digit numbers with larger numbers. The default is 'false'.
static uk { __uk }
static uk=(b) { __uk = (b is Bool) ? b : __uk}
 
// Gets or sets the prefix word for negative numbers. The default is "minus" though another
// possibility is "negative".
static negative { __neg }
static negative=(n) { __neg = (n is String) ? n : __neg }
 
// Gets or sets the word for the decimal point in non-integral numbers. The default is "point"
// though other possibilities are "dot" or "spot".
static point { __point }
static point=(p) { __point = (p is String) ? p : __point }
 
// Gets or sets the word for the number '0'. The default is "zero" though another
// possibility is 'nought'.
static zero { __zero }
static zero=(z) { __zero = (z is String) ? __small[0] = z : __zero }
 
// Returns the name of a number which can be positive or negative and can include a decimal point.
// Note that this is unsafe for numbers with an absolute value >= 2^53 but may work in some cases.
static fromNum(n) {
if (!(n is Num)) Fiber.abort("'n' must be a number.")
if (n.isInfinity || n.isNan) return Fmt.s(0, n)
var f = 0
if (!n.isInteger) {
f = n.fraction
n = n.truncate
}
var and = __uk ? "and " : ""
var t = ""
if (n < 0) {
t = __neg + " "
n = -n
}
if (n < 20) {
t = t + __small[n]
} else if (n < 100) {
t = t + __tens[(n/10).floor]
var s = n % 10
if (s > 0) t = t + "-" + __small[s]
} else if (n < 1000) {
t = t + __small[(n/100).floor] + " hundred"
var s = n % 100
if (s > 0) t = t + " " + and + fromNum(s)
} else {
var sx = ""
var i = 0
while (n > 0) {
var p = n % 1000
n = (n/1000).floor
if (p > 0) {
var ix = fromNum(p) + __illions[i]
if (sx != "") ix = ix + " " + sx
sx = ix
}
i = i + 1
}
t = t + sx
}
if (f > 0) {
t = t + " " + __point
for (d in f.toString.skip(2)) {
t = t + " %(__small[Num.fromString(d)])"
}
}
return t
}
 
// Returns the ordinal name of an integer (including negative integers).
// Note that this is unsafe for integers with an absolute value >= 2^53 but may work in some cases.
static ordinal(n) {
if (!(n is Num && n.isInteger)) Fiber.abort("'n' must be an integer.")
var s = fromNum(n)
var r = s[-1..0]
var i1 = r.indexOf(" ")
if (i1 != -1) i1 = s.count - 1 - i1
var i2 = r.indexOf("-")
if (i2 != -1) i2 = s.count - 1 - i2
var i = (i1 > i2) ? i1 : i2
i = i + 1
var x = __irregularOrdinals[s[i..-1]]
if (x) {
return s[0...i] + x
} else if (s[-1] == "y") {
return s[0...i] + s[i..-2] + "ieth"
} else {
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
}
}
 
Name.init_()</syntaxhighlight>
// Type aliases for classes in case of any name clashes with other modules.
var Fmt_Conv = Conv
var Fmt_Fmt = Fmt</lang>
9,476

edits