NickelEval.jl

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

README.md (4975B)


      1 # NickelEval.jl
      2 
      3 Julia bindings for the [Nickel](https://nickel-lang.org/) configuration language, using the official Nickel C API.
      4 
      5 Evaluate Nickel code directly from Julia with native type conversion and export to JSON/TOML/YAML. No Nickel CLI required.
      6 
      7 ## Installation
      8 
      9 ### From LouLouLibs Registry (Recommended)
     10 
     11 ```julia
     12 using Pkg
     13 Pkg.Registry.add(url="https://github.com/LouLouLibs/loulouJL")
     14 Pkg.add("NickelEval")
     15 ```
     16 
     17 ### From GitHub URL
     18 
     19 ```julia
     20 using Pkg
     21 Pkg.add(url="https://github.com/LouLouLibs/NickelEval.jl")
     22 ```
     23 
     24 Pre-built binaries are downloaded automatically on supported platforms (macOS Apple Silicon, Linux x86_64). For other platforms, build from source:
     25 
     26 ```bash
     27 NICKELEVAL_BUILD_FFI=true julia -e 'using Pkg; Pkg.build("NickelEval")'
     28 ```
     29 
     30 ## Quick Start
     31 
     32 ```julia
     33 using NickelEval
     34 
     35 # Simple evaluation
     36 nickel_eval("1 + 2")  # => 3
     37 
     38 # Records return Dict{String, Any}
     39 config = nickel_eval("{ name = \"alice\", age = 30 }")
     40 config["name"]  # => "alice"
     41 config["age"]   # => 30
     42 
     43 # String macro for inline Nickel
     44 ncl"[1, 2, 3] |> std.array.map (fun x => x * 2)"
     45 # => [2, 4, 6]
     46 ```
     47 
     48 ## Typed Evaluation
     49 
     50 Convert Nickel values directly to Julia types:
     51 
     52 ```julia
     53 # Typed dictionaries
     54 nickel_eval("{ a = 1, b = 2 }", Dict{String, Int})
     55 # => Dict{String, Int64}("a" => 1, "b" => 2)
     56 
     57 # Typed arrays
     58 nickel_eval("[1, 2, 3, 4, 5]", Vector{Int})
     59 # => [1, 2, 3, 4, 5]
     60 
     61 # NamedTuples for structured data
     62 config = nickel_eval("""
     63 {
     64   host = "localhost",
     65   port = 8080,
     66   debug = true
     67 }
     68 """, @NamedTuple{host::String, port::Int, debug::Bool})
     69 # => (host = "localhost", port = 8080, debug = true)
     70 
     71 config.port  # => 8080
     72 ```
     73 
     74 ## Enums
     75 
     76 Nickel enum types are preserved:
     77 
     78 ```julia
     79 result = nickel_eval("'Some 42")
     80 result.tag   # => :Some
     81 result.arg   # => 42
     82 result == :Some  # => true
     83 
     84 nickel_eval("'None").tag  # => :None
     85 ```
     86 
     87 ## Export to Configuration Formats
     88 
     89 Generate JSON, TOML, or YAML from Nickel:
     90 
     91 ```julia
     92 # JSON
     93 nickel_to_json("{ name = \"myapp\", port = 8080 }")
     94 # => "{\n  \"name\": \"myapp\",\n  \"port\": 8080\n}"
     95 
     96 # TOML
     97 nickel_to_toml("{ name = \"myapp\", port = 8080 }")
     98 # => "name = \"myapp\"\nport = 8080\n"
     99 
    100 # YAML
    101 nickel_to_yaml("{ name = \"myapp\", port = 8080 }")
    102 # => "name: myapp\nport: 8080\n"
    103 ```
    104 
    105 ### Generate Config Files
    106 
    107 ```julia
    108 config_ncl = """
    109 {
    110   database = {
    111     host = "localhost",
    112     port = 5432,
    113     name = "mydb"
    114   },
    115   server = {
    116     host = "0.0.0.0",
    117     port = 8080
    118   }
    119 }
    120 """
    121 
    122 write("config.toml", nickel_to_toml(config_ncl))
    123 write("config.yaml", nickel_to_yaml(config_ncl))
    124 ```
    125 
    126 ## File Evaluation
    127 
    128 Evaluate `.ncl` files with full import support:
    129 
    130 ```julia
    131 # config.ncl:
    132 # let shared = import "shared.ncl" in
    133 # { name = shared.project_name, version = "1.0" }
    134 
    135 config = nickel_eval_file("config.ncl")
    136 config["name"]  # => "MyProject"
    137 
    138 # Typed
    139 nickel_eval_file("config.ncl", @NamedTuple{name::String, version::String})
    140 ```
    141 
    142 Import paths are resolved relative to the file's directory.
    143 
    144 ## Building from Source
    145 
    146 Pre-built binaries are downloaded automatically on macOS Apple Silicon and Linux x86_64. On other platforms (or if `check_ffi_available()` returns `false`), build from source:
    147 
    148 ```bash
    149 # Requires Rust (https://rustup.rs/)
    150 NICKELEVAL_BUILD_FFI=true julia -e 'using Pkg; Pkg.build("NickelEval")'
    151 ```
    152 
    153 ### HPC / Slurm Clusters
    154 
    155 The pre-built Linux binary requires glibc >= 2.29. On older systems (RHEL 8, CentOS 8), it will warn and fall back gracefully. Build from source inside Julia:
    156 
    157 ```julia
    158 using NickelEval
    159 build_ffi()   # builds from source, no restart needed
    160 ```
    161 
    162 This requires Rust. If `cargo` isn't available:
    163 
    164 ```bash
    165 curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
    166 source ~/.cargo/env
    167 ```
    168 
    169 Then run `build_ffi()` again.
    170 
    171 ## API Reference
    172 
    173 ### Evaluation
    174 
    175 | Function | Description |
    176 |----------|-------------|
    177 | `nickel_eval(code)` | Evaluate Nickel code, return Julia native types |
    178 | `nickel_eval(code, T)` | Evaluate and convert to type `T` |
    179 | `nickel_eval_file(path)` | Evaluate a `.ncl` file with import support |
    180 | `@ncl_str` | String macro for inline evaluation |
    181 | `check_ffi_available()` | Check if the C API library is loaded |
    182 | `build_ffi()` | Build C API library from source (requires Rust) |
    183 
    184 ### Export
    185 
    186 | Function | Description |
    187 |----------|-------------|
    188 | `nickel_to_json(code)` | Export to JSON string |
    189 | `nickel_to_toml(code)` | Export to TOML string |
    190 | `nickel_to_yaml(code)` | Export to YAML string |
    191 
    192 ## Type Conversion
    193 
    194 | Nickel Type | Julia Type |
    195 |-------------|------------|
    196 | Number (integer) | `Int64` |
    197 | Number (float) | `Float64` |
    198 | String | `String` |
    199 | Bool | `Bool` |
    200 | Array | `Vector{Any}` (or `Vector{T}` with typed eval) |
    201 | Record | `Dict{String, Any}` (or `NamedTuple` / `Dict{K,V}` with typed eval) |
    202 | Null | `nothing` |
    203 | Enum | `NickelEnum(tag, arg)` |
    204 
    205 ## Error Handling
    206 
    207 ```julia
    208 try
    209     nickel_eval("{ x = }")  # syntax error
    210 catch e
    211     if e isa NickelError
    212         println("Nickel error: ", e.message)
    213     end
    214 end
    215 ```
    216 
    217 ## License
    218 
    219 MIT