compiler: self-hosted analyzer compiles set literals (#{…})

Stage 1 Task 1. analyzer.clj punted set literals to the interpreter
((form-set? form) (uncompilable "set literal")); now it builds the set-node IR
(already defined in ir.clj) from (form-set-items form), and backend.janet emits
(make-phs e1 e2 …) — each element evaluated then the persistent set built,
mirroring compiler.janet's emit-set-expr and the interpreter's :jolt/set path.

Closes a self-hosted-analyzer vs bootstrap-compiler parity gap: #{…} no longer
forces interpreter fallback on the compile path.

Gate: conformance 262x3 (+4 set-literal cases incl computed elements / empty /
in-let), fixpoint, self-host, sci, suite 3981/66, specs+unit green; core-bench
neutral (A/B). set?/disj-as-fns remain deliberately interpreted (in-sync across
all three lists) — adjudicated in Task 2.
This commit is contained in:
Yogthos 2026-06-08 22:38:35 -04:00
parent fdf2c742c4
commit b6d9247b80
3 changed files with 16 additions and 3 deletions

View file

@ -15,12 +15,12 @@
declared the bootstrap compiles forward refs through var cells, but keeping declared the bootstrap compiles forward refs through var cells, but keeping
them to one keeps the compiled namespace simple." them to one keeps the compiled namespace simple."
(:require [jolt.ir :refer [const local var-ref host-ref if-node do-node invoke (:require [jolt.ir :refer [const local var-ref host-ref if-node do-node invoke
def-node let-node fn-node vector-node map-node def-node let-node fn-node vector-node map-node set-node
quote-node throw-node]] quote-node throw-node]]
[jolt.host :refer [form-sym? form-sym-name form-sym-ns form-list? [jolt.host :refer [form-sym? form-sym-name form-sym-ns form-list?
form-vec? form-map? form-set? form-char? form-vec? form-map? form-set? form-char?
form-literal? form-elements form-vec-items form-literal? form-elements form-vec-items
form-map-pairs form-special? compile-ns form-map-pairs form-set-items form-special? compile-ns
form-macro? form-expand-1 resolve-global form-macro? form-expand-1 resolve-global
form-sym-meta host-intern! form-syntax-quote-lower]])) form-sym-meta host-intern! form-syntax-quote-lower]]))
@ -207,6 +207,6 @@
(form-map? form) (map-node (mapv (fn [p] [(analyze ctx (first p) env) (form-map? form) (map-node (mapv (fn [p] [(analyze ctx (first p) env)
(analyze ctx (second p) env)]) (analyze ctx (second p) env)])
(form-map-pairs form))) (form-map-pairs form)))
(form-set? form) (uncompilable "set literal") (form-set? form) (set-node (mapv #(analyze ctx % env) (form-set-items form)))
(form-list? form) (analyze-list ctx form env) (form-list? form) (analyze-list ctx form env)
:else (uncompilable "unsupported form")))) :else (uncompilable "unsupported form"))))

View file

@ -230,6 +230,12 @@
(array/push args (emit ctx (in p 1)))) (array/push args (emit ctx (in p 1))))
(tuple/slice args)) (tuple/slice args))
# A set literal: build (make-phs e1 e2 …) so each element is evaluated at runtime
# then the persistent set is constructed — mirrors compiler.janet's emit-set-expr.
(defn- emit-set [ctx node]
(def items (map |(emit ctx $) (vview (node :items))))
(tuple/slice (array/concat @[phm/make-phs] items)))
(set emit (set emit
(fn emit [ctx raw] (fn emit [ctx raw]
(def node (norm-node raw)) (def node (norm-node raw))
@ -259,6 +265,7 @@
:invoke (emit-invoke ctx node) :invoke (emit-invoke ctx node)
:vector (emit-vector ctx node) :vector (emit-vector ctx node)
:map (emit-map ctx node) :map (emit-map ctx node)
:set (emit-set ctx node)
:quote ['quote (node :form)] :quote ['quote (node :form)]
(error (string "backend: unhandled op " (node :op)))))) (error (string "backend: unhandled op " (node :op))))))

View file

@ -50,6 +50,12 @@
["map as fn default" "99" "({:a 1} :z 99)"] ["map as fn default" "99" "({:a 1} :z 99)"]
["set as fn" "2" "(#{1 2 3} 2)"] ["set as fn" "2" "(#{1 2 3} 2)"]
["set as fn miss" "nil" "(#{1 2 3} 9)"] ["set as fn miss" "nil" "(#{1 2 3} 9)"]
# set literals compile (Stage 1 Task 1): computed elements are each evaluated
# then the persistent set is built, matching the interpreter.
["set literal computed" "true" "(= #{1 2} #{(inc 0) 2})"]
["empty set literal" "true" "(empty? #{})"]
["set literal count" "3" "(count #{1 2 3})"]
["set literal in let" "true" "(let [x 5] (= #{5 6} #{x (inc x)}))"]
["keyword as fn" "1" "(:a {:a 1})"] ["keyword as fn" "1" "(:a {:a 1})"]
["map fn over coll" "(quote (1 3))" "(map {:a 1 :b 3} [:a :b])"] ["map fn over coll" "(quote (1 3))" "(map {:a 1 :b 3} [:a :b])"]