nandi/rustnimpublic Fork
⑂ 5861a17
Commits
⬇ Clone ▾
git clone https://git.rickub.com/nandi/rustnim.git
git clone ssh://git@rickub.com/nandi/rustnim.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP

Lower `.take(n)` and a block used as a value

`.take(n)` is the first `n` elements, or all of them if there are fewer. That
is a shorter index loop over the same container, so it joins the chain like
`enumerate` and `zip` rather than materialising anything -- and the binding is
the inner chain's, since taking fewer elements changes nothing about what each
one is.

A block is an expression in Rust and Nim's statements are not, so the
statements run where they stand and the trailing expression is the value. The
single-expression case was already handled; this is the rest of it.

tests/cases/080.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nandithebull committed 2026-09-20T14:33:17-07:00 Browse files
5861a17 parent: 7303426
modified src/lower.rs +32 -2
@@ -118,6 +118,10 @@ enum Iter {
118118 /// lazy iterator this is exact; it is only in an index loop that it
119119 /// changes the loop's shape.
120120 Filter { inner: Box<Iter>, param: String, body: Box<Expr> },
121+ /// `.take(n)` -- the first `n` elements, or all of them if there are
122+ /// fewer. That is a shorter index loop over the same container, so it
123+ /// composes with the rest rather than materialising anything.
124+ Take { inner: Box<Iter>, n: String },
121125 }
122126
123127 impl Iter {
@@ -134,6 +138,7 @@ impl Iter {
134138 Iter::Windows { len, k, .. } => format!("(max(0, {} - int({}) + 1))", len, k),
135139 Iter::Enumerate(i) => i.len(),
136140 Iter::Map { inner, .. } | Iter::Filter { inner, .. } => inner.len(),
141+ Iter::Take { inner, n } => format!("min({}, int({}))", inner.len(), n),
137142 // A closure iterator has no length; it is iterated directly.
138143 Iter::Closure { .. } => "0".into(),
139144 Iter::Zip(a, b) => format!("min({}, {})", a.len(), b.len()),
@@ -145,6 +150,7 @@ impl Iter {
145150 match self {
146151 Iter::Elems { elem, .. } => elem.clone(),
147152 Iter::Enumerate(i) => i.elem(),
153+ Iter::Take { inner, .. } => inner.elem(),
148154 Iter::Closure { elem, .. } => elem.clone(),
149155 _ => None,
150156 }
@@ -155,6 +161,7 @@ impl Iter {
155161 match self {
156162 Iter::Elems { code, off, .. } => format!("{}[int({}) + {}]", code, off, i),
157163 Iter::Enumerate(inner) => inner.at(i),
164+ Iter::Take { inner, .. } => inner.at(i),
158165 _ => i.to_string(),
159166 }
160167 }
@@ -5459,6 +5466,13 @@ impl Lowerer {
54595466 mutable: false,
54605467 })
54615468 }
5469+ "take" if m.args.len() == 1 => {
5470+ let n = self.expr(&m.args[0])?;
5471+ Ok(Iter::Take {
5472+ inner: Box::new(self.resolve_iter(&m.receiver)?),
5473+ n: n.code,
5474+ })
5475+ }
54625476 "enumerate" if m.args.is_empty() => {
54635477 Ok(Iter::Enumerate(Box::new(self.resolve_iter(&m.receiver)?)))
54645478 }
@@ -5806,6 +5820,9 @@ impl Lowerer {
58065820 // Nim is already the value.
58075821 (Pat::Reference(r), _) => self.bind_pattern(&r.pat, it, i),
58085822 (Pat::Paren(p), _) => self.bind_pattern(&p.pat, it, i),
5823+ // `.take(n)` shortens the loop and changes nothing about what each
5824+ // element is, so the binding is the inner chain's.
5825+ (p, Iter::Take { inner, .. }) => self.bind_pattern(p, inner, i),
58095826 (Pat::Ident(id), _) => {
58105827 let name = id.ident.to_string();
58115828 match it {
@@ -5877,7 +5894,8 @@ impl Lowerer {
58775894 }
58785895 // Handled above.
58795896 Iter::Zip(..) | Iter::Enumerate(..) | Iter::Map { .. }
5880- | Iter::Filter { .. } | Iter::Closure { .. } => unreachable!(),
5897+ | Iter::Filter { .. } | Iter::Closure { .. }
5898+ | Iter::Take { .. } => unreachable!(),
58815899 }
58825900 }
58835901 _ => Err("unsupported `for` pattern".into()),
@@ -6910,9 +6928,14 @@ impl Lowerer {
69106928 if let Some(Stmt::Expr(e, None)) = b.block.stmts.first() {
69116929 self.expr_at(e, expect)
69126930 } else {
6913- Err("block expression with statements in value position is not implemented yet".into())
6931+ self.block_value(&b.block, expect)
69146932 }
69156933 }
6934+ // A block used as a value: the statements run where they stand and
6935+ // the trailing expression is the value. Rust's block is an
6936+ // expression; Nim's statements are not, so the two are separated
6937+ // rather than nested.
6938+ Expr::Block(b) => self.block_value(&b.block, expect),
69166939 // An assignment is an expression in Rust, of type `()`. In Nim it
69176940 // is a statement, so it is emitted as one and the value it stands
69186941 // for is the unit -- which is what Rust's is.
@@ -7683,6 +7706,13 @@ impl Lowerer {
76837706 ))
76847707 }
76857708
7709+ fn block_value(&mut self, b: &syn::Block, expect: Option<&Nim>) -> Result<Val, String> {
7710+ self.push_scope();
7711+ let r = self.block_body_at(b, expect);
7712+ self.pop_scope();
7713+ r?.ok_or_else(|| "a block used as a value has to end in one".to_string())
7714+ }
7715+
76867716 /// Lower a block's statements at the current indentation, without opening
76877717 /// a Nim `block:` -- used for `unsafe { .. }`, which introduces no scope
76887718 /// of its own in the generated code.
@@ -118,6 +118,10 @@ enum Iter {
118 /// lazy iterator this is exact; it is only in an index loop that it118 /// lazy iterator this is exact; it is only in an index loop that it
119 /// changes the loop's shape.119 /// changes the loop's shape.
120 Filter { inner: Box<Iter>, param: String, body: Box<Expr> },120 Filter { inner: Box<Iter>, param: String, body: Box<Expr> },
121+ /// `.take(n)` -- the first `n` elements, or all of them if there are
122+ /// fewer. That is a shorter index loop over the same container, so it
123+ /// composes with the rest rather than materialising anything.
124+ Take { inner: Box<Iter>, n: String },
121 }125 }
122 126
123 impl Iter {127 impl Iter {
@@ -134,6 +138,7 @@ impl Iter {
134 Iter::Windows { len, k, .. } => format!("(max(0, {} - int({}) + 1))", len, k),138 Iter::Windows { len, k, .. } => format!("(max(0, {} - int({}) + 1))", len, k),
135 Iter::Enumerate(i) => i.len(),139 Iter::Enumerate(i) => i.len(),
136 Iter::Map { inner, .. } | Iter::Filter { inner, .. } => inner.len(),140 Iter::Map { inner, .. } | Iter::Filter { inner, .. } => inner.len(),
141+ Iter::Take { inner, n } => format!("min({}, int({}))", inner.len(), n),
137 // A closure iterator has no length; it is iterated directly.142 // A closure iterator has no length; it is iterated directly.
138 Iter::Closure { .. } => "0".into(),143 Iter::Closure { .. } => "0".into(),
139 Iter::Zip(a, b) => format!("min({}, {})", a.len(), b.len()),144 Iter::Zip(a, b) => format!("min({}, {})", a.len(), b.len()),
@@ -145,6 +150,7 @@ impl Iter {
145 match self {150 match self {
146 Iter::Elems { elem, .. } => elem.clone(),151 Iter::Elems { elem, .. } => elem.clone(),
147 Iter::Enumerate(i) => i.elem(),152 Iter::Enumerate(i) => i.elem(),
153+ Iter::Take { inner, .. } => inner.elem(),
148 Iter::Closure { elem, .. } => elem.clone(),154 Iter::Closure { elem, .. } => elem.clone(),
149 _ => None,155 _ => None,
150 }156 }
@@ -155,6 +161,7 @@ impl Iter {
155 match self {161 match self {
156 Iter::Elems { code, off, .. } => format!("{}[int({}) + {}]", code, off, i),162 Iter::Elems { code, off, .. } => format!("{}[int({}) + {}]", code, off, i),
157 Iter::Enumerate(inner) => inner.at(i),163 Iter::Enumerate(inner) => inner.at(i),
164+ Iter::Take { inner, .. } => inner.at(i),
158 _ => i.to_string(),165 _ => i.to_string(),
159 }166 }
160 }167 }
@@ -5459,6 +5466,13 @@ impl Lowerer {
5459 mutable: false,5466 mutable: false,
5460 })5467 })
5461 }5468 }
5469+ "take" if m.args.len() == 1 => {
5470+ let n = self.expr(&m.args[0])?;
5471+ Ok(Iter::Take {
5472+ inner: Box::new(self.resolve_iter(&m.receiver)?),
5473+ n: n.code,
5474+ })
5475+ }
5462 "enumerate" if m.args.is_empty() => {5476 "enumerate" if m.args.is_empty() => {
5463 Ok(Iter::Enumerate(Box::new(self.resolve_iter(&m.receiver)?)))5477 Ok(Iter::Enumerate(Box::new(self.resolve_iter(&m.receiver)?)))
5464 }5478 }
@@ -5806,6 +5820,9 @@ impl Lowerer {
5806 // Nim is already the value.5820 // Nim is already the value.
5807 (Pat::Reference(r), _) => self.bind_pattern(&r.pat, it, i),5821 (Pat::Reference(r), _) => self.bind_pattern(&r.pat, it, i),
5808 (Pat::Paren(p), _) => self.bind_pattern(&p.pat, it, i),5822 (Pat::Paren(p), _) => self.bind_pattern(&p.pat, it, i),
5823+ // `.take(n)` shortens the loop and changes nothing about what each
5824+ // element is, so the binding is the inner chain's.
5825+ (p, Iter::Take { inner, .. }) => self.bind_pattern(p, inner, i),
5809 (Pat::Ident(id), _) => {5826 (Pat::Ident(id), _) => {
5810 let name = id.ident.to_string();5827 let name = id.ident.to_string();
5811 match it {5828 match it {
@@ -5877,7 +5894,8 @@ impl Lowerer {
5877 }5894 }
5878 // Handled above.5895 // Handled above.
5879 Iter::Zip(..) | Iter::Enumerate(..) | Iter::Map { .. }5896 Iter::Zip(..) | Iter::Enumerate(..) | Iter::Map { .. }
5880- | Iter::Filter { .. } | Iter::Closure { .. } => unreachable!(),5897+ | Iter::Filter { .. } | Iter::Closure { .. }
5898+ | Iter::Take { .. } => unreachable!(),
5881 }5899 }
5882 }5900 }
5883 _ => Err("unsupported `for` pattern".into()),5901 _ => Err("unsupported `for` pattern".into()),
@@ -6910,9 +6928,14 @@ impl Lowerer {
6910 if let Some(Stmt::Expr(e, None)) = b.block.stmts.first() {6928 if let Some(Stmt::Expr(e, None)) = b.block.stmts.first() {
6911 self.expr_at(e, expect)6929 self.expr_at(e, expect)
6912 } else {6930 } else {
6913- Err("block expression with statements in value position is not implemented yet".into())6931+ self.block_value(&b.block, expect)
6914 }6932 }
6915 }6933 }
6934+ // A block used as a value: the statements run where they stand and
6935+ // the trailing expression is the value. Rust's block is an
6936+ // expression; Nim's statements are not, so the two are separated
6937+ // rather than nested.
6938+ Expr::Block(b) => self.block_value(&b.block, expect),
6916 // An assignment is an expression in Rust, of type `()`. In Nim it6939 // An assignment is an expression in Rust, of type `()`. In Nim it
6917 // is a statement, so it is emitted as one and the value it stands6940 // is a statement, so it is emitted as one and the value it stands
6918 // for is the unit -- which is what Rust's is.6941 // for is the unit -- which is what Rust's is.
@@ -7683,6 +7706,13 @@ impl Lowerer {
7683 ))7706 ))
7684 }7707 }
7685 7708
7709+ fn block_value(&mut self, b: &syn::Block, expect: Option<&Nim>) -> Result<Val, String> {
7710+ self.push_scope();
7711+ let r = self.block_body_at(b, expect);
7712+ self.pop_scope();
7713+ r?.ok_or_else(|| "a block used as a value has to end in one".to_string())
7714+ }
7715+
7686 /// Lower a block's statements at the current indentation, without opening7716 /// Lower a block's statements at the current indentation, without opening
7687 /// a Nim `block:` -- used for `unsafe { .. }`, which introduces no scope7717 /// a Nim `block:` -- used for `unsafe { .. }`, which introduces no scope
7688 /// of its own in the generated code.7718 /// of its own in the generated code.
added tests/cases/080-take.rs +49 -0
new file mode 100644
@@ -0,0 +1,49 @@
1+// `.take(n)` is the first `n` elements, or all of them if there are fewer —
2+// a shorter index loop over the same container, so it composes with the rest
3+// rather than materialising anything.
4+// A block used as a value: the statements run where they stand and the
5+// trailing expression is the value.
6+fn stepped(n: u32) -> u32 {
7+ let doubled = {
8+ let half = n / 2;
9+ let rest = n - half;
10+ half + rest * 2
11+ };
12+ doubled + 1
13+}
14+
15+fn main() {
16+ println!("{} {}", stepped(10), stepped(7));
17+
18+ let v: Vec<u32> = vec![10, 20, 30, 40, 50];
19+
20+ let mut t: u32 = 0;
21+ for x in v.iter().take(3) {
22+ t += *x;
23+ }
24+ println!("{}", t);
25+
26+ // More than there are.
27+ let mut u: u32 = 0;
28+ for x in v.iter().take(99) {
29+ u += *x;
30+ }
31+ println!("{}", u);
32+
33+ // None at all.
34+ let mut z: u32 = 0;
35+ for x in v.iter().take(0) {
36+ z += *x;
37+ }
38+ println!("{}", z);
39+
40+ // Composed with `enumerate`.
41+ for (i, x) in v.iter().take(2).enumerate() {
42+ println!("{} {}", i, x);
43+ }
44+
45+ for i in (0..10).take(4) {
46+ print!("{} ", i);
47+ }
48+ println!();
49+}
new file mode 100644
@@ -0,0 +1,49 @@
1+// `.take(n)` is the first `n` elements, or all of them if there are fewer —
2+// a shorter index loop over the same container, so it composes with the rest
3+// rather than materialising anything.
4+// A block used as a value: the statements run where they stand and the
5+// trailing expression is the value.
6+fn stepped(n: u32) -> u32 {
7+ let doubled = {
8+ let half = n / 2;
9+ let rest = n - half;
10+ half + rest * 2
11+ };
12+ doubled + 1
13+}
14+
15+fn main() {
16+ println!("{} {}", stepped(10), stepped(7));
17+
18+ let v: Vec<u32> = vec![10, 20, 30, 40, 50];
19+
20+ let mut t: u32 = 0;
21+ for x in v.iter().take(3) {
22+ t += *x;
23+ }
24+ println!("{}", t);
25+
26+ // More than there are.
27+ let mut u: u32 = 0;
28+ for x in v.iter().take(99) {
29+ u += *x;
30+ }
31+ println!("{}", u);
32+
33+ // None at all.
34+ let mut z: u32 = 0;
35+ for x in v.iter().take(0) {
36+ z += *x;
37+ }
38+ println!("{}", z);
39+
40+ // Composed with `enumerate`.
41+ for (i, x) in v.iter().take(2).enumerate() {
42+ println!("{} {}", i, x);
43+ }
44+
45+ for i in (0..10).take(4) {
46+ print!("{} ", i);
47+ }
48+ println!();
49+}