Skip to content

Commit 79d8e03

Browse files
authored
Fix a bug in reverse slicing and add a test (#782)
1 parent 2dea421 commit 79d8e03

File tree

4 files changed

+31
-32
lines changed

4 files changed

+31
-32
lines changed

minijinja/src/compiler/codegen.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -615,7 +615,7 @@ impl<'source> CodeGenerator<'source> {
615615
if let Some(ref start) = s.start {
616616
self.compile_expr(start);
617617
} else {
618-
self.add(Instruction::LoadConst(Value::from(0)));
618+
self.add(Instruction::LoadConst(Value::from(())));
619619
}
620620
if let Some(ref stop) = s.stop {
621621
self.compile_expr(stop);
@@ -625,7 +625,7 @@ impl<'source> CodeGenerator<'source> {
625625
if let Some(ref step) = s.step {
626626
self.compile_expr(step);
627627
} else {
628-
self.add(Instruction::LoadConst(Value::from(1)));
628+
self.add(Instruction::LoadConst(Value::from(())));
629629
}
630630
self.add(Instruction::Slice);
631631
self.pop_span();

minijinja/src/value/ops.rs

Lines changed: 16 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,11 @@ pub fn coerce<'x>(a: &'x Value, b: &'x Value, lossy: bool) -> Option<CoerceResul
6262
}
6363

6464
fn get_offset_and_len<F: FnOnce() -> usize>(
65-
start: i64,
65+
start: Option<i64>,
6666
stop: Option<i64>,
6767
end: F,
6868
) -> (usize, usize) {
69+
let start = start.unwrap_or(0);
6970
if start < 0 || stop.map_or(true, |x| x < 0) {
7071
let end = end();
7172
let start = if start < 0 {
@@ -88,22 +89,16 @@ fn get_offset_and_len<F: FnOnce() -> usize>(
8889
}
8990

9091
fn range_step_backwards(
91-
start_was_none: bool,
92-
start: i64,
92+
start: Option<i64>,
9393
stop: Option<i64>,
9494
step: usize,
9595
end: usize,
9696
) -> impl Iterator<Item = usize> {
97-
let start = if start_was_none {
98-
end.saturating_sub(1)
99-
} else if start >= 0 {
100-
if start as usize >= end {
101-
end.saturating_sub(1)
102-
} else {
103-
start as usize
104-
}
105-
} else {
106-
(end as i64 + start).max(0) as usize
97+
let start = match start {
98+
None => end.saturating_sub(1),
99+
Some(start) if start >= end as i64 => end.saturating_sub(1),
100+
Some(start) if start >= 0 => start as usize,
101+
Some(start) => (end as i64 + start).max(0) as usize,
107102
};
108103
let stop = match stop {
109104
None => 0,
@@ -115,16 +110,14 @@ fn range_step_backwards(
115110
} else {
116111
(start - stop + step - 1) / step
117112
};
118-
std::iter::successors(Some(start), move |&i| i.checked_sub(step)).take(length)
113+
(stop..=start).rev().step_by(step).take(length)
119114
}
120115

121116
pub fn slice(value: Value, start: Value, stop: Value, step: Value) -> Result<Value, Error> {
122-
let start_was_none = start.is_none();
123-
124-
let start: i64 = if start_was_none {
125-
0
117+
let start = if start.is_none() {
118+
None
126119
} else {
127-
ok!(start.try_into())
120+
Some(ok!(start.try_into()))
128121
};
129122
let stop = if stop.is_none() {
130123
None
@@ -164,7 +157,7 @@ pub fn slice(value: Value, start: Value, stop: Value, step: Value) -> Result<Val
164157
} else {
165158
let chars: Vec<char> = s.chars().collect();
166159
Ok(Value::from(
167-
range_step_backwards(start_was_none, start, stop, -step as usize, chars.len())
160+
range_step_backwards(start, stop, -step as usize, chars.len())
168161
.map(move |i| chars[i])
169162
.collect::<String>(),
170163
))
@@ -183,7 +176,7 @@ pub fn slice(value: Value, start: Value, stop: Value, step: Value) -> Result<Val
183176
))
184177
} else {
185178
Ok(Value::from_bytes(
186-
range_step_backwards(start_was_none, start, stop, -step as usize, b.len())
179+
range_step_backwards(start, stop, -step as usize, b.len())
187180
.map(|i| b[i])
188181
.collect::<Vec<u8>>(),
189182
))
@@ -206,14 +199,8 @@ pub fn slice(value: Value, start: Value, stop: Value, step: Value) -> Result<Val
206199
if let Some(iter) = obj.try_iter() {
207200
let vec: Vec<Value> = iter.collect();
208201
Box::new(
209-
range_step_backwards(
210-
start_was_none,
211-
start,
212-
stop,
213-
-step as usize,
214-
vec.len(),
215-
)
216-
.map(move |i| vec[i].clone()),
202+
range_step_backwards(start, stop, -step as usize, vec.len())
203+
.map(move |i| vec[i].clone()),
217204
)
218205
} else {
219206
Box::new(None.into_iter())

minijinja/tests/inputs/slicing.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,9 @@
1717
{{ intrange[2:10:2] }}
1818
{{ intrange[2:10][0] }}
1919
{{ intrange[2:10][2:][0] }}
20+
{{ intrange[::-1] }}
21+
{{ intrange[::-2] }}
22+
{{ intrange[4:2:-1] }}
23+
{{ intrange[4:2:-2] }}
24+
{{ intrange[-5::-1] }}
25+
{{ intrange[-5:2:-1] }}

minijinja/tests/snapshots/[email protected]

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
source: minijinja/tests/test_templates.rs
3-
description: "{{ hello[:] }}\n{{ hello[1:] }}\n{{ hello[1:-1] }}\n{{ hello[::2] }}\n{{ hello[2:10] }}\n{{ hello[2:10:2] }}\n{{ intrange[:] }}\n{{ intrange[1:] }}\n{{ intrange[1:-1] }}\n{{ intrange[::2] }}\n{{ intrange[2:10] }}\n{{ intrange[2:10:2] }}\n{{ intrange[2:10][0] }}\n{{ intrange[2:10][2:][0] }}"
3+
description: "{{ hello[:] }}\n{{ hello[1:] }}\n{{ hello[1:-1] }}\n{{ hello[::2] }}\n{{ hello[2:10] }}\n{{ hello[2:10:2] }}\n{{ intrange[:] }}\n{{ intrange[1:] }}\n{{ intrange[1:-1] }}\n{{ intrange[::2] }}\n{{ intrange[2:10] }}\n{{ intrange[2:10:2] }}\n{{ intrange[2:10][0] }}\n{{ intrange[2:10][2:][0] }}\n{{ intrange[::-1] }}\n{{ intrange[::-2] }}\n{{ intrange[4:2:-1] }}\n{{ intrange[4:2:-2] }}\n{{ intrange[-5::-1] }}\n{{ intrange[-5:2:-1] }}"
44
info:
55
hello: Hällo Wörld
66
intrange:
@@ -30,3 +30,9 @@ loWr
3030
[2, 4, 6, 8]
3131
2
3232
4
33+
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
34+
[9, 7, 5, 3, 1]
35+
[4, 3]
36+
[4]
37+
[5, 4, 3, 2, 1, 0]
38+
[5, 4, 3]

0 commit comments

Comments
 (0)