nandi/rustnimpublic Fork
⑂ e34bf8b
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 `from_bits` and the float constants, and order the constants

`from_bits` reinterprets an integer's bytes as a float, which is what Nim's
`cast` means; the inverse of `to_bits`, with the width from the type it is
called on.

`EPSILON`, `MIN_POSITIVE`, `INFINITY`, `NEG_INFINITY` and `NAN` are written
out. Nim has its own spellings for some and none for others, and these are the
values Rust defines rather than whatever a library happens to agree on.

Module-level constants get a pass of their own, ahead of the associated
constants -- `const DEFAULT_TOLERANCE: Self::Float = F64_TOLERANCE;` names one
and was being written before it. That is the fifth ordering of this kind now:
consts, associated consts, types, forwards, statics, bodies. Each was found by
something naming something else that had not been emitted yet.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nandithebull committed 2026-09-20T12:12:22-07:00 Browse files
e34bf8b parent: 7e4c463
modified src/lower.rs +85 -1
@@ -671,6 +671,16 @@ impl Lowerer {
671671 self.resolve_discriminants()?;
672672 self.resolve_opaque()?;
673673
674+ // Pass 1.8: module-level constants, which an associated constant may
675+ // mention -- `const DEFAULT: Self::Float = F64_TOLERANCE;` does.
676+ for (m, f) in files {
677+ self.cur_mod = m.clone();
678+ for item in &f.items {
679+ let r = self.emit_consts(item);
680+ self.note(r)?;
681+ }
682+ }
683+
674684 // Pass 1.9: associated constants, which a type definition may mention.
675685 // `struct T { data: [u64; Self::N] }` is the case -- the constant has
676686 // to be declared before the object that uses it, and `impl T` is
@@ -2717,6 +2727,32 @@ impl Lowerer {
27172727 // --------------------------------------------------------------- items
27182728
27192729 /// Emit the type definitions only: they must precede every signature.
2730+ /// Write out the module-level constants, ahead of everything that can
2731+ /// name one.
2732+ fn emit_consts(&mut self, item: &Item) -> Result<(), String> {
2733+ if !self.cfg_keeps(item_attrs(item))? {
2734+ return Ok(());
2735+ }
2736+ match item {
2737+ Item::Const(c) if derive_item_block(c).is_none() => self.item_inner(item),
2738+ Item::Const(c) => {
2739+ for i in &derive_item_block(c).unwrap() {
2740+ self.emit_consts(i)?;
2741+ }
2742+ Ok(())
2743+ }
2744+ Item::Mod(m) => {
2745+ if let Some((_, items)) = &m.content {
2746+ for i in items {
2747+ self.emit_consts(i)?;
2748+ }
2749+ }
2750+ Ok(())
2751+ }
2752+ _ => Ok(()),
2753+ }
2754+ }
2755+
27202756 /// Write out the `static` items, after the types and before the procs.
27212757 fn emit_statics(&mut self, item: &Item) -> Result<(), String> {
27222758 if !self.cfg_keeps(item_attrs(item))? {
@@ -2794,7 +2830,9 @@ impl Lowerer {
27942830 }
27952831 Ok(())
27962832 }
2797- Item::Struct(_) | Item::Enum(_) | Item::Const(_) => self.item_inner(item),
2833+ // Constants were written out in their own pass.
2834+ Item::Const(_) => Ok(()),
2835+ Item::Struct(_) | Item::Enum(_) => self.item_inner(item),
27982836 Item::Macro(m) if path_name(&m.mac.path) == "bitflags" => self.item_inner(item),
27992837 Item::ForeignMod(_) => self.item_inner(item),
28002838 // A function-local type becomes a module-level type, so it is
@@ -6007,6 +6045,33 @@ impl Lowerer {
60076045 }
60086046
60096047 // `i32::MAX` and friends: an associated const on a primitive.
6048+ // The float constants, written out. Nim has its own spellings
6049+ // for some and none for others, and these are the values Rust
6050+ // defines rather than whatever a library happens to agree on.
6051+ if let Some(q) = p.path.segments.iter().rev().nth(1) {
6052+ let f32 = q.ident == "f32";
6053+ if f32 || q.ident == "f64" {
6054+ let v = match name.as_str() {
6055+ "EPSILON" if f32 => Some("1.1920928955078125e-7'f32"),
6056+ "EPSILON" => Some("2.220446049250313e-16'f64"),
6057+ "MIN_POSITIVE" if f32 => Some("1.1754943508222875e-38'f32"),
6058+ "MIN_POSITIVE" => Some("2.2250738585072014e-308'f64"),
6059+ "INFINITY" if f32 => Some("float32(Inf)"),
6060+ "INFINITY" => Some("Inf"),
6061+ "NEG_INFINITY" if f32 => Some("float32(-Inf)"),
6062+ "NEG_INFINITY" => Some("-Inf"),
6063+ "NAN" if f32 => Some("float32(NaN)"),
6064+ "NAN" => Some("NaN"),
6065+ _ => None,
6066+ };
6067+ if let Some(v) = v {
6068+ return Ok(Val::new(
6069+ v,
6070+ Some(Nim::Prim(if f32 { "float32" } else { "float64" }.into())),
6071+ ));
6072+ }
6073+ }
6074+ }
60106075 if matches!(name.as_str(), "MAX" | "MIN") {
60116076 if let Some(q) = p.path.segments.iter().rev().nth(1) {
60126077 if let Some(t @ Nim::Prim(_)) = ty::prim(&q.ident.to_string()) {
@@ -7784,6 +7849,25 @@ impl Lowerer {
77847849 Some(Nim::Prim("bool".into())),
77857850 ));
77867851 }
7852+ // `f64::from_bits(u)` reinterprets the integer's bytes as a
7853+ // float, which is what Nim's `cast` means. The inverse of
7854+ // `to_bits`, and the width comes from the type it is called on.
7855+ "from_bits" if codes.len() == 1 => {
7856+ if let Some(q) = p.path.segments.iter().rev().nth(1) {
7857+ let (to, from) = match q.ident.to_string().as_str() {
7858+ "f32" => ("float32", "uint32"),
7859+ "f64" => ("float64", "uint64"),
7860+ _ => ("", ""),
7861+ };
7862+ if !to.is_empty() {
7863+ let v = self.expr_at(&c.args[0], Some(&Nim::Prim(from.into())))?;
7864+ return Ok(Val::new(
7865+ format!("cast[{}]({}({}))", to, from, v.code),
7866+ Some(Nim::Prim(to.into())),
7867+ ));
7868+ }
7869+ }
7870+ }
77877871 // `cmp::max` / `cmp::min` are Nim's `max` / `min` on the same
77887872 // ordering.
77897873 "max" | "min" if codes.len() == 2 => {
@@ -671,6 +671,16 @@ impl Lowerer {
671 self.resolve_discriminants()?;671 self.resolve_discriminants()?;
672 self.resolve_opaque()?;672 self.resolve_opaque()?;
673 673
674+ // Pass 1.8: module-level constants, which an associated constant may
675+ // mention -- `const DEFAULT: Self::Float = F64_TOLERANCE;` does.
676+ for (m, f) in files {
677+ self.cur_mod = m.clone();
678+ for item in &f.items {
679+ let r = self.emit_consts(item);
680+ self.note(r)?;
681+ }
682+ }
683+
674 // Pass 1.9: associated constants, which a type definition may mention.684 // Pass 1.9: associated constants, which a type definition may mention.
675 // `struct T { data: [u64; Self::N] }` is the case -- the constant has685 // `struct T { data: [u64; Self::N] }` is the case -- the constant has
676 // to be declared before the object that uses it, and `impl T` is686 // to be declared before the object that uses it, and `impl T` is
@@ -2717,6 +2727,32 @@ impl Lowerer {
2717 // --------------------------------------------------------------- items2727 // --------------------------------------------------------------- items
2718 2728
2719 /// Emit the type definitions only: they must precede every signature.2729 /// Emit the type definitions only: they must precede every signature.
2730+ /// Write out the module-level constants, ahead of everything that can
2731+ /// name one.
2732+ fn emit_consts(&mut self, item: &Item) -> Result<(), String> {
2733+ if !self.cfg_keeps(item_attrs(item))? {
2734+ return Ok(());
2735+ }
2736+ match item {
2737+ Item::Const(c) if derive_item_block(c).is_none() => self.item_inner(item),
2738+ Item::Const(c) => {
2739+ for i in &derive_item_block(c).unwrap() {
2740+ self.emit_consts(i)?;
2741+ }
2742+ Ok(())
2743+ }
2744+ Item::Mod(m) => {
2745+ if let Some((_, items)) = &m.content {
2746+ for i in items {
2747+ self.emit_consts(i)?;
2748+ }
2749+ }
2750+ Ok(())
2751+ }
2752+ _ => Ok(()),
2753+ }
2754+ }
2755+
2720 /// Write out the `static` items, after the types and before the procs.2756 /// Write out the `static` items, after the types and before the procs.
2721 fn emit_statics(&mut self, item: &Item) -> Result<(), String> {2757 fn emit_statics(&mut self, item: &Item) -> Result<(), String> {
2722 if !self.cfg_keeps(item_attrs(item))? {2758 if !self.cfg_keeps(item_attrs(item))? {
@@ -2794,7 +2830,9 @@ impl Lowerer {
2794 }2830 }
2795 Ok(())2831 Ok(())
2796 }2832 }
2797- Item::Struct(_) | Item::Enum(_) | Item::Const(_) => self.item_inner(item),2833+ // Constants were written out in their own pass.
2834+ Item::Const(_) => Ok(()),
2835+ Item::Struct(_) | Item::Enum(_) => self.item_inner(item),
2798 Item::Macro(m) if path_name(&m.mac.path) == "bitflags" => self.item_inner(item),2836 Item::Macro(m) if path_name(&m.mac.path) == "bitflags" => self.item_inner(item),
2799 Item::ForeignMod(_) => self.item_inner(item),2837 Item::ForeignMod(_) => self.item_inner(item),
2800 // A function-local type becomes a module-level type, so it is2838 // A function-local type becomes a module-level type, so it is
@@ -6007,6 +6045,33 @@ impl Lowerer {
6007 }6045 }
6008 6046
6009 // `i32::MAX` and friends: an associated const on a primitive.6047 // `i32::MAX` and friends: an associated const on a primitive.
6048+ // The float constants, written out. Nim has its own spellings
6049+ // for some and none for others, and these are the values Rust
6050+ // defines rather than whatever a library happens to agree on.
6051+ if let Some(q) = p.path.segments.iter().rev().nth(1) {
6052+ let f32 = q.ident == "f32";
6053+ if f32 || q.ident == "f64" {
6054+ let v = match name.as_str() {
6055+ "EPSILON" if f32 => Some("1.1920928955078125e-7'f32"),
6056+ "EPSILON" => Some("2.220446049250313e-16'f64"),
6057+ "MIN_POSITIVE" if f32 => Some("1.1754943508222875e-38'f32"),
6058+ "MIN_POSITIVE" => Some("2.2250738585072014e-308'f64"),
6059+ "INFINITY" if f32 => Some("float32(Inf)"),
6060+ "INFINITY" => Some("Inf"),
6061+ "NEG_INFINITY" if f32 => Some("float32(-Inf)"),
6062+ "NEG_INFINITY" => Some("-Inf"),
6063+ "NAN" if f32 => Some("float32(NaN)"),
6064+ "NAN" => Some("NaN"),
6065+ _ => None,
6066+ };
6067+ if let Some(v) = v {
6068+ return Ok(Val::new(
6069+ v,
6070+ Some(Nim::Prim(if f32 { "float32" } else { "float64" }.into())),
6071+ ));
6072+ }
6073+ }
6074+ }
6010 if matches!(name.as_str(), "MAX" | "MIN") {6075 if matches!(name.as_str(), "MAX" | "MIN") {
6011 if let Some(q) = p.path.segments.iter().rev().nth(1) {6076 if let Some(q) = p.path.segments.iter().rev().nth(1) {
6012 if let Some(t @ Nim::Prim(_)) = ty::prim(&q.ident.to_string()) {6077 if let Some(t @ Nim::Prim(_)) = ty::prim(&q.ident.to_string()) {
@@ -7784,6 +7849,25 @@ impl Lowerer {
7784 Some(Nim::Prim("bool".into())),7849 Some(Nim::Prim("bool".into())),
7785 ));7850 ));
7786 }7851 }
7852+ // `f64::from_bits(u)` reinterprets the integer's bytes as a
7853+ // float, which is what Nim's `cast` means. The inverse of
7854+ // `to_bits`, and the width comes from the type it is called on.
7855+ "from_bits" if codes.len() == 1 => {
7856+ if let Some(q) = p.path.segments.iter().rev().nth(1) {
7857+ let (to, from) = match q.ident.to_string().as_str() {
7858+ "f32" => ("float32", "uint32"),
7859+ "f64" => ("float64", "uint64"),
7860+ _ => ("", ""),
7861+ };
7862+ if !to.is_empty() {
7863+ let v = self.expr_at(&c.args[0], Some(&Nim::Prim(from.into())))?;
7864+ return Ok(Val::new(
7865+ format!("cast[{}]({}({}))", to, from, v.code),
7866+ Some(Nim::Prim(to.into())),
7867+ ));
7868+ }
7869+ }
7870+ }
7787 // `cmp::max` / `cmp::min` are Nim's `max` / `min` on the same7871 // `cmp::max` / `cmp::min` are Nim's `max` / `min` on the same
7788 // ordering.7872 // ordering.
7789 "max" | "min" if codes.len() == 2 => {7873 "max" | "min" if codes.len() == 2 => {