// Helpers reproducing the behavior of a few OCaml standard library
// functions whose exact output matters (error messages, printing).
///|
/// Iterate over the UTF-8 encoding of a string.
fn utf8_bytes(s : StringView, f : (Int) -> Unit) -> Unit {
for c in s {
let code = c.to_int()
if code < 0x80 {
f(code)
} else if code < 0x800 {
f(0xC0 | (code >> 6))
f(0x80 | (code & 0x3F))
} else if code < 0x10000 {
f(0xE0 | (code >> 12))
f(0x80 | ((code >> 6) & 0x3F))
f(0x80 | (code & 0x3F))
} else {
f(0xF0 | (code >> 18))
f(0x80 | ((code >> 12) & 0x3F))
f(0x80 | ((code >> 6) & 0x3F))
f(0x80 | (code & 0x3F))
}
}
}
///|
/// OCaml's `String.escaped`, applied to the UTF-8 encoding of `s`.
pub fn ocaml_escaped(s : StringView) -> String {
let buf = StringBuilder()
utf8_bytes(s, b => {
match b {
'"' => buf.write_string("\\\"")
'\\' => buf.write_string("\\\\")
'\n' => buf.write_string("\\n")
'\t' => buf.write_string("\\t")
'\r' => buf.write_string("\\r")
'\b' => buf.write_string("\\b")
0x20..=0x7E => buf.write_char(b.unsafe_to_char())
_ => {
buf.write_char('\\')
buf.write_char((b / 100 + 48).unsafe_to_char())
buf.write_char((b / 10 % 10 + 48).unsafe_to_char())
buf.write_char((b % 10 + 48).unsafe_to_char())
}
}
})
buf.to_string()
}
///|
/// OCaml's `Printf.sprintf "%S"`.
pub fn ocaml_quote(s : StringView) -> String {
"\"" + ocaml_escaped(s) + "\""
}
///|
/// Compare strings like OCaml's `String.compare`, i.e. by UTF-8 bytes,
/// which is the same as comparing code points.
pub fn compare_strings(a : StringView, b : StringView) -> Int {
let ia = a.iter()
let ib = b.iter()
for ;; {
match (ia.next(), ib.next()) {
(None, None) => break 0
(None, Some(_)) => break -1
(Some(_), None) => break 1
(Some(x), Some(y)) => if x != y { break x.to_int().compare(y.to_int()) }
}
}
}
///|
/// Compare string lists like OCaml's polymorphic comparison.
pub fn compare_string_lists(
a : ArrayView[String],
b : ArrayView[String],
) -> Int {
let n = if a.length() < b.length() { a.length() } else { b.length() }
for i in 0..