cc8390ee |
(in-package #:fwoar.lisputils)
|
012d021d |
(defmacro neither (&rest forms)
`(not (or ,@forms)))
|
07f6eaf7 |
(defmacro neither-null (&rest forms)
`(neither ,@(loop for form
|
012d021d |
in forms
collecting `(null ,form))))
|
07f6eaf7 |
|
c0730f1a |
(defmacro let-each ((&key (be '*)) &body forms)
"Bind each element successively to the symbol specified via :be"
`(let* ,(loop for form in forms
|
012d021d |
collect (list be form))
|
c0730f1a |
,be))
|
9c529e1a |
(defmacro let-first ((&key (be '*)) bound &body forms)
|
c0730f1a |
"Bind the result of the first form to the symbol specified via :be"
|
9c529e1a |
`(let* ((,be ,bound))
,@forms
|
c0730f1a |
,be))
(defmacro let-second ((&key (be '*)) &body forms)
"Bind the result of the second form to the symbol specified via :be"
`(progn
,(car forms)
(let* ((,be ,(cadr forms)))
|
9c529e1a |
,@(cddr forms)
|
c0730f1a |
,be)))
|
cc8390ee |
(defmacro lambda-if ((test &rest args) &body body)
"Make a lambda that wraps an call to if"
`(lambda ,args
(if (,test ,@args)
|
e7c6d164 |
,@body)))
|
cc8390ee |
(defmacro lambda-cond ((&rest args) &body body)
"Make a lambda that wraps an call to cond"
`(lambda ,args
(cond
,@body)))
(defmacro alambda (&body body)
|
c4b8ada4 |
`(lambda (anaphora:it)
(declare (ignorable anaphora:it))
|
cc8390ee |
,@body))
|
e5349999 |
(eval-when (:compile-toplevel :load-toplevel :execute)
|
e143b109 |
(defun map-cons (cb cons)
(cond
((null cons) '())
((consp (cdr cons)) (cons (funcall cb (car cons))
|
e7c6d164 |
(map-cons cb (cdr cons))))
|
e143b109 |
(t (list (funcall cb (car cons))
|
e7c6d164 |
(funcall cb (cdr cons))))))
|
e143b109 |
(defun generate-declarations-for (sym ignored ignorable)
(let ((ignores (list))
|
e7c6d164 |
(ignorables (list)))
(map-cons (lambda (_1)
(cond ((member _1 ignorable)
(push _1 ignorables))
((member _1 ignored)
(push _1 ignores))))
(alexandria:ensure-cons sym))
|
e143b109 |
(if (or ignores ignorables)
|
e7c6d164 |
`((declare
,@(when ignores
`((ignore ,@ignores)))
,@(when ignorables
`((ignorable ,@ignorables)))))
'())))
|
e143b109 |
(defun find-ignored-vars (body)
(let ((possible-declarations (car body))
|
e7c6d164 |
(ignored-vars nil)
(ignorable-vars nil))
|
334428a3 |
(if (and (consp possible-declarations)
|
e7c6d164 |
(eq (car possible-declarations) 'declare)
(consp (cadr possible-declarations)))
(let* ((declarations (cdr possible-declarations)))
(setf ignored-vars (cdr (assoc 'ignore declarations))
ignorable-vars (cdr (assoc 'ignorable declarations))
body (cdr body))))
|
e143b109 |
(values ignored-vars
|
e7c6d164 |
ignorable-vars
body)))
|
e143b109 |
|
555da269 |
(defun ensure-mapping (list &optional (key-fn 'identity))
|
cc8390ee |
"Take a list and make sure that it's parseable as a let-style binding.
Very handy for certain sorts of macros."
(let ((symbols->mappings (lambda-cond (x)
|
555da269 |
((symbolp x) `(,(funcall key-fn x) ,x))
((null (cdr x)) `(,(funcall key-fn #1=(car x)) ,#1#))
|
e7c6d164 |
(t x))))
|
cc8390ee |
(mapcar symbols->mappings list)))
|
e5349999 |
|
e143b109 |
(defun rollup-list (list &optional body)
(labels ((helper (list &optional accum start)
|
e7c6d164 |
(tagbody
start
(cond
((endp list) (return-from rollup-list accum))
(t (psetf accum (cond
((null accum) (car list))
|
555da269 |
(start `(,@(car list) ,@accum))
|
e7c6d164 |
(t `(,@(car list) ,accum)))
list (cdr list)
start nil)
;; NOTE: REMEMBER! This call to #'GO is the "tail call"
(go start))))))
|
e143b109 |
(helper (reverse list) body t))))
|
cc8390ee |
|
07f6eaf7 |
(defmacro m-lambda (sym &rest args)
(let ((arglist (loop for x in args
|
012d021d |
unless (member x (list '&optional '&key '&rest))
collect (ctypecase x
(cons (car x))
((or symbol keyword string) x)))))
|
07f6eaf7 |
`(lambda (,@args)
(,sym ,@arglist))))
|
0e15cea0 |
(defun get-ignored-vars (body)
(let ((declarations (cdr (assoc 'declare body))))
(values (cdr (assoc 'ignore declarations))
(cdr (assoc 'ignorable declarations)))))
|
cc8390ee |
(defmacro destructuring-lambda ((&rest args) &body body)
"A lambda whose arguments can be lambda-lists to be destructured"
|
e143b109 |
(multiple-value-bind (ignored ignorable body) (find-ignored-vars body)
(let* ((args-syms (mapcar (lambda (_) (declare (ignore _)) (gensym "arg"))
|
e7c6d164 |
args))
(args (mapcar #'list args args-syms))
(destructuring-expressions
|
012d021d |
(rollup-list
(loop for (arg arg-sym) in args
collect (if (consp arg)
`(destructuring-bind ,arg ,arg-sym
(declare (ignore ,@ignored) (ignorable ,@ignorable)))
`(let ((,arg ,arg-sym))
,@(generate-declarations-for arg ignored ignorable))))
body)))
|
e143b109 |
`(lambda ,args-syms
|
e7c6d164 |
,destructuring-expressions))))
|
cc8390ee |
|
e5349999 |
;;; CASES:::
#|
|
43b7e4e9 |
;; (fw.lu::destructuring-lambda ((slot slot-keyword . r))
;; (make-slot-spec slot slot-keyword))
|
555da269 |
;;
|
43b7e4e9 |
;; (fw.lu::destructuring-lambda ((slot slot-keyword . r))
;; (declare (ignore r))
;; (make-slot-spec slot slot-keyword))
|
555da269 |
;;
|
43b7e4e9 |
;; (fw.lu::destructuring-lambda ((slot slot-keyword . r) b c)
;; (make-slot-spec slot slot-keyword))
|
555da269 |
;;
|
43b7e4e9 |
;; (fw.lu::destructuring-lambda ((slot slot-keyword . r) b)
;; (make-slot-spec slot slot-keyword))
|
555da269 |
;;
|
43b7e4e9 |
;; (fw.lu::destructuring-lambda ((slot slot-keyword . r) b)
;; (declare (ignore r))
;; (make-slot-spec slot slot-keyword))
|
e5349999 |
|#
|
cc8390ee |
(defun alist-string-hash-table (alist)
"Make a hash table suitable for strings and other non-eql types
from an association list"
(alexandria:alist-hash-table alist :test #'equal))
(defmacro copy-slots (slots from to)
"Given a list of slots specified as let-style bindings, copy them
from one object to another."
|
0e15cea0 |
(alexandria:once-only (from to)
|
cc8390ee |
`(progn
|
aabf976a |
(setf ,@(apply #'append
|
012d021d |
(iterate:iterate
(iterate:for (fro-slot to-slot) iterate:in (ensure-mapping slots))
(iterate:collect `((slot-value ,to ',to-slot) (slot-value ,from ',fro-slot))))))
|
cc8390ee |
,to)))
(defun transform-alist (function alist)
"Run down an alist, applying a given function to each element"
(mapcar (destructuring-lambda ((k . v)) (funcall function k v))
alist))
(defun %json-pair-transform (k v)
"Ugly hack to make jonathan work correctly with string values.
TODO: move this elsewhere"
|
0e15cea0 |
(cons (alexandria:make-keyword (string-downcase k))
|
cc8390ee |
(typecase v
(string (coerce v 'simple-string))
(t v))))
(defun %default-pair-transform (k v)
|
0e15cea0 |
(cons (alexandria:make-keyword (string-upcase k)) v))
|
cc8390ee |
|
0b31a5b5 |
(defun find-nonoperator-symbols (form)
(alexandria:flatten
(remove-duplicates
(typecase form
(symbol (list form))
(cons (append
(when (consp (car form))
(find-nonoperator-symbols (car form)))
(typecase (cdr form)
(symbol (list (cdr form)))
(cons (loop for thing in (cdr form)
|
012d021d |
append (find-nonoperator-symbols thing))))))))))
|
0b31a5b5 |
|
8ef95ab1 |
(defmacro may ((op arg &rest r))
(let ((cond (case op
(cl:funcall (car r))
(t arg))))
(alexandria:once-only (arg)
`(when ,cond
(,op ,arg ,@r)))))
|
0b31a5b5 |
|
cc8390ee |
(defmacro default-when (default test &body body)
|
222e6786 |
"return the default unless the test is true"
(warn "default-when is deprecated, renamed to default-unless")
|
0e15cea0 |
(alexandria:once-only (default)
|
222e6786 |
`(or (when ,test
,@body)
,default)))
(defmacro default-unless (default test &body body)
|
cc8390ee |
"return the default unless the test is true"
|
0e15cea0 |
(alexandria:once-only (default)
|
cc8390ee |
`(or (when ,test
,@body)
,default)))
(defmacro transform-result ((list-transform &optional (pair-transform #'identity)) &rest alist)
"Transform an alist that results from some operation as a whole and, optionally, apply a
transformation to each key-value pair."
`(funcall ,list-transform (transform-alist ,pair-transform ,@alist)))
(defun make-pairs (symbols)
|
e7c6d164 |
;TODO: does this duplicate ensure-mapping?
|
cc8390ee |
(cons 'list
|
012d021d |
(iterate:iterate
(iterate:for (key value) in symbols)
(iterate:collect `(list* ,(symbol-name key) ,value)))))
|
cc8390ee |
|
41cbd2ef |
(defun inits (l)
(serapeum:with-collector (c)
(let ((its ()))
(mapc (lambda (it)
(push it its)
(c (reverse its)))
l))))
|
c8375f9b |
(defmacro closing ((op &rest args))
(let ((stream-sym (gensym "STRING")))
`(with-open-stream (,stream-sym ,(first args))
(,op ,stream-sym ,@(cdr args)))))
#+fw.ignore
(progn
(closing (read-line (open "/foo/bar"
:element-type '(unsigned-byte 8))
nil
:the-end))
)
|
cc8390ee |
(defmacro slots-to-pairs (obj (&rest slots))
|
0e15cea0 |
(declare (optimize (debug 3)))
|
cc8390ee |
"Produce a alist from a set of object slots and their values"
|
0e15cea0 |
(alexandria:once-only (obj)
|
cc8390ee |
(let* ((slots (ensure-mapping slots))
|
012d021d |
(bindings (iterate:iterate
(iterate:for (slot v &key bind-from) in slots)
(iterate:collect (or bind-from slot)))))
|
cc8390ee |
`(with-slots ,bindings ,obj
,(make-pairs slots)))))
|
a16eb0ec |
(defmacro setfs (&body body)
"Make setf a bit nicer to use with paredit"
(list* 'setf (apply #'append body)))
|
e143b109 |
(defmacro prog2-let (first-form (&rest result-binding) &body body)
"Execute a form, make a bunch of bindings and retern the bound values via prog1 after executing body"
`(progn ,first-form
|
e7c6d164 |
(let (,@result-binding)
(prog1 (list ,@(mapcar #'car result-binding))
,@body))))
|
e143b109 |
|
0052b5eb |
;; TODO: use multiple values . . .
|
21620473 |
(defmacro prog1-let ((&rest result-binding) &body body)
|
e143b109 |
"Bind a bunch of symbols to values and return them via prog1"
|
21620473 |
`(let (,@result-binding)
|
1837a009 |
(multiple-value-prog1 (values ,@(mapcar #'car result-binding))
|
21620473 |
,@body)))
|
e4551d8e |
(defmacro prog1-bind ((var val) &body body)
`(let ((,var ,val))
(prog1 ,var
,@body)))
|
e143b109 |
(defmacro if-let* ((&rest bindings) &body (then-form &optional else-form))
"Like if-let, but sets bindings sequentially. Doesn't short-circuit."
`(let* ,bindings
(if (and ,@(mapcar #'car bindings))
|
e7c6d164 |
,then-form
,else-form)))
|
e143b109 |
(defmacro with ((var val) &body body)
"A stripped down let for binding a single name"
`(let ((,var ,val))
,@body))
|
0e15cea0 |
(defun do-acons (alist key datum)
(acons key datum alist))
(define-modify-macro aconsf (key datum) do-acons)
|
e143b109 |
|
afe39782 |
(defun do-adjoin (list item &rest r &key key test test-not)
(declare (ignore key test test-not))
(apply #'adjoin item list r))
(define-compiler-macro do-adjoin (list item &rest r)
(alexandria:once-only (list item)
`(adjoin ,item ,list ,@r)))
(define-modify-macro adjoinf (item &rest r)
do-adjoin)
|
455afb29 |
;;(defun ensure-list (val)
;; (typecase val
;; (list val)
;; (t (list val))))
|
a16eb0ec |
|
40f16faf |
(defmacro defun-ct (name (&rest args) &body body)
`(eval-when (:load-toplevel :compile-toplevel :execute)
(defun ,name ,args
,@body)))
|
efb4c8a7 |
(defmacro retry-once (&body body)
(alexandria:with-gensyms (flag)
`(let ((,flag t))
(tagbody
start
,@body
(when ,flag
(setf ,flag nil)
(go start))))))
|