97 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Common Lisp
		
	
	
	
			
		
		
	
	
			97 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Common Lisp
		
	
	
	
| ;;;; kmop.lisp
 | |
| 
 | |
| (in-package #:kutils-mop)
 | |
| 
 | |
| ;;; metaobject interactions
 | |
| 
 | |
| (defun sym-lookup (sym &optional (package *package*))
 | |
|   (find-symbol (mkstr sym) package))
 | |
| 
 | |
| (defun class-symbol (sym &optional (package *package*))
 | |
|   (cond
 | |
|     ((symbolp sym)
 | |
|      (whenlet (sym (sym-lookup sym package))
 | |
|        (find-class sym)))
 | |
|     ((keywordp sym) nil)
 | |
|     (t sym)))
 | |
| 
 | |
| (defvar *standard-object* (class-symbol 'standard-object))
 | |
| 
 | |
| (defun subclassp (child parent)
 | |
|   (whenlet ((child  (class-symbol child))
 | |
| 	    (parent (class-symbol parent)))
 | |
|     (closer-mop:subclassp child parent)))
 | |
| 
 | |
| (defun objectp (sym)
 | |
|   (subclassp sym *standard-object*))
 | |
| 
 | |
| (defun get-class-initargs (slots)
 | |
|   (flatten
 | |
|    (mapcar #'closer-mop:slot-definition-initargs slots)))
 | |
| 
 | |
| 
 | |
| (defun list-all-slots (class-sym &optional (package *package*))
 | |
|   "Given a class symbol (and optionally a package to search in),
 | |
| return a list of all the slots in an instance of that class."
 | |
|   (let ((class-val (find-class
 | |
| 			  (find-symbol
 | |
| 			   (mkstr class-sym)
 | |
| 			   package))))
 | |
|     (when class-val
 | |
|       (closer-mop:ensure-finalized class-val)
 | |
|       (flatten
 | |
|        (remove-if (lambda (slot)
 | |
| 		    (null (closer-mop:slot-definition-initargs slot)))
 | |
| 		  (closer-mop:class-slots class-val))))))
 | |
| 
 | |
| (defun kw-key (slot &optional snake-case)
 | |
|   (let ((s (string-downcase
 | |
| 	    (mkstr
 | |
| 	     (first
 | |
| 	      (closer-mop:slot-definition-initargs slot))))))
 | |
|     (if snake-case
 | |
| 	(nsubstitute #\- #\_ s)
 | |
| 	(nsubstitute #\_ #\- s))))
 | |
| 
 | |
| (defun dispatch-get-value (slot value)
 | |
|   (let ((slot-type (closer-mop:slot-definition-type slot)))
 | |
|     (if (objectp slot-type)
 | |
| 	(make-instance-from-hash-table slot-type value)
 | |
| 	value)))
 | |
| 
 | |
| (defun slot-initarg (slot)
 | |
|   (first
 | |
|    (closer-mop:slot-definition-initargs slot)))
 | |
| 
 | |
| (defun slot-table-value (slot ht &optional snake-case)
 | |
|   (when ht
 | |
|     (gethash (kw-key slot snake-case) ht)))
 | |
| 
 | |
| (defun zip-initargs-hash-table (slots ht &optional snake-case)
 | |
|   (flatten
 | |
|    (remove-if #'null
 | |
| 	      (mapcar (lambda (slot)
 | |
| 			(whenlet ((kwarg (slot-initarg slot))
 | |
| 				  (value (dispatch-get-value
 | |
| 					  slot
 | |
| 					  (slot-table-value
 | |
| 					   slot ht snake-case))))
 | |
| 			  (list kwarg value)))
 | |
| 		      slots))))
 | |
| 
 | |
| (defun make-instance-from-hash-table
 | |
|     (class-type table
 | |
|      &optional (package *package*) snake-case)
 | |
|   "Given a class symbol and a hash table, attempt to build an instance
 | |
| from it. The instance initargs are derived from the slot definitions,
 | |
| and an attempt is made to pair the slot with a string derivation. It
 | |
| is expected that the hash table keys will be downcase. If
 | |
| @c(snake-case) is t, the keys should use hyphens; otherwise, they
 | |
| should use underscores. The slot type is used to determine whether
 | |
| to attempt to parse another object as a hash table entry."
 | |
|   (whenlet ((class-sym (class-symbol class-type))
 | |
| 	    (arglst (zip-initargs-hash-table
 | |
| 		     (list-all-slots class-type package)
 | |
| 		     table snake-case)))
 | |
|     (apply #'make-instance class-sym arglst)))
 |