Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Has been cancelled
202 lines
6.2 KiB
Plaintext
202 lines
6.2 KiB
Plaintext
;; Value-level pattern matching.
|
|
;;
|
|
;; Constructor values are tagged lists whose first element is the
|
|
;; constructor name (a string). Tuples use the special tag "Tuple".
|
|
;; Lists use the spine of `:` cons and `[]` nil.
|
|
;;
|
|
;; Just 5 → ("Just" 5)
|
|
;; Nothing → ("Nothing")
|
|
;; (1, 2) → ("Tuple" 1 2)
|
|
;; [1, 2] → (":" 1 (":" 2 ("[]")))
|
|
;; () → ("()")
|
|
;;
|
|
;; Primitive values (numbers, strings, chars) are stored raw.
|
|
;;
|
|
;; The matcher takes a pattern AST node, a value, and an environment
|
|
;; dict; it returns an extended dict on success, or `nil` on failure.
|
|
|
|
;; ── Value builders ──────────────────────────────────────────
|
|
(define
|
|
hk-mk-con
|
|
(fn
|
|
(cname args)
|
|
(let ((result (list cname)))
|
|
(for-each (fn (a) (append! result a)) args)
|
|
result)))
|
|
|
|
(define
|
|
hk-mk-tuple
|
|
(fn
|
|
(items)
|
|
(let ((result (list "Tuple")))
|
|
(for-each (fn (x) (append! result x)) items)
|
|
result)))
|
|
|
|
(define hk-mk-nil (fn () (list "[]")))
|
|
|
|
(define hk-mk-cons (fn (h t) (list ":" h t)))
|
|
|
|
(define
|
|
hk-mk-list
|
|
(fn
|
|
(items)
|
|
(cond
|
|
((empty? items) (hk-mk-nil))
|
|
(:else
|
|
(hk-mk-cons (first items) (hk-mk-list (rest items)))))))
|
|
|
|
;; ── Predicates / accessors on constructor values ───────────
|
|
(define
|
|
hk-is-con-val?
|
|
(fn
|
|
(v)
|
|
(and
|
|
(list? v)
|
|
(not (empty? v))
|
|
(string? (first v)))))
|
|
|
|
(define hk-val-con-name (fn (v) (first v)))
|
|
|
|
(define hk-val-con-args (fn (v) (rest v)))
|
|
|
|
;; ── The matcher ────────────────────────────────────────────
|
|
;;
|
|
;; Pattern match forces the scrutinee to WHNF before inspecting it
|
|
;; — except for `p-wild`, `p-var`, and `p-lazy`, which never need
|
|
;; to look at the value. Args of constructor / tuple / list values
|
|
;; remain thunked (they're forced only when their own pattern needs
|
|
;; to inspect them, recursively).
|
|
(define
|
|
hk-match
|
|
(fn
|
|
(pat val env)
|
|
(cond
|
|
((not (list? pat)) nil)
|
|
((empty? pat) nil)
|
|
(:else
|
|
(let
|
|
((tag (first pat)))
|
|
(cond
|
|
((= tag "p-wild") env)
|
|
((= tag "p-var") (assoc env (nth pat 1) val))
|
|
((= tag "p-lazy") (hk-match (nth pat 1) val env))
|
|
((= tag "p-as")
|
|
(let
|
|
((res (hk-match (nth pat 2) val env)))
|
|
(cond
|
|
((nil? res) nil)
|
|
(:else (assoc res (nth pat 1) val)))))
|
|
(:else
|
|
(let ((fv (hk-force val)))
|
|
(cond
|
|
((= tag "p-int")
|
|
(if
|
|
(and (number? fv) (= fv (nth pat 1)))
|
|
env
|
|
nil))
|
|
((= tag "p-float")
|
|
(if
|
|
(and (number? fv) (= fv (nth pat 1)))
|
|
env
|
|
nil))
|
|
((= tag "p-string")
|
|
(if
|
|
(and (string? fv) (= fv (nth pat 1)))
|
|
env
|
|
nil))
|
|
((= tag "p-char")
|
|
(if
|
|
(and (string? fv) (= fv (nth pat 1)))
|
|
env
|
|
nil))
|
|
((= tag "p-con")
|
|
(let
|
|
((pat-name (nth pat 1)) (pat-args (nth pat 2)))
|
|
(cond
|
|
((not (hk-is-con-val? fv)) nil)
|
|
((not (= (hk-val-con-name fv) pat-name)) nil)
|
|
(:else
|
|
(let
|
|
((val-args (hk-val-con-args fv)))
|
|
(cond
|
|
((not (= (len pat-args) (len val-args)))
|
|
nil)
|
|
(:else
|
|
(hk-match-all
|
|
pat-args
|
|
val-args
|
|
env))))))))
|
|
((= tag "p-tuple")
|
|
(let
|
|
((items (nth pat 1)))
|
|
(cond
|
|
((not (hk-is-con-val? fv)) nil)
|
|
((not (= (hk-val-con-name fv) "Tuple")) nil)
|
|
((not (= (len (hk-val-con-args fv)) (len items)))
|
|
nil)
|
|
(:else
|
|
(hk-match-all
|
|
items
|
|
(hk-val-con-args fv)
|
|
env)))))
|
|
((= tag "p-list")
|
|
(hk-match-list-pat (nth pat 1) fv env))
|
|
(:else nil))))))))))
|
|
|
|
(define
|
|
hk-match-all
|
|
(fn
|
|
(pats vals env)
|
|
(cond
|
|
((empty? pats) env)
|
|
(:else
|
|
(let
|
|
((res (hk-match (first pats) (first vals) env)))
|
|
(cond
|
|
((nil? res) nil)
|
|
(:else
|
|
(hk-match-all (rest pats) (rest vals) res))))))))
|
|
|
|
(define
|
|
hk-match-list-pat
|
|
(fn
|
|
(items val env)
|
|
(let ((fv (hk-force val)))
|
|
(cond
|
|
((empty? items)
|
|
(if
|
|
(and
|
|
(hk-is-con-val? fv)
|
|
(= (hk-val-con-name fv) "[]"))
|
|
env
|
|
nil))
|
|
(:else
|
|
(cond
|
|
((not (hk-is-con-val? fv)) nil)
|
|
((not (= (hk-val-con-name fv) ":")) nil)
|
|
(:else
|
|
(let
|
|
((args (hk-val-con-args fv)))
|
|
(let
|
|
((h (first args)) (t (first (rest args))))
|
|
(let
|
|
((res (hk-match (first items) h env)))
|
|
(cond
|
|
((nil? res) nil)
|
|
(:else
|
|
(hk-match-list-pat
|
|
(rest items)
|
|
t
|
|
res)))))))))))))
|
|
|
|
;; ── Convenience: parse a pattern from source for tests ─────
|
|
;; (Uses the parser's case-alt entry — `case _ of pat -> 0` —
|
|
;; to extract a pattern AST.)
|
|
(define
|
|
hk-parse-pat-source
|
|
(fn
|
|
(src)
|
|
(let
|
|
((expr (hk-parse (str "case 0 of " src " -> 0"))))
|
|
(nth (nth (nth expr 2) 0) 1))))
|