Files
rose-ash/lib/haskell/desugar.sx
giles 76d141737a
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 1m6s
haskell: Phase 14 — record update r { field = v } (parser + desugar + eval)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 16:43:20 +00:00

390 lines
12 KiB
Plaintext

;; Desugar the Haskell surface AST into a smaller core AST.
;;
;; Eliminates the three surface-only shapes produced by the parser:
;; :where BODY DECLS → :let DECLS BODY
;; :guarded GUARDS → :if C1 E1 (:if C2 E2 … (:app error …))
;; :list-comp EXPR QUALS → concatMap-based expression (§3.11)
;;
;; Everything else (:app, :op, :lambda, :let, :case, :do, :tuple,
;; :list, :range, :if, :neg, :sect-left / :sect-right, plus all
;; leaf forms and pattern / type nodes) is passed through after
;; recursing into children.
(define
hk-guards-to-if
(fn
(guards)
(cond
((empty? guards)
(list
:app
(list :var "error")
(list :string "Non-exhaustive guards")))
(:else
(let
((g (first guards)))
(list
:if
(hk-desugar (nth g 1))
(hk-desugar (nth g 2))
(hk-guards-to-if (rest guards))))))))
;; do-notation desugaring (Haskell 98 §3.14):
;; do { e } = e
;; do { e ; ss } = e >> do { ss }
;; do { p <- e ; ss } = e >>= \p -> do { ss }
;; do { let decls ; ss } = let decls in do { ss }
(define
hk-desugar-do
(fn
(stmts)
(cond
((empty? stmts) (raise "empty do block"))
((empty? (rest stmts))
(let ((s (first stmts)))
(cond
((= (first s) "do-expr") (hk-desugar (nth s 1)))
(:else
(raise "do block must end with an expression")))))
(:else
(let
((s (first stmts)) (rest-stmts (rest stmts)))
(let
((rest-do (hk-desugar-do rest-stmts)))
(cond
((= (first s) "do-expr")
(list
:app
(list
:app
(list :var ">>")
(hk-desugar (nth s 1)))
rest-do))
((= (first s) "do-bind")
(list
:app
(list
:app
(list :var ">>=")
(hk-desugar (nth s 2)))
(list :lambda (list (nth s 1)) rest-do)))
((= (first s) "do-let")
(list
:let
(map hk-desugar (nth s 1))
rest-do))
(:else (raise "unknown do-stmt tag")))))))))
;; List-comprehension desugaring (Haskell 98 §3.11):
;; [e | ] = [e]
;; [e | b, Q ] = if b then [e | Q] else []
;; [e | p <- l, Q ] = concatMap (\p -> [e | Q]) l
;; [e | let ds, Q ] = let ds in [e | Q]
(define
hk-lc-desugar
(fn
(e quals)
(cond
((empty? quals) (list :list (list e)))
(:else
(let
((q (first quals)))
(let
((qtag (first q)))
(cond
((= qtag "q-guard")
(list
:if
(hk-desugar (nth q 1))
(hk-lc-desugar e (rest quals))
(list :list (list))))
((= qtag "q-gen")
(list
:app
(list
:app
(list :var "concatMap")
(list
:lambda
(list (nth q 1))
(hk-lc-desugar e (rest quals))))
(hk-desugar (nth q 2))))
((= qtag "q-let")
(list
:let
(map hk-desugar (nth q 1))
(hk-lc-desugar e (rest quals))))
(:else
(raise
(str
"hk-lc-desugar: unknown qualifier tag "
qtag))))))))))
(define
hk-desugar
(fn
(node)
(cond
((not (list? node)) node)
((empty? node) node)
(:else
(let
((tag (first node)))
(cond
((= tag "where")
(list
:let (map hk-desugar (nth node 2))
(hk-desugar (nth node 1))))
((= tag "guarded") (hk-guards-to-if (nth node 1)))
((= tag "list-comp")
(hk-lc-desugar (hk-desugar (nth node 1)) (nth node 2)))
((= tag "app")
(list
:app (hk-desugar (nth node 1))
(hk-desugar (nth node 2))))
((= tag "rec-update")
(list
:rec-update
(hk-desugar (nth node 1))
(map
(fn (p) (list (first p) (hk-desugar (nth p 1))))
(nth node 2))))
((= tag "rec-create")
(let
((cname (nth node 1))
(field-pairs (nth node 2))
(field-order (hk-record-field-names cname)))
(cond
((nil? field-order)
(raise (str "rec-create: no record info for " cname)))
(:else
(let
((acc (list :con cname)))
(begin
(for-each
(fn
(fname)
(let
((pair
(hk-find-rec-pair field-pairs fname)))
(cond
((nil? pair)
(raise
(str
"rec-create: missing field "
fname
" for "
cname)))
(:else
(set!
acc
(list
:app
acc
(hk-desugar (nth pair 1))))))))
field-order)
acc))))))
((= tag "op")
(list
:op (nth node 1)
(hk-desugar (nth node 2))
(hk-desugar (nth node 3))))
((= tag "neg") (list :neg (hk-desugar (nth node 1))))
((= tag "if")
(list
:if (hk-desugar (nth node 1))
(hk-desugar (nth node 2))
(hk-desugar (nth node 3))))
((= tag "tuple") (list :tuple (map hk-desugar (nth node 1))))
((= tag "list") (list :list (map hk-desugar (nth node 1))))
((= tag "range")
(list
:range (hk-desugar (nth node 1))
(hk-desugar (nth node 2))))
((= tag "range-step")
(list
:range-step (hk-desugar (nth node 1))
(hk-desugar (nth node 2))
(hk-desugar (nth node 3))))
((= tag "lambda")
(list :lambda (nth node 1) (hk-desugar (nth node 2))))
((= tag "let")
(list
:let (map hk-desugar (nth node 1))
(hk-desugar (nth node 2))))
((= tag "case")
(list
:case (hk-desugar (nth node 1))
(map hk-desugar (nth node 2))))
((= tag "alt")
(list :alt (nth node 1) (hk-desugar (nth node 2))))
((= tag "do") (hk-desugar-do (nth node 1)))
((= tag "sect-left")
(list :sect-left (nth node 1) (hk-desugar (nth node 2))))
((= tag "sect-right")
(list :sect-right (nth node 1) (hk-desugar (nth node 2))))
((= tag "program")
(list :program (map hk-desugar (hk-expand-records (nth node 1)))))
((= tag "module")
(list
:module (nth node 1)
(nth node 2)
(nth node 3)
(map hk-desugar (hk-expand-records (nth node 4)))))
((= tag "fun-clause")
(list
:fun-clause (nth node 1)
(nth node 2)
(hk-desugar (nth node 3))))
((= tag "instance-decl")
(list
:instance-decl (nth node 1)
(nth node 2)
(map hk-desugar (nth node 3))))
((= tag "pat-bind")
(list :pat-bind (nth node 1) (hk-desugar (nth node 2))))
((= tag "bind")
(list :bind (nth node 1) (hk-desugar (nth node 2))))
(:else node)))))))
;; Convenience — tokenize + layout + parse + desugar.
(define hk-record-fields (dict))
(define
hk-register-record-fields!
(fn (cname fields) (dict-set! hk-record-fields cname fields)))
(define
hk-record-field-names
(fn
(cname)
(if (has-key? hk-record-fields cname) (get hk-record-fields cname) nil)))
(define
hk-record-field-index
(fn
(cname fname)
(let
((fields (hk-record-field-names cname)))
(cond
((nil? fields) -1)
(:else
(let
((i 0) (idx -1))
(begin
(for-each
(fn
(f)
(begin (when (= f fname) (set! idx i)) (set! i (+ i 1))))
fields)
idx)))))))
(define
hk-find-rec-pair
(fn
(pairs name)
(cond
((empty? pairs) nil)
((= (first (first pairs)) name) (first pairs))
(:else (hk-find-rec-pair (rest pairs) name)))))
(define
hk-record-accessors
(fn
(cname rec-fields)
(let
((n (len rec-fields)) (i 0) (out (list)))
(define
hk-ra-loop
(fn
()
(when
(< i n)
(let
((field (nth rec-fields i)))
(let
((fname (first field)) (j 0) (pats (list)))
(define
hk-pat-loop
(fn
()
(when
(< j n)
(begin
(append!
pats
(if
(= j i)
(list "p-var" "__rec_field")
(list "p-wild")))
(set! j (+ j 1))
(hk-pat-loop)))))
(hk-pat-loop)
(append!
out
(list
"fun-clause"
fname
(list (list "p-con" cname pats))
(list "var" "__rec_field")))
(set! i (+ i 1))
(hk-ra-loop))))))
(hk-ra-loop)
out)))
(define
hk-expand-records
(fn
(decls)
(let
((out (list)))
(for-each
(fn
(d)
(cond
((and (list? d) (= (first d) "data"))
(let
((dname (nth d 1))
(tvars (nth d 2))
(cons-list (nth d 3))
(deriving (if (> (len d) 4) (nth d 4) (list)))
(new-cons (list))
(accessors (list)))
(begin
(for-each
(fn
(c)
(cond
((= (first c) "con-rec")
(let
((cname (nth c 1)) (rec-fields (nth c 2)))
(begin
(hk-register-record-fields!
cname
(map (fn (f) (first f)) rec-fields))
(append!
new-cons
(list
"con-def"
cname
(map (fn (f) (nth f 1)) rec-fields)))
(for-each
(fn (a) (append! accessors a))
(hk-record-accessors cname rec-fields)))))
(:else (append! new-cons c))))
cons-list)
(append!
out
(if
(empty? deriving)
(list "data" dname tvars new-cons)
(list "data" dname tvars new-cons deriving)))
(for-each (fn (a) (append! out a)) accessors))))
(:else (append! out d))))
decls)
out)))
(define hk-core (fn (src) (hk-desugar (hk-parse-top src))))
(define hk-core-expr (fn (src) (hk-desugar (hk-parse src))))