215 lines
7.2 KiB
EmacsLisp
215 lines
7.2 KiB
EmacsLisp
;;; dvc-lisp.el --- DVC lisp helper functions
|
|
|
|
;; Copyright (C) 2003-2007 by all contributors
|
|
|
|
;; Author: Stefan Reichoer, <stefan@xsteve.at>
|
|
;; Contributions from:
|
|
;; Matthieu Moy <Matthieu.Moy@imag.fr>
|
|
;; Masatake YAMATO <jet@gyve.org>
|
|
;; Milan Zamazal <pdm@zamazal.org>
|
|
;; Martin Pool <mbp@sourcefrog.net>
|
|
;; Robert Widhopf-Fenk <hack@robf.de>
|
|
;; Mark Triggs <mst@dishevelled.net>
|
|
;; Michael Olson <mwolson@gnu.org>
|
|
|
|
;; DVC is free software; you can redistribute it and/or modify
|
|
;; it under the terms of the GNU General Public License as published by
|
|
;; the Free Software Foundation; either version 2, or (at your option)
|
|
;; any later version.
|
|
|
|
;; DVC is distributed in the hope that it will be useful,
|
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
;; GNU General Public License for more details.
|
|
|
|
;; You should have received a copy of the GNU General Public License
|
|
;; along with GNU Emacs; see the file COPYING. If not, write to
|
|
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
;; Boston, MA 02110-1301, USA.
|
|
|
|
;;; Commentary:
|
|
|
|
;; Helper functions unrelated from GNU Arch.
|
|
|
|
;;; History:
|
|
;;
|
|
;; Created in May 2005 by Matthieu Moy
|
|
;;
|
|
;; Overhauled in Aug 2007 by Michael Olson
|
|
|
|
(autoload 'edebug-unwrap "edebug")
|
|
|
|
(defvar dvc-gensym-counter 0)
|
|
|
|
(defun dvc-gensym (&optional prefix)
|
|
"Generate a new uninterned symbol.
|
|
|
|
If PREFIX is a string, then the name is made by appending a
|
|
number to PREFIX. The default is to use \"dvc\".
|
|
|
|
If PREFIX is a number, then use that number at the end of the
|
|
symbol name."
|
|
(let* ((prefix (if (stringp prefix) prefix "dvc-gensym-uniq-"))
|
|
(num (if (integerp prefix) prefix
|
|
(prog1
|
|
dvc-gensym-counter
|
|
(setq dvc-gensym-counter (1+ dvc-gensym-counter)))))
|
|
(symbol (make-symbol (format "%s%d" prefix num))))
|
|
(eval `(defvar ,symbol nil "lint trap"))
|
|
symbol))
|
|
|
|
(defun dvc-capturing-lambda-helper (l)
|
|
"Traverse list L, replacing captured symbols with newly generated
|
|
symbols.
|
|
|
|
A pair is added to `captured-values' for each new symbol,
|
|
containing the name of the new symbol and the name of the old
|
|
symbol.
|
|
|
|
This is used by `dvc-capturing-lambda'."
|
|
(cond ((atom l) l)
|
|
((eq (car l) 'capture)
|
|
(let ((sym (edebug-unwrap (cadr l))))
|
|
(unless (symbolp sym)
|
|
(error "Expected a symbol in capture statement: %S" sym))
|
|
(let ((g (car (rassq sym captured-values))))
|
|
(unless g
|
|
(setq g (dvc-gensym))
|
|
(push (cons g sym) captured-values))
|
|
g)))
|
|
(t (mapcar 'dvc-capturing-lambda-helper l))))
|
|
|
|
(eval-and-compile
|
|
;; NOTE: We keep the contents of this block flush against the left
|
|
;; margin, so that C-M-x continues to work.
|
|
(defmacro dvc-capturing-lambda (args &rest body)
|
|
"Return a `lambda' form with ARGS, containing BODY, after capturing
|
|
symbol values in BODY from the defining context.
|
|
|
|
Symbols to be captured should be surrounded by (capture ...).
|
|
The remainder of BODY's forms are left as-is.
|
|
|
|
For development on DVC, using either `dvc-capturing-lambda' or
|
|
`lexical-let' is acceptable, with the condition that you must use
|
|
one consistently within a particular source file.
|
|
|
|
A practical example:
|
|
|
|
;; Using dvc-capturing-lambda
|
|
(defun sort-by-nearness-1 (values middle)
|
|
\"Sort VALUES in order of how close they are to MIDDLE.\"
|
|
(sort values (dvc-capturing-lambda (a b)
|
|
(< (abs (- a (capture middle)))
|
|
(abs (- b (capture middle)))))))
|
|
|
|
(sort-by-nearness-1 '(1 2 3 4 8 5 9) 6)
|
|
=> (5 4 8 3 9 2 1)
|
|
|
|
;; Using backquote
|
|
(defun sort-by-nearness-2 (values middle)
|
|
\"Sort VALUES in order of how close they are to MIDDLE.\"
|
|
(sort values `(lambda (a b)
|
|
(< (abs (- a ,middle))
|
|
(abs (- b ,middle))))))
|
|
|
|
(sort-by-nearness-2 '(1 2 3 4 8 5 9) 6)
|
|
=> (5 4 8 3 9 2 1)
|
|
|
|
;; Using lexical-let
|
|
(defun sort-by-nearness-3 (values middle)
|
|
\"Sort VALUES in order of how close they are to MIDDLE.\"
|
|
(lexical-let ((middle middle))
|
|
(sort values (lambda (a b)
|
|
(< (abs (- a middle))
|
|
(abs (- b middle)))))))
|
|
|
|
(sort-by-nearness-3 '(1 2 3 4 8 5 9) 6)
|
|
=> (5 4 8 3 9 2 1)
|
|
|
|
An example for the well-read Lisp fan:
|
|
|
|
(let* ((x 'lexical-x)
|
|
(y 'lexical-y)
|
|
(l (dvc-capturing-lambda (arg)
|
|
(list x (capture y) arg))))
|
|
(let ((y 'dynamic-y)
|
|
(x 'dynamic-x))
|
|
(funcall l 'dummy-arg)))
|
|
|
|
=> (dynamic-x lexical-y dummy-arg)"
|
|
(declare (indent 1)
|
|
(debug (sexp body)))
|
|
(let* ((captured-values nil)
|
|
(body (dvc-capturing-lambda-helper body)))
|
|
`(list 'lambda ',args
|
|
(list 'apply
|
|
(lambda ,(append args (mapcar #'car captured-values))
|
|
. ,body)
|
|
,@(mapcar #'(lambda (arg) (list 'quote arg)) args)
|
|
(list 'quote (list ,@(mapcar #'cdr captured-values))))))))
|
|
|
|
(defun dvc-lexical-let-perform-replacement-in-source ()
|
|
"Replace instances of quoted lambda forms with `lexical-let'
|
|
in the current buffer."
|
|
(interactive)
|
|
(goto-char (point-min))
|
|
(while (search-forward "`(lambda" nil t)
|
|
(search-backward "(")
|
|
(save-excursion (forward-sexp 1) (insert ")"))
|
|
(backward-delete-char 1)
|
|
(insert "(lexical-let ")
|
|
(search-backward "(lex")
|
|
(let ((beginning (point))
|
|
(letlist "")
|
|
(namelist nil))
|
|
(forward-sexp 1)
|
|
(save-restriction
|
|
(narrow-to-region beginning (point))
|
|
(goto-char (point-min))
|
|
(while (search-forward "," nil t)
|
|
(backward-delete-char 1)
|
|
(let* ((beg (point))
|
|
(end (progn (forward-sexp 1) (point)))
|
|
(name (buffer-substring-no-properties beg end))
|
|
(var (concat (replace-regexp-in-string "[^a-zA-Z\\-]" "-"
|
|
name) "-lex")))
|
|
(when (not (member name namelist))
|
|
(push name namelist)
|
|
(setq letlist (concat
|
|
letlist (when (not (string= letlist ""))
|
|
" ")
|
|
"(" var " "
|
|
name
|
|
")")))
|
|
(delete-region beg end)
|
|
(goto-char beg)
|
|
(insert var)
|
|
))
|
|
(goto-char (point-min))
|
|
(search-forward "(lexical-let ")
|
|
(insert "(" letlist ")")
|
|
(newline-and-indent)))))
|
|
|
|
(defun dvc-capturing-lambda-perform-replacement-in-source ()
|
|
"Replace instances of quoted lambda forms with `dvc-capturing-lambda'
|
|
in the current buffer."
|
|
(interactive)
|
|
(goto-char (point-min))
|
|
(while (search-forward "`(lambda" nil t)
|
|
(delete-region (match-beginning 0) (match-end 0))
|
|
(insert "(dvc-capturing-lambda")
|
|
(search-backward "(")
|
|
(let ((beginning (point)))
|
|
(forward-sexp 1)
|
|
(save-restriction
|
|
(narrow-to-region beginning (point))
|
|
(goto-char (point-min))
|
|
(while (search-forward "," nil t)
|
|
(backward-delete-char 1)
|
|
(insert "(capture ")
|
|
(forward-sexp 1)
|
|
(insert ")"))))))
|
|
|
|
(provide 'dvc-lisp)
|
|
;;; dvc-lisp.el ends here
|