// Helpers reproducing the exact semantics of the Python `str` methods that
// difflib relies on (`isspace`, `strip`, `rstrip`, `expandtabs`, and
// code-point-wise ordering).
///|
/// Python's `str.isspace()` for a single character: characters whose
/// bidirectional type is WS, B or S, or whose general category is Zs.
fn is_py_space(c : Char) -> Bool {
match c {
'\t' | '\n' | '\u{0b}' | '\u{0c}' | '\r' => true
'\u{1c}'..='\u{1f}' => true
' ' | '\u{85}' | '\u{a0}' | '\u{1680}' => true
'\u{2000}'..='\u{200a}' => true
'\u{2028}' | '\u{2029}' | '\u{202f}' | '\u{205f}' | '\u{3000}' => true
_ => false
}
}
///|
/// Python's `str.rstrip()` (no arguments).
fn py_rstrip(s : String) -> String {
let chars = s.to_array()
let mut end = chars.length()
while end > 0 && is_py_space(chars[end - 1]) {
end -= 1
}
if end == chars.length() {
s
} else {
String::from_array(chars[:end])
}
}
///|
/// Python's `str.strip()` (no arguments).
fn py_strip(s : String) -> String {
let chars = s.to_array()
let mut start = 0
let mut end = chars.length()
while start < end && is_py_space(chars[start]) {
start += 1
}
while end > start && is_py_space(chars[end - 1]) {
end -= 1
}
String::from_array(chars[start:end])
}
///|
/// Python's `str.expandtabs(tabsize)`: the column resets after `\n` or `\r`.
fn py_expandtabs(s : String, tabsize : Int) -> String {
let buf = StringBuilder()
let mut column = 0
for c in s {
match c {
'\t' =>
if tabsize > 0 {
let pad = tabsize - column % tabsize
for _ in 0.. {
buf.write_char(c)
column = 0
}
_ => {
buf.write_char(c)
column += 1
}
}
}
buf.to_string()
}
///|
/// Compares two strings by code point, like Python's `str` ordering.
fn py_str_compare(x : String, y : String) -> Int {
let xs = x.to_array()
let ys = y.to_array()
let n = @cmp.minimum(xs.length(), ys.length())
for i in 0.. String {
if x.is_nan() {
return "nan"
}
if x.is_inf() {
return if x > 0.0 { "inf" } else { "-inf" }
}
// Decompose MoonBit's shortest representation into sign, significant
// digits and `decpt`, where x = 0.d1d2... * 10^decpt.
let s = x.to_string()
let negative = s.has_prefix("-")
let s = if negative { s.unsafe_substring(start=1, end=s.length()) } else { s }
let (mantissa, exp) = match s.find("e") {
Some(i) => {
let e = s.unsafe_substring(start=i + 1, end=s.length())
let e = if e.has_prefix("+") {
e.unsafe_substring(start=1, end=e.length())
} else {
e
}
(
s.unsafe_substring(start=0, end=i),
@string.parse_int(e) catch {
_ => 0
},
)
}
None => (s, 0)
}
let (int_part, frac_part) = match mantissa.find(".") {
Some(i) =>
(
mantissa.unsafe_substring(start=0, end=i),
mantissa.unsafe_substring(start=i + 1, end=mantissa.length()),
)
None => (mantissa, "")
}
let mut digits = int_part + frac_part
let mut decpt = int_part.length() + exp
while digits.has_prefix("0") {
digits = digits.unsafe_substring(start=1, end=digits.length())
decpt -= 1
}
while digits.has_suffix("0") {
digits = digits.unsafe_substring(start=0, end=digits.length() - 1)
}
let sign = if negative { "-" } else { "" }
if digits == "" {
// MoonBit prints -0.0 as "0"; Python keeps the sign
return if 1.0 / x < 0.0 { "-0.0" } else { "0.0" }
}
let n = digits.length()
if decpt <= -4 || decpt > 16 {
let e = decpt - 1
let mant = if n == 1 {
digits
} else {
digits.unsafe_substring(start=0, end=1) +
"." +
digits.unsafe_substring(start=1, end=n)
}
let e_abs = e.abs()
let e_str = if e_abs < 10 { "0\{e_abs}" } else { e_abs.to_string() }
sign + mant + (if e < 0 { "e-" } else { "e+" }) + e_str
} else if decpt <= 0 {
sign + "0." + "0".repeat(-decpt) + digits
} else if decpt >= n {
sign + digits + "0".repeat(decpt - n) + ".0"
} else {
sign +
digits.unsafe_substring(start=0, end=decpt) +
"." +
digits.unsafe_substring(start=decpt, end=n)
}
}