diff --git a/examples/tests/strings.nbt b/examples/tests/strings.nbt index 2678dee..be01dec 100644 --- a/examples/tests/strings.nbt +++ b/examples/tests/strings.nbt @@ -1,11 +1,19 @@ assert_eq(str_length(""), 0) assert_eq(str_length("foo"), 3) +# Length and slicing operate on Unicode code points, not on UTF-8 bytes +assert_eq(str_length("café"), 4) +assert_eq(str_length("❤"), 1) + assert_eq(str_slice(0, 5, "hello world"), "hello") assert_eq(str_slice(6, 11, "hello world"), "world") assert_eq(str_slice(0, 0, "hello world"), "") assert_eq(str_slice(0, 100, "hello world"), "") +assert_eq(str_slice(0, 4, "café"), "café") +assert_eq(str_slice(3, 4, "café"), "é") +assert_eq(str_slice(0, 3, "café"), "caf") + assert_eq(chr(65), "A") assert_eq(chr(97), "a") assert_eq(chr(0x2764), "❤") diff --git a/numbat/src/ffi/strings.rs b/numbat/src/ffi/strings.rs index a810080..1ec031c 100644 --- a/numbat/src/ffi/strings.rs +++ b/numbat/src/ffi/strings.rs @@ -12,7 +12,7 @@ pub fn str_length( mut args: Args, _return_type: &TypeScheme, ) -> Result> { - let len = string_arg!(args).len(); + let len = string_arg!(args).chars().count(); return_scalar!(len as f64) } @@ -41,9 +41,16 @@ pub fn str_slice( let end = quantity_arg!(args).unsafe_value().to_f64() as usize; let input = string_arg!(args); - let output = input.get(start..end).unwrap_or_default(); + // Indices refer to code points, not bytes; out-of-range requests yield an + // empty string as before. + let characters = input.chars().collect::>(); + let output = if start <= end && end <= characters.len() { + characters[start..end].iter().collect::() + } else { + String::new() + }; - return_string!(borrowed = output) + return_string!(from = &output) } pub fn chr(