diff --git a/Cargo.lock b/Cargo.lock index 9be080f..73e19a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -289,9 +289,12 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.1" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +dependencies = [ + "allocator-api2", +] [[package]] name = "bytes" @@ -959,6 +962,27 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" +[[package]] +name = "env_filter" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a1c3cc8e57274ec99de65301228b537f1e4eedc1b8e0f9411c6caac8ae7308f" +dependencies = [ + "log", +] + +[[package]] +name = "env_logger" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2daee4ea451f429a58296525ddf28b45a3b64f1acf6587e2067437bb11e218d" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "log", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -2022,6 +2046,7 @@ version = "0.1.0" dependencies = [ "anyhow", "base64 0.22.1", + "bumpalo", "bzip2", "clap", "criterion", @@ -2061,6 +2086,7 @@ dependencies = [ "tap", "tar", "tempfile", + "test-log", "thiserror 2.0.18", "tokio", "toml", @@ -2075,6 +2101,7 @@ name = "nix-js-macros" version = "0.1.0" dependencies = [ "convert_case 0.11.0", + "proc-macro2", "quote", "syn", ] @@ -3393,6 +3420,28 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "test-log" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d53ac171c92a39e4769491c4b4dde7022c60042254b5fc044ae409d34a24d4" +dependencies = [ + "env_logger", + "test-log-macros", + "tracing-subscriber", +] + +[[package]] +name = "test-log-macros" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be35209fd0781c5401458ab66e4f98accf63553e8fae7425503e92fdd319783b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "text-size" version = "1.1.1" diff --git a/nix-js-macros/Cargo.toml b/nix-js-macros/Cargo.toml index 4e11635..b37fd1c 100644 --- a/nix-js-macros/Cargo.toml +++ b/nix-js-macros/Cargo.toml @@ -8,5 +8,6 @@ proc-macro = true [dependencies] convert_case = "0.11" +proc-macro2 = "1.0" quote = "1.0" syn = { version = "2.0", features = ["full"] } diff --git a/nix-js-macros/src/ir.rs b/nix-js-macros/src/ir.rs index 6bd37e3..7be10de 100644 --- a/nix-js-macros/src/ir.rs +++ b/nix-js-macros/src/ir.rs @@ -4,22 +4,22 @@ //! an Intermediate Representation (IR) that follows a specific pattern. It generates: //! 1. An enum representing the different kinds of IR nodes. //! 2. Structs for each of the variants that have fields. -//! 3. `Ref` and `Mut` versions of the main enum for ergonomic pattern matching on references. -//! 4. `From` implementations to easily convert from a struct variant (e.g., `BinOp`) to the main enum (`Ir::BinOp`). -//! 5. A `To[IrName]` trait to provide a convenient `.to_ir()` method on the variant structs. +//! 3. `From` implementations to easily convert from a struct variant (e.g., `BinOp`) to the main enum (`Ir::BinOp`). +//! 4. A `To[IrName]` trait to provide a convenient `.to_ir()` method on the variant structs. use convert_case::{Case, Casing}; use proc_macro::TokenStream; use quote::{format_ident, quote}; use syn::{ - FieldsNamed, Ident, Token, Type, parenthesized, + Expr, ExprPath, FieldsNamed, GenericArgument, GenericParam, Generics, Ident, Path, PathSegment, + Token, Type, TypePath, parenthesized, parse::{Parse, ParseStream, Result}, punctuated::Punctuated, token, }; /// Represents one of the variants passed to the `ir!` macro. -pub enum VariantInput { +enum VariantInput { /// A unit-like variant, e.g., `Arg`. Unit(Ident), /// A tuple-like variant with one unnamed field, e.g., `ExprRef(ExprId)`. @@ -29,11 +29,12 @@ pub enum VariantInput { } /// The top-level input for the `ir!` macro. -pub struct MacroInput { +struct MacroInput { /// The name of the main IR enum to be generated (e.g., `Ir`). - pub base_name: Ident, + base_name: Ident, + generics: Generics, /// The list of variants for the enum. - pub variants: Punctuated, + variants: Punctuated, } impl Parse for VariantInput { @@ -64,13 +65,14 @@ impl Parse for VariantInput { impl Parse for MacroInput { fn parse(input: ParseStream) -> Result { - // The macro input is expected to be: `IrName, Variant1, Variant2, ...` let base_name = input.parse()?; - input.parse::()?; + let generics = Generics::parse(input)?; + input.parse::()?; let variants = Punctuated::parse_terminated(input)?; Ok(MacroInput { base_name, + generics, variants, }) } @@ -81,17 +83,39 @@ pub fn ir_impl(input: TokenStream) -> TokenStream { let parsed_input = syn::parse_macro_input!(input as MacroInput); let base_name = &parsed_input.base_name; - let ref_name = format_ident!("{}Ref", base_name); - let mut_name = format_ident!("{}Mut", base_name); + let generic_params = &parsed_input.generics.params; + let mk_ident_path = |ident| Path { + leading_colon: None, + segments: Punctuated::from_iter(std::iter::once(PathSegment { + ident, + arguments: Default::default(), + })), + }; + let generic_args = { + generic_params + .iter() + .map(|arg| match arg { + GenericParam::Lifetime(lifetime) => { + GenericArgument::Lifetime(lifetime.lifetime.clone()) + } + GenericParam::Const(cnst) => GenericArgument::Const(Expr::Path(ExprPath { + path: mk_ident_path(cnst.ident.clone()), + attrs: Vec::new(), + qself: None, + })), + GenericParam::Type(ty) => GenericArgument::Type(Type::Path(TypePath { + path: mk_ident_path(ty.ident.clone()), + qself: None, + })), + }) + .collect::>() + }; + let where_clause = &parsed_input.generics.where_clause; let to_trait_name = format_ident!("To{}", base_name); let to_trait_fn_name = format_ident!("to_{}", base_name.to_string().to_case(Case::Snake)); let mut enum_variants = Vec::new(); let mut struct_defs = Vec::new(); - let mut ref_variants = Vec::new(); - let mut mut_variants = Vec::new(); - let mut as_ref_arms = Vec::new(); - let mut as_mut_arms = Vec::new(); let mut span_arms = Vec::new(); let mut from_impls = Vec::new(); let mut to_trait_impls = Vec::new(); @@ -109,19 +133,15 @@ pub fn ir_impl(input: TokenStream) -> TokenStream { }); enum_variants.push(quote! { #name(#inner_type) }); - ref_variants.push(quote! { #name(&'a #inner_type) }); - mut_variants.push(quote! { #name(&'a mut #inner_type) }); - as_ref_arms.push(quote! { Self::#name(inner) => #ref_name::#name(inner) }); - as_mut_arms.push(quote! { Self::#name(inner) => #mut_name::#name(inner) }); span_arms.push(quote! { Self::#name(inner) => inner.span }); from_impls.push(quote! { - impl From<#inner_type> for #base_name { + impl <#generic_params> From<#inner_type> for #base_name <#generic_args> #where_clause { fn from(val: #inner_type) -> Self { #base_name::#name(val) } } }); to_trait_impls.push(quote! { - impl #to_trait_name for #name { - fn #to_trait_fn_name(self) -> #base_name { #base_name::from(self) } + impl <#generic_params> #to_trait_name <#generic_args> for #name #where_clause { + fn #to_trait_fn_name(self) -> #base_name <#generic_args> { #base_name::from(self) } } }); } @@ -138,25 +158,24 @@ pub fn ir_impl(input: TokenStream) -> TokenStream { let inner_type = name.clone(); enum_variants.push(quote! { #name(#inner_type) }); - ref_variants.push(quote! { #name(&'a #inner_type) }); - mut_variants.push(quote! { #name(&'a mut #inner_type) }); - as_ref_arms.push(quote! { Self::#name(inner) => #ref_name::#name(inner) }); - as_mut_arms.push(quote! { Self::#name(inner) => #mut_name::#name(inner) }); span_arms.push(quote! { Self::#name(inner) => inner.span }); from_impls.push(quote! { - impl From<#inner_type> for #base_name { + impl <#generic_params> From<#inner_type> for #base_name <#generic_args> #where_clause { fn from(val: #inner_type) -> Self { #base_name::#name(val) } } }); to_trait_impls.push(quote! { - impl #to_trait_name for #name { - fn #to_trait_fn_name(self) -> #base_name { #base_name::from(self) } + impl <#generic_params> #to_trait_name <#generic_args> for #name #where_clause { + fn #to_trait_fn_name(self) -> #base_name <#generic_args> { #base_name::from(self) } } }); } VariantInput::Struct(name, mut fields) => { let inner_type = name.clone(); + fields.named.iter_mut().for_each(|field| { + field.vis = syn::Visibility::Public(syn::token::Pub::default()); + }); fields.named.push(syn::Field { attrs: vec![], vis: syn::Visibility::Public(syn::token::Pub::default()), @@ -168,22 +187,18 @@ pub fn ir_impl(input: TokenStream) -> TokenStream { struct_defs.push(quote! { #[derive(Debug)] - pub struct #name #fields + pub struct #name <#generic_params> #where_clause #fields }); - enum_variants.push(quote! { #name(#inner_type) }); - ref_variants.push(quote! { #name(&'a #inner_type) }); - mut_variants.push(quote! { #name(&'a mut #inner_type) }); - as_ref_arms.push(quote! { Self::#name(inner) => #ref_name::#name(inner) }); - as_mut_arms.push(quote! { Self::#name(inner) => #mut_name::#name(inner) }); + enum_variants.push(quote! { #name(#inner_type <#generic_args>) }); span_arms.push(quote! { Self::#name(inner) => inner.span }); from_impls.push(quote! { - impl From<#inner_type> for #base_name { - fn from(val: #inner_type) -> Self { #base_name::#name(val) } + impl <#generic_params> From<#inner_type <#generic_args>> for #base_name <#generic_args> #where_clause { + fn from(val: #inner_type <#generic_args>) -> Self { #base_name::#name(val) } } }); to_trait_impls.push(quote! { - impl #to_trait_name for #name { - fn #to_trait_fn_name(self) -> #base_name { #base_name::from(self) } + impl <#generic_params> #to_trait_name <#generic_args> for #name <#generic_args> #where_clause { + fn #to_trait_fn_name(self) -> #base_name <#generic_args> { #base_name::from(self) } } }); } @@ -193,41 +208,15 @@ pub fn ir_impl(input: TokenStream) -> TokenStream { // Assemble the final generated code. let expanded = quote! { /// The main IR enum, generated by the `ir!` macro. - #[derive(Debug, IsVariant, Unwrap, TryUnwrap)] - pub enum #base_name { + #[derive(Debug)] + pub enum #base_name <#generic_params> #where_clause { #( #enum_variants ),* } // The struct definitions for the enum variants. #( #struct_defs )* - /// An immutable reference version of the IR enum. - #[derive(Debug, IsVariant, Unwrap, TryUnwrap)] - pub enum #ref_name<'a> { - #( #ref_variants ),* - } - - /// A mutable reference version of the IR enum. - #[derive(Debug, IsVariant, Unwrap, TryUnwrap)] - pub enum #mut_name<'a> { - #( #mut_variants ),* - } - - impl #base_name { - /// Converts a `&Ir` into a `IrRef`. - pub fn as_ref(&self) -> #ref_name<'_> { - match self { - #( #as_ref_arms ),* - } - } - - /// Converts a `&mut Ir` into a `IrMut`. - pub fn as_mut(&mut self) -> #mut_name<'_> { - match self { - #( #as_mut_arms ),* - } - } - + impl <#generic_params> #base_name <#generic_args> #where_clause { pub fn span(&self) -> rnix::TextRange { match self { #( #span_arms ),* @@ -239,9 +228,9 @@ pub fn ir_impl(input: TokenStream) -> TokenStream { #( #from_impls )* /// A trait for converting a variant struct into the main IR enum. - pub trait #to_trait_name { + pub trait #to_trait_name <#generic_params> #where_clause { /// Performs the conversion. - fn #to_trait_fn_name(self) -> #base_name; + fn #to_trait_fn_name(self) -> #base_name <#generic_args>; } // Implement the `ToIr` trait for each variant struct. diff --git a/nix-js/Cargo.toml b/nix-js/Cargo.toml index 2456952..342eee0 100644 --- a/nix-js/Cargo.toml +++ b/nix-js/Cargo.toml @@ -27,6 +27,7 @@ miette = { version = "7.4", features = ["fancy"] } hashbrown = "0.16" string-interner = "0.19" +bumpalo = { version = "3.20", features = ["allocator-api2", "boxed", "collections"] } rust-embed="8.11" @@ -80,6 +81,7 @@ prof = [] [dev-dependencies] criterion = { version = "0.8", features = ["html_reports"] } +test-log = { version = "0.2", features = ["trace"] } [[bench]] name = "basic_ops" diff --git a/nix-js/runtime-ts/package-lock.json b/nix-js/runtime-ts/package-lock.json index f0579fe..af4cafe 100644 --- a/nix-js/runtime-ts/package-lock.json +++ b/nix-js/runtime-ts/package-lock.json @@ -8,16 +8,15 @@ "name": "nix-js-runtime", "version": "0.1.0", "dependencies": { - "@eslint/json": "^1.0.1", - "eslint": "^9.39.2", "globals": "^17.3.0", "jiti": "^2.6.1", - "js-sdsl": "^4.4.2", - "typescript-eslint": "^8.55.0" + "js-sdsl": "^4.4.2" }, "devDependencies": { "esbuild": "^0.24.2", - "typescript": "^5.7.2" + "eslint": "^9.39.2", + "typescript": "^5.7.2", + "typescript-eslint": "^8.55.0" } }, "node_modules/@esbuild/aix-ppc64": { @@ -449,6 +448,7 @@ "version": "4.9.1", "resolved": "https://registry.npmmirror.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, "license": "MIT", "dependencies": { "eslint-visitor-keys": "^3.4.3" @@ -467,6 +467,7 @@ "version": "3.4.3", "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -479,6 +480,7 @@ "version": "4.12.2", "resolved": "https://registry.npmmirror.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" @@ -488,6 +490,7 @@ "version": "0.21.1", "resolved": "https://registry.npmmirror.com/@eslint/config-array/-/config-array-0.21.1.tgz", "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "dev": true, "license": "Apache-2.0", "dependencies": { "@eslint/object-schema": "^2.1.7", @@ -502,6 +505,7 @@ "version": "0.4.2", "resolved": "https://registry.npmmirror.com/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, "license": "Apache-2.0", "dependencies": { "@eslint/core": "^0.17.0" @@ -514,6 +518,7 @@ "version": "0.17.0", "resolved": "https://registry.npmmirror.com/@eslint/core/-/core-0.17.0.tgz", "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, "license": "Apache-2.0", "dependencies": { "@types/json-schema": "^7.0.15" @@ -522,22 +527,11 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/core": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/@eslint/core/-/core-1.1.0.tgz", - "integrity": "sha512-/nr9K9wkr3P1EzFTdFdMoLuo1PmIxjmwvPozwoSodjNBdefGujXQUF93u1DDZpEaTuDvMsIQddsd35BwtrW9Xw==", - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, "node_modules/@eslint/eslintrc": { "version": "3.3.3", "resolved": "https://registry.npmmirror.com/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", + "dev": true, "license": "MIT", "dependencies": { "ajv": "^6.12.4", @@ -561,6 +555,7 @@ "version": "14.0.0", "resolved": "https://registry.npmmirror.com/globals/-/globals-14.0.0.tgz", "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -573,6 +568,7 @@ "version": "9.39.2", "resolved": "https://registry.npmmirror.com/@eslint/js/-/js-9.39.2.tgz", "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", + "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -581,47 +577,21 @@ "url": "https://eslint.org/donate" } }, - "node_modules/@eslint/json": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/@eslint/json/-/json-1.0.1.tgz", - "integrity": "sha512-bE2nGv8/U+uRvQEJWOgCsZCa65XsCBgxyyx/sXtTHVv0kqdauACLzyp7A1C3yNn7pRaWjIt5acxY+TAbSyIJXw==", - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^1.1.0", - "@eslint/plugin-kit": "^0.6.0", - "@humanwhocodes/momoa": "^3.3.10", - "natural-compare": "^1.4.0" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, "node_modules/@eslint/object-schema": { "version": "2.1.7", "resolved": "https://registry.npmmirror.com/@eslint/object-schema/-/object-schema-2.1.7.tgz", "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/plugin-kit": { - "version": "0.6.0", - "resolved": "https://registry.npmmirror.com/@eslint/plugin-kit/-/plugin-kit-0.6.0.tgz", - "integrity": "sha512-bIZEUzOI1jkhviX2cp5vNyXQc6olzb2ohewQubuYlMXZ2Q/XjBO0x0XhGPvc9fjSIiUN0vw+0hq53BJ4eQSJKQ==", - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^1.1.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmmirror.com/@humanfs/core/-/core-0.19.1.tgz", "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, "license": "Apache-2.0", "engines": { "node": ">=18.18.0" @@ -631,6 +601,7 @@ "version": "0.16.7", "resolved": "https://registry.npmmirror.com/@humanfs/node/-/node-0.16.7.tgz", "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, "license": "Apache-2.0", "dependencies": { "@humanfs/core": "^0.19.1", @@ -644,6 +615,7 @@ "version": "1.0.1", "resolved": "https://registry.npmmirror.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, "license": "Apache-2.0", "engines": { "node": ">=12.22" @@ -653,19 +625,11 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@humanwhocodes/momoa": { - "version": "3.3.10", - "resolved": "https://registry.npmmirror.com/@humanwhocodes/momoa/-/momoa-3.3.10.tgz", - "integrity": "sha512-KWiFQpSAqEIyrTXko3hFNLeQvSK8zXlJQzhhxsyVn58WFRYXST99b3Nqnu+ttOtjds2Pl2grUHGpe2NzhPynuQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=18" - } - }, "node_modules/@humanwhocodes/retry": { "version": "0.4.3", "resolved": "https://registry.npmmirror.com/@humanwhocodes/retry/-/retry-0.4.3.tgz", "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, "license": "Apache-2.0", "engines": { "node": ">=18.18" @@ -679,18 +643,21 @@ "version": "1.0.8", "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, "license": "MIT" }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.55.0", "resolved": "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.55.0.tgz", "integrity": "sha512-1y/MVSz0NglV1ijHC8OT49mPJ4qhPYjiK08YUQVbIOyu+5k862LKUHFkpKHWu//zmr7hDR2rhwUm6gnCGNmGBQ==", + "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", @@ -719,6 +686,7 @@ "version": "7.0.5", "resolved": "https://registry.npmmirror.com/ignore/-/ignore-7.0.5.tgz", "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 4" @@ -728,6 +696,7 @@ "version": "8.55.0", "resolved": "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-8.55.0.tgz", "integrity": "sha512-4z2nCSBfVIMnbuu8uinj+f0o4qOeggYJLbjpPHka3KH1om7e+H9yLKTYgksTaHcGco+NClhhY2vyO3HsMH1RGw==", + "dev": true, "license": "MIT", "peer": true, "dependencies": { @@ -753,6 +722,7 @@ "version": "8.55.0", "resolved": "https://registry.npmmirror.com/@typescript-eslint/project-service/-/project-service-8.55.0.tgz", "integrity": "sha512-zRcVVPFUYWa3kNnjaZGXSu3xkKV1zXy8M4nO/pElzQhFweb7PPtluDLQtKArEOGmjXoRjnUZ29NjOiF0eCDkcQ==", + "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.55.0", @@ -774,6 +744,7 @@ "version": "8.55.0", "resolved": "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-8.55.0.tgz", "integrity": "sha512-fVu5Omrd3jeqeQLiB9f1YsuK/iHFOwb04bCtY4BSCLgjNbOD33ZdV6KyEqplHr+IlpgT0QTZ/iJ+wT7hvTx49Q==", + "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/types": "8.55.0", @@ -791,6 +762,7 @@ "version": "8.55.0", "resolved": "https://registry.npmmirror.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.55.0.tgz", "integrity": "sha512-1R9cXqY7RQd7WuqSN47PK9EDpgFUK3VqdmbYrvWJZYDd0cavROGn+74ktWBlmJ13NXUQKlZ/iAEQHI/V0kKe0Q==", + "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -807,6 +779,7 @@ "version": "8.55.0", "resolved": "https://registry.npmmirror.com/@typescript-eslint/type-utils/-/type-utils-8.55.0.tgz", "integrity": "sha512-x1iH2unH4qAt6I37I2CGlsNs+B9WGxurP2uyZLRz6UJoZWDBx9cJL1xVN/FiOmHEONEg6RIufdvyT0TEYIgC5g==", + "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/types": "8.55.0", @@ -831,6 +804,7 @@ "version": "8.55.0", "resolved": "https://registry.npmmirror.com/@typescript-eslint/types/-/types-8.55.0.tgz", "integrity": "sha512-ujT0Je8GI5BJWi+/mMoR0wxwVEQaxM+pi30xuMiJETlX80OPovb2p9E8ss87gnSVtYXtJoU9U1Cowcr6w2FE0w==", + "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -844,6 +818,7 @@ "version": "8.55.0", "resolved": "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.55.0.tgz", "integrity": "sha512-EwrH67bSWdx/3aRQhCoxDaHM+CrZjotc2UCCpEDVqfCE+7OjKAGWNY2HsCSTEVvWH2clYQK8pdeLp42EVs+xQw==", + "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/project-service": "8.55.0", @@ -871,6 +846,7 @@ "version": "2.0.2", "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.2.tgz", "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -880,6 +856,7 @@ "version": "9.0.5", "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -895,6 +872,7 @@ "version": "8.55.0", "resolved": "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-8.55.0.tgz", "integrity": "sha512-BqZEsnPGdYpgyEIkDC1BadNY8oMwckftxBT+C8W0g1iKPdeqKZBtTfnvcq0nf60u7MkjFO8RBvpRGZBPw4L2ow==", + "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", @@ -918,6 +896,7 @@ "version": "8.55.0", "resolved": "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.55.0.tgz", "integrity": "sha512-AxNRwEie8Nn4eFS1FzDMJWIISMGoXMb037sgCBJ3UR6o0fQTzr2tqN9WT+DkWJPhIdQCfV7T6D387566VtnCJA==", + "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/types": "8.55.0", @@ -935,6 +914,7 @@ "version": "8.15.0", "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, "license": "MIT", "peer": true, "bin": { @@ -948,6 +928,7 @@ "version": "5.3.2", "resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" @@ -957,6 +938,7 @@ "version": "6.12.6", "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", @@ -973,6 +955,7 @@ "version": "4.3.0", "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -988,18 +971,21 @@ "version": "2.0.1", "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, "license": "Python-2.0" }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, "license": "MIT" }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.12.tgz", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -1010,6 +996,7 @@ "version": "3.1.0", "resolved": "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -1019,6 +1006,7 @@ "version": "4.1.2", "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -1035,6 +1023,7 @@ "version": "2.0.1", "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -1047,18 +1036,21 @@ "version": "1.1.4", "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, "license": "MIT" }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, "license": "MIT" }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -1073,6 +1065,7 @@ "version": "4.4.3", "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -1090,6 +1083,7 @@ "version": "0.1.4", "resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, "license": "MIT" }, "node_modules/esbuild": { @@ -1137,6 +1131,7 @@ "version": "4.0.0", "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -1149,6 +1144,7 @@ "version": "9.39.2", "resolved": "https://registry.npmmirror.com/eslint/-/eslint-9.39.2.tgz", "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", + "dev": true, "license": "MIT", "peer": true, "dependencies": { @@ -1209,6 +1205,7 @@ "version": "8.4.0", "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-8.4.0.tgz", "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", @@ -1225,6 +1222,7 @@ "version": "4.2.1", "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1237,6 +1235,7 @@ "version": "0.17.0", "resolved": "https://registry.npmmirror.com/@eslint/core/-/core-0.17.0.tgz", "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, "license": "Apache-2.0", "dependencies": { "@types/json-schema": "^7.0.15" @@ -1249,6 +1248,7 @@ "version": "0.4.1", "resolved": "https://registry.npmmirror.com/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, "license": "Apache-2.0", "dependencies": { "@eslint/core": "^0.17.0", @@ -1262,6 +1262,7 @@ "version": "10.4.0", "resolved": "https://registry.npmmirror.com/espree/-/espree-10.4.0.tgz", "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, "license": "BSD-2-Clause", "dependencies": { "acorn": "^8.15.0", @@ -1279,6 +1280,7 @@ "version": "1.7.0", "resolved": "https://registry.npmmirror.com/esquery/-/esquery-1.7.0.tgz", "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" @@ -1291,6 +1293,7 @@ "version": "4.3.0", "resolved": "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" @@ -1303,6 +1306,7 @@ "version": "5.3.0", "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=4.0" @@ -1312,6 +1316,7 @@ "version": "2.0.3", "resolved": "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" @@ -1321,24 +1326,28 @@ "version": "3.1.3", "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, "license": "MIT" }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, "license": "MIT" }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, "license": "MIT", "engines": { "node": ">=12.0.0" @@ -1356,6 +1365,7 @@ "version": "8.0.0", "resolved": "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz", "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, "license": "MIT", "dependencies": { "flat-cache": "^4.0.0" @@ -1368,6 +1378,7 @@ "version": "5.0.0", "resolved": "https://registry.npmmirror.com/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, "license": "MIT", "dependencies": { "locate-path": "^6.0.0", @@ -1384,6 +1395,7 @@ "version": "4.0.1", "resolved": "https://registry.npmmirror.com/flat-cache/-/flat-cache-4.0.1.tgz", "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, "license": "MIT", "dependencies": { "flatted": "^3.2.9", @@ -1397,12 +1409,14 @@ "version": "3.3.3", "resolved": "https://registry.npmmirror.com/flatted/-/flatted-3.3.3.tgz", "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, "license": "ISC" }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.3" @@ -1427,6 +1441,7 @@ "version": "4.0.0", "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -1436,6 +1451,7 @@ "version": "5.3.2", "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, "license": "MIT", "engines": { "node": ">= 4" @@ -1445,6 +1461,7 @@ "version": "3.3.1", "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, "license": "MIT", "dependencies": { "parent-module": "^1.0.0", @@ -1461,6 +1478,7 @@ "version": "0.1.4", "resolved": "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.8.19" @@ -1470,6 +1488,7 @@ "version": "2.1.1", "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -1479,6 +1498,7 @@ "version": "4.0.3", "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -1491,6 +1511,7 @@ "version": "2.0.0", "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, "license": "ISC" }, "node_modules/jiti": { @@ -1517,6 +1538,7 @@ "version": "4.1.1", "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.1.tgz", "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -1529,24 +1551,28 @@ "version": "3.0.1", "resolved": "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, "license": "MIT" }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmmirror.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, "license": "MIT" }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, "license": "MIT", "dependencies": { "json-buffer": "3.0.1" @@ -1556,6 +1582,7 @@ "version": "0.4.1", "resolved": "https://registry.npmmirror.com/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1", @@ -1569,6 +1596,7 @@ "version": "6.0.0", "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, "license": "MIT", "dependencies": { "p-locate": "^5.0.0" @@ -1584,12 +1612,14 @@ "version": "4.6.2", "resolved": "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, "license": "MIT" }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -1602,18 +1632,21 @@ "version": "2.1.3", "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, "license": "MIT" }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, "license": "MIT" }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmmirror.com/optionator/-/optionator-0.9.4.tgz", "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, "license": "MIT", "dependencies": { "deep-is": "^0.1.3", @@ -1631,6 +1664,7 @@ "version": "3.1.0", "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" @@ -1646,6 +1680,7 @@ "version": "5.0.0", "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, "license": "MIT", "dependencies": { "p-limit": "^3.0.2" @@ -1661,6 +1696,7 @@ "version": "1.0.1", "resolved": "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, "license": "MIT", "dependencies": { "callsites": "^3.0.0" @@ -1673,6 +1709,7 @@ "version": "4.0.0", "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -1682,6 +1719,7 @@ "version": "3.1.1", "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -1691,6 +1729,7 @@ "version": "4.0.3", "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, "license": "MIT", "peer": true, "engines": { @@ -1704,6 +1743,7 @@ "version": "1.2.1", "resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.8.0" @@ -1713,6 +1753,7 @@ "version": "2.3.1", "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -1722,6 +1763,7 @@ "version": "4.0.0", "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -1731,6 +1773,7 @@ "version": "7.7.4", "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.4.tgz", "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -1743,6 +1786,7 @@ "version": "2.0.0", "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -1755,6 +1799,7 @@ "version": "3.0.0", "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -1764,6 +1809,7 @@ "version": "3.1.1", "resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -1776,6 +1822,7 @@ "version": "7.2.0", "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -1788,6 +1835,7 @@ "version": "0.2.15", "resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.15.tgz", "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", @@ -1804,6 +1852,7 @@ "version": "2.4.0", "resolved": "https://registry.npmmirror.com/ts-api-utils/-/ts-api-utils-2.4.0.tgz", "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", + "dev": true, "license": "MIT", "engines": { "node": ">=18.12" @@ -1816,6 +1865,7 @@ "version": "0.4.0", "resolved": "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1" @@ -1828,6 +1878,7 @@ "version": "5.9.3", "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, "license": "Apache-2.0", "peer": true, "bin": { @@ -1842,6 +1893,7 @@ "version": "8.55.0", "resolved": "https://registry.npmmirror.com/typescript-eslint/-/typescript-eslint-8.55.0.tgz", "integrity": "sha512-HE4wj+r5lmDVS9gdaN0/+iqNvPZwGfnJ5lZuz7s5vLlg9ODw0bIiiETaios9LvFI1U94/VBXGm3CB2Y5cNFMpw==", + "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/eslint-plugin": "8.55.0", @@ -1865,6 +1917,7 @@ "version": "4.4.1", "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" @@ -1874,6 +1927,7 @@ "version": "2.0.2", "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -1889,6 +1943,7 @@ "version": "1.2.5", "resolved": "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -1898,6 +1953,7 @@ "version": "0.1.0", "resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=10" diff --git a/nix-js/runtime-ts/package.json b/nix-js/runtime-ts/package.json index 8d6e544..f1bbace 100644 --- a/nix-js/runtime-ts/package.json +++ b/nix-js/runtime-ts/package.json @@ -10,12 +10,13 @@ }, "devDependencies": { "esbuild": "^0.24.2", - "typescript": "^5.7.2" + "eslint": "^9.39.2", + "typescript": "^5.7.2", + "typescript-eslint": "^8.55.0", + "jiti": "^2.6.1" }, "dependencies": { - "eslint": "^9.39.2", "globals": "^17.3.0", - "js-sdsl": "^4.4.2", - "typescript-eslint": "^8.55.0" + "js-sdsl": "^4.4.2" } } diff --git a/nix-js/runtime-ts/src/types/global.d.ts b/nix-js/runtime-ts/src/types/global.d.ts index 752be8f..b2c8acf 100644 --- a/nix-js/runtime-ts/src/types/global.d.ts +++ b/nix-js/runtime-ts/src/types/global.d.ts @@ -1,6 +1,5 @@ import type { NixRuntime } from ".."; import type { builtins } from "../builtins"; -import { JsonValue } from "../builtins/conversion"; import type { FetchGitResult, FetchTarballResult, FetchUrlResult } from "../builtins/io"; import type { assert, diff --git a/nix-js/src/codegen.rs b/nix-js/src/codegen.rs index 9ce14a3..015d6c0 100644 --- a/nix-js/src/codegen.rs +++ b/nix-js/src/codegen.rs @@ -188,7 +188,6 @@ impl Compile for rnix::TextRange { } pub(crate) trait CodegenContext { - fn get_ir(&self, id: ExprId) -> &Ir; fn get_sym(&self, id: SymId) -> Symbol<'_>; fn get_current_dir(&self) -> &Path; fn get_store_dir(&self) -> &str; @@ -196,19 +195,13 @@ pub(crate) trait CodegenContext { fn register_span(&self, range: rnix::TextRange) -> usize; } -impl Compile for ExprId { - fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) { - ctx.get_ir(*self).compile(ctx, buf); - } -} - impl Compile for Symbol<'_> { fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) { quoted(self).compile(ctx, buf); } } -impl Compile for Ir { +impl Compile for Ir<'_> { fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) { match self { Ir::Int(int) => { @@ -224,11 +217,11 @@ impl Compile for Ir { code!(buf, ctx; "null"); } Ir::Str(s) => { - code!(buf, ctx; quoted(&s.val)); + code!(buf, ctx; quoted(&s.inner)); } Ir::Path(p) => { // Nix.resolvePath - code!(buf, ctx; "$r(_d," ctx.get_ir(p.expr) ")"); + code!(buf, ctx; "$r(_d," p.expr ")"); } Ir::If(x) => x.compile(ctx, buf), Ir::BinOp(x) => x.compile(ctx, buf), @@ -265,14 +258,12 @@ impl Compile for Ir { ref assertion_raw, span: assert_span, }) => { - let assertion = ctx.get_ir(assertion); - // Nix.assert code!(buf, ctx; "$a(" assertion "," - ctx.get_ir(expr) + expr "," quoted(assertion_raw) "," @@ -316,7 +307,7 @@ impl Compile for Ir { } } -impl Compile for If { +impl Compile for If<'_> { fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) { let &If { cond, @@ -324,19 +315,18 @@ impl Compile for If { alter, span: _, } = self; - let cond = ctx.get_ir(cond); // Nix.forceBool code!(buf, ctx; "$fb(" cond ")?(" consq "):(" alter ")"); } } -impl Compile for BinOp { +impl Compile for BinOp<'_> { fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) { use BinOpKind::*; - let lhs = ctx.get_ir(self.lhs); - let rhs = ctx.get_ir(self.rhs); + let lhs = self.lhs; + let rhs = self.rhs; match self.kind { Add | Sub | Mul | Div | Eq | Neq | Lt | Gt | Leq | Geq | Con | Upd => { @@ -389,24 +379,30 @@ impl Compile for BinOp { } } -impl Compile for UnOp { +impl Compile for UnOp<'_> { fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) { use UnOpKind::*; - let rhs = ctx.get_ir(self.rhs); + let rhs = self.rhs; match self.kind { Neg => { code!(buf, ctx; "$os(0n," rhs ")"); } Not => { - code!(buf, ctx; "!$fb(" ctx.get_ir(self.rhs) ")"); + code!(buf, ctx; "!$fb(" rhs ")"); } } } } -impl Compile for Func { +impl Compile for Func<'_> { fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) { - let id = ctx.get_ir(self.arg).as_ref().unwrap_arg().inner.0; + let Ir::Arg(Arg { + inner: ArgId(id), .. + }) = self.arg + else { + // TODO: + unreachable!() + }; let has_thunks = !self.thunks.is_empty(); @@ -450,13 +446,13 @@ impl Compile for Func { } } -impl Compile for Call { +impl Compile for Call<'_> { fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) { code!(buf, ctx; "$c(" - ctx.get_ir(self.func) + self.func "," - ctx.get_ir(self.arg) + self.arg "," self.span ")" @@ -464,7 +460,7 @@ impl Compile for Call { } } -impl Compile for [(ExprId, ExprId)] { +impl<'ir, Ctx: CodegenContext> Compile for [(ThunkId, IrRef<'ir>)] { fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) { if self.is_empty() { return; @@ -474,28 +470,27 @@ impl Compile for [(ExprId, ExprId)] { buf, ctx; "const " joined(self.iter(), ",", |ctx: &Ctx, buf, &(slot, inner)| { - code!(buf, ctx; "e" slot.0 "=$t(()=>(" ctx.get_ir(inner) ")," "'e" slot.0 "')"); + code!(buf, ctx; "e" slot.0 "=$t(()=>(" inner ")," "'e" slot.0 "')"); }) ";" ); } } -impl Compile for TopLevel { +impl Compile for TopLevel<'_> { fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) { if self.thunks.is_empty() { - ctx.get_ir(self.body).compile(ctx, buf); + self.body.compile(ctx, buf); } else { - let body = ctx.get_ir(self.body); - code!(buf, ctx; "(()=>{" self.thunks "return " body "})()"); + code!(buf, ctx; "(()=>{" self.thunks "return " self.body "})()"); } } } -impl Compile for WithExpr { +impl Compile for WithExpr<'_> { fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) { - let namespace = ctx.get_ir(self.namespace); - let body = ctx.get_ir(self.body); + let namespace = self.namespace; + let body = self.body; let has_thunks = !self.thunks.is_empty(); if has_thunks { code!(buf, ctx; "((_w)=>{" self.thunks "return " body "})({env:" namespace ",last:_w})"); @@ -505,21 +500,21 @@ impl Compile for WithExpr { } } -impl Compile for Select { +impl Compile for Select<'_> { fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) { if let Some(default) = self.default { code!(buf, ctx; "$sd(" - ctx.get_ir(self.expr) + self.expr ",[" joined(self.attrpath.iter(), ",", |ctx: &Ctx, buf, attr| { match attr { Attr::Str(sym, _) => code!(buf, ctx; ctx.get_sym(*sym)), - Attr::Dynamic(expr_id, _) => code!(buf, ctx; ctx.get_ir(*expr_id)), + Attr::Dynamic(expr_id, _) => code!(buf, ctx; *expr_id), } }) "]," - ctx.get_ir(default) + default "," self.span ")" @@ -527,12 +522,12 @@ impl Compile for Select { } else { code!(buf, ctx; "$s(" - ctx.get_ir(self.expr) + self.expr ",[" joined(self.attrpath.iter(), ",", |ctx: &Ctx, buf, attr| { match attr { Attr::Str(sym, _) => code!(buf, ctx; ctx.get_sym(*sym)), - Attr::Dynamic(expr_id, _) => code!(buf, ctx; ctx.get_ir(*expr_id)), + Attr::Dynamic(expr, _) => code!(buf, ctx; expr), } }) "]," @@ -543,15 +538,13 @@ impl Compile for Select { } } -impl Compile for AttrSet { +impl Compile for AttrSet<'_> { fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) { if !self.dyns.is_empty() { code!(buf, ctx; "$ma(new Map([" - joined(self.stcs.iter(), ",", |ctx: &Ctx, buf, (&sym, &(expr, _))| { + joined(self.stcs.iter(), ",", |ctx: &Ctx, buf, (&sym, &(val, _))| { let key = ctx.get_sym(sym); - let val = ctx.get_ir(expr); - code!( buf, ctx; "[" key "," val "]" @@ -563,11 +556,10 @@ impl Compile for AttrSet { }) "]),{dynKeys:[" joined(self.dyns.iter(), ",", |ctx: &Ctx, buf, (key, _, _)| { - code!(buf, ctx; ctx.get_ir(*key)); + code!(buf, ctx; key); }) "],dynVals:[" joined(self.dyns.iter(), ",", |ctx: &Ctx, buf, (_, val, _)| { - let val = ctx.get_ir(*val); code!(buf, ctx; val); }) "],dynSpans:[" @@ -579,10 +571,8 @@ impl Compile for AttrSet { } else if !self.stcs.is_empty() { code!(buf, ctx; "$ma(new Map([" - joined(self.stcs.iter(), ",", |ctx: &Ctx, buf, (&sym, &(expr, _))| { + joined(self.stcs.iter(), ",", |ctx: &Ctx, buf, (&sym, &(val, _))| { let key = ctx.get_sym(sym); - let val = ctx.get_ir(expr); - code!( buf, ctx; "[" key "," val "]" @@ -600,12 +590,11 @@ impl Compile for AttrSet { } } -impl Compile for List { +impl Compile for List<'_> { fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) { code!(buf, ctx; "[" joined(self.items.iter(), ",", |ctx: &Ctx, buf, item| { - let item = ctx.get_ir(*item); code!(buf, ctx; item); }) "]" @@ -613,12 +602,11 @@ impl Compile for List { } } -impl Compile for ConcatStrings { +impl Compile for ConcatStrings<'_> { fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) { code!(buf, ctx; "$cs([" joined(self.parts.iter(), ",", |ctx: &Ctx, buf, part| { - let part = ctx.get_ir(*part); code!(buf, ctx; part); }) "]," self.force_string ")" @@ -626,16 +614,16 @@ impl Compile for ConcatStrings { } } -impl Compile for HasAttr { +impl Compile for HasAttr<'_> { fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) { code!(buf, ctx; "$h(" - ctx.get_ir(self.lhs) + self.lhs ",[" joined(self.rhs.iter(), ",", |ctx: &Ctx, buf, attr| { match attr { Attr::Str(sym, _) => code!(buf, ctx; ctx.get_sym(*sym)), - Attr::Dynamic(expr_id, _) => code!(buf, ctx; ctx.get_ir(*expr_id)), + Attr::Dynamic(expr, _) => code!(buf, ctx; expr), } }) "])" diff --git a/nix-js/src/context.rs b/nix-js/src/context.rs index 5cb4766..086775b 100644 --- a/nix-js/src/context.rs +++ b/nix-js/src/context.rs @@ -1,7 +1,7 @@ use std::cell::UnsafeCell; use std::path::Path; -use std::ptr::NonNull; +use bumpalo::Bump; use hashbrown::{HashMap, HashSet}; use rnix::TextRange; use string_interner::DefaultStringInterner; @@ -10,8 +10,8 @@ use crate::codegen::{CodegenContext, compile, compile_scoped}; use crate::downgrade::*; use crate::error::{Error, Result, Source}; use crate::ir::{ - Arg, ArgId, Bool, Builtin, ExprId, Ir, Null, ReplBinding, ScopedImportBinding, SymId, Thunk, - ToIr as _, WithLookup, + Arg, ArgId, Bool, Builtin, Ir, IrRef, Null, ReplBinding, ScopedImportBinding, SymId, Thunk, + ThunkId, ToIr as _, WithLookup, }; #[cfg(feature = "inspector")] use crate::runtime::inspector::InspectorServer; @@ -183,12 +183,23 @@ impl Context { } pub(crate) struct Ctx { - irs: Vec, symbols: DefaultStringInterner, - global: NonNull>, + global: HashMap>, sources: Vec, store: DaemonStore, spans: UnsafeCell>, + thunk_count: usize, +} + +struct OwnedIr { + _bump: Bump, + ir: &'static Ir<'static>, +} + +impl OwnedIr { + fn as_ref<'ir>(&'ir self) -> IrRef<'ir> { + self.ir + } } impl Ctx { @@ -196,19 +207,15 @@ impl Ctx { use crate::ir::{Builtins, ToIr as _}; let mut symbols = DefaultStringInterner::new(); - let mut irs = Vec::new(); let mut global = HashMap::new(); - - irs.push( + let builtins_sym = symbols.get_or_intern("builtins"); + global.insert( + builtins_sym, Builtins { - span: rnix::TextRange::default(), + span: TextRange::default(), } .to_ir(), ); - let builtins_expr = ExprId(0); - - let builtins_sym = symbols.get_or_intern("builtins"); - global.insert(builtins_sym, builtins_expr); let free_globals = [ "abort", @@ -258,22 +265,17 @@ impl Ctx { ]; for name in free_globals { - let name_sym = symbols.get_or_intern(name); - let id = ExprId(irs.len()); - irs.push( - Builtin { - inner: name_sym, - span: rnix::TextRange::default(), - } - .to_ir(), - ); - global.insert(name_sym, id); + let name = symbols.get_or_intern(name); + let value = Builtin { + inner: name, + span: rnix::TextRange::default(), + } + .to_ir(); + global.insert(name, value); } for (name, value) in consts { - let name_sym = symbols.get_or_intern(name); - let id = ExprId(irs.len()); - irs.push(value); - global.insert(name_sym, id); + let name = symbols.get_or_intern(name); + global.insert(name, value); } let config = StoreConfig::from_env(); @@ -281,17 +283,28 @@ impl Ctx { Ok(Self { symbols, - irs, - global: unsafe { NonNull::new_unchecked(Box::leak(Box::new(global))) }, + global, sources: Vec::new(), store, spans: UnsafeCell::new(Vec::new()), + thunk_count: 0, }) } - fn downgrade_ctx<'a>(&'a mut self, extra_scope: Option>) -> DowngradeCtx<'a> { - let global_ref = unsafe { self.global.as_ref() }; - DowngradeCtx::new(self, global_ref, extra_scope) + fn downgrade_ctx<'ctx, 'ir>( + &'ctx mut self, + bump: &'ir Bump, + extra_scope: Option>, + ) -> DowngradeCtx<'ctx, 'ir> { + let source = self.get_current_source(); + DowngradeCtx::new( + bump, + &mut self.symbols, + &self.global, + extra_scope, + &mut self.thunk_count, + source, + ) } pub(crate) fn get_current_dir(&self) -> &Path { @@ -313,7 +326,11 @@ impl Ctx { self.sources.get(id).expect("source not found").clone() } - fn downgrade<'a>(&mut self, source: Source, extra_scope: Option>) -> Result { + fn downgrade<'ctx>( + &'ctx mut self, + source: Source, + extra_scope: Option>, + ) -> Result { tracing::debug!("Parsing Nix expression"); self.sources.push(source.clone()); @@ -326,13 +343,22 @@ impl Ctx { .tree() .expr() .ok_or_else(|| Error::parse_error("unexpected EOF".into()))?; - self.downgrade_ctx(extra_scope).downgrade(expr) + let bump = Bump::new(); + let ir = self + .downgrade_ctx(&bump, extra_scope) + .downgrade_toplevel(expr)?; + let ir = unsafe { std::mem::transmute::, IrRef<'static>>(ir) }; + Ok(OwnedIr { _bump: bump, ir }) } - fn compile<'a>(&'a mut self, source: Source, extra_scope: Option>) -> Result { + fn compile<'ctx>( + &'ctx mut self, + source: Source, + extra_scope: Option>, + ) -> Result { let root = self.downgrade(source, extra_scope)?; tracing::debug!("Generating JavaScript code"); - let code = compile(self.get_ir(root), self); + let code = compile(root.as_ref(), self); tracing::debug!("Generated code: {}", &code); Ok(code) } @@ -346,16 +372,13 @@ impl Ctx { ); let root = self.downgrade(source, Some(scope))?; tracing::debug!("Generating JavaScript code for scoped import"); - let code = compile_scoped(self.get_ir(root), self); + let code = compile_scoped(root.as_ref(), self); tracing::debug!("Generated scoped code: {}", &code); Ok(code) } } impl CodegenContext for Ctx { - fn get_ir(&self, id: ExprId) -> &Ir { - self.irs.get(id.0).expect("ExprId out of bounds") - } fn get_sym(&self, id: SymId) -> Symbol<'_> { self.symbols .resolve(id) @@ -401,126 +424,129 @@ impl RuntimeContext for Ctx { fn get_store(&self) -> &DaemonStore { &self.store } - fn get_span(&self, id: usize) -> (usize, rnix::TextRange) { + fn get_span(&self, id: usize) -> (usize, TextRange) { let spans = unsafe { &*self.spans.get() }; spans[id] } } -enum Scope<'ctx> { - Global(&'ctx HashMap), +enum Scope<'ctx, 'ir> { + Global(&'ctx HashMap>), Repl(&'ctx HashSet), ScopedImport(HashSet), - Let(HashMap), - Param(SymId, ExprId), + Let(HashMap), + Param(SymId, IrRef<'ir>), } -struct ScopeGuard<'a, 'ctx> { - ctx: &'a mut DowngradeCtx<'ctx>, +struct ScopeGuard<'a, 'ctx, 'ir> { + ctx: &'a mut DowngradeCtx<'ctx, 'ir>, } -impl<'a, 'ctx> Drop for ScopeGuard<'a, 'ctx> { +impl Drop for ScopeGuard<'_, '_, '_> { fn drop(&mut self) { self.ctx.scopes.pop(); } } -impl<'a, 'ctx> ScopeGuard<'a, 'ctx> { - fn as_ctx(&mut self) -> &mut DowngradeCtx<'ctx> { +impl<'ir, 'ctx> ScopeGuard<'_, 'ctx, 'ir> { + fn as_ctx(&mut self) -> &mut DowngradeCtx<'ctx, 'ir> { self.ctx } } -pub struct DowngradeCtx<'ctx> { - ctx: &'ctx mut Ctx, - irs: Vec, - scopes: Vec>, +pub struct DowngradeCtx<'ctx, 'ir> { + bump: &'ir Bump, + symbols: &'ctx mut DefaultStringInterner, + source: Source, + scopes: Vec>, with_scope_count: usize, - arg_id: usize, - thunk_scopes: Vec>, + arg_count: usize, + thunk_count: &'ctx mut usize, + thunk_scopes: Vec)>>, } -impl<'ctx> DowngradeCtx<'ctx> { - fn new( - ctx: &'ctx mut Ctx, - global: &'ctx HashMap, - extra_scope: Option>, - ) -> Self { - Self { - scopes: std::iter::once(Scope::Global(global)) - .chain(extra_scope) - .collect(), - irs: vec![], - arg_id: 0, - with_scope_count: 0, - thunk_scopes: vec![Vec::new()], - ctx, - } - } -} - -impl DowngradeContext for DowngradeCtx<'_> { - fn new_expr(&mut self, expr: Ir) -> ExprId { - self.irs.push(expr); - ExprId(self.ctx.irs.len() + self.irs.len() - 1) - } - - fn new_arg(&mut self, span: TextRange) -> ExprId { - self.irs.push( - Arg { - inner: ArgId(self.arg_id), - span, - } - .to_ir(), - ); - self.arg_id += 1; - ExprId(self.ctx.irs.len() + self.irs.len() - 1) - } - - fn get_ir(&self, id: ExprId) -> &Ir { - if id.0 < self.ctx.irs.len() { - self.ctx.irs.get(id.0).expect("unreachable") - } else { - self.irs - .get(id.0 - self.ctx.irs.len()) - .expect("ExprId out of bounds") - } - } - - fn maybe_thunk(&mut self, id: ExprId) -> ExprId { - let ir = self.get_ir(id); - match ir { - Ir::Builtin(_) +fn should_thunk(ir: IrRef<'_>) -> bool { + !matches!( + ir, + Ir::Builtin(_) | Ir::Builtins(_) | Ir::Int(_) | Ir::Float(_) | Ir::Bool(_) | Ir::Null(_) | Ir::Str(_) - | Ir::Thunk(_) => id, - _ => { - let span = ir.span(); - let slot = self.reserve_slots(1).next().expect("reserve_slots failed"); - self.replace_ir(slot, Thunk { inner: slot, span }.to_ir()); - self.register_thunk(slot, id); - slot + | Ir::Thunk(_) + ) +} + +impl<'ctx, 'ir> DowngradeCtx<'ctx, 'ir> { + fn new( + bump: &'ir Bump, + symbols: &'ctx mut DefaultStringInterner, + global: &'ctx HashMap>, + extra_scope: Option>, + thunk_count: &'ctx mut usize, + source: Source, + ) -> Self { + Self { + bump, + symbols, + source, + scopes: std::iter::once(Scope::Global(global)) + .chain(extra_scope) + .collect(), + thunk_count, + arg_count: 0, + with_scope_count: 0, + thunk_scopes: vec![bumpalo::collections::Vec::new_in(bump)], + } + } +} + +impl<'ctx: 'ir, 'ir> DowngradeContext<'ir> for DowngradeCtx<'ctx, 'ir> { + fn new_expr(&self, expr: Ir<'ir>) -> IrRef<'ir> { + self.bump.alloc(expr) + } + + fn new_arg(&mut self, span: TextRange) -> IrRef<'ir> { + self.arg_count += 1; + self.bump.alloc( + Arg { + inner: ArgId(self.arg_count - 1), + span, } + .to_ir(), + ) + } + + fn maybe_thunk(&mut self, ir: IrRef<'ir>) -> IrRef<'ir> { + if should_thunk(ir) { + let span = ir.span(); + let id = ThunkId(*self.thunk_count); + *self.thunk_count += 1; + self.thunk_scopes + .last_mut() + .expect("no active thunk scope") + .push((id, ir)); + self.new_expr(Thunk { inner: id, span }.to_ir()) + } else { + ir } } fn new_sym(&mut self, sym: String) -> SymId { - self.ctx.symbols.get_or_intern(sym) + self.symbols.get_or_intern(sym) } fn get_sym(&self, id: SymId) -> Symbol<'_> { - self.ctx.get_sym(id) + self.symbols.resolve(id).expect("no symbol found").into() } - fn lookup(&mut self, sym: SymId, span: TextRange) -> Result { + fn lookup(&self, sym: SymId, span: TextRange) -> Result> { for scope in self.scopes.iter().rev() { match scope { &Scope::Global(global_scope) => { - if let Some(&expr) = global_scope.get(&sym) { + if let Some(expr) = global_scope.get(&sym) { return Ok(expr); } } @@ -558,49 +584,32 @@ impl DowngradeContext for DowngradeCtx<'_> { } } - fn replace_ir(&mut self, id: ExprId, expr: Ir) { - let local_id = id.0 - self.ctx.irs.len(); - *self.irs.get_mut(local_id).expect("ExprId out of bounds") = expr; - } - fn get_current_source(&self) -> Source { - self.ctx.get_current_source() + self.source.clone() } - #[allow(refining_impl_trait)] - fn reserve_slots(&mut self, slots: usize) -> impl Iterator + Clone + use<> { - let start = self.ctx.irs.len() + self.irs.len(); - let range = (start..start + slots).map(ExprId); - let span = rnix::TextRange::default(); - // Fill reserved slots with placeholder value - self.irs.extend( - range - .clone() - .map(|slot| Thunk { inner: slot, span }.to_ir()), - ); - range - } - - fn downgrade(mut self, root: rnix::ast::Expr) -> Result { - use crate::ir::TopLevel; - let body = root.downgrade(&mut self)?; - let thunks = self.thunk_scopes.pop().expect("no thunk scope left???"); - let span = self.get_ir(body).span(); - let top_level = self.new_expr(TopLevel { body, thunks, span }.to_ir()); - self.ctx.irs.extend(self.irs); - Ok(top_level) - } - - fn with_let_scope(&mut self, bindings: HashMap, f: F) -> R + fn with_let_scope(&mut self, keys: &[SymId], f: F) -> Result where - F: FnOnce(&mut Self) -> R, + F: FnOnce(&mut Self) -> Result<(bumpalo::collections::Vec<'ir, IrRef<'ir>>, R)>, { - self.scopes.push(Scope::Let(bindings)); - let mut guard = ScopeGuard { ctx: self }; - f(guard.as_ctx()) + let base = *self.thunk_count; + *self.thunk_count += keys.len(); + let iter = keys + .iter() + .enumerate() + .map(|(offset, &key)| (key, ThunkId(base + offset))); + self.scopes.push(Scope::Let(iter.collect())); + let (vals, ret) = { + let mut guard = ScopeGuard { ctx: self }; + f(guard.as_ctx())? + }; + assert_eq!(keys.len(), vals.len()); + let scope = self.thunk_scopes.last_mut().expect("no active thunk scope"); + scope.extend((base..base + keys.len()).map(ThunkId).zip(vals)); + Ok(ret) } - fn with_param_scope(&mut self, param: SymId, arg: ExprId, f: F) -> R + fn with_param_scope(&mut self, param: SymId, arg: IrRef<'ir>, f: F) -> R where F: FnOnce(&mut Self) -> R, { @@ -619,11 +628,15 @@ impl DowngradeContext for DowngradeCtx<'_> { ret } - fn with_thunk_scope(&mut self, f: F) -> (R, Vec<(ExprId, ExprId)>) + fn with_thunk_scope( + &mut self, + f: F, + ) -> (R, bumpalo::collections::Vec<'ir, (ThunkId, IrRef<'ir>)>) where F: FnOnce(&mut Self) -> R, { - self.thunk_scopes.push(Vec::new()); + self.thunk_scopes + .push(bumpalo::collections::Vec::new_in(self.bump)); let ret = f(self); ( ret, @@ -631,10 +644,17 @@ impl DowngradeContext for DowngradeCtx<'_> { ) } - fn register_thunk(&mut self, slot: ExprId, inner: ExprId) { - self.thunk_scopes - .last_mut() - .expect("register_thunk without active scope") - .push((slot, inner)); + fn bump(&self) -> &'ir bumpalo::Bump { + self.bump + } +} + +impl<'ir, 'ctx: 'ir> DowngradeCtx<'ctx, 'ir> { + fn downgrade_toplevel(mut self, root: rnix::ast::Expr) -> Result> { + use crate::ir::TopLevel; + let body = root.downgrade(&mut self)?; + let span = body.span(); + let thunks = self.thunk_scopes.pop().expect("no thunk scope left???"); + Ok(self.new_expr(TopLevel { body, thunks, span }.to_ir())) } } diff --git a/nix-js/src/downgrade.rs b/nix-js/src/downgrade.rs index 147e1f6..a882300 100644 --- a/nix-js/src/downgrade.rs +++ b/nix-js/src/downgrade.rs @@ -1,22 +1,22 @@ +use bumpalo::boxed::Box; +use bumpalo::collections::{CollectIn, Vec}; +use hashbrown::HashSet; use hashbrown::hash_map::Entry; -use hashbrown::{HashMap, HashSet}; -use itertools::Itertools as _; use rnix::TextRange; use rnix::ast::{self, AstToken, Expr, HasEntry}; use rowan::ast::AstNode; -use tap::TryConv; use crate::error::{Error, Result, Source}; use crate::ir::*; use crate::value::Symbol; -trait Require { - fn require(self, ctx: &impl DowngradeContext, span: TextRange) -> Result; +trait Require<'ir, Ctx: DowngradeContext<'ir>, T> { + fn require(self, ctx: &Ctx, span: TextRange) -> Result; } -impl Require for Option { +impl<'ir, Ctx: DowngradeContext<'ir>, T> Require<'ir, Ctx, T> for Option { #[inline] - fn require(self, ctx: &impl DowngradeContext, span: TextRange) -> Result { + fn require(self, ctx: &Ctx, span: TextRange) -> Result { self.ok_or_else(|| { Error::parse_error("invalid syntax".into()) .with_source(ctx.get_current_source()) @@ -25,9 +25,11 @@ impl Require for Option { } } -impl Require for std::result::Result { +impl<'ir, Ctx: DowngradeContext<'ir>, T, E: std::fmt::Display> Require<'ir, Ctx, T> + for std::result::Result +{ #[inline] - fn require(self, ctx: &impl DowngradeContext, span: TextRange) -> Result { + fn require(self, ctx: &Ctx, span: TextRange) -> Result { self.map_err(|e| { Error::parse_error(format!("invalid syntax: {e}")) .with_source(ctx.get_current_source()) @@ -36,43 +38,46 @@ impl Require for std::result::Result { } } -pub trait DowngradeContext { - fn downgrade(self, expr: rnix::ast::Expr) -> Result; +trait BoxIn: Sized { + fn box_in(self, bump: &bumpalo::Bump) -> Box<'_, Self> { + Box::new_in(self, bump) + } +} +impl BoxIn for T {} - fn new_expr(&mut self, expr: Ir) -> ExprId; - fn new_arg(&mut self, span: TextRange) -> ExprId; - fn maybe_thunk(&mut self, id: ExprId) -> ExprId; - fn register_thunk(&mut self, slot: ExprId, inner: ExprId); +pub trait DowngradeContext<'ir> { + fn new_expr(&self, expr: Ir<'ir>) -> IrRef<'ir>; + fn new_arg(&mut self, span: TextRange) -> IrRef<'ir>; + fn maybe_thunk(&mut self, ir: IrRef<'ir>) -> IrRef<'ir>; fn new_sym(&mut self, sym: String) -> SymId; fn get_sym(&self, id: SymId) -> Symbol<'_>; - fn lookup(&mut self, sym: SymId, span: TextRange) -> Result; + fn lookup(&self, sym: SymId, span: TextRange) -> Result>; - fn get_ir(&self, id: ExprId) -> &Ir; - fn replace_ir(&mut self, id: ExprId, expr: Ir); - fn reserve_slots(&mut self, slots: usize) -> impl Iterator + Clone + use; fn get_current_source(&self) -> Source; - fn with_param_scope(&mut self, param: SymId, arg: ExprId, f: F) -> R + fn with_param_scope(&mut self, param: SymId, arg: IrRef<'ir>, f: F) -> R where F: FnOnce(&mut Self) -> R; - fn with_let_scope(&mut self, bindings: HashMap, f: F) -> R + fn with_let_scope(&mut self, bindings: &[SymId], f: F) -> Result where - F: FnOnce(&mut Self) -> R; + F: FnOnce(&mut Self) -> Result<(Vec<'ir, IrRef<'ir>>, R)>; fn with_with_scope(&mut self, f: F) -> R where F: FnOnce(&mut Self) -> R; - fn with_thunk_scope(&mut self, f: F) -> (R, Vec<(ExprId, ExprId)>) + fn with_thunk_scope(&mut self, f: F) -> (R, Vec<'ir, (ThunkId, IrRef<'ir>)>) where F: FnOnce(&mut Self) -> R; + + fn bump(&self) -> &'ir bumpalo::Bump; } -pub trait Downgrade { - fn downgrade(self, ctx: &mut Ctx) -> Result; +pub trait Downgrade<'ir, Ctx: DowngradeContext<'ir>> { + fn downgrade(self, ctx: &mut Ctx) -> Result>; } -impl Downgrade for Expr { - fn downgrade(self, ctx: &mut Ctx) -> Result { +impl<'ir, Ctx: DowngradeContext<'ir>> Downgrade<'ir, Ctx> for Expr { + fn downgrade(self, ctx: &mut Ctx) -> Result> { use Expr::*; match self { Apply(apply) => apply.downgrade(ctx), @@ -121,8 +126,8 @@ impl Downgrade for Expr { } } -impl Downgrade for ast::Assert { - fn downgrade(self, ctx: &mut Ctx) -> Result { +impl<'ir, Ctx: DowngradeContext<'ir>> Downgrade<'ir, Ctx> for ast::Assert { + fn downgrade(self, ctx: &mut Ctx) -> Result> { let span = self.syntax().text_range(); let assertion = self.condition().require(ctx, span)?; let assertion_raw = assertion.to_string(); @@ -140,8 +145,8 @@ impl Downgrade for ast::Assert { } } -impl Downgrade for ast::IfElse { - fn downgrade(self, ctx: &mut Ctx) -> Result { +impl<'ir, Ctx: DowngradeContext<'ir>> Downgrade<'ir, Ctx> for ast::IfElse { + fn downgrade(self, ctx: &mut Ctx) -> Result> { let span = self.syntax().text_range(); let cond = self.condition().require(ctx, span)?.downgrade(ctx)?; let consq = self.body().require(ctx, span)?.downgrade(ctx)?; @@ -160,8 +165,8 @@ impl Downgrade for ast::IfElse { macro_rules! path { ($ty:ident) => { - impl Downgrade for ast::$ty { - fn downgrade(self, ctx: &mut Ctx) -> Result { + impl<'ir, Ctx: DowngradeContext<'ir>> Downgrade<'ir, Ctx> for ast::$ty { + fn downgrade(self, ctx: &mut Ctx) -> Result> { let span = self.syntax().text_range(); downgrade_path(self.parts(), span, ctx) } @@ -171,15 +176,15 @@ macro_rules! path { path!(PathAbs); path!(PathRel); path!(PathHome); -impl Downgrade for ast::PathSearch { - fn downgrade(self, ctx: &mut Ctx) -> Result { +impl<'ir, Ctx: DowngradeContext<'ir>> Downgrade<'ir, Ctx> for ast::PathSearch { + fn downgrade(self, ctx: &mut Ctx) -> Result> { let span = self.syntax().text_range(); let path = { let temp = self.content().require(ctx, span)?; let text = temp.text(); ctx.new_expr( Str { - val: text[1..text.len() - 1].to_string(), + inner: text[1..text.len() - 1].to_string().box_in(ctx.bump()), span, } .to_ir(), @@ -208,17 +213,24 @@ impl Downgrade for ast::PathSearch { } } -impl Downgrade for ast::Str { - fn downgrade(self, ctx: &mut Ctx) -> Result { +impl<'ir, Ctx: DowngradeContext<'ir>> Downgrade<'ir, Ctx> for ast::Str { + fn downgrade(self, ctx: &mut Ctx) -> Result> { let span = self.syntax().text_range(); let normalized = self.normalized_parts(); let is_single_literal = normalized.len() == 1 && matches!(normalized.first(), Some(ast::InterpolPart::Literal(_))); + let bump = ctx.bump(); let parts = normalized .into_iter() .map(|part| match part { - ast::InterpolPart::Literal(lit) => Ok(ctx.new_expr(Str { val: lit, span }.to_ir())), + ast::InterpolPart::Literal(lit) => Ok(ctx.new_expr( + Str { + inner: lit.box_in(bump), + span, + } + .to_ir(), + )), ast::InterpolPart::Interpolation(interpol) => { let inner = interpol .expr() @@ -227,7 +239,7 @@ impl Downgrade for ast::Str { Ok(ctx.maybe_thunk(inner)) } }) - .collect::>>()?; + .collect_in::>>(bump)?; Ok(if is_single_literal { parts.into_iter().next().expect("is_single_literal checked") @@ -244,8 +256,8 @@ impl Downgrade for ast::Str { } } -impl Downgrade for ast::Literal { - fn downgrade(self, ctx: &mut Ctx) -> Result { +impl<'ir, Ctx: DowngradeContext<'ir>> Downgrade<'ir, Ctx> for ast::Literal { + fn downgrade(self, ctx: &mut Ctx) -> Result> { let span = self.syntax().text_range(); Ok(ctx.new_expr(match self.kind() { ast::LiteralKind::Integer(int) => Int { @@ -259,7 +271,7 @@ impl Downgrade for ast::Literal { } .to_ir(), ast::LiteralKind::Uri(uri) => Str { - val: uri.to_string(), + inner: uri.to_string().box_in(ctx.bump()), span, } .to_ir(), @@ -267,8 +279,8 @@ impl Downgrade for ast::Literal { } } -impl Downgrade for ast::Ident { - fn downgrade(self, ctx: &mut Ctx) -> Result { +impl<'ir, Ctx: DowngradeContext<'ir>> Downgrade<'ir, Ctx> for ast::Ident { + fn downgrade(self, ctx: &mut Ctx) -> Result> { let span = self.syntax().text_range(); let text = self.ident_token().require(ctx, span)?.to_string(); let sym = ctx.new_sym(text); @@ -276,8 +288,8 @@ impl Downgrade for ast::Ident { } } -impl Downgrade for ast::AttrSet { - fn downgrade(self, ctx: &mut Ctx) -> Result { +impl<'ir, Ctx: DowngradeContext<'ir>> Downgrade<'ir, Ctx> for ast::AttrSet { + fn downgrade(self, ctx: &mut Ctx) -> Result> { let rec = self.rec_token().is_some(); let span = self.syntax().text_range(); @@ -287,29 +299,30 @@ impl Downgrade for ast::AttrSet { } // rec { a = 1; b = a; } => let a = 1; b = a; in { inherit a b; } - let entries: Vec<_> = self.entries().collect(); + let entries: Vec<'ir, _> = self.entries().collect_in(ctx.bump()); downgrade_rec_bindings(entries, ctx, span) } } /// Downgrades a list. -impl Downgrade for ast::List { - fn downgrade(self, ctx: &mut Ctx) -> Result { +impl<'ir, Ctx: DowngradeContext<'ir>> Downgrade<'ir, Ctx> for ast::List { + fn downgrade(self, ctx: &mut Ctx) -> Result> { + let bump = ctx.bump(); let items = self .items() .map(|item| { let id = item.downgrade(ctx)?; Ok(ctx.maybe_thunk(id)) }) - .collect::>()?; + .collect_in::>(bump)?; let span = self.syntax().text_range(); Ok(ctx.new_expr(List { items, span }.to_ir())) } } /// Downgrades a binary operation. -impl Downgrade for ast::BinOp { - fn downgrade(self, ctx: &mut Ctx) -> Result { +impl<'ir, Ctx: DowngradeContext<'ir>> Downgrade<'ir, Ctx> for ast::BinOp { + fn downgrade(self, ctx: &mut Ctx) -> Result> { let span = self.syntax().text_range(); let lhs = self.lhs().require(ctx, span)?.downgrade(ctx)?; let rhs = self.rhs().require(ctx, span)?.downgrade(ctx)?; @@ -327,8 +340,8 @@ impl Downgrade for ast::BinOp { } /// Downgrades a "has attribute" (`?`) expression. -impl Downgrade for ast::HasAttr { - fn downgrade(self, ctx: &mut Ctx) -> Result { +impl<'ir, Ctx: DowngradeContext<'ir>> Downgrade<'ir, Ctx> for ast::HasAttr { + fn downgrade(self, ctx: &mut Ctx) -> Result> { let span = self.syntax().text_range(); let lhs = self.expr().require(ctx, span)?.downgrade(ctx)?; let rhs = downgrade_attrpath(self.attrpath().require(ctx, span)?, ctx)?; @@ -337,8 +350,8 @@ impl Downgrade for ast::HasAttr { } /// Downgrades a unary operation. -impl Downgrade for ast::UnaryOp { - fn downgrade(self, ctx: &mut Ctx) -> Result { +impl<'ir, Ctx: DowngradeContext<'ir>> Downgrade<'ir, Ctx> for ast::UnaryOp { + fn downgrade(self, ctx: &mut Ctx) -> Result> { let span = self.syntax().text_range(); let rhs = self.expr().require(ctx, span)?.downgrade(ctx)?; let kind = self.operator().require(ctx, span)?.into(); @@ -347,8 +360,8 @@ impl Downgrade for ast::UnaryOp { } /// Downgrades an attribute selection (`.`). -impl Downgrade for ast::Select { - fn downgrade(self, ctx: &mut Ctx) -> Result { +impl<'ir, Ctx: DowngradeContext<'ir>> Downgrade<'ir, Ctx> for ast::Select { + fn downgrade(self, ctx: &mut Ctx) -> Result> { let span = self.syntax().text_range(); let expr = self.expr().require(ctx, span)?.downgrade(ctx)?; let attrpath = downgrade_attrpath(self.attrpath().require(ctx, span)?, ctx)?; @@ -373,15 +386,15 @@ impl Downgrade for ast::Select { /// Downgrades a `legacy let`, which is essentially a recursive attribute set. /// The body of the `let` is accessed via `let.body`. -impl Downgrade for ast::LegacyLet { - fn downgrade(self, ctx: &mut Ctx) -> Result { +impl<'ir, Ctx: DowngradeContext<'ir>> Downgrade<'ir, Ctx> for ast::LegacyLet { + fn downgrade(self, ctx: &mut Ctx) -> Result> { let span = self.syntax().text_range(); - let entries: Vec<_> = self.entries().collect(); + let entries: Vec<'ir, _> = self.entries().collect_in(ctx.bump()); let attrset_expr = downgrade_let_bindings(entries, ctx, span, |ctx, binding_keys| { // Create plain attrset as body with inherit let mut attrs = AttrSet { - stcs: HashMap::new(), - dyns: Vec::new(), + stcs: HashMap::new_in(ctx.bump()), + dyns: Vec::new_in(ctx.bump()), span, }; @@ -396,7 +409,10 @@ impl Downgrade for ast::LegacyLet { let body_sym = ctx.new_sym("body".to_string()); let select = Select { expr: attrset_expr, - attrpath: vec![Attr::Str(body_sym, rnix::TextRange::default())], + attrpath: Vec::from_iter_in( + [Attr::Str(body_sym, rnix::TextRange::default())], + ctx.bump(), + ), default: None, span, }; @@ -406,9 +422,9 @@ impl Downgrade for ast::LegacyLet { } /// Downgrades a `let ... in ...` expression. -impl Downgrade for ast::LetIn { - fn downgrade(self, ctx: &mut Ctx) -> Result { - let entries: Vec<_> = self.entries().collect(); +impl<'ir, Ctx: DowngradeContext<'ir>> Downgrade<'ir, Ctx> for ast::LetIn { + fn downgrade(self, ctx: &mut Ctx) -> Result> { + let entries: Vec<'ir, _> = self.entries().collect_in(ctx.bump()); let span = self.syntax().text_range(); let body_expr = self.body().require(ctx, span)?; @@ -419,8 +435,8 @@ impl Downgrade for ast::LetIn { } /// Downgrades a `with` expression. -impl Downgrade for ast::With { - fn downgrade(self, ctx: &mut Ctx) -> Result { +impl<'ir, Ctx: DowngradeContext<'ir>> Downgrade<'ir, Ctx> for ast::With { + fn downgrade(self, ctx: &mut Ctx) -> Result> { let span = self.syntax().text_range(); let namespace = self.namespace().require(ctx, span)?.downgrade(ctx)?; let namespace = ctx.maybe_thunk(namespace); @@ -444,16 +460,16 @@ impl Downgrade for ast::With { /// Downgrades a lambda (function) expression. /// This involves desugaring pattern-matching arguments into `let` bindings. -impl Downgrade for ast::Lambda { - fn downgrade(self, ctx: &mut Ctx) -> Result { +impl<'ir, Ctx: DowngradeContext<'ir>> Downgrade<'ir, Ctx> for ast::Lambda { + fn downgrade(self, ctx: &mut Ctx) -> Result> { let span = self.syntax().text_range(); let raw_param = self.param().require(ctx, span)?; let body_ast = self.body().require(ctx, span)?; let arg = ctx.new_arg(raw_param.syntax().text_range()); - struct Ret { - param: Option, - body: ExprId, + struct Ret<'ir> { + param: Option>, + body: IrRef<'ir>, } let (ret, thunks) = ctx.with_thunk_scope(|ctx| { @@ -473,7 +489,7 @@ impl Downgrade for ast::Lambda { .pat_bind() .map(|alias| { let ident = alias.ident().require(ctx, alias.syntax().text_range())?; - Ok::<_, Box>(ctx.new_sym(ident.to_string())) + Ok::<_, std::boxed::Box>(ctx.new_sym(ident.to_string())) }) .transpose()?; @@ -518,8 +534,8 @@ impl Downgrade for ast::Lambda { /// Downgrades a function application. /// In Nix, function application is left-associative, so `f a b` should be parsed as `((f a) b)`. /// Each Apply node represents a single function call with one argument. -impl Downgrade for ast::Apply { - fn downgrade(self, ctx: &mut Ctx) -> Result { +impl<'ir, Ctx: DowngradeContext<'ir>> Downgrade<'ir, Ctx> for ast::Apply { + fn downgrade(self, ctx: &mut Ctx) -> Result> { let span = self.syntax().text_range(); let func = self.lambda().require(ctx, span)?.downgrade(ctx)?; let arg = self.argument().require(ctx, span)?.downgrade(ctx)?; @@ -529,29 +545,29 @@ impl Downgrade for ast::Apply { } } -enum PendingValue { +enum PendingValue<'ir> { Expr(ast::Expr), InheritFrom(ast::Expr, SymId, TextRange), InheritScope(SymId, TextRange), - Set(PendingAttrSet), + Set(PendingAttrSet<'ir>), ExtendedRecAttrSet { base: ast::AttrSet, - extensions: Vec, + extensions: Vec<'ir, ast::Entry>, span: TextRange, }, } -struct PendingAttrSet { - stcs: HashMap, - dyns: Vec<(ast::Attr, PendingValue, TextRange)>, +struct PendingAttrSet<'ir> { + stcs: HashMap<'ir, SymId, (PendingValue<'ir>, TextRange)>, + dyns: Vec<'ir, (ast::Attr, PendingValue<'ir>, TextRange)>, span: TextRange, } -impl PendingAttrSet { - fn new(span: TextRange) -> Self { +impl<'ir> PendingAttrSet<'ir> { + fn new_in(bump: &'ir bumpalo::Bump, span: TextRange) -> Self { Self { - stcs: HashMap::new(), - dyns: Vec::new(), + stcs: HashMap::new_in(bump), + dyns: Vec::new_in(bump), span, } } @@ -560,7 +576,7 @@ impl PendingAttrSet { &mut self, path: &[ast::Attr], value: ast::Expr, - ctx: &mut impl DowngradeContext, + ctx: &mut impl DowngradeContext<'ir>, ) -> Result<()> { let first = path.first().expect("empty attrpath passed. this is a bug"); let rest = &path[1..]; @@ -580,9 +596,9 @@ impl PendingAttrSet { let sym = ctx.new_sym(lit); return self.insert_static(sym, span, rest, value, ctx); } - self.insert_dynamic(first.clone(), span, rest, value) + self.insert_dynamic(first.clone(), span, rest, value, ctx) } - ast::Attr::Dynamic(_) => self.insert_dynamic(first.clone(), span, rest, value), + ast::Attr::Dynamic(_) => self.insert_dynamic(first.clone(), span, rest, value, ctx), } } @@ -592,7 +608,7 @@ impl PendingAttrSet { span: TextRange, path: &[ast::Attr], value: ast::Expr, - ctx: &mut impl DowngradeContext, + ctx: &mut impl DowngradeContext<'ir>, ) -> Result<()> { if !path.is_empty() { match self.stcs.entry(sym) { @@ -604,17 +620,23 @@ impl PendingAttrSet { { let base = attrset.clone(); let base_span = attrset.syntax().text_range(); - let ext_entry = make_attrpath_value_entry(path.to_vec(), value); + let ext_entry = make_attrpath_value_entry( + Vec::from_iter_in(path.iter().cloned(), ctx.bump()), + value, + ); *existing = PendingValue::ExtendedRecAttrSet { base, - extensions: vec![ext_entry], + extensions: Vec::from_iter_in([ext_entry], ctx.bump()), span: base_span, }; return Ok(()); } if let PendingValue::ExtendedRecAttrSet { extensions, .. } = existing { - let ext_entry = make_attrpath_value_entry(path.to_vec(), value); + let ext_entry = make_attrpath_value_entry( + Vec::from_iter_in(path.iter().cloned(), ctx.bump()), + value, + ); extensions.push(ext_entry); return Ok(()); } @@ -623,7 +645,7 @@ impl PendingAttrSet { nested.insert(path, value, ctx) } Entry::Vacant(entry) => { - let mut nested = PendingAttrSet::new(span); + let mut nested = PendingAttrSet::new_in(ctx.bump(), span); nested.insert(path, value, ctx)?; entry.insert((PendingValue::Set(nested), span)); Ok(()) @@ -649,14 +671,16 @@ impl PendingAttrSet { span: TextRange, path: &[ast::Attr], value: ast::Expr, + ctx: &mut impl DowngradeContext<'ir>, ) -> Result<()> { if !path.is_empty() { - let mut nested = PendingAttrSet::new(span); + let mut nested = PendingAttrSet::new_in(ctx.bump(), span); nested.insert_dynamic( path[0].clone(), path[0].syntax().text_range(), &path[1..], value, + ctx, )?; self.dyns.push((attr, PendingValue::Set(nested), span)); } else { @@ -666,15 +690,16 @@ impl PendingAttrSet { } fn ensure_pending_set<'a>( - value: &'a mut PendingValue, - ctx: &mut impl DowngradeContext, + value: &'a mut PendingValue<'ir>, + ctx: &mut impl DowngradeContext<'ir>, span: TextRange, - ) -> Result<&'a mut PendingAttrSet> { + ) -> Result<&'a mut PendingAttrSet<'ir>> { match value { PendingValue::Set(set) => Ok(set), PendingValue::Expr(expr) => { if let ast::Expr::AttrSet(attrset) = expr { - let mut nested = PendingAttrSet::new(attrset.syntax().text_range()); + let mut nested = + PendingAttrSet::new_in(ctx.bump(), attrset.syntax().text_range()); nested.collect_entries(attrset.entries(), ctx)?; *value = PendingValue::Set(nested); match value { @@ -705,11 +730,11 @@ impl PendingAttrSet { } fn merge_value( - existing: &mut PendingValue, + existing: &mut PendingValue<'ir>, existing_span: TextRange, new_value: ast::Expr, new_span: TextRange, - ctx: &mut impl DowngradeContext, + ctx: &mut impl DowngradeContext<'ir>, ) -> Result<()> { match existing { PendingValue::Set(existing_set) => { @@ -729,7 +754,8 @@ impl PendingAttrSet { let is_rec = existing_attrset.rec_token().is_some(); if is_rec { let base = existing_attrset.clone(); - let extensions: Vec<_> = new_attrset.entries().collect(); + let extensions: Vec<'ir, _> = + new_attrset.entries().collect_in(ctx.bump()); *existing = PendingValue::ExtendedRecAttrSet { base, extensions, @@ -737,8 +763,10 @@ impl PendingAttrSet { }; Ok(()) } else { - let mut merged = - PendingAttrSet::new(existing_attrset.syntax().text_range()); + let mut merged = PendingAttrSet::new_in( + ctx.bump(), + existing_attrset.syntax().text_range(), + ); merged.collect_entries(existing_attrset.entries(), ctx)?; merged.collect_entries(new_attrset.entries(), ctx)?; *existing = PendingValue::Set(merged); @@ -784,7 +812,7 @@ impl PendingAttrSet { fn collect_entries( &mut self, entries: impl Iterator, - ctx: &mut impl DowngradeContext, + ctx: &mut impl DowngradeContext<'ir>, ) -> Result<()> { for entry in entries { match entry { @@ -794,7 +822,7 @@ impl PendingAttrSet { ast::Entry::AttrpathValue(value) => { let span = value.syntax().text_range(); let attrpath = value.attrpath().require(ctx, span)?; - let path: Vec<_> = attrpath.attrs().collect(); + let path: Vec<'ir, _> = attrpath.attrs().collect_in(ctx.bump()); let expr = value.value().require(ctx, span)?; self.insert(&path, expr, ctx)?; } @@ -806,7 +834,7 @@ impl PendingAttrSet { fn collect_inherit( &mut self, inherit: ast::Inherit, - ctx: &mut impl DowngradeContext, + ctx: &mut impl DowngradeContext<'ir>, ) -> Result<()> { let from = inherit .from() @@ -863,7 +891,7 @@ impl PendingAttrSet { } } -fn make_attrpath_value_entry(path: Vec, value: ast::Expr) -> ast::Entry { +fn make_attrpath_value_entry<'ir>(path: Vec<'ir, ast::Attr>, value: ast::Expr) -> ast::Entry { use rnix::{NixLanguage, SyntaxKind}; use rowan::{GreenNodeBuilder, Language, NodeOrToken}; @@ -922,18 +950,18 @@ fn make_attrpath_value_entry(path: Vec, value: ast::Expr) -> ast::Ent } /// Downgrades the entries of a non-recursive attribute set. -fn downgrade_attrs( +fn downgrade_attrs<'ir>( attrs: impl ast::HasEntry + AstNode, - ctx: &mut impl DowngradeContext, -) -> Result { + ctx: &mut impl DowngradeContext<'ir>, +) -> Result> { let span = attrs.syntax().text_range(); - let mut pending = PendingAttrSet::new(span); + let mut pending = PendingAttrSet::new_in(ctx.bump(), span); pending.collect_entries(attrs.entries(), ctx)?; - finalize_pending_set::<_, true>(pending, &HashMap::new(), ctx) + finalize_pending_set::<_, true>(pending, &HashMap::new_in(ctx.bump()), ctx) } /// Downgrades a single attribute key (part of an attribute path). -fn downgrade_attr(attr: ast::Attr, ctx: &mut impl DowngradeContext) -> Result { +fn downgrade_attr<'ir>(attr: ast::Attr, ctx: &mut impl DowngradeContext<'ir>) -> Result> { use ast::Attr::*; use ast::InterpolPart::*; match attr { @@ -958,16 +986,23 @@ fn downgrade_attr(attr: ast::Attr, ctx: &mut impl DowngradeContext) -> Result Ok(ctx.new_expr(self::Str { val: lit, span }.to_ir())), + Literal(lit) => Ok(ctx.new_expr( + self::Str { + inner: lit.box_in(bump), + span, + } + .to_ir(), + )), Interpolation(interpol) => interpol .expr() .require(ctx, interpol.syntax().text_range())? .downgrade(ctx), }) - .collect::>>()?; + .collect_in::>>(bump)?; Ok(Attr::Dynamic( ctx.new_expr( ConcatStrings { @@ -991,33 +1026,34 @@ fn downgrade_attr(attr: ast::Attr, ctx: &mut impl DowngradeContext) -> Result`. -fn downgrade_attrpath( +/// Downgrades an attribute path (e.g., `a.b."${c}".d`) into a `Vec<'ir, Attr>`. +fn downgrade_attrpath<'ir>( attrpath: ast::Attrpath, - ctx: &mut impl DowngradeContext, -) -> Result> { + ctx: &mut impl DowngradeContext<'ir>, +) -> Result>> { + let bump = ctx.bump(); attrpath .attrs() .map(|attr| downgrade_attr(attr, ctx)) - .collect::>>() + .collect_in::>>(bump) } -struct PatternBindings { - body: ExprId, - required: Vec<(SymId, TextRange)>, - optional: Vec<(SymId, TextRange)>, +struct PatternBindings<'ir> { + body: IrRef<'ir>, + required: Vec<'ir, (SymId, TextRange)>, + optional: Vec<'ir, (SymId, TextRange)>, } /// Helper function for Lambda pattern parameters. -fn downgrade_pattern_bindings( +fn downgrade_pattern_bindings<'ir, Ctx>( pat_entries: impl Iterator, alias: Option, - arg: ExprId, + arg: IrRef<'ir>, ctx: &mut Ctx, - body_fn: impl FnOnce(&mut Ctx, &[SymId]) -> Result, -) -> Result + body_fn: impl FnOnce(&mut Ctx, &[SymId]) -> Result>, +) -> Result> where - Ctx: DowngradeContext, + Ctx: DowngradeContext<'ir>, { struct Param { sym: SymId, @@ -1026,122 +1062,106 @@ where span: TextRange, } let mut seen_params = HashSet::new(); - let (params, mut binding_keys) = pat_entries - .into_iter() - .map(|entry| { - let ident = entry.ident().require(ctx, entry.syntax().text_range())?; - let sym_span = ident.syntax().text_range(); - let sym = ctx.new_sym(ident.syntax().text().to_string()); - let default = entry.default(); - let span = entry.syntax().text_range(); + let bump = ctx.bump(); + let mut params = Vec::new_in(bump); + let mut keys = Vec::new_in(bump); + for entry in pat_entries { + let ident = entry.ident().require(ctx, entry.syntax().text_range())?; + let sym_span = ident.syntax().text_range(); + let sym = ctx.new_sym(ident.syntax().text().to_string()); + let default = entry.default(); + let span = entry.syntax().text_range(); - if !seen_params.insert(sym) { - return Err(Error::downgrade_error( - format!("duplicate parameter '{}'", ctx.get_sym(sym)), - ctx.get_current_source(), - span, - )); - } - - Ok(( - Param { - sym, - sym_span, - default, - span, - }, - sym, - )) - }) - .collect::, Vec<_>)>>()?; - - if let Some(alias_sym) = alias { - binding_keys.push(alias_sym); - } - - let (required, optional) = params.iter().partition_map( - |Param { - sym, - sym_span, - default, - .. - }| { - use itertools::Either::*; - if default.is_none() { - Left((*sym, *sym_span)) - } else { - Right((*sym, *sym_span)) - } - }, - ); - - let slots: Vec<_> = ctx.reserve_slots(binding_keys.len()).collect(); - let let_bindings: HashMap<_, _> = binding_keys - .iter() - .copied() - .zip(slots.iter().copied()) - .collect(); - - for &slot in &slots { - let span = rnix::TextRange::default(); - ctx.replace_ir(slot, Thunk { inner: slot, span }.to_ir()); - } - - ctx.with_let_scope(let_bindings.clone(), |ctx| { - for Param { + if !seen_params.insert(sym) { + return Err(Error::downgrade_error( + format!("duplicate parameter '{}'", ctx.get_sym(sym)), + ctx.get_current_source(), + span, + )); + } + params.push(Param { sym, sym_span, default, span, - } in params - { - let slot = *let_bindings.get(&sym).expect("binding registered"); + }); + keys.push(sym); + } - let default = if let Some(default) = default { - let default = default.clone().downgrade(ctx)?; - Some(ctx.maybe_thunk(default)) - } else { - None - }; + if let Some(alias_sym) = alias { + keys.push(alias_sym); + } - let select_expr = ctx.new_expr( - Select { - expr: arg, - attrpath: vec![Attr::Str(sym, sym_span)], + let mut required = Vec::new_in(bump); + let mut optional = Vec::new_in(bump); + for &Param { + sym, + sym_span, + ref default, + .. + } in params.iter() + { + if default.is_none() { + required.push((sym, sym_span)); + } else { + optional.push((sym, sym_span)); + } + } + + ctx.with_let_scope(&keys, |ctx| { + let vals = params + .into_iter() + .map(|param| { + let Param { + sym, + sym_span, default, span, - } - .to_ir(), - ); - ctx.register_thunk(slot, select_expr); - } + } = param; + let default = if let Some(default) = default { + let default = default.clone().downgrade(ctx)?; + Some(ctx.maybe_thunk(default)) + } else { + None + }; - if let Some(alias_sym) = alias { - let slot = *let_bindings.get(&alias_sym).expect("binding registered"); - ctx.register_thunk(slot, arg); - } + Ok(ctx.new_expr( + Select { + expr: arg, + attrpath: Vec::from_iter_in([Attr::Str(sym, sym_span)], bump), + default, + span, + } + .to_ir(), + )) + }) + .chain(alias.into_iter().map(|_| Ok(arg))) + .collect_in::>(bump)?; - let body = body_fn(ctx, &binding_keys)?; + let body = body_fn(ctx, &keys)?; - Ok(PatternBindings { - body, - required, - optional, - }) + Ok(( + vals, + PatternBindings { + body, + required, + optional, + }, + )) }) } /// Downgrades a `let...in` expression. This is a special case of rec attrs /// that disallows dynamic attributes and has a body expression. -fn downgrade_let_bindings( - entries: Vec, +fn downgrade_let_bindings<'ir, Ctx, F>( + entries: Vec<'ir, ast::Entry>, ctx: &mut Ctx, span: TextRange, body_fn: F, -) -> Result +) -> Result> where - Ctx: DowngradeContext, - F: FnOnce(&mut Ctx, &[SymId]) -> Result, + Ctx: DowngradeContext<'ir>, + F: FnOnce(&mut Ctx, &[SymId]) -> Result>, { downgrade_rec_attrs_impl::<_, _, false>(entries, ctx, span, |ctx, binding_keys, _dyns| { body_fn(ctx, binding_keys) @@ -1149,18 +1169,18 @@ where } /// Downgrades a `rec` attribute set. -fn downgrade_rec_bindings( - entries: Vec, +fn downgrade_rec_bindings<'ir, Ctx>( + entries: Vec<'ir, ast::Entry>, ctx: &mut Ctx, span: TextRange, -) -> Result +) -> Result> where - Ctx: DowngradeContext, + Ctx: DowngradeContext<'ir>, { downgrade_rec_attrs_impl::<_, _, true>(entries, ctx, span, |ctx, binding_keys, dyns| { let mut attrs = AttrSet { - stcs: HashMap::new(), - dyns: dyns.to_vec(), + stcs: HashMap::new_in(ctx.bump()), + dyns: Vec::from_iter_in(dyns.iter().cloned(), ctx.bump()), span, }; @@ -1175,67 +1195,40 @@ where /// Core implementation for recursive bindings (rec attrs and let-in). /// ALLOW_DYN controls whether dynamic attributes are allowed. -fn downgrade_rec_attrs_impl( - entries: Vec, +fn downgrade_rec_attrs_impl<'ir, Ctx, F, const ALLOW_DYN: bool>( + entries: Vec<'ir, ast::Entry>, ctx: &mut Ctx, span: TextRange, body_fn: F, -) -> Result +) -> Result> where - Ctx: DowngradeContext, - F: FnOnce(&mut Ctx, &[SymId], &[(ExprId, ExprId, TextRange)]) -> Result, + Ctx: DowngradeContext<'ir>, + F: FnOnce(&mut Ctx, &[SymId], &[(IrRef<'ir>, IrRef<'ir>, TextRange)]) -> Result>, { - let mut pending = PendingAttrSet::new(span); + let mut pending = PendingAttrSet::new_in(ctx.bump(), span); pending.collect_entries(entries.iter().cloned(), ctx)?; let binding_syms = collect_binding_syms::<_, ALLOW_DYN>(&pending, ctx)?; + let keys: Vec<'ir, _> = binding_syms.into_iter().collect_in(ctx.bump()); let inherit_lookups = collect_inherit_lookups(&entries, ctx)?; - let binding_keys: Vec<_> = binding_syms.into_iter().collect(); - let slots: Vec<_> = ctx.reserve_slots(binding_keys.len()).collect(); - let let_bindings: HashMap<_, _> = binding_keys - .iter() - .copied() - .zip(slots.iter().copied()) - .collect(); - - for &slot in &slots { - let slot_span = rnix::TextRange::default(); - ctx.replace_ir( - slot, - Thunk { - inner: slot, - span: slot_span, - } - .to_ir(), - ); - } - - ctx.with_let_scope(let_bindings.clone(), |ctx| { + ctx.with_let_scope(&keys, |ctx| { let finalized = finalize_pending_set::<_, ALLOW_DYN>(pending, &inherit_lookups, ctx)?; - - for (sym, slot) in binding_keys.iter().copied().zip(slots.iter()) { - if let Some(&(expr, _)) = finalized.stcs.get(&sym) { - ctx.register_thunk(*slot, expr); - } else { - return Err(Error::internal(format!( - "binding '{}' not found", - ctx.get_sym(sym) - ))); - } - } - - body_fn(ctx, &binding_keys, &finalized.dyns) + let vals = keys + .iter() + .map(|sym| finalized.stcs.get(sym).expect("WTF").0) + .collect_in(ctx.bump()); + body_fn(ctx, &keys, &finalized.dyns).map(|body| (vals, body)) }) } /// Collects `inherit x` lookups from the outer scope before entering the rec scope. -fn collect_inherit_lookups( +fn collect_inherit_lookups<'ir, Ctx: DowngradeContext<'ir>>( entries: &[ast::Entry], ctx: &mut Ctx, -) -> Result> { - let mut inherit_lookups = HashMap::new(); +) -> Result, TextRange)>> { + let mut inherit_lookups = HashMap::new_in(ctx.bump()); for entry in entries { if let ast::Entry::Inherit(inherit) = entry && inherit.from().is_none() @@ -1254,7 +1247,7 @@ fn collect_inherit_lookups( } /// Collects binding symbols from a pending set, checking for duplicates. -fn collect_binding_syms( +fn collect_binding_syms<'ir, Ctx: DowngradeContext<'ir>, const ALLOW_DYN: bool>( pending: &PendingAttrSet, ctx: &mut Ctx, ) -> Result> { @@ -1275,13 +1268,13 @@ fn collect_binding_syms( /// Unified finalize function for PendingAttrSet. /// ALLOW_DYN controls whether dynamic attributes are allowed. -fn finalize_pending_set( +fn finalize_pending_set<'ir, Ctx: DowngradeContext<'ir>, const ALLOW_DYN: bool>( pending: PendingAttrSet, - inherit_lookups: &HashMap, + inherit_lookups: &HashMap, TextRange)>, ctx: &mut Ctx, -) -> Result { - let mut stcs = HashMap::new(); - let mut dyns = Vec::new(); +) -> Result> { + let mut stcs = HashMap::new_in(ctx.bump()); + let mut dyns = Vec::new_in(ctx.bump()); for (sym, (value, value_span)) in pending.stcs { let expr_id = finalize_pending_value::<_, ALLOW_DYN>(value, inherit_lookups, ctx)?; @@ -1295,7 +1288,7 @@ fn finalize_pending_set( Attr::Dynamic(id, _) => id, Attr::Str(sym, attr_span) => ctx.new_expr( Str { - val: ctx.get_sym(sym).to_string(), + inner: ctx.get_sym(sym).to_string().box_in(ctx.bump()), span: attr_span, } .to_ir(), @@ -1315,11 +1308,11 @@ fn finalize_pending_set( /// Unified finalize function for PendingValue. /// ALLOW_DYN controls whether dynamic attributes are allowed. -fn finalize_pending_value( +fn finalize_pending_value<'ir, Ctx: DowngradeContext<'ir>, const ALLOW_DYN: bool>( value: PendingValue, - inherit_lookups: &HashMap, + inherit_lookups: &HashMap, TextRange)>, ctx: &mut Ctx, -) -> Result { +) -> Result> { match value { PendingValue::Expr(expr) => { let id = Downgrade::downgrade(expr, ctx)?; @@ -1329,7 +1322,7 @@ fn finalize_pending_value( let from_id = Downgrade::downgrade(from_expr, ctx)?; let select = Select { expr: from_id, - attrpath: vec![Attr::Str(sym, span)], + attrpath: Vec::from_iter_in([Attr::Str(sym, span)], ctx.bump()), default: None, span, }; @@ -1353,24 +1346,25 @@ fn finalize_pending_value( extensions, span, } => { - let mut all_entries: Vec<_> = base.entries().collect(); + let mut all_entries: Vec<'ir, _> = base.entries().collect_in(ctx.bump()); all_entries.extend(extensions); downgrade_rec_bindings(all_entries, ctx, span) } } } -fn downgrade_path( +fn downgrade_path<'ir>( parts: impl IntoIterator>, span: rnix::TextRange, - ctx: &mut impl DowngradeContext, -) -> Result { + ctx: &mut impl DowngradeContext<'ir>, +) -> Result> { + let bump = ctx.bump(); let parts = parts .into_iter() .map(|part| match part { ast::InterpolPart::Literal(lit) => Ok(ctx.new_expr( Str { - val: lit.text().to_string(), + inner: lit.text().to_string().box_in(ctx.bump()), span: lit.syntax().text_range(), } .to_ir(), @@ -1380,17 +1374,18 @@ fn downgrade_path( .require(ctx, interpol.syntax().text_range())? .downgrade(ctx), }) - .collect::>>()?; - let expr = match parts.try_conv::<[_; 1]>() { - Ok([part]) => part, - Err(parts) => ctx.new_expr( + .collect_in::>>(bump)?; + let expr = if parts.len() == 1 { + parts.into_iter().next().expect("length checked") + } else { + ctx.new_expr( ConcatStrings { parts, span, force_string: false, } .to_ir(), - ), + ) }; Ok(ctx.new_expr(Path { expr, span }.to_ir())) } diff --git a/nix-js/src/ir.rs b/nix-js/src/ir.rs index 7bef915..bff7d40 100644 --- a/nix-js/src/ir.rs +++ b/nix-js/src/ir.rs @@ -1,46 +1,48 @@ -use derive_more::{IsVariant, TryUnwrap, Unwrap}; -use hashbrown::HashMap; +use bumpalo::{Bump, boxed::Box, collections::Vec}; use rnix::{TextRange, ast}; use string_interner::symbol::SymbolU32; use nix_js_macros::ir; +pub type HashMap<'ir, K, V> = hashbrown::HashMap; + +pub type IrRef<'ir> = &'ir Ir<'ir>; ir! { - Ir, + Ir<'ir>; Int(i64), Float(f64), Bool(bool), Null, - Str { pub val: String }, - AttrSet { pub stcs: HashMap, pub dyns: Vec<(ExprId, ExprId, rnix::TextRange)> }, - List { pub items: Vec }, + Str { inner: Box<'ir, String> }, + AttrSet { stcs: HashMap<'ir, SymId, (IrRef<'ir>, TextRange)>, dyns: Vec<'ir, (IrRef<'ir>, IrRef<'ir>, TextRange)> }, + List { items: Vec<'ir, IrRef<'ir>> }, - HasAttr { pub lhs: ExprId, pub rhs: Vec }, - BinOp { pub lhs: ExprId, pub rhs: ExprId, pub kind: BinOpKind }, - UnOp { pub rhs: ExprId, pub kind: UnOpKind }, - Select { pub expr: ExprId, pub attrpath: Vec, pub default: Option }, - If { pub cond: ExprId, pub consq: ExprId, pub alter: ExprId }, - Call { pub func: ExprId, pub arg: ExprId }, - Assert { pub assertion: ExprId, pub expr: ExprId, pub assertion_raw: String }, - ConcatStrings { pub parts: Vec, pub force_string: bool }, - Path { pub expr: ExprId }, - Func { pub body: ExprId, pub param: Option, pub arg: ExprId, pub thunks: Vec<(ExprId, ExprId)> }, - TopLevel { pub body: ExprId, pub thunks: Vec<(ExprId, ExprId)> }, + HasAttr { lhs: IrRef<'ir>, rhs: Vec<'ir, Attr<'ir>> }, + BinOp { lhs: IrRef<'ir>, rhs: IrRef<'ir>, kind: BinOpKind }, + UnOp { rhs: IrRef<'ir>, kind: UnOpKind }, + Select { expr: IrRef<'ir>, attrpath: Vec<'ir, Attr<'ir>>, default: Option> }, + If { cond: IrRef<'ir>, consq: IrRef<'ir>, alter: IrRef<'ir> }, + Call { func: IrRef<'ir>, arg: IrRef<'ir> }, + Assert { assertion: IrRef<'ir>, expr: IrRef<'ir>, assertion_raw: String }, + ConcatStrings { parts: Vec<'ir, IrRef<'ir>>, force_string: bool }, + Path { expr: IrRef<'ir> }, + Func { body: IrRef<'ir>, param: Option>, arg: IrRef<'ir>, thunks: Vec<'ir, (ThunkId, IrRef<'ir>)> }, + TopLevel { body: IrRef<'ir>, thunks: Vec<'ir, (ThunkId, IrRef<'ir>)> }, Arg(ArgId), - Thunk(ExprId), + Thunk(ThunkId), Builtins, Builtin(SymId), CurPos, ReplBinding(SymId), ScopedImportBinding(SymId), - WithExpr { pub namespace: ExprId, pub body: ExprId, pub thunks: Vec<(ExprId, ExprId)> }, + WithExpr { namespace: IrRef<'ir>, body: IrRef<'ir>, thunks: Vec<'ir, (ThunkId, IrRef<'ir>)> }, WithLookup(SymId), } #[repr(transparent)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct ExprId(pub usize); +pub struct ThunkId(pub usize); pub type SymId = SymbolU32; @@ -50,11 +52,11 @@ pub struct ArgId(pub usize); /// Represents a key in an attribute path. #[allow(unused)] -#[derive(Debug, TryUnwrap)] -pub enum Attr { +#[derive(Debug)] +pub enum Attr<'ir> { /// A dynamic attribute key, which is an expression that must evaluate to a string. /// Example: `attrs.${key}` - Dynamic(ExprId, TextRange), + Dynamic(IrRef<'ir>, TextRange), /// A static attribute key. /// Example: `attrs.key` Str(SymId, TextRange), @@ -135,8 +137,8 @@ impl From for UnOpKind { /// Describes the parameters of a function. #[derive(Debug)] -pub struct Param { - pub required: Vec<(SymId, TextRange)>, - pub optional: Vec<(SymId, TextRange)>, +pub struct Param<'ir> { + pub required: Vec<'ir, (SymId, TextRange)>, + pub optional: Vec<'ir, (SymId, TextRange)>, pub ellipsis: bool, } diff --git a/nix-js/src/nar.rs b/nix-js/src/nar.rs index 0f93242..4e38910 100644 --- a/nix-js/src/nar.rs +++ b/nix-js/src/nar.rs @@ -31,7 +31,7 @@ mod tests { use std::fs; use tempfile::TempDir; - #[test] + #[test_log::test] fn test_simple_file() { let temp = TempDir::new().unwrap(); let file_path = temp.path().join("test.txt"); @@ -46,7 +46,7 @@ mod tests { assert_eq!(hash.len(), 64); } - #[test] + #[test_log::test] fn test_directory() { let temp = TempDir::new().unwrap(); fs::write(temp.path().join("a.txt"), "aaa").unwrap(); diff --git a/nix-js/src/store/validation.rs b/nix-js/src/store/validation.rs index a2d3b73..6ea4a4b 100644 --- a/nix-js/src/store/validation.rs +++ b/nix-js/src/store/validation.rs @@ -81,7 +81,7 @@ pub fn validate_store_path(store_dir: &str, path: &str) -> Result<()> { mod tests { use super::*; - #[test] + #[test_log::test] fn test_valid_store_paths() { let store_dir = "/nix/store"; let valid_paths = vec![ @@ -100,7 +100,7 @@ mod tests { } } - #[test] + #[test_log::test] fn test_invalid_store_paths() { let store_dir = "/nix/store"; let invalid_paths = vec![ diff --git a/nix-js/tests/tests/basic_eval.rs b/nix-js/tests/tests/basic_eval.rs index 59a933c..828ab50 100644 --- a/nix-js/tests/tests/basic_eval.rs +++ b/nix-js/tests/tests/basic_eval.rs @@ -1,32 +1,32 @@ use crate::utils::{eval, eval_result}; use nix_js::value::Value; -#[test] +#[test_log::test] fn arithmetic() { assert_eq!(eval("1 + 1"), Value::Int(2)); } -#[test] +#[test_log::test] fn simple_function_application() { assert_eq!(eval("(x: x) 1"), Value::Int(1)); } -#[test] +#[test_log::test] fn curried_function() { assert_eq!(eval("(x: y: x - y) 2 1"), Value::Int(1)); } -#[test] +#[test_log::test] fn rec_attrset() { assert_eq!(eval("rec { b = a; a = 1; }.b"), Value::Int(1)); } -#[test] +#[test_log::test] fn let_binding() { assert_eq!(eval("let b = a; a = 1; in b"), Value::Int(1)); } -#[test] +#[test_log::test] fn fibonacci() { assert_eq!( eval( @@ -36,7 +36,7 @@ fn fibonacci() { ); } -#[test] +#[test_log::test] fn fixed_point_combinator() { assert_eq!( eval("((f: let x = f x; in x)(self: { x = 1; y = self.x + 1; })).y"), @@ -44,17 +44,17 @@ fn fixed_point_combinator() { ); } -#[test] +#[test_log::test] fn conditional_true() { assert_eq!(eval("if true then 1 else 0"), Value::Int(1)); } -#[test] +#[test_log::test] fn conditional_false() { assert_eq!(eval("if false then 1 else 0"), Value::Int(0)); } -#[test] +#[test_log::test] fn nested_let() { assert_eq!( eval("let x = 1; in let y = x + 1; z = y + 1; in z"), @@ -62,7 +62,7 @@ fn nested_let() { ); } -#[test] +#[test_log::test] fn rec_inherit_fails() { assert!(eval_result("{ inherit x; }").is_err()); } diff --git a/nix-js/tests/tests/builtins.rs b/nix-js/tests/tests/builtins.rs index 69b2325..c18dedb 100644 --- a/nix-js/tests/tests/builtins.rs +++ b/nix-js/tests/tests/builtins.rs @@ -2,29 +2,29 @@ use crate::utils::eval; use nix_js::value::{AttrSet, List, Value}; use std::collections::BTreeMap; -#[test] +#[test_log::test] fn builtins_accessible() { let result = eval("builtins"); assert!(matches!(result, Value::AttrSet(_))); } -#[test] +#[test_log::test] fn builtins_self_reference() { let result = eval("builtins.builtins"); assert!(matches!(result, Value::AttrSet(_))); } -#[test] +#[test_log::test] fn builtins_add() { assert_eq!(eval("builtins.add 1 2"), Value::Int(3)); } -#[test] +#[test_log::test] fn builtins_length() { assert_eq!(eval("builtins.length [1 2 3]"), Value::Int(3)); } -#[test] +#[test_log::test] fn builtins_map() { assert_eq!( eval("builtins.map (x: x * 2) [1 2 3]"), @@ -32,7 +32,7 @@ fn builtins_map() { ); } -#[test] +#[test_log::test] fn builtins_filter() { assert_eq!( eval("builtins.filter (x: x > 1) [1 2 3]"), @@ -40,7 +40,7 @@ fn builtins_filter() { ); } -#[test] +#[test_log::test] fn builtins_attrnames() { let result = eval("builtins.attrNames { a = 1; b = 2; }"); assert!(matches!(result, Value::List(_))); @@ -49,12 +49,12 @@ fn builtins_attrnames() { } } -#[test] +#[test_log::test] fn builtins_head() { assert_eq!(eval("builtins.head [1 2 3]"), Value::Int(1)); } -#[test] +#[test_log::test] fn builtins_tail() { assert_eq!( eval("builtins.tail [1 2 3]"), @@ -62,17 +62,17 @@ fn builtins_tail() { ); } -#[test] +#[test_log::test] fn builtins_in_let() { assert_eq!(eval("let b = builtins; in b.add 5 3"), Value::Int(8)); } -#[test] +#[test_log::test] fn builtins_in_with() { assert_eq!(eval("with builtins; add 10 20"), Value::Int(30)); } -#[test] +#[test_log::test] fn builtins_nested_calls() { assert_eq!( eval("builtins.add (builtins.mul 2 3) (builtins.sub 10 5)"), @@ -80,32 +80,32 @@ fn builtins_nested_calls() { ); } -#[test] +#[test_log::test] fn builtins_is_list() { assert_eq!(eval("builtins.isList [1 2 3]"), Value::Bool(true)); } -#[test] +#[test_log::test] fn builtins_is_attrs() { assert_eq!(eval("builtins.isAttrs { a = 1; }"), Value::Bool(true)); } -#[test] +#[test_log::test] fn builtins_is_function() { assert_eq!(eval("builtins.isFunction (x: x)"), Value::Bool(true)); } -#[test] +#[test_log::test] fn builtins_is_null() { assert_eq!(eval("builtins.isNull null"), Value::Bool(true)); } -#[test] +#[test_log::test] fn builtins_is_bool() { assert_eq!(eval("builtins.isBool true"), Value::Bool(true)); } -#[test] +#[test_log::test] fn builtins_shadowing() { assert_eq!( eval("let builtins = { add = x: y: x - y; }; in builtins.add 5 3"), @@ -113,13 +113,13 @@ fn builtins_shadowing() { ); } -#[test] +#[test_log::test] fn builtins_lazy_evaluation() { let result = eval("builtins.builtins.builtins.add 1 1"); assert_eq!(result, Value::Int(2)); } -#[test] +#[test_log::test] fn builtins_foldl() { assert_eq!( eval("builtins.foldl' (acc: x: acc + x) 0 [1 2 3 4 5]"), @@ -127,13 +127,13 @@ fn builtins_foldl() { ); } -#[test] +#[test_log::test] fn builtins_elem() { assert_eq!(eval("builtins.elem 2 [1 2 3]"), Value::Bool(true)); assert_eq!(eval("builtins.elem 5 [1 2 3]"), Value::Bool(false)); } -#[test] +#[test_log::test] fn builtins_concat_lists() { assert_eq!( eval("builtins.concatLists [[1 2] [3 4] [5]]"), @@ -147,7 +147,7 @@ fn builtins_concat_lists() { ); } -#[test] +#[test_log::test] fn builtins_compare_versions_basic() { assert_eq!( eval("builtins.compareVersions \"1.0\" \"2.3\""), @@ -171,7 +171,7 @@ fn builtins_compare_versions_basic() { ); } -#[test] +#[test_log::test] fn builtins_compare_versions_components() { assert_eq!( eval("builtins.compareVersions \"2.3.1\" \"2.3\""), @@ -183,7 +183,7 @@ fn builtins_compare_versions_components() { ); } -#[test] +#[test_log::test] fn builtins_compare_versions_numeric_vs_alpha() { // Numeric component comes before alpha component assert_eq!( @@ -196,7 +196,7 @@ fn builtins_compare_versions_numeric_vs_alpha() { ); } -#[test] +#[test_log::test] fn builtins_compare_versions_pre() { // "pre" is special: comes before everything except another "pre" assert_eq!( @@ -217,7 +217,7 @@ fn builtins_compare_versions_pre() { ); } -#[test] +#[test_log::test] fn builtins_compare_versions_alpha() { // Alphabetic comparison assert_eq!( @@ -230,7 +230,7 @@ fn builtins_compare_versions_alpha() { ); } -#[test] +#[test_log::test] fn builtins_compare_versions_symmetry() { // Test symmetry: compareVersions(a, b) == -compareVersions(b, a) assert_eq!( @@ -243,7 +243,7 @@ fn builtins_compare_versions_symmetry() { ); } -#[test] +#[test_log::test] fn builtins_compare_versions_complex() { // Complex version strings with multiple components assert_eq!( @@ -260,7 +260,7 @@ fn builtins_compare_versions_complex() { ); } -#[test] +#[test_log::test] fn builtins_generic_closure() { assert_eq!( eval( @@ -276,7 +276,7 @@ fn builtins_generic_closure() { ); } -#[test] +#[test_log::test] fn builtins_function_args() { assert_eq!( eval("builtins.functionArgs (x: 1)"), @@ -313,7 +313,7 @@ fn builtins_function_args() { ); } -#[test] +#[test_log::test] fn builtins_parse_drv_name() { let result = eval(r#"builtins.parseDrvName "nix-js-0.1.0pre""#).unwrap_attr_set(); assert_eq!(result.get("name"), Some(&Value::String("nix-js".into()))); diff --git a/nix-js/tests/tests/builtins_store.rs b/nix-js/tests/tests/builtins_store.rs index 95fdf54..58127e1 100644 --- a/nix-js/tests/tests/builtins_store.rs +++ b/nix-js/tests/tests/builtins_store.rs @@ -1,7 +1,7 @@ use crate::utils::eval_result; use nix_js::value::Value; -#[test] +#[test_log::test] fn to_file_simple() { let result = eval_result(r#"builtins.toFile "hello.txt" "Hello, World!""#).expect("Failed to evaluate"); @@ -18,7 +18,7 @@ fn to_file_simple() { } } -#[test] +#[test_log::test] fn to_file_with_references() { let result = eval_result( r#" @@ -41,7 +41,7 @@ fn to_file_with_references() { } } -#[test] +#[test_log::test] fn to_file_invalid_name_with_slash() { let result = eval_result(r#"builtins.toFile "foo/bar.txt" "content""#); @@ -54,7 +54,7 @@ fn to_file_invalid_name_with_slash() { ); } -#[test] +#[test_log::test] fn to_file_invalid_name_dot() { let result = eval_result(r#"builtins.toFile "." "content""#); @@ -62,7 +62,7 @@ fn to_file_invalid_name_dot() { assert!(result.unwrap_err().to_string().contains("invalid name")); } -#[test] +#[test_log::test] fn to_file_invalid_name_dotdot() { let result = eval_result(r#"builtins.toFile ".." "content""#); @@ -70,7 +70,7 @@ fn to_file_invalid_name_dotdot() { assert!(result.unwrap_err().to_string().contains("invalid name")); } -#[test] +#[test_log::test] fn store_path_validation_not_in_store() { let result = eval_result(r#"builtins.storePath "/tmp/foo""#); @@ -83,7 +83,7 @@ fn store_path_validation_not_in_store() { ); } -#[test] +#[test_log::test] fn store_path_validation_malformed_hash() { let dummy_file_result = eval_result(r#"builtins.toFile "dummy.txt" "content""#) .expect("Failed to create dummy file"); @@ -111,7 +111,7 @@ fn store_path_validation_malformed_hash() { ); } -#[test] +#[test_log::test] fn store_path_validation_missing_name() { let dummy_file_result = eval_result(r#"builtins.toFile "dummy.txt" "content""#) .expect("Failed to create dummy file"); @@ -139,7 +139,7 @@ fn store_path_validation_missing_name() { ); } -#[test] +#[test_log::test] fn to_file_curried_application() { let result = eval_result( r#" @@ -161,7 +161,7 @@ fn to_file_curried_application() { } } -#[test] +#[test_log::test] fn to_file_number_conversion() { let result = eval_result(r#"builtins.toFile "number.txt" (builtins.toString 42)"#) .expect("Failed to evaluate"); @@ -175,7 +175,7 @@ fn to_file_number_conversion() { } } -#[test] +#[test_log::test] fn to_file_list_conversion() { let result = eval_result( r#"builtins.toFile "list.txt" (builtins.concatStringsSep "\n" ["line1" "line2" "line3"])"#, diff --git a/nix-js/tests/tests/derivation.rs b/nix-js/tests/tests/derivation.rs index 378639d..5509622 100644 --- a/nix-js/tests/tests/derivation.rs +++ b/nix-js/tests/tests/derivation.rs @@ -1,7 +1,7 @@ use crate::utils::{eval_deep, eval_deep_result}; use nix_js::value::Value; -#[test] +#[test_log::test] fn add_operator_preserves_derivation_context() { let result = eval_deep( r#" @@ -37,7 +37,7 @@ fn add_operator_preserves_derivation_context() { assert_eq!(result, nix_result); } -#[test] +#[test_log::test] fn derivation_minimal() { let result = eval_deep( r#"derivation { name = "hello"; builder = "/bin/sh"; system = "x86_64-linux"; }"#, @@ -75,7 +75,7 @@ fn derivation_minimal() { } } -#[test] +#[test_log::test] fn derivation_with_args() { let result = eval_deep( r#"derivation { @@ -97,7 +97,7 @@ fn derivation_with_args() { } } -#[test] +#[test_log::test] fn derivation_to_string() { let result = eval_deep( r#"toString (derivation { name = "foo"; builder = "/bin/sh"; system = "x86_64-linux"; })"#, @@ -109,7 +109,7 @@ fn derivation_to_string() { } } -#[test] +#[test_log::test] fn derivation_missing_name() { let result = eval_deep_result(r#"derivation { builder = "/bin/sh"; system = "x86_64-linux"; }"#); @@ -119,7 +119,7 @@ fn derivation_missing_name() { assert!(err_msg.contains("missing required attribute 'name'")); } -#[test] +#[test_log::test] fn derivation_invalid_name_with_drv_suffix() { let result = eval_deep_result( r#"derivation { name = "foo.drv"; builder = "/bin/sh"; system = "x86_64-linux"; }"#, @@ -130,7 +130,7 @@ fn derivation_invalid_name_with_drv_suffix() { assert!(err_msg.contains("cannot end with .drv")); } -#[test] +#[test_log::test] fn derivation_missing_builder() { let result = eval_deep_result(r#"derivation { name = "test"; system = "x86_64-linux"; }"#); @@ -139,7 +139,7 @@ fn derivation_missing_builder() { assert!(err_msg.contains("missing required attribute 'builder'")); } -#[test] +#[test_log::test] fn derivation_missing_system() { let result = eval_deep_result(r#"derivation { name = "test"; builder = "/bin/sh"; }"#); @@ -148,7 +148,7 @@ fn derivation_missing_system() { assert!(err_msg.contains("missing required attribute 'system'")); } -#[test] +#[test_log::test] fn derivation_with_env_vars() { let result = eval_deep( r#"derivation { @@ -169,7 +169,7 @@ fn derivation_with_env_vars() { } } -#[test] +#[test_log::test] fn derivation_strict() { let result = eval_deep( r#"builtins.derivationStrict { name = "test"; builder = "/bin/sh"; system = "x86_64-linux"; }"#, @@ -186,7 +186,7 @@ fn derivation_strict() { } } -#[test] +#[test_log::test] fn derivation_deterministic_paths() { let expr = r#"derivation { name = "hello"; builder = "/bin/sh"; system = "x86_64-linux"; }"#; @@ -202,7 +202,7 @@ fn derivation_deterministic_paths() { } } -#[test] +#[test_log::test] fn derivation_escaping_in_aterm() { let result = eval_deep( r#"derivation { @@ -222,7 +222,7 @@ fn derivation_escaping_in_aterm() { } } -#[test] +#[test_log::test] fn multi_output_two_outputs() { let drv = eval_deep( r#"derivation { @@ -265,7 +265,7 @@ fn multi_output_two_outputs() { } } -#[test] +#[test_log::test] fn multi_output_three_outputs() { let result = eval_deep( r#"derivation { @@ -313,7 +313,7 @@ fn multi_output_three_outputs() { } } -#[test] +#[test_log::test] fn multi_output_backward_compat() { let result = eval_deep( r#"derivation { @@ -339,7 +339,7 @@ fn multi_output_backward_compat() { } } -#[test] +#[test_log::test] fn multi_output_deterministic() { let result1 = eval_deep( r#"derivation { @@ -362,7 +362,7 @@ fn multi_output_deterministic() { assert_eq!(result1, result2); } -#[test] +#[test_log::test] fn fixed_output_sha256_flat() { let result = eval_deep( r#"derivation { @@ -399,7 +399,7 @@ fn fixed_output_sha256_flat() { } } -#[test] +#[test_log::test] fn fixed_output_missing_hashalgo() { assert!( eval_deep_result( @@ -414,7 +414,7 @@ fn fixed_output_missing_hashalgo() { ); } -#[test] +#[test_log::test] fn fixed_output_recursive_mode() { let result = eval_deep( r#"derivation { @@ -444,7 +444,7 @@ fn fixed_output_recursive_mode() { } } -#[test] +#[test_log::test] fn fixed_output_rejects_multi_output() { let result = eval_deep_result( r#"derivation { @@ -462,7 +462,7 @@ fn fixed_output_rejects_multi_output() { assert!(err_msg.contains("fixed-output") && err_msg.contains("one")); } -#[test] +#[test_log::test] fn fixed_output_invalid_hash_mode() { let result = eval_deep_result( r#"derivation { @@ -479,7 +479,7 @@ fn fixed_output_invalid_hash_mode() { assert!(err_msg.contains("outputHashMode") && err_msg.contains("invalid")); } -#[test] +#[test_log::test] fn structured_attrs_basic() { let result = eval_deep( r#"derivation { @@ -504,7 +504,7 @@ fn structured_attrs_basic() { } } -#[test] +#[test_log::test] fn structured_attrs_nested() { let result = eval_deep( r#"derivation { @@ -525,7 +525,7 @@ fn structured_attrs_nested() { } } -#[test] +#[test_log::test] fn structured_attrs_rejects_functions() { let result = eval_deep_result( r#"derivation { @@ -542,7 +542,7 @@ fn structured_attrs_rejects_functions() { assert!(err_msg.contains("cannot convert lambda to JSON")); } -#[test] +#[test_log::test] fn structured_attrs_false() { let result = eval_deep( r#"derivation { @@ -565,7 +565,7 @@ fn structured_attrs_false() { } } -#[test] +#[test_log::test] fn ignore_nulls_true() { let result = eval_deep( r#"derivation { @@ -587,7 +587,7 @@ fn ignore_nulls_true() { } } -#[test] +#[test_log::test] fn ignore_nulls_false() { let result = eval_deep( r#"derivation { @@ -610,7 +610,7 @@ fn ignore_nulls_false() { } } -#[test] +#[test_log::test] fn ignore_nulls_with_structured_attrs() { let result = eval_deep( r#"derivation { @@ -634,7 +634,7 @@ fn ignore_nulls_with_structured_attrs() { } } -#[test] +#[test_log::test] fn all_features_combined() { let result = eval_deep( r#"derivation { @@ -661,7 +661,7 @@ fn all_features_combined() { } } -#[test] +#[test_log::test] fn fixed_output_with_structured_attrs() { let result = eval_deep( r#"derivation { diff --git a/nix-js/tests/tests/findfile.rs b/nix-js/tests/tests/findfile.rs index 19586d0..e4818df 100644 --- a/nix-js/tests/tests/findfile.rs +++ b/nix-js/tests/tests/findfile.rs @@ -1,6 +1,6 @@ use crate::utils::eval; -#[test] +#[test_log::test] fn test_find_file_corepkg_fetchurl() { let result = eval( r#" @@ -15,13 +15,13 @@ fn test_find_file_corepkg_fetchurl() { assert!(result.to_string().contains("fetchurl.nix")); } -#[test] +#[test_log::test] fn test_lookup_path_syntax() { let result = eval(r#""#); assert!(result.to_string().contains("fetchurl.nix")); } -#[test] +#[test_log::test] fn test_import_corepkg() { let result = eval( r#" diff --git a/nix-js/tests/tests/free_globals.rs b/nix-js/tests/tests/free_globals.rs index ca19ab9..8e23396 100644 --- a/nix-js/tests/tests/free_globals.rs +++ b/nix-js/tests/tests/free_globals.rs @@ -1,22 +1,22 @@ use crate::utils::{eval, eval_result}; use nix_js::value::{List, Value}; -#[test] +#[test_log::test] fn true_literal() { assert_eq!(eval("true"), Value::Bool(true)); } -#[test] +#[test_log::test] fn false_literal() { assert_eq!(eval("false"), Value::Bool(false)); } -#[test] +#[test_log::test] fn null_literal() { assert_eq!(eval("null"), Value::Null); } -#[test] +#[test_log::test] fn map_function() { assert_eq!( eval("map (x: x * 2) [1 2 3]"), @@ -24,23 +24,23 @@ fn map_function() { ); } -#[test] +#[test_log::test] fn is_null_function() { assert_eq!(eval("isNull null"), Value::Bool(true)); assert_eq!(eval("isNull 5"), Value::Bool(false)); } -#[test] +#[test_log::test] fn shadow_true() { assert_eq!(eval("let true = false; in true"), Value::Bool(false)); } -#[test] +#[test_log::test] fn shadow_map() { assert_eq!(eval("let map = x: y: x; in map 1 2"), Value::Int(1)); } -#[test] +#[test_log::test] fn mixed_usage() { assert_eq!( eval("if true then map (x: x + 1) [1 2] else []"), @@ -48,7 +48,7 @@ fn mixed_usage() { ); } -#[test] +#[test_log::test] fn in_let_bindings() { assert_eq!( eval("let x = true; y = false; in x && y"), @@ -56,18 +56,18 @@ fn in_let_bindings() { ); } -#[test] +#[test_log::test] fn shadow_in_function() { assert_eq!(eval("(true: true) false"), Value::Bool(false)); } -#[test] +#[test_log::test] fn throw_function() { let result = eval_result("throw \"error message\""); assert!(result.is_err()); } -#[test] +#[test_log::test] fn to_string_function() { assert_eq!(eval("toString 42"), Value::String("42".to_string())); } diff --git a/nix-js/tests/tests/functions.rs b/nix-js/tests/tests/functions.rs index 6368e08..b3a06b1 100644 --- a/nix-js/tests/tests/functions.rs +++ b/nix-js/tests/tests/functions.rs @@ -1,18 +1,18 @@ use crate::utils::{eval, eval_result}; use nix_js::value::Value; -#[test] +#[test_log::test] fn required_parameters() { assert_eq!(eval("({ a, b }: a + b) { a = 1; b = 2; }"), Value::Int(3)); } -#[test] +#[test_log::test] fn missing_required_parameter() { let result = eval_result("({ a, b }: a + b) { a = 1; }"); assert!(result.is_err()); } -#[test] +#[test_log::test] fn all_required_parameters_present() { assert_eq!( eval("({ x, y, z }: x + y + z) { x = 1; y = 2; z = 3; }"), @@ -20,13 +20,13 @@ fn all_required_parameters_present() { ); } -#[test] +#[test_log::test] fn reject_unexpected_arguments() { let result = eval_result("({ a, b }: a + b) { a = 1; b = 2; c = 3; }"); assert!(result.is_err()); } -#[test] +#[test_log::test] fn ellipsis_accepts_extra_arguments() { assert_eq!( eval("({ a, b, ... }: a + b) { a = 1; b = 2; c = 3; }"), @@ -34,12 +34,12 @@ fn ellipsis_accepts_extra_arguments() { ); } -#[test] +#[test_log::test] fn default_parameters() { assert_eq!(eval("({ a, b ? 5 }: a + b) { a = 1; }"), Value::Int(6)); } -#[test] +#[test_log::test] fn override_default_parameter() { assert_eq!( eval("({ a, b ? 5 }: a + b) { a = 1; b = 10; }"), @@ -47,7 +47,7 @@ fn override_default_parameter() { ); } -#[test] +#[test_log::test] fn at_pattern_alias() { assert_eq!( eval("(args@{ a, b }: args.a + args.b) { a = 1; b = 2; }"), @@ -55,17 +55,17 @@ fn at_pattern_alias() { ); } -#[test] +#[test_log::test] fn simple_parameter_no_validation() { assert_eq!(eval("(x: x.a + x.b) { a = 1; b = 2; }"), Value::Int(3)); } -#[test] +#[test_log::test] fn simple_parameter_accepts_any_argument() { assert_eq!(eval("(x: x) 42"), Value::Int(42)); } -#[test] +#[test_log::test] fn nested_function_parameters() { assert_eq!( eval("({ a }: { b }: a + b) { a = 5; } { b = 3; }"), @@ -73,12 +73,12 @@ fn nested_function_parameters() { ); } -#[test] +#[test_log::test] fn pattern_param_simple_reference_in_default() { assert_eq!(eval("({ a, b ? a }: b) { a = 10; }"), Value::Int(10)); } -#[test] +#[test_log::test] fn pattern_param_multiple_references_in_default() { assert_eq!( eval("({ a, b ? a + 5, c ? 1 }: b + c) { a = 10; }"), @@ -86,7 +86,7 @@ fn pattern_param_multiple_references_in_default() { ); } -#[test] +#[test_log::test] fn pattern_param_mutual_reference() { assert_eq!( eval("({ a, b ? c + 1, c ? 5 }: b) { a = 1; }"), @@ -94,7 +94,7 @@ fn pattern_param_mutual_reference() { ); } -#[test] +#[test_log::test] fn pattern_param_override_mutual_reference() { assert_eq!( eval("({ a, b ? c + 1, c ? 5 }: b) { a = 1; c = 10; }"), @@ -102,7 +102,7 @@ fn pattern_param_override_mutual_reference() { ); } -#[test] +#[test_log::test] fn pattern_param_reference_list() { assert_eq!( eval("({ a, b ? [ a 2 ] }: builtins.elemAt b 0) { a = 42; }"), @@ -110,7 +110,7 @@ fn pattern_param_reference_list() { ); } -#[test] +#[test_log::test] fn pattern_param_alias_in_default() { assert_eq!( eval("(args@{ a, b ? args.a + 10 }: b) { a = 5; }"), diff --git a/nix-js/tests/tests/io_operations.rs b/nix-js/tests/tests/io_operations.rs index abbc812..dd85770 100644 --- a/nix-js/tests/tests/io_operations.rs +++ b/nix-js/tests/tests/io_operations.rs @@ -3,7 +3,7 @@ use nix_js::context::Context; use nix_js::error::Source; use nix_js::value::Value; -#[test] +#[test_log::test] fn import_absolute_path() { let temp_dir = tempfile::tempdir().unwrap(); let lib_path = temp_dir.path().join("nix_test_lib.nix"); @@ -14,7 +14,7 @@ fn import_absolute_path() { assert_eq!(eval(&expr), Value::Int(8)); } -#[test] +#[test_log::test] fn import_nested() { let temp_dir = tempfile::tempdir().unwrap(); @@ -32,7 +32,7 @@ fn import_nested() { assert_eq!(eval(&expr), Value::Int(30)); } -#[test] +#[test_log::test] fn import_relative_path() { let temp_dir = tempfile::tempdir().unwrap(); let subdir = temp_dir.path().join("subdir"); @@ -63,7 +63,7 @@ fn import_relative_path() { assert_eq!(eval(&expr), Value::Int(7)); } -#[test] +#[test_log::test] fn import_returns_function() { let temp_dir = tempfile::tempdir().unwrap(); let func_path = temp_dir.path().join("nix_test_func.nix"); @@ -73,7 +73,7 @@ fn import_returns_function() { assert_eq!(eval(&expr), Value::Int(10)); } -#[test] +#[test_log::test] fn import_with_complex_dependency_graph() { let temp_dir = tempfile::tempdir().unwrap(); @@ -94,7 +94,7 @@ fn import_with_complex_dependency_graph() { // Tests for builtins.path -#[test] +#[test_log::test] fn path_with_file() { let mut ctx = Context::new().unwrap(); let temp_dir = tempfile::tempdir().unwrap(); @@ -113,7 +113,7 @@ fn path_with_file() { } } -#[test] +#[test_log::test] fn path_with_custom_name() { let temp_dir = tempfile::tempdir().unwrap(); let test_file = temp_dir.path().join("original.txt"); @@ -133,7 +133,7 @@ fn path_with_custom_name() { } } -#[test] +#[test_log::test] fn path_with_directory_recursive() { let mut ctx = Context::new().unwrap(); let temp_dir = tempfile::tempdir().unwrap(); @@ -156,7 +156,7 @@ fn path_with_directory_recursive() { } } -#[test] +#[test_log::test] fn path_flat_with_file() { let mut ctx = Context::new().unwrap(); let temp_dir = tempfile::tempdir().unwrap(); @@ -176,7 +176,7 @@ fn path_flat_with_file() { } } -#[test] +#[test_log::test] fn path_flat_with_directory_fails() { let temp_dir = tempfile::tempdir().unwrap(); let test_dir = temp_dir.path().join("mydir"); @@ -193,7 +193,7 @@ fn path_flat_with_directory_fails() { assert!(err_msg.contains("recursive") || err_msg.contains("regular file")); } -#[test] +#[test_log::test] fn path_nonexistent_fails() { let expr = r#"builtins.path { path = "/nonexistent/path/that/should/not/exist"; }"#; let result = eval_result(expr); @@ -203,7 +203,7 @@ fn path_nonexistent_fails() { assert!(err_msg.contains("does not exist")); } -#[test] +#[test_log::test] fn path_missing_path_param() { let expr = r#"builtins.path { name = "test"; }"#; let result = eval_result(expr); @@ -213,7 +213,7 @@ fn path_missing_path_param() { assert!(err_msg.contains("path") && err_msg.contains("required")); } -#[test] +#[test_log::test] fn path_with_sha256() { let temp_dir = tempfile::tempdir().unwrap(); let test_file = temp_dir.path().join("hash_test.txt"); @@ -240,7 +240,7 @@ fn path_with_sha256() { assert_eq!(store_path1, store_path2); } -#[test] +#[test_log::test] fn path_deterministic() { let temp_dir = tempfile::tempdir().unwrap(); let test_file = temp_dir.path().join("deterministic.txt"); @@ -258,7 +258,7 @@ fn path_deterministic() { assert_eq!(result1, result2); } -#[test] +#[test_log::test] fn read_file_type_regular_file() { let temp_dir = tempfile::tempdir().unwrap(); let test_file = temp_dir.path().join("test.txt"); @@ -268,7 +268,7 @@ fn read_file_type_regular_file() { assert_eq!(eval(&expr), Value::String("regular".to_string())); } -#[test] +#[test_log::test] fn read_file_type_directory() { let temp_dir = tempfile::tempdir().unwrap(); let test_dir = temp_dir.path().join("testdir"); @@ -278,7 +278,7 @@ fn read_file_type_directory() { assert_eq!(eval(&expr), Value::String("directory".to_string())); } -#[test] +#[test_log::test] fn read_file_type_symlink() { let temp_dir = tempfile::tempdir().unwrap(); let target = temp_dir.path().join("target.txt"); @@ -296,7 +296,7 @@ fn read_file_type_symlink() { } } -#[test] +#[test_log::test] fn read_dir_basic() { let temp_dir = tempfile::tempdir().unwrap(); let test_dir = temp_dir.path().join("readdir_test"); @@ -328,7 +328,7 @@ fn read_dir_basic() { } } -#[test] +#[test_log::test] fn read_dir_empty() { let temp_dir = tempfile::tempdir().unwrap(); let test_dir = temp_dir.path().join("empty_dir"); @@ -344,7 +344,7 @@ fn read_dir_empty() { } } -#[test] +#[test_log::test] fn read_dir_nonexistent_fails() { let expr = r#"builtins.readDir "/nonexistent/directory""#; let result = eval_result(expr); @@ -352,7 +352,7 @@ fn read_dir_nonexistent_fails() { assert!(result.is_err()); } -#[test] +#[test_log::test] fn read_dir_on_file_fails() { let temp_dir = tempfile::tempdir().unwrap(); let test_file = temp_dir.path().join("test.txt"); diff --git a/nix-js/tests/tests/lang.rs b/nix-js/tests/tests/lang.rs index 91d7b32..b1597f3 100644 --- a/nix-js/tests/tests/lang.rs +++ b/nix-js/tests/tests/lang.rs @@ -42,7 +42,7 @@ fn format_value(value: &Value) -> String { macro_rules! eval_okay_test { ($(#[$attr:meta])* $name:ident$(, $pre:expr)?) => { $(#[$attr])* - #[test] + #[test_log::test] fn $name() { $(($pre)();)? let test_name = concat!("eval-okay-", stringify!($name)) @@ -74,7 +74,7 @@ macro_rules! eval_okay_test { macro_rules! eval_fail_test { ($name:ident) => { - #[test] + #[test_log::test] fn $name() { let test_name = concat!("eval-fail-", stringify!($name)) .replace("_", "-") diff --git a/nix-js/tests/tests/numeric_types.rs b/nix-js/tests/tests/numeric_types.rs index d61bbfa..4b0c3f2 100644 --- a/nix-js/tests/tests/numeric_types.rs +++ b/nix-js/tests/tests/numeric_types.rs @@ -1,12 +1,12 @@ use crate::utils::eval; use nix_js::value::Value; -#[test] +#[test_log::test] fn large_i64_max() { assert_eq!(eval("9223372036854775807"), Value::Int(9223372036854775807)); } -#[test] +#[test_log::test] fn large_i64_negative() { assert_eq!( eval("-9223372036854775807"), @@ -14,7 +14,7 @@ fn large_i64_negative() { ); } -#[test] +#[test_log::test] fn large_number_arithmetic() { assert_eq!( eval("5000000000000000000 + 3000000000000000000"), @@ -22,33 +22,33 @@ fn large_number_arithmetic() { ); } -#[test] +#[test_log::test] fn is_int_with_int() { assert_eq!(eval("builtins.isInt 42"), Value::Bool(true)); } -#[test] +#[test_log::test] fn is_int_with_float() { assert_eq!(eval("builtins.isInt 42.0"), Value::Bool(false)); } -#[test] +#[test_log::test] fn is_float_with_int() { assert_eq!(eval("builtins.isFloat 42"), Value::Bool(false)); } -#[test] +#[test_log::test] fn is_float_with_float() { assert_eq!(eval("builtins.isFloat 42.5"), Value::Bool(true)); assert_eq!(eval("builtins.isFloat 1.0"), Value::Bool(true)); } -#[test] +#[test_log::test] fn typeof_int() { assert_eq!(eval("builtins.typeOf 1"), Value::String("int".to_string())); } -#[test] +#[test_log::test] fn typeof_float() { assert_eq!( eval("builtins.typeOf 1.0"), @@ -60,17 +60,17 @@ fn typeof_float() { ); } -#[test] +#[test_log::test] fn int_literal() { assert_eq!(eval("1"), Value::Int(1)); } -#[test] +#[test_log::test] fn float_literal() { assert_eq!(eval("1."), Value::Float(1.)); } -#[test] +#[test_log::test] fn int_plus_int() { assert_eq!( eval("builtins.typeOf (1 + 2)"), @@ -78,7 +78,7 @@ fn int_plus_int() { ); } -#[test] +#[test_log::test] fn int_plus_float() { assert_eq!( eval("builtins.typeOf (1 + 2.0)"), @@ -86,7 +86,7 @@ fn int_plus_float() { ); } -#[test] +#[test_log::test] fn int_times_int() { assert_eq!( eval("builtins.typeOf (3 * 4)"), @@ -94,7 +94,7 @@ fn int_times_int() { ); } -#[test] +#[test_log::test] fn int_times_float() { assert_eq!( eval("builtins.typeOf (3 * 4.0)"), @@ -102,25 +102,25 @@ fn int_times_float() { ); } -#[test] +#[test_log::test] fn integer_division() { assert_eq!(eval("5 / 2"), Value::Int(2)); assert_eq!(eval("7 / 3"), Value::Int(2)); assert_eq!(eval("10 / 3"), Value::Int(3)); } -#[test] +#[test_log::test] fn float_division() { assert_eq!(eval("5 / 2.0"), Value::Float(2.5)); assert_eq!(eval("7.0 / 2"), Value::Float(3.5)); } -#[test] +#[test_log::test] fn negative_integer_division() { assert_eq!(eval("(-7) / 3"), Value::Int(-2)); } -#[test] +#[test_log::test] fn builtin_add_with_large_numbers() { assert_eq!( eval("builtins.add 5000000000000000000 3000000000000000000"), @@ -128,7 +128,7 @@ fn builtin_add_with_large_numbers() { ); } -#[test] +#[test_log::test] fn builtin_mul_with_large_numbers() { assert_eq!( eval("builtins.mul 1000000000 1000000000"), diff --git a/nix-js/tests/tests/operators.rs b/nix-js/tests/tests/operators.rs index b815a03..7adddee 100644 --- a/nix-js/tests/tests/operators.rs +++ b/nix-js/tests/tests/operators.rs @@ -2,67 +2,67 @@ use crate::utils::eval; use nix_js::value::{AttrSet, List, Symbol, Value}; use std::collections::BTreeMap; -#[test] +#[test_log::test] fn addition() { assert_eq!(eval("1 + 1"), Value::Int(2)); } -#[test] +#[test_log::test] fn subtraction() { assert_eq!(eval("2 - 1"), Value::Int(1)); } -#[test] +#[test_log::test] fn multiplication() { assert_eq!(eval("1. * 1"), Value::Float(1.)); } -#[test] +#[test_log::test] fn division() { assert_eq!(eval("1 / 1."), Value::Float(1.)); } -#[test] +#[test_log::test] fn equality() { assert_eq!(eval("1 == 1"), Value::Bool(true)); } -#[test] +#[test_log::test] fn inequality() { assert_eq!(eval("1 != 1"), Value::Bool(false)); } -#[test] +#[test_log::test] fn less_than() { assert_eq!(eval("2 < 1"), Value::Bool(false)); } -#[test] +#[test_log::test] fn greater_than() { assert_eq!(eval("2 > 1"), Value::Bool(true)); } -#[test] +#[test_log::test] fn less_than_or_equal() { assert_eq!(eval("1 <= 1"), Value::Bool(true)); } -#[test] +#[test_log::test] fn greater_than_or_equal() { assert_eq!(eval("1 >= 1"), Value::Bool(true)); } -#[test] +#[test_log::test] fn logical_or_short_circuit() { assert_eq!(eval("true || (1 / 0)"), Value::Bool(true)); } -#[test] +#[test_log::test] fn logical_and() { assert_eq!(eval("true && 1 == 0"), Value::Bool(false)); } -#[test] +#[test_log::test] fn list_concatenation() { assert_eq!( eval("[ 1 2 3 ] ++ [ 4 5 6 ]"), @@ -70,7 +70,7 @@ fn list_concatenation() { ); } -#[test] +#[test_log::test] fn attrset_update() { assert_eq!( eval("{ a.b = 1; b = 2; } // { a.c = 2; }"), @@ -87,23 +87,23 @@ fn attrset_update() { ); } -#[test] +#[test_log::test] fn unary_negation() { assert_eq!(eval("-5"), Value::Int(-5)); } -#[test] +#[test_log::test] fn logical_not() { assert_eq!(eval("!true"), Value::Bool(false)); assert_eq!(eval("!false"), Value::Bool(true)); } -#[test] +#[test_log::test] fn select_with_default_lazy_evaluation() { assert_eq!(eval("{ a = 1; }.a or (1 / 0)"), Value::Int(1)); } -#[test] +#[test_log::test] fn select_with_default_nested_lazy() { assert_eq!( eval("{ a.b = 42; }.a.b or (builtins.abort \"should not evaluate\")"), @@ -111,32 +111,32 @@ fn select_with_default_nested_lazy() { ); } -#[test] +#[test_log::test] fn select_with_default_fallback() { assert_eq!(eval("{ a = 1; }.b or 999"), Value::Int(999)); } -#[test] +#[test_log::test] fn implication_false_false() { assert_eq!(eval("false -> false"), Value::Bool(true)); } -#[test] +#[test_log::test] fn implication_false_true() { assert_eq!(eval("false -> true"), Value::Bool(true)); } -#[test] +#[test_log::test] fn implication_true_false() { assert_eq!(eval("true -> false"), Value::Bool(false)); } -#[test] +#[test_log::test] fn implication_true_true() { assert_eq!(eval("true -> true"), Value::Bool(true)); } -#[test] +#[test_log::test] fn implication_short_circuit() { assert_eq!(eval("false -> (1 / 0)"), Value::Bool(true)); } diff --git a/nix-js/tests/tests/path_operations.rs b/nix-js/tests/tests/path_operations.rs index c61e880..470843c 100644 --- a/nix-js/tests/tests/path_operations.rs +++ b/nix-js/tests/tests/path_operations.rs @@ -1,113 +1,113 @@ use crate::utils::{eval, eval_result}; use nix_js::value::Value; -#[test] +#[test_log::test] fn path_type_of() { let result = eval("builtins.typeOf ./foo"); assert_eq!(result, Value::String("path".to_string())); } -#[test] +#[test_log::test] fn is_path_true() { let result = eval("builtins.isPath ./foo"); assert_eq!(result, Value::Bool(true)); } -#[test] +#[test_log::test] fn is_path_false_string() { let result = eval(r#"builtins.isPath "./foo""#); assert_eq!(result, Value::Bool(false)); } -#[test] +#[test_log::test] fn is_path_false_number() { let result = eval("builtins.isPath 42"); assert_eq!(result, Value::Bool(false)); } -#[test] +#[test_log::test] fn path_concat_type() { // path + string = path let result = eval(r#"builtins.typeOf (./foo + "/bar")"#); assert_eq!(result, Value::String("path".to_string())); } -#[test] +#[test_log::test] fn string_path_concat_type() { // string + path = string let result = eval(r#"builtins.typeOf ("prefix-" + ./foo)"#); assert_eq!(result, Value::String("string".to_string())); } -#[test] +#[test_log::test] fn basename_of_path() { let result = eval("builtins.baseNameOf ./path/to/file.nix"); assert!(matches!(result, Value::String(s) if s == "file.nix")); } -#[test] +#[test_log::test] fn basename_of_string() { let result = eval(r#"builtins.baseNameOf "/path/to/file.nix""#); assert_eq!(result, Value::String("file.nix".to_string())); } -#[test] +#[test_log::test] fn dir_of_path_type() { // dirOf preserves path type let result = eval("builtins.typeOf (builtins.dirOf ./path/to/file.nix)"); assert_eq!(result, Value::String("path".to_string())); } -#[test] +#[test_log::test] fn dir_of_string_type() { // dirOf preserves string type let result = eval(r#"builtins.typeOf (builtins.dirOf "/path/to/file.nix")"#); assert_eq!(result, Value::String("string".to_string())); } -#[test] +#[test_log::test] fn path_equality() { // Same path should be equal let result = eval("./foo == ./foo"); assert_eq!(result, Value::Bool(true)); } -#[test] +#[test_log::test] fn path_not_equal_string() { // Paths and strings are different types - should not be equal let result = eval(r#"./foo == "./foo""#); assert_eq!(result, Value::Bool(false)); } -#[test] +#[test_log::test] fn to_path_absolute() { // toPath with absolute path returns string let result = eval(r#"builtins.toPath "/foo/bar""#); assert_eq!(result, Value::String("/foo/bar".to_string())); } -#[test] +#[test_log::test] fn to_path_type_is_string() { // toPath returns a string, not a path let result = eval(r#"builtins.typeOf (builtins.toPath "/foo")"#); assert_eq!(result, Value::String("string".to_string())); } -#[test] +#[test_log::test] fn to_path_relative_fails() { // toPath with relative path should fail let result = eval_result(r#"builtins.toPath "foo/bar""#); assert!(result.is_err()); } -#[test] +#[test_log::test] fn to_path_empty_fails() { // toPath with empty string should fail let result = eval_result(r#"builtins.toPath """#); assert!(result.is_err()); } -#[test] +#[test_log::test] fn to_path_from_path_value() { // toPath can accept a path value too (coerces to string first) let result = eval("builtins.toPath ./foo"); diff --git a/nix-js/tests/tests/regex.rs b/nix-js/tests/tests/regex.rs index 3f826f4..4c4f328 100644 --- a/nix-js/tests/tests/regex.rs +++ b/nix-js/tests/tests/regex.rs @@ -3,7 +3,7 @@ use nix_js::value::{List, Value}; use crate::utils::eval_result; -#[test] +#[test_log::test] fn match_exact_full_string() { assert_eq!( eval(r#"builtins.match "foobar" "foobar""#), @@ -11,12 +11,12 @@ fn match_exact_full_string() { ); } -#[test] +#[test_log::test] fn match_partial_returns_null() { assert_eq!(eval(r#"builtins.match "foo" "foobar""#), Value::Null); } -#[test] +#[test_log::test] fn match_with_capture_groups() { assert_eq!( eval(r#"builtins.match "(.*)\\.nix" "foobar.nix""#), @@ -24,7 +24,7 @@ fn match_with_capture_groups() { ); } -#[test] +#[test_log::test] fn match_multiple_capture_groups() { assert_eq!( eval(r#"builtins.match "((.*)/)?([^/]*)\\.nix" "foobar.nix""#), @@ -36,7 +36,7 @@ fn match_multiple_capture_groups() { ); } -#[test] +#[test_log::test] fn match_with_path() { assert_eq!( eval(r#"builtins.match "((.*)/)?([^/]*)\\.nix" "/path/to/foobar.nix""#), @@ -48,7 +48,7 @@ fn match_with_path() { ); } -#[test] +#[test_log::test] fn match_posix_space_class() { assert_eq!( eval(r#"builtins.match "[[:space:]]+([^[:space:]]+)[[:space:]]+" " foo ""#), @@ -56,7 +56,7 @@ fn match_posix_space_class() { ); } -#[test] +#[test_log::test] fn match_posix_upper_class() { assert_eq!( eval(r#"builtins.match "[[:space:]]+([[:upper:]]+)[[:space:]]+" " foo ""#), @@ -69,7 +69,7 @@ fn match_posix_upper_class() { ); } -#[test] +#[test_log::test] fn match_quantifiers() { assert_eq!( eval(r#"builtins.match "fo*" "f""#), @@ -86,7 +86,7 @@ fn match_quantifiers() { assert_eq!(eval(r#"builtins.match "fo{1,2}" "fooo""#), Value::Null); } -#[test] +#[test_log::test] fn split_non_capturing() { assert_eq!( eval(r#"builtins.split "foobar" "foobar""#), @@ -98,7 +98,7 @@ fn split_non_capturing() { ); } -#[test] +#[test_log::test] fn split_no_match() { assert_eq!( eval(r#"builtins.split "fo+" "f""#), @@ -106,7 +106,7 @@ fn split_no_match() { ); } -#[test] +#[test_log::test] fn split_with_capture_group() { assert_eq!( eval(r#"builtins.split "(fo*)" "foobar""#), @@ -118,7 +118,7 @@ fn split_with_capture_group() { ); } -#[test] +#[test_log::test] fn split_multiple_matches() { assert_eq!( eval(r#"builtins.split "(b)" "foobarbaz""#), @@ -132,7 +132,7 @@ fn split_multiple_matches() { ); } -#[test] +#[test_log::test] fn split_with_multiple_groups() { assert_eq!( eval(r#"builtins.split "(f)(o*)" "foo""#), @@ -147,7 +147,7 @@ fn split_with_multiple_groups() { ); } -#[test] +#[test_log::test] fn split_with_optional_groups() { assert_eq!( eval(r#"builtins.split "(a)|(c)" "abc""#), @@ -161,7 +161,7 @@ fn split_with_optional_groups() { ); } -#[test] +#[test_log::test] fn split_greedy_matching() { assert_eq!( eval(r#"builtins.split "(o+)" "oooofoooo""#), @@ -175,7 +175,7 @@ fn split_greedy_matching() { ); } -#[test] +#[test_log::test] fn split_posix_classes() { assert_eq!( eval(r#"builtins.split "([[:upper:]]+)" " FOO ""#), @@ -187,7 +187,7 @@ fn split_posix_classes() { ); } -#[test] +#[test_log::test] fn replace_basic() { assert_eq!( eval(r#"builtins.replaceStrings ["o"] ["a"] "foobar""#), @@ -195,7 +195,7 @@ fn replace_basic() { ); } -#[test] +#[test_log::test] fn replace_with_empty() { assert_eq!( eval(r#"builtins.replaceStrings ["o"] [""] "foobar""#), @@ -203,7 +203,7 @@ fn replace_with_empty() { ); } -#[test] +#[test_log::test] fn replace_multiple_patterns() { assert_eq!( eval(r#"builtins.replaceStrings ["oo" "a"] ["a" "oo"] "foobar""#), @@ -211,7 +211,7 @@ fn replace_multiple_patterns() { ); } -#[test] +#[test_log::test] fn replace_first_match_wins() { assert_eq!( eval(r#"builtins.replaceStrings ["oo" "oo"] ["u" "i"] "foobar""#), @@ -219,7 +219,7 @@ fn replace_first_match_wins() { ); } -#[test] +#[test_log::test] fn replace_empty_pattern() { assert_eq!( eval(r#"builtins.replaceStrings [""] ["X"] "abc""#), @@ -227,7 +227,7 @@ fn replace_empty_pattern() { ); } -#[test] +#[test_log::test] fn replace_empty_pattern_empty_string() { assert_eq!( eval(r#"builtins.replaceStrings [""] ["X"] """#), @@ -235,7 +235,7 @@ fn replace_empty_pattern_empty_string() { ); } -#[test] +#[test_log::test] fn replace_simple_char() { assert_eq!( eval(r#"builtins.replaceStrings ["-"] ["_"] "a-b""#), @@ -243,7 +243,7 @@ fn replace_simple_char() { ); } -#[test] +#[test_log::test] fn replace_longer_pattern() { assert_eq!( eval(r#"builtins.replaceStrings ["oo"] ["u"] "foobar""#), @@ -251,13 +251,13 @@ fn replace_longer_pattern() { ); } -#[test] +#[test_log::test] fn replace_different_lengths() { let result = eval_result(r#"builtins.replaceStrings ["a" "b"] ["x"] "test""#); assert!(result.is_err()); } -#[test] +#[test_log::test] fn split_version_simple() { assert_eq!( eval(r#"builtins.splitVersion "1.2.3""#), @@ -269,7 +269,7 @@ fn split_version_simple() { ); } -#[test] +#[test_log::test] fn split_version_with_pre() { assert_eq!( eval(r#"builtins.splitVersion "2.3.0pre1234""#), @@ -283,7 +283,7 @@ fn split_version_with_pre() { ); } -#[test] +#[test_log::test] fn split_version_with_letters() { assert_eq!( eval(r#"builtins.splitVersion "2.3a""#), @@ -295,7 +295,7 @@ fn split_version_with_letters() { ); } -#[test] +#[test_log::test] fn split_version_with_dashes() { assert_eq!( eval(r#"builtins.splitVersion "2.3-beta1""#), @@ -308,7 +308,7 @@ fn split_version_with_dashes() { ); } -#[test] +#[test_log::test] fn split_version_empty() { assert_eq!( eval(r#"builtins.splitVersion """#), diff --git a/nix-js/tests/tests/string_context.rs b/nix-js/tests/tests/string_context.rs index 36190f4..4b0b0d8 100644 --- a/nix-js/tests/tests/string_context.rs +++ b/nix-js/tests/tests/string_context.rs @@ -6,13 +6,13 @@ fn eval(expr: &str) -> Value { eval_result(expr).unwrap_or_else(|e| panic!("{}", e)) } -#[test] +#[test_log::test] fn hascontext_plain_string() { let result = eval(r#"builtins.hasContext "hello""#); assert_eq!(result, Value::Bool(false)); } -#[test] +#[test_log::test] fn hascontext_derivation_output() { let result = eval( r#" @@ -24,7 +24,7 @@ fn hascontext_derivation_output() { assert_eq!(result, Value::Bool(true)); } -#[test] +#[test_log::test] fn getcontext_plain_string() { let result = eval(r#"builtins.getContext "hello""#); match result { @@ -35,7 +35,7 @@ fn getcontext_plain_string() { } } -#[test] +#[test_log::test] fn getcontext_derivation_output() { let result = eval( r#" @@ -60,7 +60,7 @@ fn getcontext_derivation_output() { } } -#[test] +#[test_log::test] fn unsafediscardstringcontext() { let result = eval( r#" @@ -74,7 +74,7 @@ fn unsafediscardstringcontext() { assert_eq!(result, Value::Bool(false)); } -#[test] +#[test_log::test] fn unsafediscardstringcontext_preserves_value() { let result = eval( r#" @@ -88,7 +88,7 @@ fn unsafediscardstringcontext_preserves_value() { assert_eq!(result, Value::Bool(true)); } -#[test] +#[test_log::test] fn appendcontext_basic() { let result = eval( r#" @@ -102,7 +102,7 @@ fn appendcontext_basic() { assert_eq!(result, Value::Bool(true)); } -#[test] +#[test_log::test] fn appendcontext_preserves_value() { let result = eval( r#" @@ -116,7 +116,7 @@ fn appendcontext_preserves_value() { assert_eq!(result, Value::Bool(true)); } -#[test] +#[test_log::test] fn string_concat_merges_context() { let result = eval( r#" @@ -133,7 +133,7 @@ fn string_concat_merges_context() { assert_eq!(result, Value::Int(2)); } -#[test] +#[test_log::test] fn string_add_merges_context() { let result = eval( r#" @@ -150,7 +150,7 @@ fn string_add_merges_context() { assert_eq!(result, Value::Int(2)); } -#[test] +#[test_log::test] fn context_in_derivation_args() { let mut ctx = Context::new().unwrap(); let result = ctx @@ -179,7 +179,7 @@ fn context_in_derivation_args() { } } -#[test] +#[test_log::test] fn context_in_derivation_env() { let mut ctx = Context::new().unwrap(); let result = ctx @@ -208,7 +208,7 @@ fn context_in_derivation_env() { } } -#[test] +#[test_log::test] fn tostring_preserves_context() { let result = eval( r#" @@ -221,7 +221,7 @@ fn tostring_preserves_context() { assert_eq!(result, Value::Bool(true)); } -#[test] +#[test_log::test] fn interpolation_derivation_returns_outpath() { let mut ctx = Context::new().unwrap(); let result = ctx @@ -244,7 +244,7 @@ fn interpolation_derivation_returns_outpath() { } } -#[test] +#[test_log::test] fn interpolation_derivation_has_context() { let result = eval( r#" @@ -256,7 +256,7 @@ fn interpolation_derivation_has_context() { assert_eq!(result, Value::Bool(true)); } -#[test] +#[test_log::test] fn interpolation_derivation_context_correct() { let result = eval( r#" @@ -277,7 +277,7 @@ fn interpolation_derivation_context_correct() { } } -#[test] +#[test_log::test] fn interpolation_multiple_derivations() { let result = eval( r#" @@ -292,7 +292,7 @@ fn interpolation_multiple_derivations() { assert_eq!(result, Value::Int(2)); } -#[test] +#[test_log::test] fn interpolation_derivation_equals_tostring() { let result = eval( r#" @@ -304,7 +304,7 @@ fn interpolation_derivation_equals_tostring() { assert_eq!(result, Value::Bool(true)); } -#[test] +#[test_log::test] fn substring_preserves_context() { let result = eval( r#" @@ -318,7 +318,7 @@ fn substring_preserves_context() { assert_eq!(result, Value::Bool(true)); } -#[test] +#[test_log::test] fn substring_zero_length_preserves_context() { let result = eval( r#" @@ -332,7 +332,7 @@ fn substring_zero_length_preserves_context() { assert_eq!(result, Value::Bool(true)); } -#[test] +#[test_log::test] fn substring_zero_length_empty_value() { let result = eval( r#" @@ -346,7 +346,7 @@ fn substring_zero_length_empty_value() { assert_eq!(result, Value::Bool(true)); } -#[test] +#[test_log::test] #[allow(non_snake_case)] fn concatStringsSep_preserves_context() { let result = eval( @@ -363,7 +363,7 @@ fn concatStringsSep_preserves_context() { assert_eq!(result, Value::Bool(true)); } -#[test] +#[test_log::test] #[allow(non_snake_case)] fn concatStringsSep_merges_contexts() { let result = eval( @@ -381,7 +381,7 @@ fn concatStringsSep_merges_contexts() { assert_eq!(result, Value::Int(2)); } -#[test] +#[test_log::test] #[allow(non_snake_case)] fn concatStringsSep_separator_has_context() { let result = eval( @@ -396,7 +396,7 @@ fn concatStringsSep_separator_has_context() { assert_eq!(result, Value::Bool(true)); } -#[test] +#[test_log::test] #[allow(non_snake_case)] fn replaceStrings_input_context_preserved() { let result = eval( @@ -411,7 +411,7 @@ fn replaceStrings_input_context_preserved() { assert_eq!(result, Value::Bool(true)); } -#[test] +#[test_log::test] #[allow(non_snake_case)] fn replaceStrings_replacement_context_collected() { let result = eval( @@ -426,7 +426,7 @@ fn replaceStrings_replacement_context_collected() { assert_eq!(result, Value::Bool(true)); } -#[test] +#[test_log::test] #[allow(non_snake_case)] fn replaceStrings_merges_contexts() { let result = eval( @@ -444,7 +444,7 @@ fn replaceStrings_merges_contexts() { assert_eq!(result, Value::Int(2)); } -#[test] +#[test_log::test] #[allow(non_snake_case)] fn replaceStrings_lazy_evaluation_context() { let result = eval( @@ -459,7 +459,7 @@ fn replaceStrings_lazy_evaluation_context() { assert_eq!(result, Value::Bool(false)); } -#[test] +#[test_log::test] #[allow(non_snake_case)] fn baseNameOf_preserves_context() { let result = eval( @@ -474,7 +474,7 @@ fn baseNameOf_preserves_context() { assert_eq!(result, Value::Bool(true)); } -#[test] +#[test_log::test] fn split_no_match_preserves_context() { let result = eval( r#" @@ -488,7 +488,7 @@ fn split_no_match_preserves_context() { assert_eq!(result, Value::Bool(true)); } -#[test] +#[test_log::test] fn builtins_path_has_context() { let temp_dir = tempfile::tempdir().unwrap(); let test_file = temp_dir.path().join("test.txt"); @@ -502,7 +502,7 @@ fn builtins_path_has_context() { assert_eq!(result, Value::Bool(true)); } -#[test] +#[test_log::test] fn builtins_path_context_tracked_in_structured_attrs_derivation() { let temp_dir = tempfile::tempdir().unwrap(); let test_file = temp_dir.path().join("test-patch.txt"); @@ -532,7 +532,7 @@ fn builtins_path_context_tracked_in_structured_attrs_derivation() { } } -#[test] +#[test_log::test] fn builtins_path_context_tracked_in_non_structured_derivation() { let temp_dir = tempfile::tempdir().unwrap(); let test_file = temp_dir.path().join("dep.txt"); diff --git a/nix-js/tests/tests/thunk_scope.rs b/nix-js/tests/tests/thunk_scope.rs index b73cccd..0bdfbc3 100644 --- a/nix-js/tests/tests/thunk_scope.rs +++ b/nix-js/tests/tests/thunk_scope.rs @@ -1,12 +1,12 @@ use crate::utils::eval; use nix_js::value::Value; -#[test] +#[test_log::test] fn non_recursive_bindings() { assert_eq!(eval("let x = 1; y = 2; z = x + y; in z"), Value::Int(3)); } -#[test] +#[test_log::test] fn non_recursive_multiple_bindings() { assert_eq!( eval("let a = 10; b = 20; c = 30; d = a + b + c; in d"), @@ -14,7 +14,7 @@ fn non_recursive_multiple_bindings() { ); } -#[test] +#[test_log::test] fn recursive_fibonacci() { assert_eq!( eval("let fib = n: if n <= 1 then 1 else fib (n - 1) + fib (n - 2); in fib 5"), @@ -22,7 +22,7 @@ fn recursive_fibonacci() { ); } -#[test] +#[test_log::test] fn recursive_factorial() { assert_eq!( eval("let factorial = n: if n == 0 then 1 else n * factorial (n - 1); in factorial 5"), @@ -30,7 +30,7 @@ fn recursive_factorial() { ); } -#[test] +#[test_log::test] fn mutual_recursion_simple() { assert_eq!( eval( @@ -40,7 +40,7 @@ fn mutual_recursion_simple() { ); } -#[test] +#[test_log::test] fn mutual_recursion_even_odd() { assert_eq!( eval( @@ -50,7 +50,7 @@ fn mutual_recursion_even_odd() { ); } -#[test] +#[test_log::test] fn mixed_recursive_and_non_recursive() { assert_eq!( eval("let x = 1; f = n: if n == 0 then x else f (n - 1); in f 5"), @@ -58,7 +58,7 @@ fn mixed_recursive_and_non_recursive() { ); } -#[test] +#[test_log::test] fn mixed_with_multiple_non_recursive() { assert_eq!( eval( @@ -68,12 +68,12 @@ fn mixed_with_multiple_non_recursive() { ); } -#[test] +#[test_log::test] fn rec_attrset_non_recursive() { assert_eq!(eval("rec { x = 1; y = 2; z = x + y; }.z"), Value::Int(3)); } -#[test] +#[test_log::test] fn rec_attrset_recursive() { assert_eq!( eval("rec { f = n: if n == 0 then 0 else f (n - 1); }.f 10"), @@ -81,7 +81,7 @@ fn rec_attrset_recursive() { ); } -#[test] +#[test_log::test] fn nested_let_non_recursive() { assert_eq!( eval("let x = 1; in let y = x + 1; z = y + 1; in z"), @@ -89,7 +89,7 @@ fn nested_let_non_recursive() { ); } -#[test] +#[test_log::test] fn nested_let_with_recursive() { assert_eq!( eval("let f = n: if n == 0 then 0 else f (n - 1); in let g = m: f m; in g 5"), @@ -97,7 +97,7 @@ fn nested_let_with_recursive() { ); } -#[test] +#[test_log::test] fn three_way_mutual_recursion() { assert_eq!( eval( @@ -107,7 +107,7 @@ fn three_way_mutual_recursion() { ); } -#[test] +#[test_log::test] fn complex_mixed_dependencies() { assert_eq!( eval( diff --git a/nix-js/tests/tests/to_string.rs b/nix-js/tests/tests/to_string.rs index 019927c..7614990 100644 --- a/nix-js/tests/tests/to_string.rs +++ b/nix-js/tests/tests/to_string.rs @@ -1,7 +1,7 @@ use crate::utils::{eval, eval_result}; use nix_js::value::Value; -#[test] +#[test_log::test] fn string_returns_as_is() { assert_eq!( eval(r#"toString "hello""#), @@ -9,32 +9,32 @@ fn string_returns_as_is() { ); } -#[test] +#[test_log::test] fn integer_to_string() { assert_eq!(eval("toString 42"), Value::String("42".to_string())); assert_eq!(eval("toString (-5)"), Value::String("-5".to_string())); assert_eq!(eval("toString 0"), Value::String("0".to_string())); } -#[test] +#[test_log::test] fn float_to_string() { assert_eq!(eval("toString 3.14"), Value::String("3.14".to_string())); assert_eq!(eval("toString 0.0"), Value::String("0".to_string())); assert_eq!(eval("toString (-2.5)"), Value::String("-2.5".to_string())); } -#[test] +#[test_log::test] fn bool_to_string() { assert_eq!(eval("toString true"), Value::String("1".to_string())); assert_eq!(eval("toString false"), Value::String("".to_string())); } -#[test] +#[test_log::test] fn null_to_string() { assert_eq!(eval("toString null"), Value::String("".to_string())); } -#[test] +#[test_log::test] fn simple_list_to_string() { assert_eq!(eval("toString [1 2 3]"), Value::String("1 2 3".to_string())); assert_eq!( @@ -43,7 +43,7 @@ fn simple_list_to_string() { ); } -#[test] +#[test_log::test] fn nested_list_flattens() { assert_eq!( eval("toString [[1 2] [3 4]]"), @@ -55,14 +55,14 @@ fn nested_list_flattens() { ); } -#[test] +#[test_log::test] fn empty_list_in_list_no_extra_space() { assert_eq!(eval("toString [1 [] 2]"), Value::String("1 2".to_string())); assert_eq!(eval("toString [[] 1 2]"), Value::String("1 2".to_string())); assert_eq!(eval("toString [1 2 []]"), Value::String("1 2 ".to_string())); } -#[test] +#[test_log::test] fn list_with_multiple_empty_lists() { assert_eq!( eval("toString [1 [] [] 2]"), @@ -71,7 +71,7 @@ fn list_with_multiple_empty_lists() { assert_eq!(eval("toString [[] [] 1]"), Value::String("1".to_string())); } -#[test] +#[test_log::test] fn list_with_bool_and_null() { assert_eq!( eval("toString [true false null]"), @@ -83,7 +83,7 @@ fn list_with_bool_and_null() { ); } -#[test] +#[test_log::test] fn mixed_type_list() { assert_eq!( eval(r#"toString [1 "hello" 2.5 true]"#), @@ -91,7 +91,7 @@ fn mixed_type_list() { ); } -#[test] +#[test_log::test] fn attrs_with_out_path() { assert_eq!( eval(r#"toString { outPath = "/nix/store/foo"; }"#), @@ -99,7 +99,7 @@ fn attrs_with_out_path() { ); } -#[test] +#[test_log::test] fn attrs_with_to_string_method() { assert_eq!( eval(r#"toString { __toString = self: "custom"; }"#), @@ -107,7 +107,7 @@ fn attrs_with_to_string_method() { ); } -#[test] +#[test_log::test] fn attrs_to_string_self_reference() { assert_eq!( eval( @@ -117,7 +117,7 @@ fn attrs_to_string_self_reference() { ); } -#[test] +#[test_log::test] fn attrs_to_string_priority() { assert_eq!( eval(r#"toString { __toString = self: "custom"; outPath = "/nix/store/foo"; }"#), @@ -125,7 +125,7 @@ fn attrs_to_string_priority() { ); } -#[test] +#[test_log::test] fn derivation_like_object() { assert_eq!( eval( @@ -135,7 +135,7 @@ fn derivation_like_object() { ); } -#[test] +#[test_log::test] fn string_interpolation_with_int() { assert_eq!( eval(r#""value: ${toString 42}""#), @@ -143,7 +143,7 @@ fn string_interpolation_with_int() { ); } -#[test] +#[test_log::test] fn string_interpolation_with_list() { assert_eq!( eval(r#""items: ${toString [1 2 3]}""#), @@ -151,7 +151,7 @@ fn string_interpolation_with_list() { ); } -#[test] +#[test_log::test] fn nested_to_string_calls() { assert_eq!( eval(r#"toString (toString 42)"#), @@ -159,7 +159,7 @@ fn nested_to_string_calls() { ); } -#[test] +#[test_log::test] fn to_string_in_let_binding() { assert_eq!( eval(r#"let x = toString 42; y = toString 10; in "${x}-${y}""#), @@ -167,17 +167,17 @@ fn to_string_in_let_binding() { ); } -#[test] +#[test_log::test] fn empty_string() { assert_eq!(eval(r#"toString """#), Value::String("".to_string())); } -#[test] +#[test_log::test] fn empty_list() { assert_eq!(eval("toString []"), Value::String("".to_string())); } -#[test] +#[test_log::test] fn to_string_preserves_spaces_in_strings() { assert_eq!( eval(r#"toString "hello world""#), @@ -185,7 +185,7 @@ fn to_string_preserves_spaces_in_strings() { ); } -#[test] +#[test_log::test] fn list_of_empty_strings() { assert_eq!( eval(r#"toString ["" "" ""]"#), @@ -193,7 +193,7 @@ fn list_of_empty_strings() { ); } -#[test] +#[test_log::test] fn deeply_nested_lists() { assert_eq!( eval("toString [[[1] [2]] [[3] [4]]]"), @@ -201,7 +201,7 @@ fn deeply_nested_lists() { ); } -#[test] +#[test_log::test] fn list_with_nested_empty_lists() { assert_eq!( eval("toString [1 [[]] 2]"), @@ -209,19 +209,19 @@ fn list_with_nested_empty_lists() { ); } -#[test] +#[test_log::test] fn attrs_without_out_path_or_to_string_fails() { let result = eval_result(r#"toString { foo = "bar"; }"#); assert!(result.is_err()); } -#[test] +#[test_log::test] fn function_to_string_fails() { let result = eval_result("toString (x: x)"); assert!(result.is_err()); } -#[test] +#[test_log::test] fn to_string_method_must_return_string() { assert_eq!( eval(r#"toString { __toString = self: 42; }"#), @@ -233,7 +233,7 @@ fn to_string_method_must_return_string() { ); } -#[test] +#[test_log::test] fn out_path_can_be_nested() { assert_eq!( eval(r#"toString { outPath = { outPath = "/final/path"; }; }"#), @@ -241,7 +241,7 @@ fn out_path_can_be_nested() { ); } -#[test] +#[test_log::test] fn list_spacing_matches_nix_behavior() { assert_eq!( eval(r#"toString ["a" "b"]"#),