Category talk:Wren-fmt: Difference between revisions

Content added Content deleted
(→‎Source code: Added 'numbers to names' routines, plus several other minor improvements.)
(→‎Source code: Added Name.toNum method and two more floating point formats.)
Line 466: Line 466:
return f
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.
// As above but pads with leading zeros instead of spaces.
Line 530: Line 536:
return f
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'
// Applies the 'f' format to each component, x and y, of a complex number 'n'
Line 561: Line 573:
static g(w, n) { g(w, n, precision) }
static g(w, n) { g(w, n, precision) }
static h(w, n) { h(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 z(w, n) { z(w, n, precision) }
static fz(w, n) { fz(w, n, precision) }
static fz(w, n) { fz(w, n, precision) }
Line 574: Line 588:
static fc(w, n) { fc(w, n, precision) }
static fc(w, n) { fc(w, n, precision) }
static gc(w, n) { gc(w, n, precision) }
static gc(w, n) { gc(w, n, precision) }
static hc(w, n) { hc(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.
// Private worker method which calls a 'short name' method and returns its result.
static callFn_(fn, w, v, p) {
static callFn_(fn, w, v, p) {
Line 603: Line 619:
(fn == "g") ? g(w, v, p) :
(fn == "g") ? g(w, v, p) :
(fn == "h") ? h(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 == "z") ? z(w, v, p) :
(fn == "dz") ? dz(w, v) :
(fn == "dz") ? dz(w, v) :
Line 630: Line 648:
(fn == "fc") ? fc(w, v, p) :
(fn == "fc") ? fc(w, v, p) :
(fn == "gc") ? gc(w, v, p) :
(fn == "gc") ? gc(w, v, p) :
(fn == "hc") ? hc(w, v, p) : Fiber.abort("Method not recognized.")
(fn == "hc") ? hc(w, v, p) :
(fn == "jc") ? jc(w, v, p) :
(fn == "lc") ? lc(w, v, p) : Fiber.abort("Method not recognized.")
}
}


Line 636: Line 656:
// The method to be applied is specified (as a string) in 'fn'.
// 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'
// The parameters to be passed to the method are specified in 'w' and 'p'
// 'p' is needed for 'e', 'E', 'f', 'g', 'h', 'z', 'fz', 'gz', 'hz', 'fp', 'gp', 'hp',
// 'p' is needed for 'e', 'E', 'f', 'g', 'h', 'j', 'l', 'z', 'fz', 'gz', 'hz', 'fp', 'gp',
// 'fm', 'gm', 'hm', 'zm', 'fc', 'gc' or 'hc' but is ignored otherwise.
// 'hp', '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'.
// The resulting strings are then joined together using the separator 'sep'.
// having first applied the 'q' method, with parameter 'cc', to each of them.
// having first applied the 'q' method, with parameter 'cc', to each of them.
Line 713: Line 733:
// $[flag][width][.precision][letter] of which all bracketed items except [letter] are optional.
// $[flag][width][.precision][letter] of which all bracketed items except [letter] are optional.
// The letter must be one of the 'short' methods:
// The letter must be one of the 'short' methods:
// a, b, c, d, e, E, f, g, h, i, I, k, m, n, N, o, O, q, r, s, S, t, u, x, X or z.
// a, b, c, d, e, E, f, g, h, i, I, j, k, l, m, n, N, o, O, q, r, s, S, t, u, x, X or z.
// If present, the flag (there can only be one) must be one of the following:
// If present, the flag (there can only be one) must be one of the following:
// + always prints a + or - sign ('dp', 'fp', 'gp' or 'hp' methods)
// + always prints a + or - sign ('dp', 'fp', 'gp' or 'hp' methods)
// (space) leaves a space for the sign but only prints minus ('dm', 'fm', 'gm', 'hm' or 'zm' 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' or 'hc' methods)
// , commatizes the following number ('dc', 'rc', 'sc', 'ic', 'fc', 'gc, 'hc', 'jc' or 'lc' methods)
// # adds the appropriate prefix for the number formats: b, t, o, d, x and X
// # 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
// * reads the width from the argument before the one to be formatted
Line 725: Line 745:
// It doesn't include any '#' flag prefix. If [width] is absent, a width of one is passed.
// 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
// If present, the precision is the number of decimal places to be passed to the appropriate
// 'e', 'E', 'f', 'g', 'h' or 'z' style method. If absent, the default precision is passed.
// '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 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
// Where one of the arguments is a sequence (other than a string) this method senses it
Line 783: Line 803:
var fn = ""
var fn = ""
var ds = ""
var ds = ""
if ("abcdeEfghiIkmnNoOqrsStuxXz".codePoints.contains(cp)) { // format letter
if ("abcdeEfghiIjklmnNoOqrsStuxXz".codePoints.contains(cp)) { // format letter
fn = Conv.itoc(cp)
fn = Conv.itoc(cp)
} else if (cp == 42) { // star
} else if (cp == 42) { // star
Line 818: Line 838:


if (fn == "") {
if (fn == "") {
if (!"abcdeEfghiIkmnNoOqrsStuxXz".codePoints.contains(cp)) {
if (!"abcdeEfghiIjklmnNoOqrsStuxXz".codePoints.contains(cp)) {
Fiber.abort("Unrecognized character in format string.")
Fiber.abort("Unrecognized character in format string.")
}
}
Line 836: Line 856:
fn = fn + "m"
fn = fn + "m"
} else if ((fn == "r" || fn == "s" || fn == "i" || fn == "f" ||
} else if ((fn == "r" || fn == "s" || fn == "i" || fn == "f" ||
fn == "g" || fn == "h") && comma) {
fn == "g" || fn == "h" || fn == "j" || fn == "l") && comma) {
fn = fn + "c"
fn = fn + "c"
}
}
Line 1,039: Line 1,059:
p = p + Fmt.swrite("$s$s$d", variable, symbol, pow)
p = p + Fmt.swrite("$s$s$d", variable, symbol, pow)
} else {
} else {
p = p + Fmt.swrite("$s$s", variable, Conv.superscript(pow))
p = p + Fmt.swrite("$s$S", variable, pow)
}
}
} else if (pow == 1) {
} else if (pow == 1) {
Line 1,090: Line 1,110:
"twelve": "twelfth"
"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 { ( __precision != null) ? __precision : 6 }
static lower_(s) { s.bytes.map { |b|
static precision=(p) { __precision = ((p is Num) && p.isInteger && p >= 0) ? p : __precision }
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
// Gets or sets whether names are to expressed in UK English i.e. 'and' is used to connect
Line 1,186: Line 1,221:
return s[0...i] + s[i..-1] + "th"
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
}
}
}
}