Commit graph

16 commits

Author SHA1 Message Date
Yogthos
c275112a5f feat: transients, unchecked arithmetic, hash helpers + fix REPL set printer
More clojure.core coverage (real implementations, not stubs):
- transient/persistent!/conj!/assoc!/dissoc!/pop! as correctness-preserving
  aliases (Jolt collections are persistent, so no in-place speedup but correct)
- unchecked-* arithmetic (Jolt numbers don't overflow -> plain ops)
- hash-combine/hash-ordered-coll/hash-unordered-coll (24-bit masked to stay
  in integer range), ex-cause (ex-info now records a cause), prefers, random-uuid

Fix a pre-existing REPL bug: the set printer in main.janet iterated the backing
phm's table keys (:buckets/:cnt) instead of the elements, so every set printed
as #{:buckets :cnt} at the REPL (pr-str was already correct). Now uses phs-seq.

conformance 218/218, features 78/78, jank 120.
2026-06-04 19:43:19 -04:00
Yogthos
a0c9696900 feat: persistent singly-linked lists with O(1) conj/cons prepend
Round 3 of the persistent-collections work. Lists were immutable Janet arrays,
so conj/cons-prepend was an O(n) copy (O(n^2) to build a list) — a large perf
gap vs Clojure's PersistentList.

Add src/jolt/plist.janet: an immutable cons-cell list (first/rest/count), same
algorithm as Clojure/CLJS/jank PersistentList. conj/cons onto a list now creates
an O(1) node that shares the existing list as its tail (no copy), with a cached
O(1) count. Repeated conj is O(n) total instead of O(n^2).

Hooked plist through first/rest/next/seq/count/peek/pop/nth/empty/empty?, the
predicates (list?/seq?/coll?/sequential?), realize-for-iteration, =, coll->cells
(concat/lazy), both printers, destructuring, and instance? tags. (list ...) and
quoted lists stay arrays; only conj/cons introduce plist nodes, so the surface
and risk stay small.

Verified: reduce-conj of 200k elements runs in ~0.4s (was effectively O(n^2)).
conformance 206/206, features 78/78 (+7 list regressions), jank 120 (+1).
2026-06-04 19:20:27 -04:00
Yogthos
1eb2843365 feat: structural-sharing persistent vectors (immutable build) + mutable toggle
Round 2 of the persistent-collections work.

Add a real 32-way branching-trie persistent vector (src/jolt/pv.janet) with a
tail buffer: O(log32 n) conj/assoc/nth/pop, with unchanged subtrees shared by
identity. Vector literals and vec/vector/conj/assoc/subvec/etc. now produce and
maintain these in the default (immutable) build, replacing the old tuple-based
vectors. Every core seq op, the destructurer, IFn application, the printers, =,
and the evaluator's literal/splice paths were taught to handle the pvec type.

Define several Clojure seq fns that were silently leaking to Janet builtins
(some, keep, interleave, flatten, mapcat, interpose) and broke once vectors
became tables; normalize collections through realize-for-iteration everywhere.

Build-time JOLT_MUTABLE flag now selects fast Janet-native mutable collections:
in that mode vectors are arrays (conj appends in place, vector? true, print []),
sharing one representation with lists. Default build is immutable.

Tests: conformance 206/206, features 71/71, jank 119 (baseline). Test helpers
normalized so Janet-level = compares against tuple literals regardless of repr.
The 2 test-load-sci failures (bit-clear/get-method) pre-date this work.
2026-06-04 18:56:55 -04:00
Yogthos
3e12ef31e3 fix: assert was a no-op (fell through to Janet builtin); add proper Clojure assert macro that throws on falsy; err-message unwraps ex-info 2026-06-04 17:58:30 -04:00
Yogthos
7d490843e5 feat: clojure-features demo runs end-to-end (Janet interop in §14, real exception demo in §11); fix syntax-quote (auto-gensym foo#, leave core/special unqualified) so macros work; (. obj sym) is a zero-arg method call; add long/double/float/num coercions; print Janet stack trace on errors (REPL/file/-e) 2026-06-04 17:08:18 -04:00
Yogthos
e87f6db10e feat: CLI file execution — 'jolt file.clj [args]' runs a script (binds *command-line-args*/*file*), '-e EXPR' evaluates, '-h' help; fix print/println/pr/prn to space-separate, render collections, and not double-newline 2026-06-04 16:40:04 -04:00
Yogthos
0d841ef83d feat: proper char type ({:jolt/type :jolt/char :ch N}) — reader literals, char?/char/int/str, =, seq/first/rest/nth of strings yield chars, char printing; fix REPL multi-line input accumulation 2026-06-04 15:40:59 -04:00
Yogthos
7ec2fdfa18 feat: close conformance gaps — full atoms/volatiles/delays, quot/rem/mod sign semantics, ~40 core fns (split-at/take-nth/butlast/filterv/mapv/reduced/min-key/etc), sorted-map/set, regex (#"..." reader + PEG engine + re-find/matches/seq + string split/replace), Math statics, ex-info/ex-data/ex-message, namespaced keywords, vary-meta/defonce/macroexpand-1/letfn/doseq; fix doto/assoc-on-vector/frequencies/find/coll?/sort/partition; recursive equality 2026-06-04 15:27:36 -04:00
Yogthos
77c52553cb fix: REPL lazy-seq printer walks full chain (capped); def/defn return var (prints #'ns/name); fix var-name printer bug 2026-06-04 14:55:11 -04:00
Yogthos
0e08f65016 Phases 15-16: SCI bootstrap, Janet interop, eval, lazy-cat, CLJS ported tests
- SCI bootstrap complete: all 9 SCI source files load (317 forms, 0 failures)
- prefer-method/remove-method/remove-all-methods promoted to special forms
- eval special form (interpreter + compiler) with eval-test.janet
- lazy-cat macro with structural equality tests in lazy-test.janet
- Janet-native interop via . special form on tables/structs:
  field access (. obj :key), method calls (. obj method args...)
  fn* form compilation support, .- reader sugar
  interop-test.janet with 7 test sections (14 assertions)
- New core bindings: with-meta, var-dynamic?, load-string
- ^:dynamic def handler, core-str nil handling, core-meta for with-meta
- 7 new CLJS ported test files: cljs-port-6 through -10, cljs-core-test, cljs-collections-test
- test-sci-runtime.janet verifies SCI namespaces/types/Var/IBox/IVar
- 317/317 tests pass, 0 failing scripts, 440+ assertions across 31 test files
- README updated with Janet interop documentation
2026-06-03 23:44:49 -04:00
Yogthos
e63c2ce8d5 Fix REPL: buffer-based output prevents interleaved raw tuples
Rewrite print-value/print-collection to use StringBuffer:
- write-collection/v buf appends formatted output to buf
- write-value/v buf dispatches by type, appends to buf
- print-value creates buffer, builds string, outputs atomically

Prevents Janet C runtime from interleaving <tuple 0x...> with
formatted [v1 v2 ...] output in jpm build executables.

315 ok, 2 fail (pre-existing, unchanged)
2026-06-03 01:07:42 -04:00
Yogthos
5cd9c47fe8 Fix REPL: tuple rendering via struct? collision
Janet's struct? returns true for tuples, causing print-value's
symbol-struct check to call (get tuple :jolt/type) which fails with
'expected integer key for tuple...got :name'.

Fix: reorder cond in print-value — check tuple? and array? before
struct? checks. Split (or tuple? array? struct? table?) into
individual arms so cond stops at the first match.

( range 10) now renders [0 1 2 3 4 5 6 7 8 9] instead of error.
315 ok, 2 fail (pre-existing, unchanged)
2026-06-03 00:45:09 -04:00
Yogthos
09c4cb2242 Fix REPL collection rendering + namespace initialization
- print-value now renders tuples as [1 2 3], arrays as (1 2 3),
  structs/tables as {:k v}, sets as #{v}, keywords as :kw,
  Jolt symbols as name or ns/name
- print-value uses prin (no trailing newline) for all scalars;
  only adds newline at end of collection output
- print-collection recursively renders nested values
- ctx-set-current-ns "user" after init so REPL starts in user ns
- Forward var declaration for mutual recursion between
  print-collection and print-value

Note: tuple/struct eval in piped REPL fails due to pre-existing
evaluator issue ("expected integer key for tuple") — this is
NOT caused by the print changes. Scalars render correctly.
315 tests pass, 0 regressions.
2026-06-02 23:48:17 -04:00
Yogthos
32a1fff1b8 Fix REPL collection rendering — print values instead of memory addresses
- print-value now renders tuples as [val1 val2 ...]
- print-value now renders arrays as (val1 val2 ...)
- print-value now renders structs/tables as {key val ...}
- print-value now renders sets as #{val1 val2 ...}
- print-value renders keywords with : prefix
- Forward var declaration for mutual recursion (print-collection → print-value)
- 315 tests pass, 0 regressions
2026-06-02 23:41:49 -04:00
Yogthos
b20536d1e3 bootstrap: SCI core deps loading with 284/304 forms passing
reader: #?@ empty splice fix (nil→@[]), #? nil→:jolt/skip, map reader handles #_/#?@ in K/V
evaluator: unwrap-meta-name helper, deftype interns ->Name too, dot-suffix fix
core: comment/prefer-method stubs, *unchecked-math*/*clojure-version* dynamic vars
2026-06-01 23:24:13 -04:00
Yogthos
cdcf569506 Initial commit: Jolt — Clojure interpreter on Janet
- PEG-based Clojure reader (symbols, keywords, numbers, strings, lists,
  vectors, maps, sets, quote forms, reader macros, metadata)
- Tree-walking evaluator (quote, do, if, def, fn*, let*, loop*/recur,
  syntax-quote/unquote/unquote-splicing, macro system, ns/require/in-ns)
- 95+ clojure.core functions (predicates, math, collections, seq ops,
  higher-order, atoms, I/O)
- Public API (init, eval-string, eval-string*)
- REPL (jolt/main.janet)
- 7 test suites, all green
- MIT license
2026-06-01 16:48:56 -04:00