Phase 5: Multimethods + Hierarchy system
- types.janet: make-hierarchy, derive*, ancestors, descendants, isa?, underive - evaluator.janet: defmulti extended with :default and :hierarchy options + keyword dispatch-fns wrapped as (fn [x] (get x kw)) for Janet compat + hierarchy-based dispatch walks isa? chain when no direct match - core.janet: real derive, isa?, ancestors, descendants implementations replacing stubs; core-remove-method, core-remove-all-methods, core-prefer-method added; new core-bindings entries - 5 test sections (22-26): hierarchy ops, basic dispatch, :default, hierarchy dispatch, remove-method — all pass - 317 tests, 0 failures
This commit is contained in:
parent
fb66851e06
commit
a1bfd55b38
9 changed files with 329 additions and 112 deletions
|
|
@ -1,11 +1,9 @@
|
|||
Phase 0 (defn fix): compile-and-eval interns def/defn results in Jolt namespace via ns-intern so interpreter can resolve bare symbols.
|
||||
|
||||
Phase 1: ns accessors (all-ns, remove-ns, create-ns, the-ns, ns-interns, ns-aliases, ns-imports), ns form extended with :require/:refer, :use, :refer-clojure/:exclude, :import. binding macro via push-thread-bindings/pop-thread-bindings.
|
||||
|
||||
Phase 2 (PersistentHashMap): Live in src/jolt/phm.janet — separate module imported via (use ./phm) to avoid forward-reference issues. PHM is a table with :jolt/deftype tag "jolt.lang.persistent-hash-map.PersistentHashMap". Has :cnt, :buckets (array of 8 arrays), :_meta. Bucket-based: each bucket is flat [k v k v ...] array. phm-assoc, phm-dissoc, phm-get, phm-contains?, phm-entries, phm-to-struct (→ Janet struct for compatibility). Core functions updated with PHM branches: core-map?, core-hash-map, core-get, core-count, core-keys, core-vals, core-contains?, core-empty?, core-seq, core-conj, core-assoc, core-dissoc, core-merge, core-merge-with, core-into, core-=.
|
||||
|
||||
Macro expansion: resolve-macro at analyze time → expand → re-analyze. Loop: (do (var _loop_N nil) (set _loop_N (fn [params] body)) (_loop_N vals...)). Recur: emits (loop-name args...) via :loop-name in AST.
|
||||
§
|
||||
Test files: test/phase6-final.janet (47 tests, 58 assertions — collections, math, predicates, comparison, seq ops, special forms, macros, complex nesting). Phase 1 tests appended to test/compiler-test.janet (ns accessors, ns form extensions). All 317 tests pass.
|
||||
§
|
||||
Phase 3 (Var system): find-var (ctx-based, resolve-q/nq symbol), alter-meta!, reset-meta!, var-get/var-set/var?/alter-var-root all in types.janet + evaluator dispatch arms + core-bindings wrappers. core-meta fixed: (var? x) branch → var-meta, struct? branch → :meta. 10 tests pass.
|
||||
§
|
||||
Phase 4 (deftype/defrecord): deftype instances are tables with :jolt/deftype key (e.g. "user.Point"). Field access via (. obj field), mutation via (set! (.-field obj) val) — reader parses .-field as (. -field obj) in array form. core-map? recognizes table+deftype. core-count skips :jolt/deftype key. core-defrecord emits (deftype ...) + ->TypeName arrow factory + map->TypeName factory (deferred). 11 tests pass including record equality. 317 total, 0 fail.
|
||||
§
|
||||
Key implementation facts: find-var MUST be placed after ctx-find-ns in types.janet (forward-reference). intern dispatch arm needs eval-form on args. core-meta: check (var? x) before (struct? x) — var-meta returns metadata for vars. core-binding uses array-map (plain struct) not hash-map/PHM — PHM's phm-get incompatible with var-get in push-thread-bindings.
|
||||
§
|
||||
Phase 5 (Multimethods + hierarchy): Not yet started. defmulti/defmethod exist in evaluator.janet (lines 611-656) but are routed to interpreter in compile mode. core-derive, core-isa?, core-ancestors, core-descendants are stubs in core.janet.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
Bare tuples in Janet's `eval` are function calls: `(eval [1 2 3])` tries to call `1` as function. Always emit `['tuple 1 2 3]` or `(tuple 1 2 3)` in data-structure emitter. Similarly, `(eval (try body (sym handler)))` fails because `catch` is not a Janet special form — must be `(try body ([sym] handler))`. Discovered during Phase 5/6 compiler work.
|
||||
§
|
||||
Duplicate function definitions in the same file cause hard-to-diagnose "unknown symbol" errors. In compiler.janet, emit-quote-str was defined twice (once before emit-ast dispatch, once before emit-expr section). The second definition compiled but the first was used by emit-ast dispatch — causing "unknown symbol raw-form->janet". Always grep for the fn name before adding a new definition.
|
||||
§
|
||||
Janet break can't be used inside let blocks — break returns from the innermost loop, and in a let, there's no loop. Pattern: use (var found nil) + (while ... (if condition (do (set found val) (break)))) then check found after loop.
|
||||
§
|
||||
core-binding macro: use array-map (plain struct) NOT hash-map/PHM for the binding frame. PHM's phm-get doesn't work with push-thread-bindings' var-get lookup. Symptom: "error: dynamic binding" with no useful message. Fix: emit (array-map [var sym] val ...) instead of (hash-map ...).
|
||||
§
|
||||
Janet `and` returns the last truthy value (not boolean true). `(and (table? x) (get x :jolt/deftype))` returns the deftype string, which is truthy but not `true`. Wrap with `(if (and ...) true false)` when the return value must be a boolean. This hit core-map? and would hit any predicate returning the result of `and`.
|
||||
§
|
||||
set! field mutation: `(set! (.-x obj) val)` is read as `(set! (. -x obj) val)` — the target is an array with `.` as head. Check for this case BEFORE the existing `(.-field obj)` shorthand check (which is just `(. obj -field)`). The reader transforms `.`-prefixed symbols differently than expected: `.-x` becomes a symbol `-x` inside a `(. -x obj)` array form, NOT a standalone `.-x` symbol.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue