arthur...@gmail.com writes:
> Until now, I have been able to avoid dealing with implementation
> dependent lisp, but now I have been given the task of resurrecting
> some very old code that contains a lot inline # for dealing with
> implementation dependent features and I need to know whether I am
> understanding them correctly. I have always assumed that if I see
> #+foo preceding a lisp form, then that means execute the form if foo
> exists and if I see #-foo preceding a form then execute the form if
> foo does not exist. Are these symbols stored in *features* ?
Yes and no. #+ and #- read the following symbols with *package* bound
to the KEYWORD package. It doesn't override any other readtable or
reader variable, so #+foo tests for (member :FOO *features*) with the
default readtable settings.
Similarly in #+(and) the expression read and evaluated is actually (:AND).
#+ and #- have special rules to evaluatre (:AND …), (:OR …) and (:NOT …).
You can also test of a specific symbol in a specific package by
qualifying it:
(pushnew 'my-package:present *features*)
#+my-package:present (my-package:do-something)
> Is there a way to evaluate them in the REPL, so I can see what they
> are returning.
The easiest way is to use #+:
#+foo t #-foo nil
or:
'(#+foo t)
otherwise you can use this eval-feature function:
(defun eval-feature (expression)
"Evaluates a feature expression as a BOOLEAN."
(flet ((illegal-feature ()
(error "illegal feature ~S" expression)))
(cond
;; Some implementations accept any atom:
((atom expression)
(not (null (member expression *features*))))
(t (case (first expression)
((:not) (if (cddr expression)
(illegal-feature)
(not (eval-feature (second expression)))))
((:and) (every (function eval-feature) (rest expression)))
((:or) (some (function eval-feature) (rest expression)))
(t (illegal-feature)))))))
(eval-feature '(:and :ccl :darwin))
--> nil
(eval-feature '(:and :ccl :linux))
--> t
#+(and ccl linux) t #-(and ccl linux) nil
--> t
Notice that EVAL-FEATURE expects the expression as it would have been
read by #+/#-, ie. with keywords.
> for example
>
> #+symbolics
> (push :clos *features*)
>
> #-:clos
> (unless (find-package 'pcl)(require 'pcl))
#+foo and #+:foo are identical.
While it's not complete and not necessarily up-to-date, you may have a
look at
http://cliki.net/features
Of course, if your sources are litered with a lot of #+/#- it'll be a
maintainance nightmare. Just like C #ifdef/#ifndef, it should be
restricted to a small OS/implementation interface layer and the rest of
the software should use that layer with no need for #+/#-. Then you can
also choose to implement this layer in different files for different OS
or implementations, and just load one or another of these files
depending on the OS or implementation, ie. needing #+/#- only in the
ASDF file (or in the Makefile for C projects). But strangely,
programming against clean interfaces doesn't seem to be to the taste of
a lot of people :-(
--
__Pascal Bourguignon__
http://www.informatimago.com/
A bad day in () is better than a good day in {}.