diff --git a/ndc_stdlib/src/index.rs b/ndc_stdlib/src/index.rs index 3955578e..49c6af6e 100644 --- a/ndc_stdlib/src/index.rs +++ b/ndc_stdlib/src/index.rs @@ -225,6 +225,13 @@ enum VmOffset { Range(usize, usize), } +fn char_index_to_byte_offset(string: &str, index: usize) -> usize { + string + .char_indices() + .nth(index) + .map_or(string.len(), |(byte_offset, _)| byte_offset) +} + fn extract_vm_offset(index_value: &Value, size: usize) -> Result { if let Value::Object(obj) = index_value && let Object::Iterator(iter) = obj.as_ref() @@ -514,9 +521,13 @@ fn vm_set_at_index(container: &Value, index_value: &Value, rhs: Value) -> Result let mut s = s.borrow_mut(); match extract_vm_offset(index_value, size)? { VmOffset::Element(idx) => { - s.replace_range(idx..=idx, &rhs_str); + let from = char_index_to_byte_offset(&s, idx); + let to = char_index_to_byte_offset(&s, idx + 1); + s.replace_range(from..to, &rhs_str); } VmOffset::Range(from, to) => { + let from = char_index_to_byte_offset(&s, from); + let to = char_index_to_byte_offset(&s, to); s.replace_range(from..to, &rhs_str); } } diff --git a/tests/functional/programs/006_lists/008_assign_string_index.ndc b/tests/functional/programs/006_lists/008_assign_string_index.ndc index ff6a9894..b8fdb64d 100644 --- a/tests/functional/programs/006_lists/008_assign_string_index.ndc +++ b/tests/functional/programs/006_lists/008_assign_string_index.ndc @@ -1,3 +1,9 @@ let str = "foobar"; str[0] = "w"; assert_eq(str, "woobar"); + +let unicode = "aé🦀z"; +unicode[1] = "ø"; +assert_eq(unicode, "aø🦀z"); +unicode[2] = "X"; +assert_eq(unicode, "aøXz"); diff --git a/tests/functional/programs/006_lists/011_assign_negative_index_in_string.ndc b/tests/functional/programs/006_lists/011_assign_negative_index_in_string.ndc index 0cfa9ce8..3e3e21b3 100644 --- a/tests/functional/programs/006_lists/011_assign_negative_index_in_string.ndc +++ b/tests/functional/programs/006_lists/011_assign_negative_index_in_string.ndc @@ -1,3 +1,7 @@ let str = "foobak"; str[-1] = "r"; assert_eq(str, "foobar"); + +let unicode = "aé🦀z"; +unicode[-2] = "X"; +assert_eq(unicode, "aéXz"); diff --git a/tests/functional/programs/009_slicing/004_assign_string_slice.ndc b/tests/functional/programs/009_slicing/004_assign_string_slice.ndc index 5c2f8d72..b1fa4f33 100644 --- a/tests/functional/programs/009_slicing/004_assign_string_slice.ndc +++ b/tests/functional/programs/009_slicing/004_assign_string_slice.ndc @@ -11,3 +11,6 @@ let s = "The world is mine"; s[4..8] = "foo"; assert_eq(s, "The food is mine"); +let unicode = "aé🦀z"; +unicode[1..3] = "Ω"; +assert_eq(unicode, "aΩz");