(defpackage :patmatch (:use :cl :alexandria :serapeum) (:export pat-match)) (in-package :patmatch) (eval-when (:compile-toplevel :load-toplevel :execute) (defgeneric handle-pattern (pattern form &rest args) (:method ((pattern cons) form &rest args) (let ((val-sym (gensym "VAL"))) (destructuring-bind (car cdr) args `((,val-sym ,form) (,car (car ,val-sym)) (,cdr (cdr ,val-sym)))))) (:method ((pattern vector) form &rest args) (let ((val-sym (gensym "VAL"))) `((,val-sym ,form) ,@ (loop for arg in args for idx from 0 collect `(,arg (aref ,val-sym ,idx)))))) (:method ((pattern hash-table) form &rest args) (let* ((val-sym (gensym "VAL")) (binding-forms (loop for (key sym) in args append `((,sym (gethash ',key ,val-sym)))))) `((,val-sym ,form) ,@binding-forms))) (:method ((pattern symbol) form &rest args) (apply #'handle-pattern (closer-mop:class-prototype (find-class pattern)) form args)))) (defmacro pattern-match ((&rest clauses) &body body) `(let* (,@ (loop for ((discriminator . args) val-form) in clauses append (apply 'handle-pattern discriminator val-form args))) ,@body)) (pattern-match (((hash-table (:a a) (:b b)) (plist-hash-table '(:a 1 :b 2)))) (+ a b))