From 03766219825e531c61a19046dddd54acfab0ceb4 Mon Sep 17 00:00:00 2001 From: imxyy_soope_ Date: Fri, 9 Jan 2026 17:51:51 +0800 Subject: [PATCH] refactor: flatten Ir::Const & Value::Const; add Ir::Builtin to represent globally available builtins --- nix-js/src/codegen.rs | 13 +-- nix-js/src/context.rs | 228 +++++++++++++++---------------------- nix-js/src/ir.rs | 22 +--- nix-js/src/ir/downgrade.rs | 4 +- nix-js/src/lib.rs | 4 +- nix-js/src/runtime.rs | 89 +++++++-------- nix-js/src/value.rs | 58 ++-------- 7 files changed, 160 insertions(+), 258 deletions(-) diff --git a/nix-js/src/codegen.rs b/nix-js/src/codegen.rs index 2981d03..008b0cb 100644 --- a/nix-js/src/codegen.rs +++ b/nix-js/src/codegen.rs @@ -2,11 +2,11 @@ use itertools::Itertools as _; use crate::ir::*; -pub trait Compile { +pub(crate) trait Compile { fn compile(&self, ctx: &Ctx) -> String; } -pub trait CodegenContext { +pub(crate) trait CodegenContext { fn get_ir(&self, id: ExprId) -> &Ir; fn get_sym(&self, id: SymId) -> &str; } @@ -14,12 +14,8 @@ pub trait CodegenContext { impl Compile for Ir { fn compile(&self, ctx: &Ctx) -> String { match self { - Ir::Const(Const { val }) => match val { - crate::value::Const::Null => "null".to_string(), - crate::value::Const::Int(val) => format!("{}n", val), // Generate BigInt literal - crate::value::Const::Float(val) => val.to_string(), - crate::value::Const::Bool(val) => val.to_string(), - }, + Ir::Int(int) => format!("{int}n"), // Generate BigInt literal + Ir::Float(float) => float.to_string(), Ir::Str(s) => { // Escape string for JavaScript let escaped = s @@ -63,6 +59,7 @@ impl Compile for Ir { format!("expr{}", expr_id.0) } Ir::Builtins(_) => "Nix.builtins".to_string(), + &Ir::Builtin(Builtin(name)) => format!("Nix.builtins[\"{}\"]", ctx.get_sym(name)), Ir::ConcatStrings(x) => x.compile(ctx), Ir::HasAttr(x) => x.compile(ctx), &Ir::Assert(Assert { assertion, expr }) => { diff --git a/nix-js/src/context.rs b/nix-js/src/context.rs index 86e62b2..f540982 100644 --- a/nix-js/src/context.rs +++ b/nix-js/src/context.rs @@ -8,7 +8,7 @@ use string_interner::DefaultStringInterner; use crate::codegen::{CodegenContext, Compile}; use crate::error::{Error, Result}; -use crate::ir::{DowngradeContext, ExprId, Ir, SymId}; +use crate::ir::{Builtin, DowngradeContext, ExprId, Ir, SymId}; use crate::runtime::Runtime; use crate::value::Value; @@ -118,7 +118,7 @@ impl Drop for PathDropGuard<'_> { impl Default for Ctx { fn default() -> Self { - use crate::ir::{Attr, Builtins, Select, ToIr}; + use crate::ir::{Builtins, ToIr as _}; let mut symbols = DefaultStringInterner::new(); let mut irs = Vec::new(); @@ -157,14 +157,9 @@ impl Default for Ctx { for name in free_globals { let name_sym = symbols.get_or_intern(name); - let select_ir = Select { - expr: builtins_expr, - attrpath: vec![Attr::Str(name_sym)], - default: None, - }; - let select_expr = ExprId(irs.len()); - irs.push(select_ir.to_ir()); - global.insert(name_sym, select_expr); + let id = ExprId(irs.len()); + irs.push(Builtin(name_sym).to_ir()); + global.insert(name_sym, id); } Self { @@ -178,16 +173,16 @@ impl Default for Ctx { } impl Ctx { - pub fn new() -> Self { + pub(crate) fn new() -> Self { Self::default() } - pub fn downgrade_ctx<'a>(self: Pin<&'a mut Self>) -> DowngradeCtx<'a> { + pub(crate) fn downgrade_ctx<'a>(self: Pin<&'a mut Self>) -> DowngradeCtx<'a> { let global_ref = unsafe { self.global.as_ref() }; DowngradeCtx::new(self, global_ref) } - pub fn get_current_dir(&self) -> PathBuf { + pub(crate) fn get_current_dir(&self) -> PathBuf { self.path_stack .last() .unwrap() @@ -212,63 +207,55 @@ mod test { use std::collections::BTreeMap; use super::*; - use crate::value::{AttrSet, Const, List, Symbol}; + use crate::value::{AttrSet, List, Symbol}; #[test] fn basic_eval() { - assert_eq!( - Context::new().eval_code("1 + 1").unwrap(), - Value::Const(Const::Int(2)) - ); - assert_eq!( - Context::new().eval_code("(x: x) 1").unwrap(), - Value::Const(Const::Int(1)) - ); + assert_eq!(Context::new().eval_code("1 + 1").unwrap(), Value::Int(2)); + assert_eq!(Context::new().eval_code("(x: x) 1").unwrap(), Value::Int(1)); assert_eq!( Context::new().eval_code("(x: y: x - y) 2 1").unwrap(), - Value::Const(Const::Int(1)) + Value::Int(1) ); assert_eq!( Context::new().eval_code("rec { b = a; a = 1; }.b").unwrap(), - Value::Const(Const::Int(1)) + Value::Int(1) ); assert_eq!( Context::new().eval_code("let b = a; a = 1; in b").unwrap(), - Value::Const(Const::Int(1)) + Value::Int(1) ); assert_eq!( Context::new().eval_code("let fib = n: if n == 1 || n == 2 then 1 else (fib (n - 1)) + (fib (n - 2)); in fib 30").unwrap(), - Value::Const(Const::Int(832040)) + Value::Int(832040) ); assert_eq!( Context::new() .eval_code("((f: let x = f x; in x)(self: { x = 1; y = self.x + 1; })).y") .unwrap(), - Value::Const(Const::Int(2)) + Value::Int(2) ); } #[test] fn test_binop() { let tests = [ - ("1 + 1", Value::Const(Const::Int(2))), - ("2 - 1", Value::Const(Const::Int(1))), - ("1. * 1", Value::Const(Const::Float(1.))), - ("1 / 1.", Value::Const(Const::Float(1.))), - ("1 == 1", Value::Const(Const::Bool(true))), - ("1 != 1", Value::Const(Const::Bool(false))), - ("2 < 1", Value::Const(Const::Bool(false))), - ("2 > 1", Value::Const(Const::Bool(true))), - ("1 <= 1", Value::Const(Const::Bool(true))), - ("1 >= 1", Value::Const(Const::Bool(true))), + ("1 + 1", Value::Int(2)), + ("2 - 1", Value::Int(1)), + ("1. * 1", Value::Float(1.)), + ("1 / 1.", Value::Float(1.)), + ("1 == 1", Value::Bool(true)), + ("1 != 1", Value::Bool(false)), + ("2 < 1", Value::Bool(false)), + ("2 > 1", Value::Bool(true)), + ("1 <= 1", Value::Bool(true)), + ("1 >= 1", Value::Bool(true)), // Short-circuit evaluation: true || should not evaluate - ("true || (1 / 0)", Value::Const(Const::Bool(true))), - ("true && 1 == 0", Value::Const(Const::Bool(false))), + ("true || (1 / 0)", Value::Bool(true)), + ("true && 1 == 0", Value::Bool(false)), ( "[ 1 2 3 ] ++ [ 4 5 6 ]", - Value::List(List::new( - (1..=6).map(Const::Int).map(Value::Const).collect(), - )), + Value::List(List::new((1..=6).map(Value::Int).collect())), ), ( "{ a.b = 1; b = 2; } // { a.c = 2; }", @@ -277,10 +264,10 @@ mod test { Symbol::from("a"), Value::AttrSet(AttrSet::new(BTreeMap::from([( Symbol::from("c"), - Value::Const(Const::Int(2)), + Value::Int(2), )]))), ), - (Symbol::from("b"), Value::Const(Const::Int(2))), + (Symbol::from("b"), Value::Int(2)), ]))), ), ]; @@ -296,7 +283,7 @@ mod test { Context::new() .eval_code("({ a, b }: a + b) { a = 1; b = 2; }") .unwrap(), - Value::Const(Const::Int(3)) + Value::Int(3) ); // Test missing required parameter should fail @@ -308,7 +295,7 @@ mod test { Context::new() .eval_code("({ x, y, z }: x + y + z) { x = 1; y = 2; z = 3; }") .unwrap(), - Value::Const(Const::Int(6)) + Value::Int(6) ); } @@ -323,7 +310,7 @@ mod test { Context::new() .eval_code("({ a, b, ... }: a + b) { a = 1; b = 2; c = 3; }") .unwrap(), - Value::Const(Const::Int(3)) + Value::Int(3) ); } @@ -334,7 +321,7 @@ mod test { Context::new() .eval_code("({ a, b ? 5 }: a + b) { a = 1; }") .unwrap(), - Value::Const(Const::Int(6)) + Value::Int(6) ); // Test overriding default parameter @@ -342,7 +329,7 @@ mod test { Context::new() .eval_code("({ a, b ? 5 }: a + b) { a = 1; b = 10; }") .unwrap(), - Value::Const(Const::Int(11)) + Value::Int(11) ); } @@ -353,7 +340,7 @@ mod test { Context::new() .eval_code("(args@{ a, b }: args.a + args.b) { a = 1; b = 2; }") .unwrap(), - Value::Const(Const::Int(3)) + Value::Int(3) ); } @@ -364,13 +351,13 @@ mod test { Context::new() .eval_code("(x: x.a + x.b) { a = 1; b = 2; }") .unwrap(), - Value::Const(Const::Int(3)) + Value::Int(3) ); // Simple parameter accepts any argument assert_eq!( Context::new().eval_code("(x: x) 42").unwrap(), - Value::Const(Const::Int(42)) + Value::Int(42) ); } @@ -394,7 +381,7 @@ mod test { // Test calling builtin function: builtins.add 1 2 assert_eq!( Context::new().eval_code("builtins.add 1 2").unwrap(), - Value::Const(Const::Int(3)) + Value::Int(3) ); } @@ -403,7 +390,7 @@ mod test { // Test builtin with list: builtins.length [1 2 3] assert_eq!( Context::new().eval_code("builtins.length [1 2 3]").unwrap(), - Value::Const(Const::Int(3)) + Value::Int(3) ); } @@ -412,11 +399,9 @@ mod test { // Test higher-order builtin: map (x: x * 2) [1 2 3] assert_eq!( Context::new().eval_code("map (x: x * 2) [1 2 3]").unwrap(), - Value::List(List::new(vec![ - Value::Const(Const::Int(2)), - Value::Const(Const::Int(4)), - Value::Const(Const::Int(6)), - ])) + Value::List(List::new( + vec![Value::Int(2), Value::Int(4), Value::Int(6),] + )) ); } @@ -427,10 +412,7 @@ mod test { Context::new() .eval_code("builtins.filter (x: x > 1) [1 2 3]") .unwrap(), - Value::List(List::new(vec![ - Value::Const(Const::Int(2)), - Value::Const(Const::Int(3)), - ])) + Value::List(List::new(vec![Value::Int(2), Value::Int(3),])) ); } @@ -453,7 +435,7 @@ mod test { // Test builtins.head [1 2 3] assert_eq!( Context::new().eval_code("builtins.head [1 2 3]").unwrap(), - Value::Const(Const::Int(1)) + Value::Int(1) ); } @@ -462,10 +444,7 @@ mod test { // Test builtins.tail [1 2 3] assert_eq!( Context::new().eval_code("builtins.tail [1 2 3]").unwrap(), - Value::List(List::new(vec![ - Value::Const(Const::Int(2)), - Value::Const(Const::Int(3)), - ])) + Value::List(List::new(vec![Value::Int(2), Value::Int(3),])) ); } @@ -476,7 +455,7 @@ mod test { Context::new() .eval_code("let b = builtins; in b.add 5 3") .unwrap(), - Value::Const(Const::Int(8)) + Value::Int(8) ); } @@ -487,7 +466,7 @@ mod test { Context::new() .eval_code("with builtins; add 10 20") .unwrap(), - Value::Const(Const::Int(30)) + Value::Int(30) ); } @@ -498,7 +477,7 @@ mod test { Context::new() .eval_code("builtins.add (builtins.mul 2 3) (builtins.sub 10 5)") .unwrap(), - Value::Const(Const::Int(11)) // (2*3) + (10-5) = 6 + 5 = 11 + Value::Int(11) // (2*3) + (10-5 = 6 + 5 = 11 ); } @@ -507,27 +486,27 @@ mod test { // Test type checking functions assert_eq!( Context::new().eval_code("builtins.isList [1 2 3]").unwrap(), - Value::Const(Const::Bool(true)) + Value::Bool(true) ); assert_eq!( Context::new() .eval_code("builtins.isAttrs { a = 1; }") .unwrap(), - Value::Const(Const::Bool(true)) + Value::Bool(true) ); assert_eq!( Context::new() .eval_code("builtins.isFunction (x: x)") .unwrap(), - Value::Const(Const::Bool(true)) + Value::Bool(true) ); assert_eq!( Context::new().eval_code("builtins.isNull null").unwrap(), - Value::Const(Const::Bool(true)) + Value::Bool(true) ); assert_eq!( Context::new().eval_code("builtins.isBool true").unwrap(), - Value::Const(Const::Bool(true)) + Value::Bool(true) ); } @@ -538,7 +517,7 @@ mod test { Context::new() .eval_code("let builtins = { add = x: y: x - y; }; in builtins.add 5 3") .unwrap(), - Value::Const(Const::Int(2)) // Uses shadowed version + Value::Int(2) // Uses shadowed version ); } @@ -549,32 +528,26 @@ mod test { let result = Context::new() .eval_code("builtins.builtins.builtins.add 1 1") .unwrap(); - assert_eq!(result, Value::Const(Const::Int(2))); + assert_eq!(result, Value::Int(2)); } // Free globals tests #[test] fn test_free_global_true() { - assert_eq!( - Context::new().eval_code("true").unwrap(), - Value::Const(Const::Bool(true)) - ); + assert_eq!(Context::new().eval_code("true").unwrap(), Value::Bool(true)); } #[test] fn test_free_global_false() { assert_eq!( Context::new().eval_code("false").unwrap(), - Value::Const(Const::Bool(false)) + Value::Bool(false) ); } #[test] fn test_free_global_null() { - assert_eq!( - Context::new().eval_code("null").unwrap(), - Value::Const(Const::Null) - ); + assert_eq!(Context::new().eval_code("null").unwrap(), Value::Null); } #[test] @@ -582,11 +555,9 @@ mod test { // Test free global function: map (x: x * 2) [1 2 3] assert_eq!( Context::new().eval_code("map (x: x * 2) [1 2 3]").unwrap(), - Value::List(List::new(vec![ - Value::Const(Const::Int(2)), - Value::Const(Const::Int(4)), - Value::Const(Const::Int(6)), - ])) + Value::List(List::new( + vec![Value::Int(2), Value::Int(4), Value::Int(6),] + )) ); } @@ -595,11 +566,11 @@ mod test { // Test isNull function assert_eq!( Context::new().eval_code("isNull null").unwrap(), - Value::Const(Const::Bool(true)) + Value::Bool(true) ); assert_eq!( Context::new().eval_code("isNull 5").unwrap(), - Value::Const(Const::Bool(false)) + Value::Bool(false) ); } @@ -610,13 +581,13 @@ mod test { Context::new() .eval_code("let true = false; in true") .unwrap(), - Value::Const(Const::Bool(false)) + Value::Bool(false) ); assert_eq!( Context::new() .eval_code("let map = x: y: x; in map 1 2") .unwrap(), - Value::Const(Const::Int(1)) + Value::Int(1) ); } @@ -627,10 +598,7 @@ mod test { Context::new() .eval_code("if true then map (x: x + 1) [1 2] else []") .unwrap(), - Value::List(List::new(vec![ - Value::Const(Const::Int(2)), - Value::Const(Const::Int(3)), - ])) + Value::List(List::new(vec![Value::Int(2), Value::Int(3),])) ); } @@ -641,7 +609,7 @@ mod test { Context::new() .eval_code("let x = true; y = false; in x && y") .unwrap(), - Value::Const(Const::Bool(false)) + Value::Bool(false) ); } @@ -653,21 +621,21 @@ mod test { // Test large i64 values assert_eq!( ctx.eval_code("9223372036854775807").unwrap(), - Value::Const(Const::Int(9223372036854775807)) + Value::Int(9223372036854775807) ); // Test negative large value // Can't use -9223372036854775808 since unary minus is actually desugared to (0 - ) assert_eq!( ctx.eval_code("-9223372036854775807").unwrap(), - Value::Const(Const::Int(-9223372036854775807)) + Value::Int(-9223372036854775807) ); // Test large number arithmetic assert_eq!( ctx.eval_code("5000000000000000000 + 3000000000000000000") .unwrap(), - Value::Const(Const::Int(8000000000000000000i64)) + Value::Int(8000000000000000000i64) ); } @@ -678,25 +646,25 @@ mod test { // isInt tests assert_eq!( ctx.eval_code("builtins.isInt 42").unwrap(), - Value::Const(Const::Bool(true)) + Value::Bool(true) ); assert_eq!( ctx.eval_code("builtins.isInt 42.0").unwrap(), - Value::Const(Const::Bool(false)) + Value::Bool(false) ); // isFloat tests assert_eq!( ctx.eval_code("builtins.isFloat 42").unwrap(), - Value::Const(Const::Bool(false)) + Value::Bool(false) ); assert_eq!( ctx.eval_code("builtins.isFloat 42.5").unwrap(), - Value::Const(Const::Bool(true)) + Value::Bool(true) ); assert_eq!( ctx.eval_code("builtins.isFloat 1.0").unwrap(), - Value::Const(Const::Bool(true)) + Value::Bool(true) ); // typeOf tests @@ -714,8 +682,8 @@ mod test { ); // literal tests - assert_eq!(ctx.eval_code("1").unwrap(), Value::Const(Const::Int(1))); - assert_eq!(ctx.eval_code("1.").unwrap(), Value::Const(Const::Float(1.))) + assert_eq!(ctx.eval_code("1").unwrap(), Value::Int(1)); + assert_eq!(ctx.eval_code("1.").unwrap(), Value::Float(1.)) } #[test] @@ -751,30 +719,18 @@ mod test { fn test_integer_division() { let mut ctx = Context::new(); - assert_eq!(ctx.eval_code("5 / 2").unwrap(), Value::Const(Const::Int(2))); + assert_eq!(ctx.eval_code("5 / 2").unwrap(), Value::Int(2)); - assert_eq!(ctx.eval_code("7 / 3").unwrap(), Value::Const(Const::Int(2))); + assert_eq!(ctx.eval_code("7 / 3").unwrap(), Value::Int(2)); - assert_eq!( - ctx.eval_code("10 / 3").unwrap(), - Value::Const(Const::Int(3)) - ); + assert_eq!(ctx.eval_code("10 / 3").unwrap(), Value::Int(3)); // Float division returns float - assert_eq!( - ctx.eval_code("5 / 2.0").unwrap(), - Value::Const(Const::Float(2.5)) - ); + assert_eq!(ctx.eval_code("5 / 2.0").unwrap(), Value::Float(2.5)); - assert_eq!( - ctx.eval_code("7.0 / 2").unwrap(), - Value::Const(Const::Float(3.5)) - ); + assert_eq!(ctx.eval_code("7.0 / 2").unwrap(), Value::Float(3.5)); - assert_eq!( - ctx.eval_code("(-7) / 3").unwrap(), - Value::Const(Const::Int(-2)) - ); + assert_eq!(ctx.eval_code("(-7) / 3").unwrap(), Value::Int(-2)); } #[test] @@ -785,13 +741,13 @@ mod test { assert_eq!( ctx.eval_code("builtins.add 5000000000000000000 3000000000000000000") .unwrap(), - Value::Const(Const::Int(8000000000000000000i64)) + Value::Int(8000000000000000000i64) ); // Test builtin mul with large numbers assert_eq!( ctx.eval_code("builtins.mul 1000000000 1000000000").unwrap(), - Value::Const(Const::Int(1000000000000000000i64)) + Value::Int(1000000000000000000i64) ); } @@ -805,7 +761,7 @@ mod test { std::fs::write(&lib_path, "{ add = a: b: a + b; }").unwrap(); let expr = format!(r#"(import "{}").add 3 5"#, lib_path.display()); - assert_eq!(ctx.eval_code(&expr).unwrap(), Value::Const(Const::Int(8))); + assert_eq!(ctx.eval_code(&expr).unwrap(), Value::Int(8)); } #[test] @@ -825,7 +781,7 @@ mod test { std::fs::write(&main_path, main_content).unwrap(); let expr = format!(r#"(import "{}").result"#, main_path.display()); - assert_eq!(ctx.eval_code(&expr).unwrap(), Value::Const(Const::Int(30))); + assert_eq!(ctx.eval_code(&expr).unwrap(), Value::Int(30)); } #[test] @@ -855,10 +811,10 @@ mod test { std::fs::write(&main_path, main_content).unwrap(); let expr = format!(r#"let x = import "{}"; in x.result1"#, main_path.display()); - assert_eq!(ctx.eval_code(&expr).unwrap(), Value::Const(Const::Int(12))); + assert_eq!(ctx.eval_code(&expr).unwrap(), Value::Int(12)); let expr = format!(r#"let x = import "{}"; in x.result2"#, main_path.display()); - assert_eq!(ctx.eval_code(&expr).unwrap(), Value::Const(Const::Int(7))); + assert_eq!(ctx.eval_code(&expr).unwrap(), Value::Int(7)); } #[test] @@ -870,6 +826,6 @@ mod test { std::fs::write(&func_path, "x: x * 2").unwrap(); let expr = format!(r#"(import "{}") 5"#, func_path.display()); - assert_eq!(ctx.eval_code(&expr).unwrap(), Value::Const(Const::Int(10))); + assert_eq!(ctx.eval_code(&expr).unwrap(), Value::Int(10)); } } diff --git a/nix-js/src/ir.rs b/nix-js/src/ir.rs index 4b75333..09d1d6d 100644 --- a/nix-js/src/ir.rs +++ b/nix-js/src/ir.rs @@ -4,7 +4,6 @@ use rnix::ast; use string_interner::symbol::SymbolU32; use crate::error::{Error, Result}; -use crate::value::Const as PubConst; use crate::value::format_symbol; use nix_js_macros::ir; @@ -44,8 +43,12 @@ pub trait DowngradeContext { ir! { Ir, + Int(i64), + Float(f64), + Str, AttrSet, List, + HasAttr, BinOp, UnOp, @@ -54,8 +57,6 @@ ir! { Call, Assert, ConcatStrings, - Const, - Str, Path, Func, Let, @@ -63,6 +64,7 @@ ir! { ExprRef(ExprId), Thunk(ExprId), Builtins, + Builtin, } impl AttrSet { @@ -354,18 +356,6 @@ pub struct ConcatStrings { pub parts: Vec, } -/// Represents a constant value (e.g., integer, float, boolean, null). -#[derive(Clone, Copy, Debug)] -pub struct Const { - pub val: PubConst, -} - -impl> From for Const { - fn from(value: T) -> Self { - Self { val: value.into() } - } -} - /// Represents a simple, non-interpolated string literal. #[derive(Debug)] pub struct Str { @@ -386,4 +376,4 @@ pub struct Builtins; /// Represents an attribute in `builtins`. #[derive(Debug)] -pub struct Builtin(pub String); +pub struct Builtin(pub SymId); diff --git a/nix-js/src/ir/downgrade.rs b/nix-js/src/ir/downgrade.rs index e0b5e1d..36a015d 100644 --- a/nix-js/src/ir/downgrade.rs +++ b/nix-js/src/ir/downgrade.rs @@ -139,8 +139,8 @@ impl Downgrade for ast::Str { impl Downgrade for ast::Literal { fn downgrade(self, ctx: &mut Ctx) -> Result { Ok(ctx.new_expr(match self.kind() { - ast::LiteralKind::Integer(int) => Const::from(int.value().unwrap()).to_ir(), - ast::LiteralKind::Float(float) => Const::from(float.value().unwrap()).to_ir(), + ast::LiteralKind::Integer(int) => Ir::Int(int.value().unwrap()), + ast::LiteralKind::Float(float) => Ir::Float(float.value().unwrap()), ast::LiteralKind::Uri(uri) => Str { val: uri.to_string(), } diff --git a/nix-js/src/lib.rs b/nix-js/src/lib.rs index f11b95c..f880c90 100644 --- a/nix-js/src/lib.rs +++ b/nix-js/src/lib.rs @@ -1,8 +1,8 @@ -pub mod codegen; +mod codegen; pub mod context; pub mod error; pub mod ir; -pub mod runtime; +mod runtime; pub mod value; #[global_allocator] diff --git a/nix-js/src/runtime.rs b/nix-js/src/runtime.rs index dd87ac9..9836e22 100644 --- a/nix-js/src/runtime.rs +++ b/nix-js/src/runtime.rs @@ -3,13 +3,12 @@ use std::pin::Pin; use std::sync::Once; use deno_core::{Extension, ExtensionFileSource, JsRuntime, OpDecl, OpState, RuntimeOptions, v8}; -use deno_error::js_error_wrapper; use crate::codegen::{CodegenContext, Compile}; use crate::context::{CtxPtr, PathDropGuard}; use crate::error::{Error, Result}; use crate::ir::DowngradeContext; -use crate::value::{AttrSet, Const, List, Symbol, Value}; +use crate::value::{AttrSet, List, Symbol, Value}; type ScopeRef<'p, 's> = v8::PinnedRef<'p, v8::HandleScope<'s>>; type LocalValue<'a> = v8::Local<'a, v8::Value>; @@ -38,33 +37,32 @@ fn runtime_extension(ctx: CtxPtr) -> Extension { } } -#[derive(Debug)] -pub struct SimpleErrorWrapper(String); +mod private { + use deno_error::js_error_wrapper; + #[allow(dead_code)] + #[derive(Debug)] + pub struct SimpleErrorWrapper(pub(crate) String); + impl std::fmt::Display for SimpleErrorWrapper { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(self, f) + } + } + impl std::error::Error for SimpleErrorWrapper {} -impl std::fmt::Display for SimpleErrorWrapper { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - std::fmt::Debug::fmt(self, f) - } -} - -impl std::error::Error for SimpleErrorWrapper { - fn cause(&self) -> Option<&dyn std::error::Error> { - None - } - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - None - } - fn description(&self) -> &str { - &self.0 - } -} - -js_error_wrapper!(SimpleErrorWrapper, NixError, "EvalError"); -impl From for NixError { - fn from(value: String) -> Self { - NixError(SimpleErrorWrapper(value)) + js_error_wrapper!(SimpleErrorWrapper, NixError, "EvalError"); + + impl From for NixError { + fn from(value: String) -> Self { + NixError(SimpleErrorWrapper(value)) + } + } + impl From<&str> for NixError { + fn from(value: &str) -> Self { + NixError(SimpleErrorWrapper(value.to_string())) + } } } +use private::NixError; #[deno_core::op2] #[string] @@ -76,14 +74,13 @@ fn op_import(state: &mut OpState, #[string] path: String) -> std::result::Result let absolute_path = current_dir .join(&path) .canonicalize() - .map_err(|e| -> NixError { format!("Failed to resolve path {}: {}", path, e).into() })?; + .map_err(|e| format!("Failed to resolve path {}: {}", path, e))?; let mut guard = PathDropGuard::new(absolute_path.clone(), ctx); let ctx = guard.as_ctx(); - let content = std::fs::read_to_string(&absolute_path).map_err(|e| -> NixError { - format!("Failed to read {}: {}", absolute_path.display(), e).into() - })?; + let content = std::fs::read_to_string(&absolute_path) + .map_err(|e| format!("Failed to read {}: {}", absolute_path.display(), e))?; let root = rnix::Root::parse(&content); if !root.errors().is_empty() { @@ -95,15 +92,12 @@ fn op_import(state: &mut OpState, #[string] path: String) -> std::result::Result .into()); } - let expr = root - .tree() - .expr() - .ok_or_else(|| -> NixError { "No expression in file".to_string().into() })?; + let expr = root.tree().expr().ok_or("No expression in file")?; let expr_id = ctx .as_mut() .downgrade_ctx() .downgrade(expr) - .map_err(|e| -> NixError { format!("Downgrade error: {}", e).into() })?; + .map_err(|e| format!("Downgrade error: {}", e))?; Ok(ctx.get_ir(expr_id).compile(Pin::get_ref(ctx.as_ref()))) } @@ -111,8 +105,7 @@ fn op_import(state: &mut OpState, #[string] path: String) -> std::result::Result #[deno_core::op2] #[string] fn op_read_file(#[string] path: String) -> std::result::Result { - std::fs::read_to_string(&path) - .map_err(|e| -> NixError { format!("Failed to read {}: {}", path, e).into() }) + Ok(std::fs::read_to_string(&path).map_err(|e| format!("Failed to read {}: {}", path, e))?) } #[deno_core::op2(fast)] @@ -137,11 +130,11 @@ fn op_resolve_path( // Resolve relative path against current file directory (or CWD) let current_dir = ctx.get_current_dir(); - current_dir + Ok(current_dir .join(&path) .canonicalize() .map(|p| p.to_string_lossy().to_string()) - .map_err(|e| -> NixError { format!("Failed to resolve path {}: {}", path, e).into() }) + .map_err(|e| format!("Failed to resolve path {}: {}", path, e))?) } pub(crate) struct Runtime { @@ -234,16 +227,16 @@ fn to_value<'a>( if !lossless { panic!("BigInt value out of i64 range: conversion lost precision"); } - Value::Const(Const::Int(val)) + Value::Int(val) } _ if val.is_number() => { let val = val.to_number(scope).unwrap().value(); // number is always NixFloat - Value::Const(Const::Float(val)) + Value::Float(val) } - _ if val.is_true() => Value::Const(Const::Bool(true)), - _ if val.is_false() => Value::Const(Const::Bool(false)), - _ if val.is_null() => Value::Const(Const::Null), + _ if val.is_true() => Value::Bool(true), + _ if val.is_false() => Value::Bool(false), + _ if val.is_null() => Value::Null, _ if val.is_string() => { let val = val.to_string(scope).unwrap(); Value::String(val.to_rust_string_lossy(scope)) @@ -349,10 +342,10 @@ mod test { Value::AttrSet(AttrSet::new(std::collections::BTreeMap::from([( Symbol::from("test"), Value::List(List::new(vec![ - Value::Const(Const::Float(1.)), - Value::Const(Const::Int(9223372036854775807)), - Value::Const(Const::Bool(true)), - Value::Const(Const::Bool(false)), + Value::Float(1.), + Value::Int(9223372036854775807), + Value::Bool(true), + Value::Bool(false), Value::String("hello world!".to_string()) ])) )]))) diff --git a/nix-js/src/value.rs b/nix-js/src/value.rs index 3454653..bda1289 100644 --- a/nix-js/src/value.rs +++ b/nix-js/src/value.rs @@ -9,49 +9,6 @@ use std::sync::LazyLock; use derive_more::{Constructor, IsVariant, Unwrap}; use regex::Regex; -/// Represents a constant, primitive value in Nix. -#[derive(Debug, Clone, Copy, PartialEq, IsVariant, Unwrap)] -pub enum Const { - /// A boolean value (`true` or `false`). - Bool(bool), - /// A 64-bit signed integer. - Int(i64), - /// A 64-bit floating-point number. - Float(f64), - /// The `null` value. - Null, -} - -impl Display for Const { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - use Const::*; - match self { - Int(x) => write!(f, "{x}"), - Float(x) => write!(f, "{x}"), - Bool(x) => write!(f, "{x}"), - Null => write!(f, "null"), - } - } -} - -impl From for Const { - fn from(value: bool) -> Self { - Const::Bool(value) - } -} - -impl From for Const { - fn from(value: i64) -> Self { - Const::Int(value) - } -} - -impl From for Const { - fn from(value: f64) -> Self { - Const::Float(value) - } -} - /// Represents a Nix symbol, which is used as a key in attribute sets. #[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Constructor)] pub struct Symbol(String); @@ -172,8 +129,14 @@ impl Display for List { /// Represents any possible Nix value that can be returned from an evaluation. #[derive(IsVariant, Unwrap, Clone, Debug, PartialEq)] pub enum Value { - /// A constant value (int, float, bool, null). - Const(Const), + /// An integer value. + Int(i64), + /// An floating-point value. + Float(f64), + /// An boolean value. + Bool(bool), + /// An null value. + Null, /// A string value. String(String), /// An attribute set. @@ -197,7 +160,10 @@ impl Display for Value { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { use Value::*; match self { - Const(x) => write!(f, "{x}"), + &Int(x) => write!(f, "{x}"), + &Float(x) => write!(f, "{x}"), + &Bool(x) => write!(f, "{x}"), + Null => write!(f, "null"), String(x) => write!(f, r#""{x}""#), AttrSet(x) => write!(f, "{x}"), List(x) => write!(f, "{x}"),