NickelEval.jl

Julia FFI bindings for Nickel configuration language
Log | Files | Refs | README | LICENSE

commit b6669452679a49b3ff825daf266b546fbca08a7b
parent 0c2e8788cd86c23df960644b67e0543fc9519cf0
Author: Erik Loualiche <[email protected]>
Date:   Fri,  6 Feb 2026 11:10:55 -0600

Use Nickel std.enum.to_tag_and_arg format for enums

Change enum encoding to match Nickel standard library:
- Simple enum: { tag = "Name" }
- Enum with arg: { tag = "Name", arg = value }

Previously used _tag/_value, now uses tag/arg to be consistent
with std.enum.to_tag_and_arg.

Co-Authored-By: Claude Opus 4.5 <[email protected]>

Diffstat:
Mdocs/src/man/ffi.md | 14++++++++++----
Mrust/nickel-jl/src/lib.rs | 20++++++++++----------
Mtest/test_ffi.jl | 14+++++++-------
3 files changed, 27 insertions(+), 21 deletions(-)

diff --git a/docs/src/man/ffi.md b/docs/src/man/ffi.md @@ -53,21 +53,27 @@ nickel_eval_ffi("{ a = 1 }", Dict{String, Int}) # Typed Dict ### Enums -Nickel enums are converted to `Dict{String, Any}` with special fields: +Nickel enums are converted to `Dict{String, Any}` matching the format of `std.enum.to_tag_and_arg`: **Simple enum** (no argument): ```julia nickel_eval_native("let x = 'Foo in x") -# => Dict("_tag" => "Foo") +# => Dict("tag" => "Foo") ``` **Enum with argument**: ```julia nickel_eval_native("let x = 'Some 42 in x") -# => Dict("_tag" => "Some", "_value" => 42) +# => Dict("tag" => "Some", "arg" => 42) nickel_eval_native("let x = 'Ok { value = 123 } in x") -# => Dict("_tag" => "Ok", "_value" => Dict("value" => 123)) +# => Dict("tag" => "Ok", "arg" => Dict("value" => 123)) +``` + +This matches Nickel's standard library convention: +```nickel +'Some 42 |> std.enum.to_tag_and_arg +# => { tag = "Some", arg = 42 } ``` ### Nested Structures diff --git a/rust/nickel-jl/src/lib.rs b/rust/nickel-jl/src/lib.rs @@ -206,12 +206,12 @@ fn encode_term(term: &RichTerm, buffer: &mut Vec<u8>) -> Result<(), String> { } } Term::Enum(tag) => { - // Simple enum without argument: encode as record with just _tag + // Simple enum: encode as { tag = "Name" } (matches std.enum.to_tag_and_arg) buffer.push(TYPE_RECORD); buffer.extend_from_slice(&1u32.to_le_bytes()); // 1 field - // _tag field - let tag_key = b"_tag"; + // tag field + let tag_key = b"tag"; buffer.extend_from_slice(&(tag_key.len() as u32).to_le_bytes()); buffer.extend_from_slice(tag_key); buffer.push(TYPE_STRING); @@ -220,12 +220,12 @@ fn encode_term(term: &RichTerm, buffer: &mut Vec<u8>) -> Result<(), String> { buffer.extend_from_slice(tag_bytes); } Term::EnumVariant { tag, arg, .. } => { - // Enum variant with argument: encode as record with _tag and _value + // Enum with argument: encode as { tag = "Name", arg = value } (matches std.enum.to_tag_and_arg) buffer.push(TYPE_RECORD); buffer.extend_from_slice(&2u32.to_le_bytes()); // 2 fields - // _tag field - let tag_key = b"_tag"; + // tag field + let tag_key = b"tag"; buffer.extend_from_slice(&(tag_key.len() as u32).to_le_bytes()); buffer.extend_from_slice(tag_key); buffer.push(TYPE_STRING); @@ -233,10 +233,10 @@ fn encode_term(term: &RichTerm, buffer: &mut Vec<u8>) -> Result<(), String> { buffer.extend_from_slice(&(tag_bytes.len() as u32).to_le_bytes()); buffer.extend_from_slice(tag_bytes); - // _value field - let value_key = b"_value"; - buffer.extend_from_slice(&(value_key.len() as u32).to_le_bytes()); - buffer.extend_from_slice(value_key); + // arg field + let arg_key = b"arg"; + buffer.extend_from_slice(&(arg_key.len() as u32).to_le_bytes()); + buffer.extend_from_slice(arg_key); encode_term(arg, buffer)?; } other => { diff --git a/test/test_ffi.jl b/test/test_ffi.jl @@ -84,21 +84,21 @@ end @testset "Enums" begin - # Simple enum (no argument) + # Simple enum (no argument) - matches std.enum.to_tag_and_arg format result = nickel_eval_native("let x = 'Foo in x") @test result isa Dict{String, Any} - @test result["_tag"] == "Foo" - @test !haskey(result, "_value") + @test result["tag"] == "Foo" + @test !haskey(result, "arg") # Enum with integer argument result = nickel_eval_native("let x = 'Some 42 in x") - @test result["_tag"] == "Some" - @test result["_value"] === Int64(42) + @test result["tag"] == "Some" + @test result["arg"] === Int64(42) # Enum with record argument result = nickel_eval_native("let x = 'Ok { value = 123 } in x") - @test result["_tag"] == "Ok" - @test result["_value"]["value"] === Int64(123) + @test result["tag"] == "Ok" + @test result["arg"]["value"] === Int64(123) # Match expression result = nickel_eval_native("let x = 'Success 42 in x |> match { 'Success v => v, 'Failure _ => 0 }")