(cl:defpackage :chillax.utils  (:use :cl :alexandria)  (:export  :fun :mkhash :hashget :strcat :dequote :at)) (in-package :chillax.utils) ;;; Functions (defmacro fun (&body body)  "This macro puts the FUN back in FUNCTION."  `(lambda (&optional _) (declare (ignorable _)) ,@body)) ;;; Hash tables (defun mkhash (&rest keys-and-values &aux (table (make-hash-table :test #'equal)))  "Convenience function for `literal' hash table definition."  (loop for (key val) on keys-and-values by #'cddr do (setf (gethash key table) val)  finally (return table))) (defun hashget (hash &rest keys)  "Convenience function for recursively accessing hash tables."  (reduce (lambda (h k) (gethash k h)) keys :initial-value hash)) (define-compiler-macro hashget (hash &rest keys)  (if (null keys) hash  (let ((hash-sym (make-symbol "HASH"))  (key-syms (loop for i below (length keys)  collect (make-symbol (format nil "~:@(~:R~)-KEY" i)))))  `(let ((,hash-sym ,hash)  ,@(loop for key in keys for sym in key-syms  collect `(,sym ,key)))  ,(reduce (lambda (hash key) `(gethash ,key ,hash))  key-syms :initial-value hash-sym))))) (defun (setf hashget) (new-value hash key &rest more-keys)  "Uses the last key given to hashget to insert NEW-VALUE into the hash table returned by the second-to-last key. tl;dr: DWIM SETF function for HASHGET."  (if more-keys  (setf (gethash (car (last more-keys))  (apply #'hashget hash key (butlast more-keys)))  new-value)  (setf (gethash key hash) new-value))) ;;; Strings (defun strcat (string &rest more-strings)  (apply #'concatenate 'string string more-strings)) (defun dequote (string)  (let ((len (length string)))  (if (and (> len 1) (starts-with #\" string) (ends-with #\" string))  (subseq string 1 (- len 1))  string))) ;;; ;;; At ;;; (defgeneric at (doc &rest keys)) (defgeneric (setf at) (new-value doc key &rest more-keys)) (defmethod at ((doc hash-table) &rest keys)  (apply #'hashget doc keys)) (defmethod (setf at) (new-value (doc hash-table) key &rest more-keys)  (apply #'(setf hashget) new-value doc key more-keys)) (defmethod at ((doc list) &rest keys)  (reduce (lambda (alist key)  (cdr (assoc key alist :test #'equal)))  keys :initial-value doc)) (defmethod (setf at) (new-value (doc list) key &rest more-keys)  (if more-keys  (setf (cdr (assoc (car (last more-keys))  (apply #'at doc key (butlast more-keys))  :test #'equal))  new-value)  (setf (cdr (assoc key doc :test #'equal)) new-value))) ;; A playful alias. (defun @ (doc &rest keys)  (apply #'at doc keys)) (defun (setf @) (new-value doc key &rest more-keys)  (apply #'(setf at) new-value doc key more-keys))