nandi/rustnimpublic Fork
⑂ 9ee9823
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

Add `cmp`/`partial_cmp`, `Default::default`, `recip` and `to_bits`

`Ordering` becomes `RsOrdering` in the prelude: Nim's `cmp` returns an `int`
whose sign carries the same information, and naming the three cases lets a
`match` on the result read as it does in Rust. Both spellings are lowered --
the derived impls call `PartialOrd::partial_cmp(&a, &b)`, hand-written code
writes `a.cmp(&b)`. A derived `PartialOrd` never answers `None`, so the
partial form is the total one in `Some`.

`Default::default()` is the zero value, which the expected type decides;
without one there is nothing to build and it stays refused.

`to_bits` converts the receiver to its declared width before the `cast`. A
`cast` reinterprets whatever bytes are there, and an unsuffixed literal is
`float64` until something says otherwise -- so `1.0f32.to_bits()` was reading
four of the wrong eight bytes and answering 0 where rustc says 1065353216.
Caught by the differential runner, which is the point of it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nandithebull committed 2026-09-19T22:41:09-07:00 Browse files
9ee9823 parent: 27c690d
modified src/lower.rs +65 -0
@@ -6811,6 +6811,32 @@ impl Lowerer {
68116811 }
68126812 }
68136813 }
6814+ // `Ord::cmp` / `PartialOrd::partial_cmp`. Nim's `cmp` returns an
6815+ // `int` whose sign carries the same information, and `rsOrd` names
6816+ // the three cases. `partial_cmp` is the total one wrapped in
6817+ // `Some`: these are the derived impls, and a derived `PartialOrd`
6818+ // never answers `None`.
6819+ "cmp" | "partial_cmp" if codes.len() == 2 => {
6820+ let o = Nim::Named("RsOrdering".into(), Vec::new());
6821+ let code = format!("rsOrd(cmp({}, {}))", codes[0], codes[1]);
6822+ return Ok(if name == "cmp" {
6823+ Val::new(code, Some(o))
6824+ } else {
6825+ Val::new(
6826+ format!("rsSome[RsOrdering]({code})"),
6827+ Some(Nim::Named("Option".into(), vec![o])),
6828+ )
6829+ });
6830+ }
6831+ // `Default::default()` is the zero value, which the expected type
6832+ // decides. Without one there is nothing to build.
6833+ "default" if codes.is_empty() => {
6834+ if let Some(t) = expect {
6835+ if let Some(d) = default_of(t) {
6836+ return Ok(Val::new(d, Some(t.clone())));
6837+ }
6838+ }
6839+ }
68146840 // `vec![v; n]`, after expansion.
68156841 "from_elem" if codes.len() == 2 => {
68166842 let t = args[0].ty.clone().map(|t| Nim::Seq(Box::new(t.owned())));
@@ -7244,6 +7270,45 @@ impl Lowerer {
72447270 "push" => (format!("{}.add({})", recv.code, a0.unwrap_or_default()), Some(Nim::Unit)),
72457271 // `clear` drops every element and keeps the allocation, which is
72467272 // what setting the length to zero does in Nim too.
7273+ // `f.recip()` is `1/f`, and `to_bits`/`from_bits` reinterpret the
7274+ // float's bytes -- which is what Nim's `cast` means.
7275+ // The method spelling of `Ord::cmp`; the derived impls use the
7276+ // free-function one, hand-written code this.
7277+ "cmp" | "partial_cmp" if m.args.len() == 1 => {
7278+ let o = Nim::Named("RsOrdering".into(), Vec::new());
7279+ let code = format!(
7280+ "rsOrd(cmp({}, {}))",
7281+ recv.code,
7282+ a0.clone().unwrap_or_default()
7283+ );
7284+ if name == "cmp" {
7285+ (code, Some(o))
7286+ } else {
7287+ (
7288+ format!("rsSome[RsOrdering]({code})"),
7289+ Some(Nim::Named("Option".into(), vec![o])),
7290+ )
7291+ }
7292+ }
7293+ "recip" if matches!(&rt, Some(Nim::Prim(p)) if p.starts_with("float")) => {
7294+ let t = rt.clone().unwrap();
7295+ (format!("({}(1.0) / {})", t.render(), recv.code), Some(t))
7296+ }
7297+ "to_bits" if matches!(&rt, Some(Nim::Prim(p)) if p.starts_with("float")) => {
7298+ let from = match &rt {
7299+ Some(Nim::Prim(p)) => p.clone(),
7300+ _ => unreachable!(),
7301+ };
7302+ let to = if from == "float32" { "uint32" } else { "uint64" };
7303+ // The receiver is converted to its declared width first: a
7304+ // `cast` reinterprets whatever bytes are there, and an
7305+ // unsuffixed literal is `float64` until something says
7306+ // otherwise -- which would take the wrong four of eight.
7307+ (
7308+ format!("cast[{}]({}({}))", to, from, recv.code),
7309+ Some(Nim::Prim(to.into())),
7310+ )
7311+ }
72477312 "clear" if matches!(rt, Some(Nim::Seq(_)) | Some(Nim::Prim(_))) => {
72487313 (format!("{}.setLen(0)", recv.code), Some(Nim::Unit))
72497314 }
@@ -6811,6 +6811,32 @@ impl Lowerer {
6811 }6811 }
6812 }6812 }
6813 }6813 }
6814+ // `Ord::cmp` / `PartialOrd::partial_cmp`. Nim's `cmp` returns an
6815+ // `int` whose sign carries the same information, and `rsOrd` names
6816+ // the three cases. `partial_cmp` is the total one wrapped in
6817+ // `Some`: these are the derived impls, and a derived `PartialOrd`
6818+ // never answers `None`.
6819+ "cmp" | "partial_cmp" if codes.len() == 2 => {
6820+ let o = Nim::Named("RsOrdering".into(), Vec::new());
6821+ let code = format!("rsOrd(cmp({}, {}))", codes[0], codes[1]);
6822+ return Ok(if name == "cmp" {
6823+ Val::new(code, Some(o))
6824+ } else {
6825+ Val::new(
6826+ format!("rsSome[RsOrdering]({code})"),
6827+ Some(Nim::Named("Option".into(), vec![o])),
6828+ )
6829+ });
6830+ }
6831+ // `Default::default()` is the zero value, which the expected type
6832+ // decides. Without one there is nothing to build.
6833+ "default" if codes.is_empty() => {
6834+ if let Some(t) = expect {
6835+ if let Some(d) = default_of(t) {
6836+ return Ok(Val::new(d, Some(t.clone())));
6837+ }
6838+ }
6839+ }
6814 // `vec![v; n]`, after expansion.6840 // `vec![v; n]`, after expansion.
6815 "from_elem" if codes.len() == 2 => {6841 "from_elem" if codes.len() == 2 => {
6816 let t = args[0].ty.clone().map(|t| Nim::Seq(Box::new(t.owned())));6842 let t = args[0].ty.clone().map(|t| Nim::Seq(Box::new(t.owned())));
@@ -7244,6 +7270,45 @@ impl Lowerer {
7244 "push" => (format!("{}.add({})", recv.code, a0.unwrap_or_default()), Some(Nim::Unit)),7270 "push" => (format!("{}.add({})", recv.code, a0.unwrap_or_default()), Some(Nim::Unit)),
7245 // `clear` drops every element and keeps the allocation, which is7271 // `clear` drops every element and keeps the allocation, which is
7246 // what setting the length to zero does in Nim too.7272 // what setting the length to zero does in Nim too.
7273+ // `f.recip()` is `1/f`, and `to_bits`/`from_bits` reinterpret the
7274+ // float's bytes -- which is what Nim's `cast` means.
7275+ // The method spelling of `Ord::cmp`; the derived impls use the
7276+ // free-function one, hand-written code this.
7277+ "cmp" | "partial_cmp" if m.args.len() == 1 => {
7278+ let o = Nim::Named("RsOrdering".into(), Vec::new());
7279+ let code = format!(
7280+ "rsOrd(cmp({}, {}))",
7281+ recv.code,
7282+ a0.clone().unwrap_or_default()
7283+ );
7284+ if name == "cmp" {
7285+ (code, Some(o))
7286+ } else {
7287+ (
7288+ format!("rsSome[RsOrdering]({code})"),
7289+ Some(Nim::Named("Option".into(), vec![o])),
7290+ )
7291+ }
7292+ }
7293+ "recip" if matches!(&rt, Some(Nim::Prim(p)) if p.starts_with("float")) => {
7294+ let t = rt.clone().unwrap();
7295+ (format!("({}(1.0) / {})", t.render(), recv.code), Some(t))
7296+ }
7297+ "to_bits" if matches!(&rt, Some(Nim::Prim(p)) if p.starts_with("float")) => {
7298+ let from = match &rt {
7299+ Some(Nim::Prim(p)) => p.clone(),
7300+ _ => unreachable!(),
7301+ };
7302+ let to = if from == "float32" { "uint32" } else { "uint64" };
7303+ // The receiver is converted to its declared width first: a
7304+ // `cast` reinterprets whatever bytes are there, and an
7305+ // unsuffixed literal is `float64` until something says
7306+ // otherwise -- which would take the wrong four of eight.
7307+ (
7308+ format!("cast[{}]({}({}))", to, from, recv.code),
7309+ Some(Nim::Prim(to.into())),
7310+ )
7311+ }
7247 "clear" if matches!(rt, Some(Nim::Seq(_)) | Some(Nim::Prim(_))) => {7312 "clear" if matches!(rt, Some(Nim::Seq(_)) | Some(Nim::Prim(_))) => {
7248 (format!("{}.setLen(0)", recv.code), Some(Nim::Unit))7313 (format!("{}.setLen(0)", recv.code), Some(Nim::Unit))
7249 }7314 }
modified src/prelude.nim +17 -0
@@ -327,3 +327,20 @@ template rsArrayFill*(n: static int, T: typedesc, v: untyped): untyped =
327327 ## like `Result<T, !>` has something to name. No translated program can
328328 ## construct one, because no Rust program can.
329329 type RsNever* = object
330+
331+## `core::cmp::Ordering`. Nim's `cmp` returns an `int` whose sign carries the
332+## same information; this names the three cases so a `match` on them reads as
333+## it does in Rust.
334+type RsOrdering* = enum
335+ rsLess, rsEqual, rsGreater
336+
337+proc rsOrd*(n: int): RsOrdering =
338+ if n < 0: rsLess elif n > 0: rsGreater else: rsEqual
339+
340+## Rust prints these as `Less` / `Equal` / `Greater`.
341+proc rsDebug*(o: RsOrdering): string =
342+ case o
343+ of rsLess: "Less"
344+ of rsEqual: "Equal"
345+ of rsGreater: "Greater"
346+
@@ -327,3 +327,20 @@ template rsArrayFill*(n: static int, T: typedesc, v: untyped): untyped =
327 ## like `Result<T, !>` has something to name. No translated program can327 ## like `Result<T, !>` has something to name. No translated program can
328 ## construct one, because no Rust program can.328 ## construct one, because no Rust program can.
329 type RsNever* = object329 type RsNever* = object
330+
331+## `core::cmp::Ordering`. Nim's `cmp` returns an `int` whose sign carries the
332+## same information; this names the three cases so a `match` on them reads as
333+## it does in Rust.
334+type RsOrdering* = enum
335+ rsLess, rsEqual, rsGreater
336+
337+proc rsOrd*(n: int): RsOrdering =
338+ if n < 0: rsLess elif n > 0: rsGreater else: rsEqual
339+
340+## Rust prints these as `Less` / `Equal` / `Greater`.
341+proc rsDebug*(o: RsOrdering): string =
342+ case o
343+ of rsLess: "Less"
344+ of rsEqual: "Equal"
345+ of rsGreater: "Greater"
346+
modified tests/cases/066-std-subset.rs +7 -0
@@ -39,6 +39,13 @@ fn main() {
3939 t.clear();
4040 println!("[{}] {}", t, t.len());
4141
42+ // `cmp` and `partial_cmp`; `recip` and `to_bits`.
43+ println!("{:?} {:?}", 3u32.cmp(&5), 5u32.cmp(&3));
44+ println!("{:?}", 4u32.cmp(&4));
45+ println!("{:?}", 3u32.partial_cmp(&5));
46+ println!("{} {}", 4.0f64.recip(), 0.5f32.recip());
47+ println!("{} {}", 1.0f32.to_bits(), 2.0f64.to_bits());
48+
4249 // `#[derive(PartialOrd)]` compares discriminants.
4350 println!("{} {}", Rank::Low < Rank::High, Rank::Mid > Rank::High);
4451 }
@@ -39,6 +39,13 @@ fn main() {
39 t.clear();39 t.clear();
40 println!("[{}] {}", t, t.len());40 println!("[{}] {}", t, t.len());
41 41
42+ // `cmp` and `partial_cmp`; `recip` and `to_bits`.
43+ println!("{:?} {:?}", 3u32.cmp(&5), 5u32.cmp(&3));
44+ println!("{:?}", 4u32.cmp(&4));
45+ println!("{:?}", 3u32.partial_cmp(&5));
46+ println!("{} {}", 4.0f64.recip(), 0.5f32.recip());
47+ println!("{} {}", 1.0f32.to_bits(), 2.0f64.to_bits());
48+
42 // `#[derive(PartialOrd)]` compares discriminants.49 // `#[derive(PartialOrd)]` compares discriminants.
43 println!("{} {}", Rank::Low < Rank::High, Rank::Mid > Rank::High);50 println!("{} {}", Rank::Low < Rank::High, Rank::Mid > Rank::High);
44 }51 }