;; 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 ;; Transformations ((= 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))) ;; Expression nodes ((= tag "app") (list :app (hk-desugar (nth node 1)) (hk-desugar (nth node 2)))) ((= 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)))) ;; Top-level ((= tag "program") (list :program (map hk-desugar (nth node 1)))) ((= tag "module") (list :module (nth node 1) (nth node 2) (nth node 3) (map hk-desugar (nth node 4)))) ;; Decls carrying a body ((= tag "fun-clause") (list :fun-clause (nth node 1) (nth node 2) (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)))) ;; Everything else: leaf literals, vars, cons, patterns, ;; types, imports, type-sigs, data / newtype / fixity, … (:else node))))))) ;; Convenience — tokenize + layout + parse + desugar. (define hk-core (fn (src) (hk-desugar (hk-parse-top src)))) (define hk-core-expr (fn (src) (hk-desugar (hk-parse src))))