fix(runtime::to_value): numbers are always NixFloat

This commit is contained in:
2026-01-03 12:19:43 +08:00
parent add715f560
commit c79eb0951e
3 changed files with 14 additions and 25 deletions

1
.envrc
View File

@@ -1 +1,2 @@
use flake use flake
dotenv

View File

@@ -1,6 +1,7 @@
use std::ptr::NonNull; use std::ptr::NonNull;
use hashbrown::HashMap; use hashbrown::HashMap;
use itertools::Itertools as _;
use string_interner::DefaultStringInterner; use string_interner::DefaultStringInterner;
use crate::codegen::{CodegenContext, Compile}; use crate::codegen::{CodegenContext, Compile};
@@ -15,7 +16,7 @@ mod downgrade;
pub struct Context { pub struct Context {
irs: Vec<Ir>, irs: Vec<Ir>,
symbols: DefaultStringInterner, symbols: DefaultStringInterner,
global: NonNull<HashMap<crate::ir::SymId, ExprId>>, global: NonNull<HashMap<SymId, ExprId>>,
} }
impl Drop for Context { impl Drop for Context {
@@ -99,14 +100,7 @@ impl Context {
pub fn eval(&mut self, expr: &str) -> Result<Value> { pub fn eval(&mut self, expr: &str) -> Result<Value> {
let root = rnix::Root::parse(expr); let root = rnix::Root::parse(expr);
if !root.errors().is_empty() { if !root.errors().is_empty() {
return Err(Error::parse_error(root.errors().iter().fold( return Err(Error::parse_error(root.errors().iter().join("; ")));
String::new(),
|mut acc, err| {
acc.push_str(&err.to_string());
acc.push_str("; ");
acc
},
)));
} }
let root = self let root = self
.downgrade_ctx() .downgrade_ctx()
@@ -331,11 +325,9 @@ mod test {
#[test] #[test]
fn test_builtin_function_map() { fn test_builtin_function_map() {
// Test higher-order builtin: builtins.map (x: x * 2) [1 2 3] // Test higher-order builtin: map (x: x * 2) [1 2 3]
assert_eq!( assert_eq!(
Context::new() Context::new().eval("map (x: x * 2) [1 2 3]").unwrap(),
.eval("builtins.map (x: x * 2) [1 2 3]")
.unwrap(),
Value::List(List::new(vec![ Value::List(List::new(vec![
Value::Const(Const::Int(2)), Value::Const(Const::Int(2)),
Value::Const(Const::Int(4)), Value::Const(Const::Int(4)),
@@ -628,6 +620,10 @@ mod test {
ctx.eval("builtins.typeOf 3.14").unwrap(), ctx.eval("builtins.typeOf 3.14").unwrap(),
Value::String("float".to_string()) Value::String("float".to_string())
); );
// literal tests
assert_eq!(ctx.eval("1").unwrap(), Value::Const(Const::Int(1)));
assert_eq!(ctx.eval("1.").unwrap(), Value::Const(Const::Float(1.)))
} }
#[test] #[test]

View File

@@ -146,17 +146,9 @@ fn to_value<'a, 'b>(val: v8::Local<'a, v8::Value>, ctx: &RuntimeContext<'a, 'b>)
} }
_ if val.is_number() => { _ if val.is_number() => {
let val = val.to_number(scope).unwrap().value(); let val = val.to_number(scope).unwrap().value();
// Heuristic: convert whole numbers to Int (for backward compatibility and JS interop) // number is always NixFloat
if val.is_finite()
&& val.fract() == 0.0
&& val >= i64::MIN as f64
&& val <= i64::MAX as f64
{
Value::Const(Const::Int(val as i64))
} else {
Value::Const(Const::Float(val)) Value::Const(Const::Float(val))
} }
}
_ if val.is_true() => Value::Const(Const::Bool(true)), _ if val.is_true() => Value::Const(Const::Bool(true)),
_ if val.is_false() => Value::Const(Const::Bool(false)), _ if val.is_false() => Value::Const(Const::Bool(false)),
_ if val.is_null() => Value::Const(Const::Null), _ if val.is_null() => Value::Const(Const::Null),
@@ -280,13 +272,13 @@ fn primop_app_name<'a, 'b>(
fn to_value_working() { fn to_value_working() {
assert_eq!( assert_eq!(
run("({ run("({
test: [1, 9223372036854775807n, true, false, 'hello world!'] test: [1., 9223372036854775807n, true, false, 'hello world!']
})") })")
.unwrap(), .unwrap(),
Value::AttrSet(AttrSet::new(std::collections::BTreeMap::from([( Value::AttrSet(AttrSet::new(std::collections::BTreeMap::from([(
Symbol::from("test"), Symbol::from("test"),
Value::List(List::new(vec![ Value::List(List::new(vec![
Value::Const(Const::Int(1)), Value::Const(Const::Float(1.)),
Value::Const(Const::Int(9223372036854775807)), Value::Const(Const::Int(9223372036854775807)),
Value::Const(Const::Bool(true)), Value::Const(Const::Bool(true)),
Value::Const(Const::Bool(false)), Value::Const(Const::Bool(false)),