(require 'cl-lib)
(defgroup anaphora nil
"Anaphoric macros providing implicit temp variables"
:version "1.0.4"
:link '(emacs-commentary-link :tag "Commentary" "anaphora")
:link '(url-link :tag "GitHub" "http://github.com/rolandwalker/anaphora")
:link '(url-link :tag "EmacsWiki" "http://emacswiki.org/emacs/Anaphora")
:prefix "anaphora-"
:group 'extensions)
(defcustom anaphora-use-long-names-only nil
"Use only long names such as `anaphoric-if' instead of traditional `aif'."
:type 'boolean
:group 'anaphora)
(defun anaphora-install-font-lock-keywords nil
"Fontify keywords `it' and `self'."
(font-lock-add-keywords 'emacs-lisp-mode `((,(concat "\\<" (regexp-opt '("it" "self") 'paren) "\\>")
1 font-lock-variable-name-face)) 'append))
(progn
(defun anaphora--install-traditional-aliases (&optional arg)
"Install traditional short aliases for anaphoric macros.
With negative numeric ARG, remove traditional aliases."
(let ((syms '(
(if . t)
(prog1 . t)
(prog2 . t)
(when . when)
(while . t)
(and . t)
(cond . cond)
(lambda . lambda)
(block . block)
(case . case)
(ecase . ecase)
(typecase . typecase)
(etypecase . etypecase)
(let . let)
(+ . t)
(- . t)
(* . t)
(/ . t)
)))
(cond
((and (numberp arg)
(< arg 0))
(dolist (cell syms)
(when (ignore-errors
(eq (symbol-function (intern-soft (format "a%s" (car cell))))
(intern-soft (format "anaphoric-%s" (car cell)))))
(fmakunbound (intern (format "a%s" (car cell)))))))
(t
(dolist (cell syms)
(let* ((builtin (car cell))
(traditional (intern (format "a%s" builtin)))
(long (intern (format "anaphoric-%s" builtin))))
(defalias traditional long)
(put traditional 'lisp-indent-function
(get builtin 'lisp-indent-function))
(put traditional 'edebug-form-spec (cdr cell)))))))))
(unless anaphora-use-long-names-only
(anaphora--install-traditional-aliases))
(defmacro anaphoric-if (cond then &rest else)
"Like `if', but the result of evaluating COND is bound to `it'.
The variable `it' is available within THEN and ELSE.
COND, THEN, and ELSE are otherwise as documented for `if'."
(declare (debug t)
(indent 2))
`(let ((it ,cond))
(if it ,then ,@else)))
(defmacro anaphoric-prog1 (first &rest body)
"Like `prog1', but the result of evaluating FIRST is bound to `it'.
The variable `it' is available within BODY.
FIRST and BODY are otherwise as documented for `prog1'."
(declare (debug t)
(indent 1))
`(let ((it ,first))
(progn ,@body)
it))
(defmacro anaphoric-prog2 (form1 form2 &rest body)
"Like `prog2', but the result of evaluating FORM2 is bound to `it'.
The variable `it' is available within BODY.
FORM1, FORM2, and BODY are otherwise as documented for `prog2'."
(declare (debug t)
(indent 2))
`(progn
,form1
(let ((it ,form2))
(progn ,@body)
it)))
(defmacro anaphoric-when (cond &rest body)
"Like `when', but the result of evaluating COND is bound to `it'.
The variable `it' is available within BODY.
COND and BODY are otherwise as documented for `when'."
(declare (debug when)
(indent 1))
`(anaphoric-if ,cond
(progn ,@body)))
(defmacro anaphoric-while (test &rest body)
"Like `while', but the result of evaluating TEST is bound to `it'.
The variable `it' is available within BODY.
TEST and BODY are otherwise as documented for `while'."
(declare (debug t)
(indent 1))
`(do ((it ,test ,test))
((not it))
,@body))
(defmacro anaphoric-and (&rest conditions)
"Like `and', but the result of the previous condition is bound to `it'.
The variable `it' is available within all CONDITIONS after the
initial one.
CONDITIONS are otherwise as documented for `and'.
Note that some implementations of this macro bind only the first
condition to `it', rather than each successive condition."
(declare (debug t))
(cond
((null conditions)
t)
((null (cdr conditions))
(car conditions))
(t
`(anaphoric-if ,(car conditions) (anaphoric-and ,@(cdr conditions))))))
(defmacro anaphoric-cond (&rest clauses)
"Like `cond', but the result of each condition is bound to `it'.
The variable `it' is available within the remainder of each of CLAUSES.
CLAUSES are otherwise as documented for `cond'."
(declare (debug cond))
(if (null clauses)
nil
(let ((cl1 (car clauses))
(sym (gensym)))
`(let ((,sym ,(car cl1)))
(if ,sym
(if (null ',(cdr cl1))
,sym
(let ((it ,sym)) ,@(cdr cl1)))
(anaphoric-cond ,@(cdr clauses)))))))
(defmacro anaphoric-lambda (args &rest body)
"Like `lambda', but the function may refer to itself as `self'.
ARGS and BODY are otherwise as documented for `lambda'."
(declare (debug lambda)
(indent defun))
`(cl-labels ((self ,args ,@body))
#'self))
(defmacro anaphoric-block (name &rest body)
"Like `block', but the result of the previous expression is bound to `it'.
The variable `it' is available within all expressions of BODY
except the initial one.
NAME and BODY are otherwise as documented for `block'."
(declare (debug block)
(indent 1))
`(cl-block ,name
,(funcall (anaphoric-lambda (body)
(cl-case (length body)
(0 nil)
(1 (car body))
(t `(let ((it ,(car body)))
,(self (cdr body))))))
body)))
(defmacro anaphoric-case (expr &rest clauses)
"Like `case', but the result of evaluating EXPR is bound to `it'.
The variable `it' is available within CLAUSES.
EXPR and CLAUSES are otherwise as documented for `case'."
(declare (debug case)
(indent 1))
`(let ((it ,expr))
(cl-case it ,@clauses)))
(defmacro anaphoric-ecase (expr &rest clauses)
"Like `ecase', but the result of evaluating EXPR is bound to `it'.
The variable `it' is available within CLAUSES.
EXPR and CLAUSES are otherwise as documented for `ecase'."
(declare (debug ecase)
(indent 1))
`(let ((it ,expr))
(cl-ecase it ,@clauses)))
(defmacro anaphoric-typecase (expr &rest clauses)
"Like `typecase', but the result of evaluating EXPR is bound to `it'.
The variable `it' is available within CLAUSES.
EXPR and CLAUSES are otherwise as documented for `typecase'."
(declare (debug typecase)
(indent 1))
`(let ((it ,expr))
(cl-typecase it ,@clauses)))
(defmacro anaphoric-etypecase (expr &rest clauses)
"Like `etypecase', but result of evaluating EXPR is bound to `it'.
The variable `it' is available within CLAUSES.
EXPR and CLAUSES are otherwise as documented for `etypecase'."
(declare (debug etypecase)
(indent 1))
`(let ((it ,expr))
(cl-etypecase it ,@clauses)))
(defmacro anaphoric-let (form &rest body)
"Like `let', but the result of evaluating FORM is bound to `it'.
FORM and BODY are otherwise as documented for `let'."
(declare (debug let)
(indent 1))
`(let ((it ,form))
(progn ,@body)))
(defmacro anaphoric-+ (&rest numbers-or-markers)
"Like `+', but the result of evaluating the previous expression is bound to `it'.
The variable `it' is available within all expressions after the
initial one.
NUMBERS-OR-MARKERS are otherwise as documented for `+'."
(declare (debug t))
(cond
((null numbers-or-markers)
0)
(t
`(let ((it ,(car numbers-or-markers)))
(+ it (anaphoric-+ ,@(cdr numbers-or-markers)))))))
(defmacro anaphoric-- (&optional number-or-marker &rest numbers-or-markers)
"Like `-', but the result of evaluating the previous expression is bound to `it'.
The variable `it' is available within all expressions after the
initial one.
NUMBER-OR-MARKER and NUMBERS-OR-MARKERS are otherwise as
documented for `-'."
(declare (debug t))
(cond
((null number-or-marker)
0)
((null numbers-or-markers)
`(- ,number-or-marker))
(t
`(let ((it ,(car numbers-or-markers)))
(- ,number-or-marker (+ it (anaphoric-+ ,@(cdr numbers-or-markers))))))))
(defmacro anaphoric-* (&rest numbers-or-markers)
"Like `*', but the result of evaluating the previous expression is bound to `it'.
The variable `it' is available within all expressions after the
initial one.
NUMBERS-OR-MARKERS are otherwise as documented for `*'."
(declare (debug t))
(cond
((null numbers-or-markers)
1)
(t
`(let ((it ,(car numbers-or-markers)))
(* it (anaphoric-* ,@(cdr numbers-or-markers)))))))
(defmacro anaphoric-/ (dividend divisor &rest divisors)
"Like `/', but the result of evaluating the previous divisor is bound to `it'.
The variable `it' is available within all expressions after the
first divisor.
DIVIDEND, DIVISOR, and DIVISORS are otherwise as documented for `/'."
(declare (debug t))
(cond
((null divisors)
`(/ ,dividend ,divisor))
(t
`(let ((it ,divisor))
(/ ,dividend (* it (anaphoric-* ,@divisors)))))))
(provide 'anaphora)