NickelEval.jl

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

NickelEval.jl (2890B)


      1 module NickelEval
      2 
      3 export nickel_eval, nickel_eval_file, @ncl_str, NickelError, NickelEnum
      4 export nickel_to_json, nickel_to_yaml, nickel_to_toml
      5 export check_ffi_available, build_ffi
      6 export nickel_open, NickelValue, NickelSession, nickel_kind
      7 
      8 """
      9     NickelError <: Exception
     10 
     11 Exception thrown when Nickel evaluation fails.
     12 
     13 # Fields
     14 - `message::String`: The error message from Nickel
     15 
     16 # Examples
     17 ```julia
     18 try
     19     nickel_eval("{ x = }")  # syntax error
     20 catch e
     21     if e isa NickelError
     22         println("Nickel error: ", e.message)
     23     end
     24 end
     25 ```
     26 """
     27 struct NickelError <: Exception
     28     message::String
     29 end
     30 
     31 Base.showerror(io::IO, e::NickelError) = print(io, "NickelError: ", e.message)
     32 
     33 """
     34     NickelEnum
     35 
     36 Represents a Nickel enum value.
     37 
     38 # Fields
     39 - `tag::Symbol`: The enum variant name
     40 - `arg::Any`: The argument (nothing for simple enums)
     41 
     42 # Examples
     43 ```julia
     44 result = nickel_eval("let x = 'Some 42 in x")
     45 result.tag   # => :Some
     46 result.arg   # => 42
     47 result == :Some  # => true
     48 
     49 result = nickel_eval("let x = 'None in x")
     50 result.tag   # => :None
     51 result.arg   # => nothing
     52 ```
     53 """
     54 struct NickelEnum
     55     tag::Symbol
     56     arg::Any
     57 end
     58 
     59 # Convenience: compare enum to symbol
     60 Base.:(==)(e::NickelEnum, s::Symbol) = e.tag == s
     61 Base.:(==)(s::Symbol, e::NickelEnum) = e.tag == s
     62 
     63 # Pretty printing
     64 function Base.show(io::IO, e::NickelEnum)
     65     if e.arg === nothing
     66         print(io, "'", e.tag)
     67     else
     68         print(io, "'", e.tag, " ", repr(e.arg))
     69     end
     70 end
     71 
     72 # ── Lazy evaluation types ─────────────────────────────────────────────────────
     73 
     74 """
     75     NickelSession
     76 
     77 Owns a Nickel evaluation context for lazy (shallow) evaluation.
     78 Tracks all allocated expressions and frees them on `close`.
     79 
     80 Not thread-safe. All access must occur on a single thread.
     81 """
     82 mutable struct NickelSession
     83     ctx::Ptr{Cvoid}                  # Ptr{LibNickel.nickel_context} — Cvoid avoids forward ref
     84     exprs::Vector{Ptr{Cvoid}}        # tracked allocations, freed on close
     85     closed::Bool
     86 end
     87 
     88 """
     89     NickelValue
     90 
     91 A lazy reference to a Nickel expression. Accessing fields (`.field` or `["field"]`)
     92 evaluates only the requested sub-expression. Use `collect` to materialize the
     93 full subtree into plain Julia types.
     94 
     95 # Examples
     96 ```julia
     97 nickel_open("{ x = 1, y = { z = 2 } }") do cfg
     98     cfg.x        # => 1
     99     cfg.y.z      # => 2
    100     collect(cfg)  # => Dict("x" => 1, "y" => Dict("z" => 2))
    101 end
    102 ```
    103 """
    104 struct NickelValue
    105     session::NickelSession
    106     expr::Ptr{Cvoid}                 # Ptr{LibNickel.nickel_expr}
    107 end
    108 
    109 """
    110     @ncl_str -> Any
    111 
    112 String macro for inline Nickel evaluation.
    113 
    114 # Examples
    115 ```julia
    116 julia> ncl"1 + 1"
    117 2
    118 
    119 julia> ncl"{ x = 10 }"["x"]
    120 10
    121 ```
    122 """
    123 macro ncl_str(code)
    124     :(nickel_eval($code))
    125 end
    126 
    127 include("ffi.jl")
    128 
    129 function __init__()
    130     __init_ffi__()
    131 end
    132 
    133 end # module