Chez Phase 1 (increment 3h): host-interop method-call emit

(.method target arg*) now analyzes to a :host-call IR node instead of
punting at analyze. The Chez back end lowers it to a jolt-host-call
dispatch for the methods the RT shims (.write -> port display,
.isDirectory -> file-directory?, .listFiles -> directory-list); any
other method stays out of subset (clean emit-time reject, so it can't
read as a compiled-but-broken corpus divergence). The Janet back end
punts ALL :host-call to the interpreter, same shape as letfn: compiles
on Chez, interprets on Janet, zero change to the main language.

Closes the io tier's print-method defmethods and file-seq: prelude emit
reach 348 -> 354/355 (50-io 20/20). The one remaining gap is the regex
literal in parse-uuid (needs a regex engine on Chez; deferred).

emit-test 122/122; Chez subset 672/672, 0 divergences; full gate green.
This commit is contained in:
Yogthos 2026-06-17 18:58:44 -04:00
parent 0f7d2753a8
commit b1cdfd1c9b
9 changed files with 96 additions and 13 deletions

View file

@ -276,6 +276,23 @@
(uncompilable (str "var of non-var " (form-sym-name sym)))))
(uncompilable (str "special form " op))))
;; Host interop method call (jolt-0kf5). `(.method target arg*)` — a head that
;; starts with "." but not ".-" (field access stays punted). Analyzes to a
;; :host-call node; the Janet back end punts it at emit (no interop model -> the
;; interpreter runs it), the Chez back end lowers it to a jolt-host-call dispatch.
(defn- method-head? [nm]
(and (> (count nm) 1)
(= "." (subs nm 0 1))
(not (= "-" (subs nm 1 2)))))
(defn- analyze-host-call [ctx hname items env]
(when (< (count items) 2)
(throw (str "Malformed member expression, expecting (.method target ...): " hname)))
{:op :host-call
:method (subs hname 1)
:target (analyze ctx (nth items 1) env)
:args (mapv #(analyze ctx % env) (drop 2 items))})
(defn- analyze-symbol [ctx form env]
(let [nm (form-sym-name form) ns (form-sym-ns form)]
(cond
@ -311,6 +328,8 @@
(cond
(and hname (not shadowed) (contains? handled hname))
(analyze-special ctx hname items env)
(and hname (not shadowed) (method-head? hname))
(analyze-host-call ctx hname items env)
(and hname (not shadowed) (form-special? hname))
(uncompilable (str "special form " hname))
(and (form-sym? head) (not shadowed) (form-macro? ctx head))

View file

@ -101,6 +101,8 @@
(= op :fn) (assoc node :arities (mapv (fn [a] (assoc a :body (f (get a :body))))
(get node :arities)))
(= op :def) (assoc node :init (f (get node :init)))
(= op :host-call) (assoc node :target (f (get node :target))
:args (mapv f (get node :args)))
;; :catch-body / :finally are optional; recurse them only when PRESENT.
;; Assoc'ing them nil-when-absent would turn the node into a phm (jolt's
;; nil-valued-key representation) and force backend densification — so we