Abstract away:
{load-packages-from-system-and-eval (:swank) :swank
(swank:create-server :port 4005)}
so that you don't care what working solution is hidden below the API.
;;; Infrastructure for named reader macros. We'll introduce them with
;;; a #\{ followed by a symbol naming the reader macro function to ;;
;;; call.
(eval-when (:compile-toplevel :load-toplevel :execute)
(defvar *named-reader-macros* (make-hash-table)))
(defmacro define-named-reader-macro (name (stream) &body body)
(check-type name symbol)
(check-type stream symbol)
`(eval-when (:compile-toplevel :load-toplevel :execute)
(setf (gethash ',name *named-reader-macros*)
(lambda (,stream)
(block ,name ,@body)))))
(defun named-reader-macro (name)
(gethash name *named-reader-macros*))
(defun brace-reader-macro (stream ch)
(declare (ignore ch))
(let ((name (read stream)))
(if (symbolp name)
(let ((reader-macro (named-reader-macro name)))
(if reader-macro
(funcall reader-macro stream)
(error "No reader macro named ~S" name)))
(error "The reader macro should be a symbol, not the ~S ~S" (type-of name) name))))
(set-macro-character #\{ 'brace-reader-macro nil *readtable*)
;;; To implement {load-packages-from-system-and-eval …} we need a non-interning reader.
(ql:quickload :com.informatimago.common-lisp.lisp-reader)
(defstruct future-symbol
name
package
qualified
external)
(com.informatimago.common-lisp.lisp-reader.reader::defparser parse-symbol-token (token)
"symbol ::= symbol-name
symbol ::= package-marker symbol-name
symbol ::= package-marker package-marker symbol-name
symbol ::= package-name package-marker symbol-name
symbol ::= package-name package-marker package-marker symbol-name
symbol-name ::= {alphabetic}+
package-name ::= {alphabetic}+ "
(let ((colon (position-if
(lambda (traits) (com.informatimago.common-lisp.lisp-reader.reader::traitp
com.informatimago.common-lisp.lisp-reader.reader::+ct-package-marker+
traits))
(com.informatimago.common-lisp.lisp-reader.reader::token-traits token))))
(if colon
(let* ((double-colon (and (< (1+ colon) (com.informatimago.common-lisp.lisp-reader.reader::token-length token))
(com.informatimago.common-lisp.lisp-reader.reader::traitp
com.informatimago.common-lisp.lisp-reader.reader::+ct-package-marker+
(com.informatimago.common-lisp.lisp-reader.reader::token-char-traits
token (1+ colon)))))
(pname (subseq (com.informatimago.common-lisp.lisp-reader.reader::token-text token)
0 colon))
(sname (subseq (com.informatimago.common-lisp.lisp-reader.reader::token-text token)
(+ colon (if double-colon 2 1)))))
(when (position-if
(lambda (traits) (com.informatimago.common-lisp.lisp-reader.reader::traitp
com.informatimago.common-lisp.lisp-reader.reader::+ct-package-marker+
traits))
(com.informatimago.common-lisp.lisp-reader.reader::token-traits token)
:start (+ colon (if double-colon 2 1)))
(com.informatimago.common-lisp.lisp-reader.reader::reject
t "Too many package markers in token ~S"
(com.informatimago.common-lisp.lisp-reader.reader::token-text token)))
(if (zerop colon)
(com.informatimago.common-lisp.lisp-reader.reader::accept
;; Keywords always exist, so let's intern them before finding them.
'symbol (intern sname (load-time-value (find-package "KEYWORD"))))
(com.informatimago.common-lisp.lisp-reader.reader::accept
'symbol (make-future-symbol :name sname :package pname :qualified t :external (not double-colon)))))
(com.informatimago.common-lisp.lisp-reader.reader::accept
;; no colon in token, let's just intern the symbol in the current package:
'symbol (make-future-symbol :name (com.informatimago.common-lisp.lisp-reader.reader::token-text token)
:package (package-name *package*)
:qualified nil)))))
(defun parse-token-non-interning (token)
"
RETURN: okp ; the parsed lisp object if okp, or an error message if (not okp)
"
(let ((message nil))
(macrolet
((rom (&body body)
"Result Or Message"
(if (null body)
'nil
(let ((vals (gensym)))
`(let ((,vals (multiple-value-list ,(car body))))
;; (format *trace-output* "~S --> ~S~%" ',(car body) ,vals)
(if (first ,vals)
(values-list ,vals)
(progn
(when (second ,vals)
(setf message (third ,vals)))
(rom ,@(cdr body)))))))))
(multiple-value-bind (ok type object)
(rom (com.informatimago.common-lisp.lisp-reader.reader::parse-decimal-integer-token token)
(com.informatimago.common-lisp.lisp-reader.reader::parse-integer-token token)
(com.informatimago.common-lisp.lisp-reader.reader::parse-ratio-token token)
(com.informatimago.common-lisp.lisp-reader.reader::parse-float-1-token token)
(com.informatimago.common-lisp.lisp-reader.reader::parse-float-2-token token)
(parse-symbol-token token))
(declare (ignorable type))
(values ok (if ok object message))))))
;;; Now we can write it:
(eval-when (:compile-toplevel :load-toplevel :execute)
(defun ensure-list (object)
(if (listp object) object (list object))))
(define-named-reader-macro load-packages-from-system-and-eval (stream)
;; TODO: use FIND-SYMBOL and check the external qualification instead of INTERN.
(flet ((read-in-keyword (stream)
(let ((*package* (load-time-value (find-package "KEYWORD"))))
(read stream)))
(read-non-interning (stream)
(let ((com.informatimago.common-lisp.lisp-reader.reader:*readtable*
(load-time-value (com.informatimago.common-lisp.lisp-reader.reader:copy-readtable nil))))
(setf (com.informatimago.common-lisp.lisp-reader.reader:readtable-parse-token
com.informatimago.common-lisp.lisp-reader.reader:*readtable*)
(function parse-token-non-interning))
(com.informatimago.common-lisp.lisp-reader.reader:read stream)))
(end-of-brace-p (item)
(and (future-symbol-p item)
(string= "}" (future-symbol-name item))
(not(future-symbol-qualified item)))))
(let ((packages (ensure-list (read-in-keyword stream)))
(systems (ensure-list (read-in-keyword stream)))
(body (loop
:for sexp = (read-non-interning stream)
:until (end-of-brace-p sexp)
:collect sexp)))
(labels ((process-atom (atom)
(if (future-symbol-p atom)
(if (member (future-symbol-package atom) packages :test (function string=))
(values (if (future-symbol-qualified atom)
`(intern ,(future-symbol-name atom) ,(future-symbol-package atom))
`(intern ,(future-symbol-name atom)))
t)
(values (if (future-symbol-qualified atom)
(intern (future-symbol-name atom) (future-symbol-package atom))
(intern (future-symbol-name atom)))
nil))
atom))
(process-sexp (sexp)
(if (atom sexp)
(process-atom sexp)
(let ((operator (first sexp))
(arguments (mapcar (lambda (sexp)
(multiple-value-bind (object future) (process-sexp sexp)
(if future
;; a variable in a future loaded package.
;; Notice it cannot be a lexical variable.
`(eval ,object)
object)))
(rest sexp))))
(if (atom operator)
(multiple-value-bind (object future) (process-atom operator)
(if future
`(funcall ,object ,@arguments)
`(,object ,@arguments)))
`(,(process-sexp operator) ,@arguments))))))
`(progn
,@(mapcar (lambda (system) `(ql:quickload ,system)) systems)
,@(mapcar (function process-sexp) body))))))
{load-packages-from-system-and-eval (:swank) :swank
(print '(creating server on port 4005))
(swank:create-server :port 4005)}
(pprint '{load-packages-from-system-and-eval (:swank) :swank
(print '(creating server on port 4005))
(swank:create-server :port 4005)})
(progn (quicklisp-client:quickload :swank)
(print '(creating server on port 4005))
(funcall (intern "CREATE-SERVER" "SWANK") :port 4005))
--
__Pascal Bourguignon__
http://www.informatimago.com/
A bad day in () is better than a good day in {}.
You can take the lisper out of the lisp job, but you can't take the lisp out
of the lisper (; -- antifuchs