sjsonnet
CLAUDE.md319 starsChanged 5 months agoApache-2.0View on GitHub
Uses the Mill version declared in build.mill. Cross-built against the latest patch releases on the configured Scala 2.12.x (JVM only), 2.13.x, and 3.3.x LTS lines; exact versions are declared by scalaVersions and jvmScalaVersions in build.mill. Assembly JARs are written under out/sjsonnet/jvm/<scala-version>/assembly.dest/out.jar. Run one with java -Xss100m -jar out.jar. We follow the Databricks Scala style guide. Scala sources are formatted with scalafmt (config in .scalafmt.conf). The SjsonnetCrossModule and bench modules mix in ScalafmtModule. CI checks formatting on PRs, so run reformat…
In this repositoryOne other file it gives its agents.
- AGENTS.mdAGENTS.md
The file
We split this CLAUDE.md into the rules it gives an agent
4 rules. About 0 are common, 1 is distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 68
Every bug fix should include a regression test:
- Very specific: line 70
Create new_test_suite/<descriptive_name>.jsonnet with a minimal reproducer.
- Very specific: line 71
Create new_test_suite/<descriptive_name>.jsonnet.golden with expected output.
- Very specific: line 74
Run ./mill 'sjsonnet.jvm[_]'.test to verify.
≈ “Common” is an estimate: at least 1 in 5 of the 26,980 CLAUDE.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
sjsonnet Development Guide
Build & Test
Uses the Mill version declared in build.mill. Cross-built against the latest patch releases on the configured Scala 2.12.x (JVM only), 2.13.x, and 3.3.x LTS lines; exact versions are declared by scalaVersions and jvmScalaVersions in build.mill.
# Compile all JVM Scala versions
./mill 'sjsonnet.jvm[_]'.compile
# Run all JVM tests
./mill 'sjsonnet.jvm[_]'.test
# Run all tests across all platforms
./mill __.test
# Build assembly JARs
./mill 'sjsonnet.jvm[_]'.assembly
# GraalVM native image
./mill sjsonnet.graal.nativeImageAssembly JARs are written under out/sjsonnet/jvm/<scala-version>/assembly.dest/out.jar. Run one with java -Xss100m -jar out.jar.
Formatting
We follow the Databricks Scala style guide.
Scala sources are formatted with scalafmt (config in .scalafmt.conf). The SjsonnetCrossModule and bench modules mix in ScalafmtModule.
# Format all sources
./mill __.reformat
# Check formatting without changing files
./mill __.checkFormatCI checks formatting on PRs, so run reformat before committing.
Test Structure
- Framework: uTest
- Test suites (
sjsonnet/test/resources/):test_suite/— From C++ Jsonnet (upstream). ~183.jsonnetfiles with.goldenexpected output.go_test_suite/— From go-jsonnet.new_test_suite/— sjsonnet-specific tests (deep nesting, regex, etc.).
- Test sources:
test/src/— Shared tests (parser, evaluator, renderer, stdlib).test/src-jvm/— JVM-only (MainTests, file I/O, XxHash64).test/src-jvm-native/— JVM + Native (FileTests,ConfigTests, gzip, md5, sha).test/src-js/— Scala.js (FileTests).
Golden file tests
Each .jsonnet test file has a .golden file containing expected output. Files named error.* expect non-zero exit and their .golden holds the expected stderr (including stack traces).
Refresh golden outputs:
./sjsonnet/test/resources/refresh_golden_outputs.shThe .sync_ignore files in test_suite/ and go_test_suite/ list tests to skip when syncing from upstream (e.g. differing YAML parser behavior).
Writing tests for bug fixes
Every bug fix should include a regression test:
- Create
new_test_suite/<descriptive_name>.jsonnetwith a minimal reproducer. - Create
new_test_suite/<descriptive_name>.jsonnet.goldenwith expected output.- Success tests: JSON output followed by a newline, using
std.assertEqualchains ending intrue. - Error tests (filename starts with
error.): expected stderr including stack traces.
- Success tests: JSON output followed by a newline, using
- Run
./mill 'sjsonnet.jvm[_]'.testto verify.
Debug Stats
The --debug-stats flag prints runtime counters and timing to stderr after evaluation:
sjsonnet --debug-stats myfile.jsonnetOutput includes thunk creation counts, function/builtin call counts, comprehension iterations, import/parse counts, and phase timing (eval, materialize). Counters are formatted with stable labels for machine parsing. The DebugStats class (sjsonnet/src/sjsonnet/DebugStats.scala) is wired through Interpreter -> Evaluator, with parse counting handled by CountingParseCache.
Benchmarks
JMH benchmarks live in bench/. Benchmark suites in bench/resources/: bug_suite/, cpp_suite/, go_suite/, sjsonnet_suite/.
# Run all regression tests
./mill bench.runRegressions
# Run specific regressions
./mill bench.runRegressions bench/resources/bug_suite/assertions.jsonnet
# List all benchmark regression files
./mill bench.listRegressions
# Refresh benchmark golden outputs
./bench/resources/refresh_golden_outputs.shFor ad-hoc benchmarking, hyperfine is available on the PATH.
Architecture
Pipeline
Source String → Parser → Expr (AST) → StaticOptimizer → Evaluator → Val → Materializer → ujson.Value → Renderer → Output- Parser (
Parser.scala): Fastparse-based, producesExprAST. - StaticOptimizer (
StaticOptimizer.scala): Constant folding, arity specialization,ValidIdindexing, static checks. - Evaluator (
Evaluator.scala): WalksExpr, produces lazyValruntime values. Handles imports, function application, tail-call optimization. - Materializer (
Materializer.scala): WalksVal, producesujson.Value. Hybrid recursive/iterative to avoid stack overflow. - Interpreter (
Interpreter.scala): Orchestrates the full pipeline; main programmatic entry point.
Key types
Expr(Expr.scala): AST nodes — literals, binary ops, function defs/calls, objects, arrays, comprehensions, imports.ExprTagsprovides byte tags for fast pattern matching.Val(Val.scala): Runtime values —Str,Num,Bool(True/False),Null,Arr,Obj,Func,Builtin.Val.Literalextends bothValandExpr.Evaltrait:def value: Val. Implemented byLazy(deferred, cached) andVal(immediate). Arrays and objects holdEval, notVal.ValScope(ValScope.scala): Array-based lexical scope with copy-on-write extension.TailCall: Sentinel value for tail-call optimization; resolved via trampoline inTailCall.resolve.
Standard library
Implemented as Scala builtins (not Jsonnet code) in sjsonnet/src/sjsonnet/stdlib/:
StdLibModule— builds thestdobject combining all modules.ArrayModule,StringModule,ObjectModule,MathModule,TypeModule,EncodingModule,ManifestModule,SetModule,NativeRegex.
Platform-specific stdlib extensions:
- JVM:
NativeXz(xz compression),NativeGzip, regex viare2j. - Native:
NativeGzip, crypto. - JS:
scala-yamlfor YAML parsing.
Source directory layout
Shared and platform-specific sources are split across directories:
| Directory | Contents |
sjsonnet/src/ | Shared core (parser, evaluator, materializer, stdlib) |
sjsonnet/src-jvm/ | JVM main class, Platform.scala |
sjsonnet/src-jvm-native/ | Shared JVM + Native (SjsonnetMainBase, Config) |
sjsonnet/src-js/ | Scala.js main class, Platform.scala |
sjsonnet/src-native/ | Scala Native main class, Platform.scala |
Modules
sjsonnet.jvm— JVM library + CLI (SjsonnetMain)sjsonnet.js— Scala.js (CommonJS)sjsonnet.wasm— WebAssembly (via Scala.js)sjsonnet.native— Scala Nativesjsonnet.graal— GraalVM native imagesjsonnet.jvm.client/sjsonnet.jvm.server— Client-server mode (background daemon)bench— JMH benchmarksplayground— Browser-based Jsonnet playground
Error handling
Error— Main exception with message, stack frames (Error.Frame), optional cause.ParseError/StaticError— Parse-time and static analysis failures.Error.fail(msg, pos)throws with position info;Error.withStackFrame(expr)adds frames to exceptions.- Public APIs (e.g.
Interpreter.interpret) returnEither[String, ujson.Value].
Key Design Decisions
- Lazy evaluation: Arrays and objects hold
Eval(notVal).Lazywraps a thunk, caches the result, and clears the closure after first evaluation. - Tail-call optimization:
TailCallsentinel + trampoline loop inTailCall.resolve. - Hybrid materialization: Recursive up to a depth limit, then switches to an explicit stack to avoid
StackOverflowError. - Static optimization:
StaticOptimizerdoes constant folding, resolves variable names to scope indices (ValidId), and specializes function application by arity (Apply0–Apply3,ApplyBuiltin0–ApplyBuiltin4). - Parse caching:
ParseCache/DefaultParseCachecaches parsed ASTs to avoid re-parsing imports. - Platform abstraction:
PathandImportertraits abstract filesystem access across JVM/JS/Native.
Threading Model
- No internal multithreading requirement: A single
Interpreterdoes not need to evaluate a program using multiple threads. - Independent interpreters should not interfere: Applications should be able to run multiple independent
Interpreterinstances concurrently on platforms that support concurrency, without one interpreter affecting another. - Shared mutable state is exceptional: State should belong to an interpreter or evaluation unless it has an explicit concurrency contract and tests.
Dependencies
Key runtime dependencies:
fastparse— Parser combinatorsujson— JSON AST and renderingpprint— Pretty printingscalatags— HTML generation (for error output)os-lib— Filesystem (JVM/Native)mainargs— CLI argument parsing (JVM/Native)snakeyaml— YAML parsing (JVM)re2j— Regular expressions (JVM)xz/lz4-java— Compression (JVM)
CI
GitHub Actions workflows in .github/workflows/:
pr-build.yaml— PR validation: compile + test across JVM/JS/WASM/Native × JDK 17/21/25, format check, GraalVM native tests.release-build.yaml— Release builds (JAR, JS, WASM, playground, native binaries).
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

