- resolve-macro: resolve symbols to macro vars via ctx - Macro expansion in analyze-form: detects macro heads, expands, re-analyzes - compile-ast: emits Janet data structures with resolved core fn values - compile-and-eval uses compile-ast (no source parse roundtrip) - eval-string routes macros through compiler (expanded at analyze time) - Fix - mapping: core-sub (core-- doesn't exist) - All 317 tests pass + 6 new Phase 4 macro tests
3.2 KiB
| name | description |
|---|---|
| jolt-dev | Jolt development workflow — build, test, special form patterns, Janet gotchas |
Jolt Development
Build & Test
cd /Users/yogthos/src/jolt
jpm build # produces build/jolt
jpm test # runs all tests
janet test/foo.janet # run a single test file from project root
Compiler Development
See jolt-compiler skill for the Clojure→Janet source-to-source compiler workflow.
Janet Eval Pipeline (critical)
Janet's (parse s) does NOT return a parsed form — it returns [symbol, error-position].
For evaluating Janet source strings, use the parser pipeline:
(def p (parser/new))
(parser/consume p source)
(parser/eof p) # REQUIRED — otherwise produce returns nil
(def form (parser/produce p))
(eval form)
Never try (eval [if true 1 2]) — Janet's eval doesn't recognize special forms in tuple data structures.
var vs def
When you need to mutate a local with set, use (var x nil) not (def x nil). def creates constants.
Compiler (see also jolt-compiler skill)
src/jolt/compiler.janet — Clojure→Janet source compiler with macro expansion.
test/compiler-test.janet — 11 test groups covering all ops.
Key design decision: compile-and-eval emits Janet DATA STRUCTURES, not source strings, because Janet's eval doesn't see use-imported symbols. core-fn-values table resolves Janet names to actual function values at compile time.
Special Form Checklist
To add a new special form to the evaluator AND compiler:
- Add the name to
special-symbol?insrc/jolt/evaluator.janet - Add a match arm in
eval-list(the match onname) - Add tests in
test/evaluator-test.janet
The match arm receives ctx, bindings, and form (the full list). Use (in form 1) for first arg, etc.
Non-symbol heads (keywords, etc.): eval-list first checks (and (struct? first-form) (= :symbol (...))) before extracting name. If not a symbol, falls through to default function application.
Current special forms (22):
quote, syntax-quote, unquote, unquote-splicing, do, if, def, defmacro, fn*, let*, loop*, recur, throw, try, set!, var, locking, instance?, defmulti, defmethod, deftype, new, .
Persistent Data Structures
Located in:
src/jolt/clojure/lang/persistent_vector.cljsrc/jolt/clojure/lang/persistent_hash_map.clj
Loaded at init time by load-persistent-structures in api.janet. Use {:mutable? true} to skip and use Janet-native types.
Implementation detail
Simple array-based implementation (node-assoc/node-find/find-key-index), NOT HAMT bit-trie. HAMT failed because Janet uses 64-bit doubles and bit operations require 32-bit signed ints.
Janet Gotchas
- Bit operations (brshift, brushift, band) use 32-bit signed integers. Hash values can exceed 32-bit range. Use
(band x 0xFFFFFFFF)before shifting. deftypecreates tables, not structs.struct?returns false.(get child :key)DOES follow table prototype chain — resolved and confirmed working.- Janet LSP produces many false positives on
.janetfiles — safe to ignore.
Symbol representation
Jolt symbols are {:jolt/type :symbol :ns <string-or-nil> :name <string>} as produced by the reader.