From 1101a8370f33bf31e4d9840ab8d932b8449267e8 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Mon, 11 Apr 2022 16:30:34 +0200 Subject: [PATCH] Negative array indexing --- src/eval/array.rs | 36 +++++++++++++++++++------------- tests/typ/code/array.typ | 26 ++++++++++++++++------- tests/typ/utility/collection.typ | 8 +++++++ 3 files changed, 49 insertions(+), 21 deletions(-) diff --git a/src/eval/array.rs b/src/eval/array.rs index 6fb278e39..9abab8cf2 100644 --- a/src/eval/array.rs +++ b/src/eval/array.rs @@ -48,8 +48,7 @@ impl Array { /// Borrow the value at the given index. pub fn get(&self, index: i64) -> StrResult<&Value> { - usize::try_from(index) - .ok() + self.locate(index) .and_then(|i| self.0.get(i)) .ok_or_else(|| out_of_bounds(index, self.len())) } @@ -57,8 +56,7 @@ impl Array { /// Mutably borrow the value at the given index. pub fn get_mut(&mut self, index: i64) -> StrResult<&mut Value> { let len = self.len(); - usize::try_from(index) - .ok() + self.locate(index) .and_then(move |i| Arc::make_mut(&mut self.0).get_mut(i)) .ok_or_else(|| out_of_bounds(index, len)) } @@ -77,8 +75,8 @@ impl Array { /// Insert a value at the specified index. pub fn insert(&mut self, index: i64, value: Value) -> StrResult<()> { let len = self.len(); - let i = usize::try_from(index) - .ok() + let i = self + .locate(index) .filter(|&i| i <= self.0.len()) .ok_or_else(|| out_of_bounds(index, len))?; @@ -89,8 +87,8 @@ impl Array { /// Remove and return the value at the specified index. pub fn remove(&mut self, index: i64) -> StrResult<()> { let len = self.len(); - let i = usize::try_from(index) - .ok() + let i = self + .locate(index) .filter(|&i| i < self.0.len()) .ok_or_else(|| out_of_bounds(index, len))?; @@ -106,16 +104,17 @@ impl Array { /// Extract a contigous subregion of the array. pub fn slice(&self, start: i64, end: Option) -> StrResult { let len = self.len(); - let start = usize::try_from(start) - .ok() + let start = self + .locate(start) .filter(|&start| start <= self.0.len()) .ok_or_else(|| out_of_bounds(start, len))?; let end = end.unwrap_or(self.len()); - let end = usize::try_from(end) - .ok() + let end = self + .locate(end) .filter(|&end| end <= self.0.len()) - .ok_or_else(|| out_of_bounds(end, len))?; + .ok_or_else(|| out_of_bounds(end, len))? + .max(start); Ok(Self::from_vec(self.0[start .. end].to_vec())) } @@ -225,11 +224,20 @@ impl Array { self.0.as_slice() } - /// Iterate over references to the contained values. pub fn iter(&self) -> std::slice::Iter { self.0.iter() } + + /// Resolve an index. + fn locate(&self, index: i64) -> Option { + usize::try_from(if index >= 0 { + index + } else { + self.len().checked_add(index)? + }) + .ok() + } } /// The out of bounds access error message. diff --git a/tests/typ/code/array.typ b/tests/typ/code/array.typ index df37dd454..cd1631759 100644 --- a/tests/typ/code/array.typ +++ b/tests/typ/code/array.typ @@ -31,20 +31,32 @@ --- // Test rvalue out of bounds. -{ - let array = (1, 2, 3) - // Error: 3-11 array index out of bounds (index: 5, len: 3) - array(5) -} +// Error: 2-14 array index out of bounds (index: 5, len: 3) +{(1, 2, 3)(5)} --- // Test lvalue out of bounds. { let array = (1, 2, 3) - // Error: 3-12 array index out of bounds (index: -1, len: 3) - array(-1) = 5 + // Error: 3-11 array index out of bounds (index: 3, len: 3) + array(3) = 5 } +--- +// Test negative indices. +{ + let array = (1, 2, 3, 4) + test(array(0), 1) + test(array(-1), 4) + test(array(-2), 3) + test(array(-3), 2) + test(array(-4), 1) +} + +--- +// Error: 2-15 array index out of bounds (index: -4, len: 3) +{(1, 2, 3)(-4)} + --- // Test non-collection indexing. diff --git a/tests/typ/utility/collection.typ b/tests/typ/utility/collection.typ index 924200cb0..3414f0a9e 100644 --- a/tests/typ/utility/collection.typ +++ b/tests/typ/utility/collection.typ @@ -38,11 +38,19 @@ #test((1, 2, 3, 4).slice(2), (3, 4)) #test(range(10).slice(2, 6), (2, 3, 4, 5)) #test(range(10).slice(4, count: 3), (4, 5, 6)) +#test((1, 2, 3).slice(2, -2), ()) +#test((1, 2, 3).slice(-2, 2), (2,)) +#test((1, 2, 3).slice(-3, 2), (1, 2)) +#test("ABCD".split("").slice(1, -1).join("-"), "A-B-C-D") --- // Error: 3-31 array index out of bounds (index: 12, len: 10) { range(10).slice(9, count: 3) } +--- +// Error: 3-25 array index out of bounds (index: -4, len: 3) +{ (1, 2, 3).slice(0, -4) } + --- // Error: 2:17-2:19 missing argument: index #let numbers = ()