Sign in

    moonjsonnet

    An embeddable, lazy Jsonnet interpreter for MoonBit native applications

    jsonnet
    configuration
    interpreter
    json
    Download zip
    Author
    Version
    0.1.0
    License
    Apache-2.0
    Last updated
    10 days ago
    Downloads
    13

    No dependencies

    #MoonJsonnet

    An embeddable Jsonnet interpreter written in MoonBit. It parses and evaluates source directly; no Jsonnet executable or shared library is required at runtime.

    The library and CLI support lazy evaluation, lexical closures, object inheritance, controlled imports and JSON output. The language target is Jsonnet v0.21.0 with no intentionally excluded language constructs. All 163 standard-library members in the fixed reference inventory are registered; finite differential checks and documented differences define the tested scope, not universal equivalence.

    google/go-jsonnet is the functional reference for language semantics, standard-library behavior, embedding APIs and CLI behavior. The differential tools record pinned Go/C++ Jsonnet v0.21.0 results separately. See compatibility for the reference contract and tested scope.

    #Status and installation

    Version 0.1.0 targets native on macOS and Linux. File access uses POSIX libc; the evaluator itself has no third-party runtime dependency. See compatibility before adopting existing Jsonnet programs.

    The recorded compatibility boundary retains all known differences:

    Reference corpusRaw result
    C++108 cases: 98 compatible + 10 declared differences
    Go whole CLIARM64 1132/1150; AMD64 1131/1150
    Go reusable evaluator / lint20/21 / 409/410

    The documented acceptance contract retains 18 CLI and 1 lint reference-crash exceptions, the separate optional import_ast API and same-architecture NaN behavior. Those raw failures are not relabeled as passes.

    After this version is published to Mooncakes:

    moon add pangbit/moonjsonnet@0.1.0

    Import "pangbit/moonjsonnet" in your moon.pkg, and use the native target.

    A minimal executable uses these files after adding the dependency:

    # moon.pkg import { "pangbit/moonjsonnet" } supported_targets = "native" pkgtype(kind: "executable")

    ///|
    fn main raise {
    let value = @moonjsonnet.evaluate("{ answer: 6 * 7 }")
    println(value.stringify())
    }

    Save the MoonBit source as main.mbt, then run moon run --target native .. It prints {"answer":42}. Until publication, the same example can use an unpacked local archive as a workspace member; the dependency name stays the same.

    ///|
    test "readme embedded evaluation" {
    let value = @moonjsonnet.evaluate(
    "local template(n=1) = { replicas: n, port: 8080 }; template(3)",
    )
    assert_eq(value, { "replicas": 3, "port": 8080 })
    assert_eq(
    @moonjsonnet.evaluate("std.thisFile", filename="<cmdline>", anonymous=true),
    Json::string(""),
    )
    assert_eq(
    @moonjsonnet.evaluate_text("'hello'", string_output=true),
    "hello\n",
    )
    assert_eq(@moonjsonnet.evaluate_stream("[42,'hello']"), [
    "42\n", "\"hello\"\n",
    ])
    assert_eq(@moonjsonnet.evaluate_multi("{a:42}")["a"], "42\n")
    }

    The root library takes source text, JSON/Jsonnet external variables and top-level arguments, an optional host import resolver and per-evaluation limits. It returns Json or JsonnetError. evaluate_text returns formatted JSON or plain strings; evaluate_stream returns JSON documents from a top-level array. evaluate_multi returns filename/content pairs without writing files. The separate pangbit/moonjsonnet/fs package loads files within a configured root. See API and host contract.

    ///|
    test "readme code and top-level arguments" {
    let result = @moonjsonnet.evaluate(
    "function(replicas, port=8080) {replicas:replicas,port:port}",
    ext_code=Map([("base", "1+2")]),
    tla_code=Map([("replicas", "std.extVar('base')")]),
    )
    assert_eq(result, { "replicas": 3, "port": 8080 })
    }

    Host callbacks receive JSON arguments in parameter order. Trace output defaults to stderr; an optional callback receives the call location and message.

    ///|
    test "readme native callback and trace" {
    let identity : @moonjsonnet.NativeFunction = {
    params: ["value"],
    callback: args => args[0],
    }
    let messages : Array[String] = []
    let result = @moonjsonnet.evaluate(
    "std.trace('calling host', std.native('identity')(value=42))",
    native_functions=Map([("identity", identity)]),
    trace=fn(_, message) { messages.push(message) },
    )
    assert_eq(result, 42)
    assert_eq(messages, ["calling host"])
    }

    #Evaluation inspection

    Diagnostics retain source ranges and named/synthetic trace frames. The configurable DiagnosticFormatter renders Go-style errors; the original Diagnostic::format remains available. Supply source text to render static error snippets and optional color. See diagnostics.

    ///|
    test "readme structured diagnostic trace" {
    let formatter = @moonjsonnet.DiagnosticFormatter::new()
    try @moonjsonnet.evaluate("error 'failed'", filename="entry") catch {
    @moonjsonnet.Error(d) => {
    assert_eq(d.trace.length(), 2)
    assert_eq(
    formatter.format(d),
    "RUNTIME ERROR: failed\n\tentry:1:1-15\t$\n\tDuring evaluation\t\n",
    )
    }
    } noraise {
    _ => fail("runtime diagnostic expected")
    }
    }

    Evaluator hooks observe demanded expressions. Inspect values inside the callback; copy returned nodes, text or JSON to retain them. The synchronous debugger adds breakpoints, stepping, finish and termination. See API.

    ///|
    test "readme evaluation hook" {
    let evaluator = @moonjsonnet.Evaluator::new()
    let values : Array[Json] = []
    evaluator.set_eval_hook(hook={
    before: ctx => {
    if ctx.node().kind is Var("x") {
    values.push(ctx.lookup("x").json())
    }
    },
    after: (_, _) => (),
    })
    assert_eq(
    evaluator.evaluate(@moonjsonnet.Program::parse("local x=21;x*2")),
    42,
    )
    assert_eq(values, [21])
    evaluator.set_eval_hook()
    }

    #Source formatting

    format_source and ParsedSource::format format source without evaluating it or resolving imports. The formatter, editable raw tree and formatter CLI are available; their tested scope and remaining compatibility boundaries are recorded in compatibility. format_syntax_source and SyntaxDocument provide independent source-tooling budgets for deep trees; evaluator admission and existing Limits stay unchanged. RawAst exposes ordered tokens, trivia and child nodes for lossless edits. format_node formats the edited expression with separate EOF fodder; clone isolates mutable containers. See the raw syntax contract.

    ///|
    test "readme source formatting" {
    assert_eq(@moonjsonnet.format_source("{\"a\":[1,2]}"), "{ a: [1, 2] }\n")
    let source = "0" + "+1".repeat(299)
    let syntax = @moonjsonnet.SyntaxDocument::parse(source)
    assert_eq(syntax.format(), "0" + " + 1".repeat(299) + "\n")
    }

    ///|
    test "readme raw syntax editing" {
    let raw = @moonjsonnet.RawAst::parse("/*head*/ 1 //tail\n")
    let original = raw.clone()
    for i, part in raw.root.parts {
    if part is Text(text) && text.kind is TokenText {
    raw.root.parts[i] = Text({ ..text, text: "42", })
    }
    }
    assert_eq(original.text(), "/*head*/ 1 //tail\n")
    assert_eq(raw.text(), "/*head*/ 42 //tail\n")
    assert_eq(raw.format(), "/*head*/ 42 //tail\n")
    }

    #Run from source

    Validated toolchain: Moon 0.1.20260920 / MoonBit v0.10.14+7d59c7ec9.

    moon check --target native --deny-warn moon test --target native --deny-warn moon run cmd/main -- examples/environments/main.jsonnet moon run cmd/main -- examples/dashboards/main.jsonnet moon run examples/embedded

    The examples generate development/production configuration, monitoring panels, and an embedded application with import and assertion error handling. Their inputs and expected outputs are checked into the repository. See examples.

    moon run cmd/main -- --root examples/embedded/config --ext-code replicas=3 main.jsonnet moon run cmd/main -- --root examples/environments -o result.json main.jsonnet

    CLI success writes manifested output; errors go to stderr and return a nonzero status. Output is normally JSON; Go-compatible sum/avg overflow can emit +Inf, -Inf or NaN, which are outside strict JSON. See compatibility. --ext-code accepts Jsonnet expressions evaluated lazily when used. See CLI.

    #Validation and contribution

    The project includes semantic tests, executable documentation, example tests, a pinned official-reference differential suite, host/CLI integration tests and package-consumer checks. CI runs the repository's pinned checks on macOS and Linux; the full Go differential and sanitizer evidence comes from separate recorded runs. Development commands and fixtures require a source checkout and are excluded from the published archive. See repository validation, release procedure and contribution guide.

    Resource limits do not provide a general security sandbox. The host controls import permissions; read the security boundary. The project uses Apache-2.0; reference material and test provenance are recorded in NOTICE.

    JsonnetError

    pub(all) suberror JsonnetError {
    Error(Diagnostic)
    } derive(
    Debug
    )

    Ast

    pub(all) struct Ast {
    kind : AstKind
    loc : Location
    end : Location?
    }

    Editable semantic syntax tree. Containers may be edited by the caller. Program conversion copies them; comments and formatting are not represented.

    Ast::clone

    fn Ast::clone(self : Ast, limits? : Limits) -> Ast raise JsonnetError

    Copy a tree (including a subtree with free variables), checking shape and depth/node budgets. Scope validation happens when constructing a Program.

    Ast::source_range

    fn Ast::source_range(self : Ast) -> SourceRange?

    Return retained source coordinates, or None for a synthetic/unlocated node.

    test {
    let node = @moonjsonnet.Program::parse("'中'", filename="example").ast()
    assert_eq(node.source_range().unwrap().format(), "example:1:1-6")
    }

    AstBinding

    pub(all) struct AstBinding {
    name : String
    value : Ast
    range : SourceRange?
    }

    Local declaration metadata is independent of the initializer's location.

    AstClause

    pub(all) enum AstClause {
    For(String, Ast)
    When(Ast)
    }

    AstField

    pub(all) struct AstField {
    key : Ast
    value : Ast
    visibility : Int
    additive : Bool
    }

    AstKind

    pub(all) enum AstKind {
    NumberLiteral(String)
    Literal(Json)
    Var(String)
    Local(Array[AstBinding], Ast)
    If(Ast, Ast, Ast)
    Func(Array[AstParameter], Ast)
    Call(Ast, Array[(String?, Ast)], Bool)
    Arr(Array[Ast])
    Obj(Array[AstField], Array[AstBinding], Array[(Ast, Ast?)])
    ArrayComp(Ast, Array[AstClause])
    ObjectComp(Ast, Ast, Array[AstClause], Array[AstBinding])
    Index(Ast, Ast)
    Slice(Ast, Ast?, Ast?, Ast?)
    Unary(String, Ast)
    Binary(String, Ast, Ast)
    Assert(Ast, Ast?, Ast)
    Fail(Ast)
    ImportFile(String, Bool)
    ImportBinary(String)
    }

    Scalar Literal values only; use Arr/Obj for containers. Field visibility is 1 (inherit), 2 (hidden), or 3 (visible). Call's Bool denotes tailstrict.

    AstParameter

    pub(all) struct AstParameter {
    name : String
    default_value : Ast?
    range : SourceRange?
    }

    A formal parameter; repeated names are allowed, as in go-jsonnet.

    BinarySource

    pub(all) struct BinarySource {
    id : String
    bytes : Bytes
    } derive(
    Debug
    )

    Raw import response. Binary data is never decoded as UTF-8.

    DebugCommand

    pub(all) enum DebugCommand {
    Continue
    Step
    Finish
    Terminate
    }

    Return a continuation from the stop callback. Finish runs until the current expression returns, then requests a step at the next nonliteral expression.

    DebugStop

    pub(all) struct DebugStop {
    reason : DebugStopReason
    context : EvalContext
    outcome : EvalOutcome?
    }

    DebugStopReason

    pub(all) enum DebugStopReason {
    Step
    Breakpoint
    Exception
    } derive(Eq,
    Debug
    )

    DebugStopReason::equal

    DebugStopReason::not_equal

    fn DebugStopReason::not_equal(x : DebugStopReason, y : DebugStopReason) -> Bool

    DebugValue

    pub struct DebugValue {
    // private fields
    }

    DebugValue::display

    fn DebugValue::display(self : DebugValue) -> String raise JsonnetError

    Preview cached content without forcing child thunks, fields or assertions. Unevaluated/busy/failed thunks are explicit placeholders. Recursive/cyclic values and large previews obey execution step/depth and output-byte limits.

    DebugValue::json

    fn DebugValue::json(self : DebugValue) -> Json raise JsonnetError

    Manifest an explicitly inspected value; this forces its children and may execute assertions/imports. Inspection shares normal step/depth/stack budgets.

    DebugValue::kind

    fn DebugValue::kind(self : DebugValue) -> String raise JsonnetError

    Scalar/function/container category without forcing children or assertions.

    Debugger

    pub struct Debugger {
    // private fields
    }

    Synchronous host-controlled debugger. A stop callback may wait for a UI or terminal command before returning. The supplied evaluator retains its normal parameters/import configuration and caches; its previous hook is restored.

    Debugger::active_breakpoints

    fn Debugger::active_breakpoints(self : Debugger) -> Array[String]

    Debugger::breakpoint_locations

    fn Debugger::breakpoint_locations(program : Program) -> Array[SourceRange]

    Independent ranges in lowered semantic traversal order. Nodes without a source range are omitted; repeated ranges from compiler sugar are retained.

    Debugger::clear_breakpoints

    fn Debugger::clear_breakpoints(self : Debugger, filename : String) -> Unit

    Debugger::new

    fn Debugger::new(evaluator? : Evaluator) -> Debugger

    Debugger::run

    fn Debugger::run(self : Debugger, program : Program, on_stop~ : (DebugStop) -> DebugCommand raise, start_with_step? : Bool) -> String raise JsonnetError

    Evaluate text while servicing breakpoints, stepping and exception stops. Live contexts/values expire when the callback returns. Metadata and copied nodes can be retained. Reentrant runs/configuration changes are rejected.

    Debugger::set_breakpoint

    fn Debugger::set_breakpoint(self : Debugger, program : Program, line : Int, column? : Int) -> String raise JsonnetError

    Diagnostic

    pub(all) struct Diagnostic {
    kind : ErrorKind
    message : String
    location : Location
    context : Array[Location]
    trace : Array[TraceFrame]
    } derive(
    Debug
    )

    impl Show for Diagnostic

    Diagnostic::format

    fn Diagnostic::format(self : Diagnostic, max_trace? : Int) -> String

    Render the error header and context. A positive limit keeps half the context at each end (the outer end gets the odd frame); zero or negative is unlimited. The primary location stays in the header and the stored context is unchanged.

    test {
    let loc : @moonjsonnet.Location = { file: "example", line: 1, column: 1, }
    let diagnostic : @moonjsonnet.Diagnostic = {
    kind: @moonjsonnet.Runtime,
    message: "failed",
    location: loc,
    context: [loc, loc, loc],
    trace: [],
    }
    assert_eq(
    diagnostic.format(max_trace=1),
    "example:1:1: Runtime: failed\n ...\n at example:1:1",
    )
    }

    Diagnostic::format_syntax

    fn Diagnostic::format_syntax(self : Diagnostic, source : String) -> String

    Render a raw-parser diagnostic in Go formatter style, without a final LF. Successful lexing supplies the offending token's exclusive end position; lexer failures and EOF retain a point location. This is a syntax rendering helper, not an evaluator stack formatter. Source lookup is limited to 1 MiB. Computed import operands are reparsed under default syntax budgets to recover their complete range; budget failure falls back to the opening token.

    Diagnostic::format_syntax_bytes

    fn Diagnostic::format_syntax_bytes(self : Diagnostic, source : String) -> Bytes

    Render exact raw-parser diagnostic bytes, without a final LF. Fixed Go truncates malformed Unicode context at four bytes, potentially splitting a UTF-8 character; this byte API preserves that behavior. The String helper retains the normalized message exposed by Diagnostic instead.

    Diagnostic::output

    fn Diagnostic::output(self : Diagnostic, logger : &Logger) -> Unit

    Diagnostic::to_string

    fn Diagnostic::to_string(self : Diagnostic) -> String

    DiagnosticFormatter

    pub struct DiagnosticFormatter {
    // private fields
    }

    Go-style error rendering is separate from the legacy Diagnostic::format. Source text is supplied by the host. Color transforms the selected static source fragment; runtime traces do not invoke color, matching pinned Go.

    DiagnosticFormatter::clear_sources

    fn DiagnosticFormatter::clear_sources(self : DiagnosticFormatter) -> Unit

    DiagnosticFormatter::format

    fn DiagnosticFormatter::format(self : DiagnosticFormatter, diagnostic : Diagnostic) -> String

    DiagnosticFormatter::format_bytes

    fn DiagnosticFormatter::format_bytes(self : DiagnosticFormatter, diagnostic : Diagnostic) -> Bytes

    Preserve malformed-Unicode diagnostic bytes, which pinned Go may truncate inside a UTF-8 character. The String API normalizes those bytes instead.

    DiagnosticFormatter::new

    DiagnosticFormatter::set_color

    fn DiagnosticFormatter::set_color(self : DiagnosticFormatter, color? : (String) -> String) -> Unit

    DiagnosticFormatter::set_max_trace

    fn DiagnosticFormatter::set_max_trace(self : DiagnosticFormatter, size : Int) -> Unit

    DiagnosticFormatter::set_source

    fn DiagnosticFormatter::set_source(self : DiagnosticFormatter, source : Source) -> Unit

    ErrorKind

    pub(all) enum ErrorKind {
    Syntax
    Static
    Type
    Runtime
    Assertion
    Import
    Limit
    Unsupported
    } derive(Eq,
    Debug
    )

    ErrorKind::equal

    fn ErrorKind::equal(ErrorKind, ErrorKind) -> Bool

    ErrorKind::not_equal

    fn ErrorKind::not_equal(x : ErrorKind, y : ErrorKind) -> Bool

    EvalContext

    pub struct EvalContext {
    // private fields
    }

    EvalContext::evaluation_id

    fn EvalContext::evaluation_id(self : EvalContext) -> Int

    Unique within one execution; before/after callbacks share the same ID.

    EvalContext::location

    fn EvalContext::location(self : EvalContext) -> Location

    EvalContext::lookup

    fn EvalContext::lookup(self : EvalContext, name : String) -> DebugValue raise JsonnetError

    Force only the named root value. Other hooks are suppressed while inspecting.

    EvalContext::node

    fn EvalContext::node(self : EvalContext) -> Ast raise JsonnetError

    Return an independent semantic node without evaluating its children.

    EvalContext::peek

    fn EvalContext::peek(self : EvalContext, name : String) -> DebugValue? raise JsonnetError

    Return a handle only when the named thunk is already cached; never force it.

    EvalContext::source_range

    fn EvalContext::source_range(self : EvalContext) -> SourceRange?

    EvalContext::stack

    fn EvalContext::stack(self : EvalContext) -> Array[Ast] raise JsonnetError

    Copy the active expression stack, outermost first. This semantic hook stack also includes direct expressions and transparent local/conditional bodies.

    EvalContext::variables

    fn EvalContext::variables(self : EvalContext) -> Array[String] raise JsonnetError

    List lexical identifiers without forcing their values. Sorted deterministically.

    EvalHook

    pub(all) struct EvalHook {
    before : (EvalContext) -> Unit raise
    after : (EvalContext, EvalOutcome) -> Unit raise
    }

    Callbacks observe admitted expressions and their lazy results. Contexts and value handles are valid only during the callback; copy node/range/text views to retain them. Explicit lookups may force values under the execution budgets.

    EvalOutcome

    pub(all) enum EvalOutcome {
    Success(DebugValue)
    Failure(Diagnostic)
    }

    Evaluator

    pub struct Evaluator {
    // private fields
    }

    A reusable interpreter with explicit configuration and import-cache lifetime. Ext/native changes invalidate evaluated imports; TLA and trace changes do not. Replacing the importer clears both sources and values. Each execution receives fresh resource counters. Instances cannot be reentered or changed by callbacks.

    Evaluator::evaluate

    fn Evaluator::evaluate(self : Evaluator, program : Program) -> Json raise JsonnetError

    Evaluator::evaluate_multi

    fn Evaluator::evaluate_multi(self : Evaluator, program : Program) -> Map[String, String] raise JsonnetError

    Evaluator::evaluate_stream

    fn Evaluator::evaluate_stream(self : Evaluator, program : Program) -> Array[String] raise JsonnetError

    Evaluator::evaluate_text

    fn Evaluator::evaluate_text(self : Evaluator, program : Program) -> String raise JsonnetError

    Evaluator::ext_code

    fn Evaluator::ext_code(self : Evaluator, name : String, value : String) -> Unit raise JsonnetError

    Evaluator::ext_program

    fn Evaluator::ext_program(self : Evaluator, name : String, value : Program) -> Unit raise JsonnetError

    Evaluator::ext_reset

    fn Evaluator::ext_reset(self : Evaluator) -> Unit raise JsonnetError

    Evaluator::ext_var

    fn Evaluator::ext_var(self : Evaluator, name : String, value : Json) -> Unit raise JsonnetError

    Evaluator::find_dependencies

    fn Evaluator::find_dependencies(self : Evaluator, imported_from : String, imported_paths : Array[String]) -> Array[String] raise JsonnetError

    Scan dependencies without evaluation, sharing source/AST caches with this evaluator. Runtime values, ext/TLA configuration and callbacks are not forced.

    Evaluator::format_error

    fn Evaluator::format_error(self : Evaluator, diagnostic : Diagnostic) -> String

    Evaluator::format_error_bytes

    fn Evaluator::format_error_bytes(self : Evaluator, diagnostic : Diagnostic) -> Bytes

    Evaluator::import_ast

    fn Evaluator::import_ast(self : Evaluator, imported_from : String, imported_path : String) -> (Program?, String) raise JsonnetError

    Query an imported AST using Go ImportAST's nullable failure-cache behavior. The first Syntax/Static failure raises and records a nil AST; later calls to this method return None and the same identity. Importer replacement clears that history. Valid ASTs and source bytes share the ordinary import caches. Import/UTF-8/resource errors are never cached as successful nil results. This does not execute code. import_program remains strict, including retries, and its failures do not seed this method's optional failure history.

    test {
    let evaluator = @moonjsonnet.Evaluator::new()
    evaluator.set_importer(resolver=fn(_, path) { { id: path, text: "missing", } })
    try evaluator.import_ast("", "main") catch {
    @moonjsonnet.Error(d) => assert_eq(d.kind, @moonjsonnet.Static)
    } noraise {
    _ => fail("expected first parse error")
    }
    let (program, id) = evaluator.import_ast("", "main")
    assert_true(program is None)
    assert_eq(id, "main")
    }

    Evaluator::import_data

    fn Evaluator::import_data(self : Evaluator, imported_from : String, imported_path : String) -> BinarySource raise JsonnetError

    Fetch raw imported bytes and their resolved identity without UTF-8 decoding. Unlike a String result, BinarySource can preserve invalid UTF-8 and NUL bytes.

    Evaluator::import_program

    fn Evaluator::import_program(self : Evaluator, imported_from : String, imported_path : String) -> (Program, String) raise JsonnetError

    Parse and validate an imported program, returning its AST wrapper and identity. This neither executes the program nor applies TLA/ext/native configuration.

    test {
    let evaluator = @moonjsonnet.Evaluator::new()
    evaluator.set_importer(binary_resolver=fn(_, _) {
    { id: "lib/answer", bytes: b"42", }
    })
    let (program, identity) = evaluator.import_program("", "answer")
    assert_eq(identity, "lib/answer")
    assert_eq(evaluator.evaluate(program), 42)
    }

    Evaluator::native_function

    fn Evaluator::native_function(self : Evaluator, name : String, native : NativeFunction) -> Unit raise JsonnetError

    Evaluator::new

    fn Evaluator::new() -> Evaluator

    Create a reusable evaluator. Imports start disabled.

    test {
    let evaluator = @moonjsonnet.Evaluator::new()
    let program = @moonjsonnet.Program::parse("std.extVar('x')")
    evaluator.ext_var("x", 42)
    assert_eq(evaluator.evaluate(program), 42)
    evaluator.ext_var("x", 7)
    assert_eq(evaluator.evaluate(program), 7)
    }

    Evaluator::resolve_import

    fn Evaluator::resolve_import(self : Evaluator, imported_from : String, imported_path : String) -> String raise JsonnetError

    Resolve an imported identity without parsing or evaluating its contents. Source bytes are cached immediately and shared with later queries/evaluation.

    Evaluator::set_diagnostic_formatter

    fn Evaluator::set_diagnostic_formatter(self : Evaluator, formatter? : DiagnosticFormatter) -> Unit raise JsonnetError

    Configure subsequent explicit error rendering without changing execution or import caches. Omit formatter to restore a fresh default renderer per call.

    Evaluator::set_eval_hook

    fn Evaluator::set_eval_hook(self : Evaluator, hook? : EvalHook) -> Unit raise JsonnetError

    Hook replacement preserves import caches, like trace replacement. Reentrant configuration changes are rejected. Omit the callback to disable hooks.

    Evaluator::set_importer

    fn Evaluator::set_importer(self : Evaluator, resolver? : (String, String) -> Source raise, binary_resolver? : (String, String) -> BinarySource raise) -> Unit raise JsonnetError

    Replace import callbacks and invalidate all imported sources, ASTs and values. A binary callback is authoritative for all forms; otherwise the text callback supplies UTF-8 bytes. Omitting both callbacks disables imports.

    Evaluator::set_limits

    fn Evaluator::set_limits(self : Evaluator, limits : Limits) -> Unit raise JsonnetError

    Evaluator::set_string_output

    fn Evaluator::set_string_output(self : Evaluator, enabled : Bool) -> Unit raise JsonnetError

    Select raw string output for text/multi methods. JSON and stream ignore this.

    Evaluator::set_trace

    fn Evaluator::set_trace(self : Evaluator, trace : (Location, String) -> Unit) -> Unit raise JsonnetError

    Evaluator::tla_code

    fn Evaluator::tla_code(self : Evaluator, name : String, value : String) -> Unit raise JsonnetError

    Evaluator::tla_program

    fn Evaluator::tla_program(self : Evaluator, name : String, value : Program) -> Unit raise JsonnetError

    Evaluator::tla_reset

    fn Evaluator::tla_reset(self : Evaluator) -> Unit raise JsonnetError

    Evaluator::tla_var

    fn Evaluator::tla_var(self : Evaluator, name : String, value : Json) -> Unit raise JsonnetError

    FormatCommentStyle

    pub(all) enum FormatCommentStyle {
    HashComments
    SlashComments
    PreserveComments
    } derive(Eq)

    FormatCommentStyle::equal

    FormatCommentStyle::not_equal

    FormatOptions

    pub(all) struct FormatOptions {
    indent : Int
    max_blank_lines : Int
    string_style : FormatStringStyle
    comment_style : FormatCommentStyle
    pretty_field_names : Bool
    pad_arrays : Bool
    pad_objects : Bool
    sort_imports : Bool
    use_implicit_plus : Bool
    strip_everything : Bool
    strip_comments : Bool
    strip_all_but_comments : Bool
    }

    Formatting controls corresponding to go-jsonnet formatter.Options. See docs/reference/COMPATIBILITY.md for the bounded formatter validation scope.

    FormatOptions::default

    fn FormatOptions::default() -> FormatOptions

    FormatStringStyle

    pub(all) enum FormatStringStyle {
    DoubleQuotes
    SingleQuotes
    PreserveQuotes
    } derive(Eq)

    FormatStringStyle::equal

    FormatStringStyle::not_equal

    fn FormatStringStyle::not_equal(x : FormatStringStyle, y : FormatStringStyle) -> Bool

    Limits

    pub(all) struct Limits {
    steps : Int
    depth : Int
    stack : Int
    source_bytes : Int
    import_bytes : Int
    imports : Int
    output_bytes : Int
    } derive(
    Debug
    )

    Limits are per evaluation. Byte limits count UTF-8; all must be positive. stack counts runtime frames independently of the syntax/evaluation depth.

    test {
    let limits = { ..@moonjsonnet.Limits::default(), stack: 2, }
    assert_eq(@moonjsonnet.evaluate("local x=42; x", limits~), 42)
    }

    Limits::default

    fn Limits::default() -> Limits

    Limits::to_repr

    Location

    pub(all) struct Location {
    file : String
    line : Int
    column : Int
    } derive(Eq,
    Debug
    )

    Source location. Lines and UTF-8 byte columns are one-based, matching Go.

    Location::equal

    fn Location::equal(Location, Location) -> Bool

    Location::not_equal

    fn Location::not_equal(x : Location, y : Location) -> Bool

    Location::to_repr

    NativeFunction

    pub(all) struct NativeFunction {
    params : Array[String]
    callback : (Array[Json]) -> Json raise
    }

    A host callback registered by name in evaluate(native_functions=...). Arguments are fully manifested JSON in parameter order. The callback may raise an error, which becomes a runtime diagnostic at its call site.

    ParsedSource

    pub struct ParsedSource {
    // private fields
    }

    Parsed source with exact lexical spelling and trivia, before scope checking. Returned parts and AST containers are independent copies.

    ParsedSource::ast

    fn ParsedSource::ast(self : ParsedSource) -> Ast

    Return an independent semantic AST view. It omits syntax sugar and trivia; edits to this view do not change the retained source or its parts.

    ParsedSource::format

    fn ParsedSource::format(self : ParsedSource, options? : FormatOptions, limits? : Limits) -> String raise JsonnetError

    Format the retained source; the parsed document is not mutated.

    ParsedSource::parse

    fn ParsedSource::parse(source : String, filename? : String, limits? : Limits) -> ParsedSource raise JsonnetError

    Parse without evaluation or lexical-scope validation. Source and AST depth limits apply. Undefined names can be retained for formatting or analysis; Program::from_ast performs scope validation before execution.

    ParsedSource::parts

    Return tokens and intervening trivia in source order, including trailing trivia. Ranges use one-based UTF-8 byte columns and exclusive ends.

    ParsedSource::text

    fn ParsedSource::text(self : ParsedSource) -> String

    Reconstruct the original source exactly, including comments and line endings.

    Program

    pub struct Program {
    // private fields
    }

    A validated program with fixed source identity. Its AST is isolated and can be reused across evaluations; values, imports and resource counters are not shared.

    Program::assertion_ranges

    fn Program::assertion_ranges(self : Program) -> Map[String, SourceRange]

    Preserve original object-assert declaration spans when exporting an editable semantic AST. Keys are the condition's source-range identity. The result is independent; pass it to from_ast after edits that retain those identities.

    Program::ast

    fn Program::ast(self : Program) -> Ast

    Return an editable deep copy of the program's semantic AST. Mutating this tree cannot alter the program, imported caches, or another returned copy.

    Program::evaluate

    fn Program::evaluate(self : Program, ext_vars? : Map[String, Json], ext_code? : Map[String, String], ext_programs? : Map[String, Program], tla_vars? : Map[String, Json], tla_code? : Map[String, String], tla_programs? : Map[String, Program], native_functions? : Map[String, NativeFunction], trace? : (Location, String) -> Unit, resolver? : (String, String) -> Source raise, binary_resolver? : (String, String) -> BinarySource raise, limits? : Limits) -> Json raise JsonnetError

    Execute this validated program with fresh evaluation state. Options and output match evaluate; source filename and anonymous mode were fixed when parsed.

    Program::evaluate_multi

    fn Program::evaluate_multi(self : Program, string_output? : Bool, ext_vars? : Map[String, Json], ext_code? : Map[String, String], ext_programs? : Map[String, Program], tla_vars? : Map[String, Json], tla_code? : Map[String, String], tla_programs? : Map[String, Program], native_functions? : Map[String, NativeFunction], trace? : (Location, String) -> Unit, resolver? : (String, String) -> Source raise, binary_resolver? : (String, String) -> BinarySource raise, limits? : Limits) -> Map[String, String] raise JsonnetError

    Execute this validated program with fresh evaluation state. Options and output match evaluate_multi; source filename and anonymous mode were fixed when parsed.

    Program::evaluate_stream

    fn Program::evaluate_stream(self : Program, ext_vars? : Map[String, Json], ext_code? : Map[String, String], ext_programs? : Map[String, Program], tla_vars? : Map[String, Json], tla_code? : Map[String, String], tla_programs? : Map[String, Program], native_functions? : Map[String, NativeFunction], trace? : (Location, String) -> Unit, resolver? : (String, String) -> Source raise, binary_resolver? : (String, String) -> BinarySource raise, limits? : Limits) -> Array[String] raise JsonnetError

    Execute this validated program with fresh evaluation state. Options and output match evaluate_stream; source filename and anonymous mode were fixed when parsed.

    Program::evaluate_text

    fn Program::evaluate_text(self : Program, string_output? : Bool, ext_vars? : Map[String, Json], ext_code? : Map[String, String], ext_programs? : Map[String, Program], tla_vars? : Map[String, Json], tla_code? : Map[String, String], tla_programs? : Map[String, Program], native_functions? : Map[String, NativeFunction], trace? : (Location, String) -> Unit, resolver? : (String, String) -> Source raise, binary_resolver? : (String, String) -> BinarySource raise, limits? : Limits) -> String raise JsonnetError

    Execute this validated program with fresh evaluation state. Options and output match evaluate_text; source filename and anonymous mode were fixed when parsed.

    Program::from_ast

    fn Program::from_ast(tree : Ast, filename? : String, anonymous? : Bool, limits? : Limits, assertion_ranges? : Map[String, SourceRange]) -> Program raise JsonnetError

    Copy and validate a manually constructed or edited AST without execution. Depth and steps bound conversion, including cycles in caller-owned arrays. There is no source text byte count for a synthetic AST. Evaluation retains all ordinary runtime limits. Filename defaults to the root location's file; individual node locations are retained for diagnostics.

    test {
    let tree = @moonjsonnet.Program::parse("21").ast()
    let edited = { ..tree, kind: @moonjsonnet.AstKind::Binary("*", tree, tree), }
    assert_eq(@moonjsonnet.Program::from_ast(edited).evaluate(), 441)
    }

    Program::parse

    fn Program::parse(source : String, filename? : String, anonymous? : Bool, limits? : Limits) -> Program raise JsonnetError

    Parse and statically validate source without evaluating it or resolving imports. Creation enforces source and parser/AST depth limits. Evaluation rechecks source bytes and AST depth against its own limits, without reparsing the source.

    test {
    let program = @moonjsonnet.Program::parse("std.extVar('x') * 2")
    assert_eq(program.evaluate(ext_vars=Map([("x", 21)])), 42)
    assert_eq(program.evaluate(ext_vars=Map([("x", 3)])), 6)
    }

    RawAst

    pub(all) struct RawAst {
    root : RawNode
    final_fodder : Array[SourcePart]
    }

    Root syntax and the final EOF trivia, kept separately like Go's final fodder.

    RawAst::clone

    fn RawAst::clone(self : RawAst, limits? : SyntaxLimits) -> RawAst raise JsonnetError

    RawAst::format

    fn RawAst::format(self : RawAst, options? : FormatOptions, limits? : SyntaxLimits) -> String raise JsonnetError

    RawAst::parse

    fn RawAst::parse(source : String, filename? : String, limits? : SyntaxLimits) -> RawAst raise JsonnetError

    RawAst::text

    fn RawAst::text(self : RawAst, limits? : SyntaxLimits) -> String raise JsonnetError

    Serialize edited syntax exactly. Cycles/depth, nodes, steps and source bytes are bounded; this does not itself validate the resulting Jsonnet grammar.

    RawNode

    pub(all) struct RawNode {
    kind : RawNodeKind
    range : SourceRange?
    parts : Array[RawPart]
    }

    Editable concrete syntax node. Ranges and tags describe the parsed source; edits to parts change text, and reparsing refreshes metadata. Containers are caller-owned; formatting reparses the edited text without evaluating it.

    RawNode::clone

    fn RawNode::clone(self : RawNode, limits? : SyntaxLimits) -> RawNode raise JsonnetError

    Clone every child occurrence, preserving metadata and isolating mutable arrays.

    RawNode::text

    fn RawNode::text(self : RawNode, limits? : SyntaxLimits) -> String raise JsonnetError

    RawNodeKind

    pub(all) enum RawNodeKind {
    Literal
    Number
    StringLiteral
    TextBlock
    Variable
    Parens
    Local
    Binding
    Parameter
    Argument
    If
    Function
    Array
    Object
    Field
    ObjectLocal
    ObjectAssert
    ArrayComp
    ObjectComp
    ForClause
    IfClause
    Call
    ApplyBrace
    Index
    DotIndex
    Slice
    Unary(String)
    Binary(String)
    Assert
    ErrorExpr
    Import
    ImportStr
    ImportBin
    } derive(Eq,
    Debug
    )

    Structural syntax categories before semantic lowering. Spelling, operators, names and comments are authoritative in RawNode.parts, not this tag.

    RawNodeKind::equal

    fn RawNodeKind::equal(RawNodeKind, RawNodeKind) -> Bool

    RawNodeKind::not_equal

    fn RawNodeKind::not_equal(x : RawNodeKind, y : RawNodeKind) -> Bool

    RawPart

    pub(all) enum RawPart {
    Text(SourcePart)
    Child(RawNode)
    }

    Ordered lossless syntax. Every token and trivia fragment occurs exactly once.

    Source

    pub(all) struct Source {
    id : String
    text : String
    } derive(
    Debug
    )

    A host import response. id must be a stable canonical identity.

    Source::to_repr

    SourcePart

    pub(all) struct SourcePart {
    kind : SourcePartKind
    text : String
    range : SourceRange
    }

    SourcePartKind

    pub(all) enum SourcePartKind {
    TokenText
    Whitespace
    LineComment
    BlockComment
    } derive(Eq)

    Original lexical material retained for source tooling.

    SourcePartKind::equal

    SourcePartKind::not_equal

    fn SourcePartKind::not_equal(x : SourcePartKind, y : SourcePartKind) -> Bool

    SourceRange

    pub(all) struct SourceRange {
    begin : Location
    end : Location
    } derive(Eq,
    Debug
    )

    Half-open source range. Lines and UTF-8 byte columns are one-based.

    SourceRange::equal

    fn SourceRange::equal(SourceRange, SourceRange) -> Bool

    SourceRange::format

    fn SourceRange::format(self : SourceRange) -> String

    Render the same-file range using Go's location-range notation.

    SourceRange::not_equal

    fn SourceRange::not_equal(x : SourceRange, y : SourceRange) -> Bool

    SyntaxDocument

    pub struct SyntaxDocument {
    // private fields
    }

    Source document admitted with independent syntax budgets. It retains exact spelling and trivia and never performs evaluation or import resolution. Returning its semantic AST does not grant evaluator admission.

    SyntaxDocument::ast

    fn SyntaxDocument::ast(self : SyntaxDocument) -> Ast

    Export an independent semantic AST iteratively. Program::from_ast checks evaluator budgets and scope separately before execution.

    SyntaxDocument::format

    fn SyntaxDocument::format(self : SyntaxDocument, options? : FormatOptions, limits? : SyntaxLimits) -> String raise JsonnetError

    Recheck source/tree/node budgets and format without mutating the document.

    SyntaxDocument::parse

    fn SyntaxDocument::parse(source : String, filename? : String, limits? : SyntaxLimits) -> SyntaxDocument raise JsonnetError

    SyntaxDocument::parts

    Return independent lexical parts with their original UTF-8 byte ranges.

    SyntaxDocument::raw_ast

    fn SyntaxDocument::raw_ast(self : SyntaxDocument, limits? : SyntaxLimits) -> RawAst raise JsonnetError

    Export a fresh concrete tree, including parentheses, fields, declarations, parameters, arguments and clauses. No returned mutable container is shared.

    SyntaxDocument::text

    fn SyntaxDocument::text(self : SyntaxDocument) -> String

    Return the retained source exactly, including original line endings.

    SyntaxLimits

    pub(all) struct SyntaxLimits {
    parser_depth : Int
    tree_depth : Int
    nodes : Int
    steps : Int
    source_bytes : Int
    output_bytes : Int
    } derive(
    Debug
    )

    Source-tooling budgets, independent of evaluator Limits. The complete tree is admitted before export or formatting; parsing still has a recursive cap.

    SyntaxLimits::default

    fn SyntaxLimits::default() -> SyntaxLimits

    TraceFrame

    pub(all) struct TraceFrame {
    location : TraceLocation
    name : String
    } derive(Eq,
    Debug
    )

    TraceFrame::equal

    fn TraceFrame::equal(TraceFrame, TraceFrame) -> Bool

    TraceFrame::not_equal

    fn TraceFrame::not_equal(x : TraceFrame, y : TraceFrame) -> Bool

    TraceLocation

    pub(all) enum TraceLocation {
    Source(SourceRange)
    Synthetic(String)
    } derive(Eq,
    Debug
    )

    Source spans and synthetic runtime sites share the same trace representation.

    TraceLocation::equal

    TraceLocation::format

    fn TraceLocation::format(self : TraceLocation) -> String

    TraceLocation::not_equal

    fn TraceLocation::not_equal(x : TraceLocation, y : TraceLocation) -> Bool

    evaluate

    fn evaluate(source : String, filename? : String, anonymous? : Bool, ext_vars? : Map[String, Json], ext_code? : Map[String, String], ext_programs? : Map[String, Program], tla_vars? : Map[String, Json], tla_code? : Map[String, String], tla_programs? : Map[String, Program], native_functions? : Map[String, NativeFunction], trace? : (Location, String) -> Unit, resolver? : (String, String) -> Source raise, binary_resolver? : (String, String) -> BinarySource raise, limits? : Limits) -> Json raise JsonnetError

    Evaluate Jsonnet source into JSON. Imports are disabled unless a resolver is provided. Each call has independent caches and resource counters. ext_code and tla_code contain lazy Jsonnet expressions in clean scopes. Parsed ext_programs/tla_programs override code, which overrides JSON for duplicate names. Parsed parameters are lazy, with synthetic std.thisFile but their original diagnostic location and import origin. A top-level function is applied once with named TLAs; other top-level values ignore them. With anonymous=true, filename is diagnostic-only: std.thisFile and the entry import origin are empty, matching Go's EvaluateAnonymousSnippet.

    test {
    let result = @moonjsonnet.evaluate("local x = 6; { answer: x * 7 }")
    assert_eq(result.stringify(), "{\"answer\":42}")
    }

    evaluate_multi

    fn evaluate_multi(source : String, string_output? : Bool, filename? : String, anonymous? : Bool, ext_vars? : Map[String, Json], ext_code? : Map[String, String], ext_programs? : Map[String, Program], tla_vars? : Map[String, Json], tla_code? : Map[String, String], tla_programs? : Map[String, Program], native_functions? : Map[String, NativeFunction], trace? : (Location, String) -> Unit, resolver? : (String, String) -> Source raise, binary_resolver? : (String, String) -> BinarySource raise, limits? : Limits) -> Map[String, String] raise JsonnetError

    Evaluate a top-level object into filename/content pairs without filesystem writes. Values are all manifested before checking types. JSON files end in one newline; with string_output=true, raw strings have no added newline. The payload byte budget is shared by all files, excluding JSON final newlines.

    test {
    let files = @moonjsonnet.evaluate_multi("{'a.json':42,'b.json':[]}")
    assert_eq(files["a.json"], "42\n")
    assert_eq(files["b.json"], "[ ]\n")
    }

    evaluate_stream

    fn evaluate_stream(source : String, filename? : String, anonymous? : Bool, ext_vars? : Map[String, Json], ext_code? : Map[String, String], ext_programs? : Map[String, Program], tla_vars? : Map[String, Json], tla_code? : Map[String, String], tla_programs? : Map[String, Program], native_functions? : Map[String, NativeFunction], trace? : (Location, String) -> Unit, resolver? : (String, String) -> Source raise, binary_resolver? : (String, String) -> BinarySource raise, limits? : Limits) -> Array[String] raise JsonnetError

    Evaluate a top-level array to JSON documents, each ending in a newline. All visible values are manifested before checking the top-level type. The output byte budget is shared across document payloads; final newlines are excluded. String output mode does not apply to streams.

    test {
    assert_eq(@moonjsonnet.evaluate_stream("[1,'x']"), ["1\n", "\"x\"\n"])
    }

    evaluate_text

    fn evaluate_text(source : String, string_output? : Bool, filename? : String, anonymous? : Bool, ext_vars? : Map[String, Json], ext_code? : Map[String, String], ext_programs? : Map[String, Program], tla_vars? : Map[String, Json], tla_code? : Map[String, String], tla_programs? : Map[String, Program], native_functions? : Map[String, NativeFunction], trace? : (Location, String) -> Unit, resolver? : (String, String) -> Source raise, binary_resolver? : (String, String) -> BinarySource raise, limits? : Limits) -> String raise JsonnetError

    Evaluate source to Go-style JSON text or an unescaped string. Both modes append one newline. The byte budget excludes that final newline. Options and fresh evaluation state match evaluate.

    test {
    assert_eq(
    @moonjsonnet.evaluate_text("'hello'", string_output=true),
    "hello\n",
    )
    }

    find_dependencies

    fn find_dependencies(imported_from : String, imported_paths : Array[String], resolver~ : (String, String) -> BinarySource raise, limits? : Limits) -> Array[String] raise JsonnetError

    Return sorted unique transitive import identities, excluding every entry. No Jsonnet code is evaluated. The resolver serves bytes for all three import forms; only code imports are decoded and parsed. Each call has fresh caches. Like Go v0.21.0 FindDependencies, an identity seen as raw data suppresses later code traversal, and object assertions are not traversed.

    test {
    let files = Map([
    ("main", b"if false then import 'data' else 1"),
    ("data", b"42"),
    ])
    let dependencies = @moonjsonnet.find_dependencies("", ["main"], resolver=fn(
    _,
    path,
    ) {
    { id: path, bytes: files[path], }
    })
    assert_eq(dependencies, ["data"])
    }

    format_node

    fn format_node(node : RawNode, final_fodder? : Array[SourcePart], options? : FormatOptions, limits? : SyntaxLimits) -> String raise JsonnetError

    Reparse and format editable expression-node syntax with separate final trivia. Tags and retained ranges are metadata; parts determine the admitted source. The input node is not mutated and no expression/import is executed.

    format_source

    fn format_source(source : String, filename? : String, options? : FormatOptions, limits? : Limits) -> String raise JsonnetError

    Format source without evaluation or import resolution. See docs/reference/COMPATIBILITY.md for the tested Go compatibility scope.

    format_syntax_source

    fn format_syntax_source(source : String, filename? : String, options? : FormatOptions, limits? : SyntaxLimits) -> String raise JsonnetError

    Format source under independent syntax budgets, without evaluator admission.

    test {
    assert_eq(@moonjsonnet.format_syntax_source("[1,2]"), "[1, 2]\n")
    }

    lint

    fn lint(snippets : Array[Source], resolver? : (String, String) -> BinarySource raise, limits? : Limits) -> Array[Diagnostic] raise JsonnetError

    Check snippets without execution. Returns parse/static/import findings, unused bindings, eager cycles, and conservative inferred-type findings for calls, arguments, indexing and unary operators. No Jsonnet code is executed. Imports are checked recursively, but only entry snippets receive lint checks. Limits abort analysis with JsonnetError instead of returning a partial result.

    test {
    let issues = @moonjsonnet.lint([{ id: "entry", text: "local unused=1;42", }])
    assert_eq(issues[0].message, "Unused variable: unused")
    }

    Source Files