Skip to content

Commit 3f17ee8

Browse files
committed
Allow map entries to be replaced by index
1 parent 7c4b095 commit 3f17ee8

File tree

3 files changed

+38
-2
lines changed

3 files changed

+38
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ The Koto project adheres to
1717
- `export` can be used with multi-assignment expressions.
1818
- E.g. expressions like `export a, b, c = foo()` are now allowed.
1919
- Maps now support `[]` indexing, returning the Nth entry as a tuple.
20+
- Entries can also be replaced by index by assigning a key/value tuple.
2021
- Objects that implement `KotoObject::call` can now be used in operations that
2122
expect functions.
2223
- `KotoObject::is_callable` has been added to support this, and needs to be

crates/cli/docs/language_guide.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,15 @@ print! m[1]
643643
check! ('oranges', 99)
644644
```
645645

646+
Entries can also be replaced by assigning a key/value tuple to the entry's index.
647+
648+
```koto
649+
m = {apples: 42, oranges: 99, lemons: 63}
650+
m[1] = ('pears', 123)
651+
print! m
652+
check! {apples: 42, pears: 123, lemons: 63}
653+
```
654+
646655
### Shorthand Values
647656

648657
Koto supports a shorthand notation when creating maps with inline syntax.

crates/runtime/src/vm.rs

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2243,18 +2243,44 @@ impl KotoVm {
22432243
if *index >= 0.0 && u_index < list_len {
22442244
list_data[u_index] = value.clone();
22452245
} else {
2246-
return runtime_error!("Index '{index}' not in List");
2246+
return runtime_error!("Invalid index ({index})");
22472247
}
22482248
}
22492249
Range(range) => {
22502250
for i in range.indices(list_len) {
22512251
list_data[i] = value.clone();
22522252
}
22532253
}
2254-
unexpected => return unexpected_type("index", unexpected),
2254+
unexpected => return unexpected_type("Number or Range", unexpected),
22552255
}
22562256
Ok(())
22572257
}
2258+
Map(map) => match index_value {
2259+
Number(index) => {
2260+
let mut map_data = map.data_mut();
2261+
let map_len = map_data.len();
2262+
let u_index = usize::from(index);
2263+
if *index >= 0.0 && u_index < map_len {
2264+
match value {
2265+
Tuple(new_entry) if new_entry.len() == 2 => {
2266+
let key = ValueKey::try_from(new_entry[0].clone())?;
2267+
// There's no API on IndexMap for replacing an entry,
2268+
// so use swap_remove_index to remove the old entry,
2269+
// then insert the new entry at the end of the map,
2270+
// followed by swap_indices to swap the new entry back into position.
2271+
map_data.swap_remove_index(u_index);
2272+
map_data.insert(key, new_entry[1].clone());
2273+
map_data.swap_indices(u_index, map_len - 1);
2274+
Ok(())
2275+
}
2276+
unexpected => unexpected_type("Tuple with 2 elements", unexpected),
2277+
}
2278+
} else {
2279+
runtime_error!("Invalid index ({index})")
2280+
}
2281+
}
2282+
unexpected => unexpected_type("Number", unexpected),
2283+
},
22582284
Object(o) => o.try_borrow_mut()?.index_mut(index_value, value),
22592285
unexpected => unexpected_type("a mutable indexable value", &unexpected),
22602286
}

0 commit comments

Comments
 (0)