Skip to content

feat(clojure): add full Clojure port at TS-canonical parity#74

Merged
rjrodger merged 7 commits into
mainfrom
claude/clojure-ts-parity-fqmhwe
Jun 20, 2026
Merged

feat(clojure): add full Clojure port at TS-canonical parity#74
rjrodger merged 7 commits into
mainfrom
claude/clojure-ts-parity-fqmhwe

Conversation

@rjrodger

Copy link
Copy Markdown
Contributor

Add a Clojure implementation of voxgig/struct that is a faithful port of
the canonical TypeScript (modelled on the Python port, which shares
Clojure's single-nil world). All 1329 shared-corpus cases pass and the
port defines every canonical public function.

  • Nodes are mutable java.util.LinkedHashMap / ArrayList to preserve the
    reference-stable in-place mutation the algorithm depends on.
  • nil unifies undefined and JSON null; Group A/B rules and a NOARG
    sentinel recover the distinctions where they matter.
  • Lower-smushed public names match the canonical export list, so
    tools/check_parity.py matches case/underscore-insensitively.
  • Zero third-party runtime deps; the test runner ships an in-tree JSON
    reader and the shared runner logic (clojure -M:test).

Wire-up: clojure/{src,test,deps.edn,Makefile,README,DOCS,AGENTS}; add
clojure to check_parity.py (COMPLETE_PORTS + a (defn ...) extractor),
the top-level Makefile LANGS/PUBLISH_LANGS, and the README/AGENTS/REPORT
parity tables; ignore clojure build artifacts.

Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01GpdHxY9WrVtJv7v8bntrVZ

claude added 7 commits June 20, 2026 16:12
Add a Clojure implementation of voxgig/struct that is a faithful port of
the canonical TypeScript (modelled on the Python port, which shares
Clojure's single-nil world). All 1329 shared-corpus cases pass and the
port defines every canonical public function.

- Nodes are mutable java.util.LinkedHashMap / ArrayList to preserve the
  reference-stable in-place mutation the algorithm depends on.
- nil unifies undefined and JSON null; Group A/B rules and a NOARG
  sentinel recover the distinctions where they matter.
- Lower-smushed public names match the canonical export list, so
  tools/check_parity.py matches case/underscore-insensitively.
- Zero third-party runtime deps; the test runner ships an in-tree JSON
  reader and the shared runner logic (clojure -M:test).

Wire-up: clojure/{src,test,deps.edn,Makefile,README,DOCS,AGENTS}; add
clojure to check_parity.py (COMPLETE_PORTS + a (defn ...) extractor),
the top-level Makefile LANGS/PUBLISH_LANGS, and the README/AGENTS/REPORT
parity tables; ignore clojure build artifacts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GpdHxY9WrVtJv7v8bntrVZ
Add an OCaml implementation of voxgig/struct that is a faithful port of the
canonical TypeScript. Like TS (and Rust), OCaml keeps undefined (Noval) and
JSON null (Null) distinct, so this port mirrors the canonical logic directly.
All 1329 shared-corpus cases pass and the port defines every canonical
public function.

- A single `value` variant models JSON-shaped data; nodes are mutable and
  reference-stable (lists are `value list ref`, maps an in-tree ordered map),
  so the in-place algorithms behave identically to the reference.
- Group A readers (getprop/getelem/haskey) collapse Null to "no value";
  Group B processors use the raw `lookup_` to preserve Null.
- Numbers are a single `Num of float` with Number.isInteger semantics.
- Lower-smushed / snake_cased public names match the canonical export list
  case/underscore-insensitively (check_placement = checkPlacement, etc.).
- Zero third-party deps: in-tree JSON reader for the runner and an in-tree
  RE2-subset backtracking regex engine (vregex.ml) for $LIKE and re_*.

Wire-up: ocaml/{src,test,Makefile,README,DOCS,AGENTS}; add ocaml to
check_parity.py (COMPLETE_PORTS + a let/and extractor), the top-level Makefile
LANGS/PUBLISH_LANGS, and the README/AGENTS/REPORT parity tables; ignore OCaml
build artifacts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GpdHxY9WrVtJv7v8bntrVZ
Add a Scala 3 implementation of voxgig/struct that is a faithful port of the
canonical TypeScript (ported from the OCaml port). Like TS/Rust/OCaml, Scala
keeps undefined (Noval) and JSON null (VNull) distinct, so this port mirrors
the canonical logic directly. All 1329 shared-corpus cases pass and the port
defines every canonical public function.

- A single sealed `Value` ADT models JSON-shaped data; nodes are mutable and
  reference-stable (lists are ArrayBuffer, maps a mutable LinkedHashMap that
  preserves insertion order), so the in-place algorithms behave identically.
- Group A readers (getprop/getelem/haskey) collapse VNull to "no value";
  Group B processors use the raw `lookup_` to preserve VNull.
- Numbers are a single VNum(Double) with Number.isInteger semantics.
- Lower-smushed / camelCased public names match the canonical export list
  case/underscore-insensitively (checkPlacement, injectorArgs, injectChild).
- Zero third-party deps: in-tree JSON reader for the runner and the JVM
  stdlib java.util.regex for $LIKE and the re_* API.

Wire-up: scala/{src,test,Makefile,README,DOCS,AGENTS}; add scala to
check_parity.py (COMPLETE_PORTS + a `def` extractor), the top-level Makefile
LANGS/PUBLISH_LANGS, and the README/AGENTS/REPORT parity tables; ignore Scala
build artifacts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GpdHxY9WrVtJv7v8bntrVZ
Promote Java and Kotlin from "Partial" to "Complete": both define the full
canonical API and now pass the shared build/test corpus in full.

Java had 6 genuine corpus failures (the "trails canonical by a release" gap),
all in the test runner's null handling rather than the library:
- The corpus runner encoded an *absent* input as the "__NULL__" marker; it now
  leaves an absent `in` as UNDEF so subjects see "undefined" (fixes walk.basic
  with no input).
- minor.stringify and minor.pathify ran with null:true; the canonical harness
  runs them with null:false so a JSON-null value stays a real null
  (stringify -> "null", pathify -> "<unknown-path:null>", null path parts
  dropped). Java now passes 1300/1300; baseline refreshed.

Kotlin already passed the corpus in full (1315/1315) — no code change needed.

Move both ports from PARTIAL_PORTS to COMPLETE_PORTS in check_parity.py (still
`ok`), and update the README/AGENTS/REPORT status tables and the per-port
README/AGENTS notes accordingly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GpdHxY9WrVtJv7v8bntrVZ
Add a Dart implementation of voxgig/struct that is a faithful port of the
canonical TypeScript. Dart has a single `null`, so this port follows the
Python/Clojure single-null model: `null` represents both the canonical
`undefined` and JSON `null`, with the Group A/B rules recovering the
distinction. All 1329 shared-corpus cases pass and the port defines every
canonical public function.

- Nodes are native Dart collections — `Map<String, dynamic>` (a LinkedHashMap,
  insertion-ordered) and growable `List<dynamic>` — which are mutable and
  reference-stable, exactly as the algorithm requires.
- Group A readers (getprop/getelem/haskey) collapse null to "no value";
  Group B processors use the raw `_lookup` to preserve null. A public
  `pathifyNoArg` sentinel distinguishes "no argument" from null for
  typify/stringify/pathify.
- JSON integers are `int`, decimals `double`; typify treats a whole double as
  an integer (Number.isInteger semantics); stringify/jsonify follow JS number
  formatting.
- Zero third-party deps: library uses only dart:core (incl. RegExp); the test
  runner reads the corpus with the SDK dart:convert.

Wire-up: dart/{lib,test,pubspec.yaml,Makefile,README,DOCS,AGENTS}; add dart to
check_parity.py (COMPLETE_PORTS; the generic name(...) extractor matches Dart
functions), the top-level Makefile LANGS/PUBLISH_LANGS, and the
README/AGENTS/REPORT parity tables; ignore Dart build artifacts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GpdHxY9WrVtJv7v8bntrVZ
Faithful Elixir port of the canonical TypeScript implementation, passing
the entire shared corpus (1329/1329) with zero third-party runtime
dependencies (only Elixir/OTP: ETS for the heap, core Regex).

The BEAM has no mutable, reference-stable native collection, so nodes are
kept in a small ETS-backed heap: a node is a tagged reference
({:vmap, id} / {:vlist, id}) whose contents are replaced on mutation while
the reference stays stable, emulating the canonical shared-reference
semantics. Like the Python/Clojure/Dart ports, Elixir has a single nil for
both undefined and JSON null; the Group A/B rules plus a NOARG sentinel
recover the distinction.

- elixir/lib/voxgig_struct.ex: the full library (all 48 canonical functions)
- elixir/test/runner.exs: corpus runner with a small hand-written JSON parser
  (no Jason/Poison) reading the corpus straight into heap nodes
- elixir/{Makefile,README.md,DOCS.md,AGENTS.md}: build + docs
- Wire into tools/check_parity.py, top Makefile, README, AGENTS, REPORT,
  .gitignore

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GpdHxY9WrVtJv7v8bntrVZ
Faithful Haskell port of the canonical TypeScript implementation, passing
the entire shared corpus (1329/1329) with zero third-party dependencies
(only GHC boot libraries: base, array).

Haskell has no mutable, reference-stable native collection, so a node
carries an IORef to its contents (VList holds IORef [Value], VMap holds
IORef of ordered pairs) — the analog of OCaml's ref or Rust's Rc<RefCell> —
and the whole API therefore runs in IO. Like the OCaml/Scala ports, the
canonical undefined (VNoval) and JSON null (VNull) are distinct
constructors, so the port mirrors the canonical logic directly.

- haskell/src/VoxgigStruct.hs: the full library (all 48 canonical functions)
- haskell/src/Vregex.hs: in-tree RE2-subset backtracking regex engine
- haskell/test/Runner.hs: corpus runner with a hand-written JSON reader
  (no aeson) building IORef-backed nodes directly
- haskell/{Makefile,README.md,DOCS.md,AGENTS.md}: build + docs
- Wire into tools/check_parity.py (new _HASKELL_DECL extractor for top-level
  type signatures), top Makefile, README, AGENTS, REPORT, .gitignore

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GpdHxY9WrVtJv7v8bntrVZ
@rjrodger rjrodger merged commit e56bb27 into main Jun 20, 2026
95 of 96 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants