- evaluator.janet: fix set! to handle (.-field obj) shorthand Previously only recognized (. obj -field) format - core.janet: rewrite core-defrecord to emit (deftype ...) instead of plain def with array-map constructor - core.janet: fix core-map? to recognize deftype table instances (and returns last truthy, not boolean true — wrap in if) - core.janet: fix core-count to skip :jolt/deftype key for records - core.janet: add ->TypeName arrow factory that calls TypeName. constructor via Clojure form emission - 11 new tests: deftype fields/mutation/instance?, defrecord construction/map-behavior/fields/factories, record equality - map->TypeName factory deferred (needs get/symbol resolution fix) - 317 tests pass, 0 failures
3.8 KiB
jolt-compiler
Source-to-source Clojure→Janet compiler. Two-phase: analyze-form (classify + macro expand) → emit-ast (generate).
Architecture
Clojure form → analyze-form [form bindings ctx] → AST {:op ...}
↓ (if head = macro var)
expand → re-analyze expanded form
↓
emit-ast (source string) or emit-expr (data structure)
Three public entry points:
(compile-form form &opt ctx)→ Janet source string (debug/display)(compile-ast form &opt ctx)→ Janet data structure (for eval)(compile-and-eval form ctx)→ compile-ast + eval + ns interning
Why data structures, not source strings
Janet's eval does NOT have access to use-imported symbols. Emit Janet tuples with embedded function VALUES via core-fn-values table.
Source-to-source (compile-form + emit-ast) exists for debugging, NOT used by compile-and-eval.
Macro expansion
analyze-form checks resolve-macro before special form dispatch:
- Look up symbol in current ns → core ns
- If
var-macro?, apply fn, re-analyze expanded form
Symbol classification
Order: qualified ns → local binding → core-symbol → bare symbol
core-renames MUST match actual fn names: "-" → "core-sub" (not "core--").
core-fn-values
Maps Janet string names to actual function values. Must be kept in sync with core-renames.
Special mappings: "apply" → apply (built-in), "some" → core-some?, "pr-str" → core-str, "nth" → core-nth.
compile-and-eval interning
def/defn results are interned in the Jolt namespace via ns-intern so the interpreter can resolve bare symbols. This is critical — without it, (defn foo [x] x) followed by (foo 1) would fail with "Unable to resolve symbol: foo".
Loop/recur compilation
loop* → (do (var name nil) (set name (fn [params] body)) (name init-vals...))
recur → (loop-name arg...) via :jolt/current-loop binding
Throw/try compilation
throw→(error val)in Janettry→(try body ([err] handler))— Janet uses([sym] handler), NOT(catch sym handler)
Quote — use raw-form->janet
Don't re-analyze quoted forms. Use raw-form->janet to pass Jolt reader forms verbatim to Janet's quote.
Remaining ops (interpreter only)
syntax-quote, set!, deftype, defmulti, defmethod — stateful/complex, always use interpreter.
Stateful forms (interpreter only even in compile mode)
defmacro, ns, deftype, defmulti, defmethod, require, in-ns
Note: def IS handled by compiler (macros expanded at analyze time).
eval-string dispatch (compile mode)
Stateful check: defmacro, ns, deftype, defmulti, defmethod, require, in-ns, syntax-quote, set!, var, ., new → interpreter. Everything else → compile-and-eval.
Adding a new op
- Add
analyze-formmatch arm - Add
emit-ast+emit-exprmatch arms - Update
core-renames+core-fn-valuesif core fn - Add tests in
test/compiler-test.janet
Var system (Phase 3)
var-get, var-set, var?, alter-var-root, find-var, alter-meta!, reset-meta!, intern — all dispatched in evaluator, with Clojure wrappers in core-bindings. find-var takes a ctx + symbol, must eval args first (NOT pass raw form). core-meta handles vars via var-meta branch.
PersistentHashMap (Phase 2)
src/jolt/phm.janet — separate module, bucket-based with :jolt/deftype tag. core-binding macro uses array-map (not hash-map) to avoid PHM get() incompatibility with push-thread-bindings.
Test files
test/compiler-test.janet— all compiler tests (Phases 0-3)test/phase6-final.janet— 47 comprehensive compile-mode tests
Run: janet test/compiler-test.janet or jpm test