1. (defgeneric consify (obj)
  2. (:documentation "Return a consy representation of something"))
  3. (defmacro define-transparent-consify (type)
  4. (with-gensyms (obj)
  5. `(defmethod consify ((,obj ,type))
  6. ,obj)))
  7. (define-transparent-consify number)
  8. (define-transparent-consify character)
  9. (define-transparent-consify string)
  10. (define-transparent-consify symbol)
  11. (define-transparent-consify pathname)
  12. (defvar *unconsify-keywords* nil)
  13. (defmethod consify ((obj list))
  14. ;; a more flexible implementation might do (keywordp (car obj)), but this makes the output uglier
  15. (if (member (car obj) *unconsify-keywords*)
  16. (cons :quote (mapcar #'consify obj))
  17. (mapcar #'consify obj)))
  18. (defgeneric consify-initargs (obj)
  19. (:documentation "Return a list of initargs that would recreate this class."))
  20. (defmethod consify ((obj standard-object))
  21. `(:make-instance ,(class-name (class-of obj)) ,@(consify-initargs obj)))
  22. (defun consify-initargs-from-slots (obj &rest slot-names)
  23. (let* ((class (class-of obj))
  24. (slots (remove-if-not
  25. (lambda (slot)
  26. (and (c2mop:slot-definition-initargs slot)
  27. (or (null slot-names)
  28. (member (c2mop:slot-definition-name slot) slot-names))))
  29. (c2mop:class-slots class))))
  30. (loop for slot in slots
  31. when (and
  32. (slot-boundp obj (c2mop:slot-definition-name slot))
  33. (c2mop:slot-definition-initargs slot))
  34. append (list (car (c2mop:slot-definition-initargs slot))
  35. (consify (slot-value obj (c2mop:slot-definition-name slot)))))))
  36. (defmethod consify-initargs ((obj standard-object))
  37. (consify-initargs-from-slots obj))
  38. (defgeneric unconsify-list (car cdr)
  39. (:documentation "A list with a keyword in the car was found during unconsify! The first element
  40. was car, the rest were cdr."))
  41. (defun unconsify (obj)
  42. "Rehydrate the output of consify."
  43. (etypecase obj
  44. ((or string number character symbol pathname) obj)
  45. (list (if (keywordp (car obj))
  46. (unconsify-list (car obj) (cdr obj))
  47. (mapcar #'unconsify obj)))))
  48. (defmethod unconsify-list (car cdr)
  49. (mapcar #'unconsify (cons car cdr)))
  50. (defmacro defunconsify (keyword car-var cdr-var &body body)
  51. (declare (symbol keyword car-var cdr-var))
  52. `(progn
  53. (push ,keyword *unconsify-keywords*)
  54. (defmethod unconsify-list ((,car-var (eql ,keyword)) ,cdr-var)
  55. ,@body)))
  56. (defunconsify :quote car cdr
  57. (mapcar #'unconsify cdr))
  58. (defunconsify :make-instance car cdr
  59. ;; Verify the format of the list as we go along, for good measure
  60. (check-type (car cdr) symbol)
  61. (apply #'make-instance (car cdr)
  62. (loop for it in (cdr cdr)
  63. for argname-p = t then (not argname-p)
  64. when argname-p
  65. collect (progn
  66. (check-type it keyword)
  67. it)
  68. else
  69. collect (unconsify it)
  70. finally (assert (not argname-p) () "Odd number of initargs while unconsifying!"))))
  71. (defun consify-copy (orig)
  72. "Copy any consifiable object by value."
  73. (unconsify (consify orig)))