(fn match [val ...]
(fn match-pattern [vals pattern unifications]
(let [[val] vals]
(if (and (sym? pattern) (or (in-scope? pattern)
(= :nil (tostring pattern))))
(values `(= @val @pattern) [])
(and (sym? pattern)
(. unifications (tostring pattern)))
(values `(= @(. unifications (tostring pattern)) @val) [])
(sym? pattern)
(do (if (~= (tostring pattern) "_")
(tset unifications (tostring pattern) val))
(values (if (: (tostring pattern) :find "^?")
true `(~= @(sym :nil) @val))
[pattern val]))
(list? pattern)
(let [condition `(and)
bindings []]
(each [i pat (ipairs pattern)]
(let [(subcondition subbindings) (match-pattern [(. vals i)] pat
unifications)]
(table.insert condition subcondition)
(each [_ b (ipairs subbindings)]
(table.insert bindings b))))
(values condition bindings))
(= (type pattern) :table)
(let [condition `(and (= (type @val) :table))
bindings []]
(each [k pat (pairs pattern)]
(if (and (sym? pat) (= "&" (tostring pat)))
(do (assert (not (. pattern (+ k 2)))
"expected rest argument in final position")
(table.insert bindings (. pattern (+ k 1)))
(table.insert bindings [`(select @k (unpack @val))]))
(and (= :number (type k))
(= "&" (tostring (. pattern (- k 1)))))
nil (let [subval `(. @val @k)
(subcondition subbindings) (match-pattern [subval] pat
unifications)]
(table.insert condition subcondition)
(each [_ b (ipairs subbindings)]
(table.insert bindings b)))))
(values condition bindings))
(values `(= @val @pattern) []))))
(fn match-condition [vals clauses]
(let [out `(if)]
(for [i 1 (# clauses) 2]
(let [pattern (. clauses i)
body (. clauses (+ i 1))
(condition bindings) (match-pattern vals pattern {})]
(table.insert out condition)
(table.insert out `(let @bindings @body))))
out))
(fn val-syms [clauses]
(let [syms (list (gensym))]
(for [i 1 (# clauses) 2]
(if (list? (. clauses i))
(each [valnum (ipairs (. clauses i))]
(if (not (. syms valnum))
(tset syms valnum (gensym))))))
syms))
(let [clauses [...]
vals (val-syms clauses)]
(if (~= 0 (% (# clauses) 2)) (table.insert clauses (# clauses) (sym :_)))
(list (sym :let) [vals val]
(match-condition vals clauses))))