Improved feature readers --> to head
/reader.lisp
Ignoring non-repository paths: /reader.lisp
Thu Mar 8 05:15:58 UTC 2012 pix@kepibu.org
* Rename notes.org to README.org
Mon Jul 20 18:34:17 UTC 2009 pix@kepibu.org
* see other links, and add reference to cl-syntax-sugar
Mon Jul 20 07:07:16 UTC 2009 pix@kepibu.org
* Some org-mode syntaxisms
Mon Jul 20 06:58:45 UTC 2009 pix@kepibu.org
* Update bug list to include reader bug
Mon Jul 20 01:31:17 UTC 2009 pix@kepibu.org
tagged VERSION 0.1.3
Mon Jul 20 01:31:10 UTC 2009 pix@kepibu.org
* Bump version
Mon Jul 20 01:14:10 UTC 2009 pix@kepibu.org
* Improved feature readers
It turns out #+/#- also need to do their thing under *read-suppress*, rather
than simply skipping two forms.
E.g.,
'(#+(or) #+(not a b) a b c) => '(c)
'(#+(or) #+(and) a b c) => '(b c)
(Not that such constructions are practically portable anyway, but meh.)
Regardless, this fixes that as best I can. Unfortunately, it also forces
the normal package problems within feature expressions:
#+(or) #+(notapackage:foo) 'a => PACKAGE-ERROR
#+(or) #+(cl:notexported) 'a => PACKAGE-ERROR
This is, so far as I can tell, portably unavoidable. However, some (all?)
implementations /already/ have this problem, so at least it's nothing new.
diff -rN -u old-portaCL/README.org new-portaCL/README.org
--- old-portaCL/README.org 1970-01-01 00:00:00.000000000 +0000
+++ new-portaCL/README.org 2013-06-13 06:03:12.000000000 +0000
@@ -0,0 +1,119 @@
+#+TITLE: PortaCL: Easing the Creation of CL Portability Libraries
+
+* Rationale
+
+Sometimes you want to do something based upon *features*. Often, that results
+in lots of reader conditionals, and a final reader conditional duplicating and
+negating all previous conditionals. Ew!
+
+* API
+
+** ASDF Components: port-file, port-module
+
+It's not uncommon for a portability library to include something like:
+ :(:file
+ : #+sbcl "port-sbcl"
+ : #+clisp "port-clisp"
+ : #-(or sbcl clisp) (error "not supported"))
+port-file and port-modules allow you to specify things more like so:
+ :(:port-file "port-~A")
+or, less positionally,
+ :(:port-file "port-~/implementation/")
+
+Whether such magical divinations are a good thing is left to you to decide.
+
+port-file and port-module both also support specification of an :alternate-file,
+which if specified will be used in place of throwing a not-implemented error.
+E.g., for use if only one or two implementations need special behavior.
+
+You can also specify :not-found-condition, the condition type which will be
+thrown if no applicable file is found. (e.g., you might prefer 'not-supported
+instead, or 'not-necessary if a missing component is okay).
+
+** Condition: not-implemented
+
+Useful for indicating a particular thing is not implemented.
+
+This is the default condition thrown when an implementation-specific ASDF
+component is not found.
+
+** Condition: not-supported
+
+A particular thing is not implemented and won't be. E.g., because the lisp
+implementation lacks the necessary features.
+
+** Condition: not-necessary
+
+If this thing is not implemented, it didn't need to be.
+
+When specified as the :not-found-condition in a defsystem form, will cause
+operations on the component to be considered successful even if the component
+could not be found.
+
+** Function: featurep feature-expression
+
+Given a feature expression, returns true if that expression is true.
+
+see [[http://www.lispworks.com/documentation/HyperSpec/Body/24_aba.htm][CLHS 24.1.2.1]] for details.
+
+** Macro: define-feature-test test-name-or-names lambda-list [documentation] &body
+
+Defines a feature test which shall return true if the given feature expressions apply.
+
+see [[http://repo.kepibu.org/portaCL/feature-tests.lisp][feature-tests.lisp]] for usage examples.
+
+** Macro: feature-cond ([feature-conditional] [clause]+)*
+
+A macro version of #+foo (thing) #+bar (thing2) #-(or foo bar) (no-thing), with
+all the caveats and shortcomings that implies.
+
+** Macro: feature-ecase ([[feature-conditional] [clause]+]+)
+
+feature-case, except always includes a final (error 'not-implemented).
+
+* Future Ideas
+
+** ASDF component enhancements
+*** platform / operating system
+
+It might be useful to also offer up the operating system for interpolation into
+port-files. (e.g., via ~/platform/ or ~/operating-system/).
+
+*** shared-implementation support
+
+It might also be useful to offer a way to specify that certain implementations
+should be treated just like another implementation. E.g.,
+ :(:port-file "port-~a" :treat-as (:ecl :sbcl))
+could be used by usocket, instead of futzing with :alternate-file.
+
+** Other porting styles?
+
+Per-file implementation is not the only possible or used porting approach.
+Perhaps some others should also be supported?
+
+ * SLIME's defimplementation
+ * Xach's CLOS-based approach
+ * Any others?
+
+* Bugs
+
+ * ASDF systems sometimes try to recursively load themselves a couple
+ hundred times. (Though I've seen that even without loading portaCL, so
+ may not be entirely my bug...)
+ * Constructs similar to the ones below will result in an incorrect
+ package-error:
+ :(list #+(or) #+package:notexported a b c)
+ :(list #+(or) #+notapackage:foo a b c)
+ NOTE: this bug is shared by the standard readers of at least SBCL,
+ Clisp, and Lispworks; but not by Allegro.
+
+* see also
+
+[[http://www.cliki.net/trivial-features][trivial-features]]
+ smooths out the unnecessary differences between implementation *features*
+[[http://common-lisp.net/project/alexandria/][alexandria]]
+ implements a #'featurep which exactly matches that used by the standard's
+ #+/#- readmacros.
+[[http://common-lisp.net/project/cl-syntax-sugar/][cl-syntax-sugar]]
+ Offers a feature-case reader which is almost certainly more useful than
+ portaCL's feature-cond macro.
diff -rN -u old-portaCL/notes.org new-portaCL/notes.org
--- old-portaCL/notes.org 2013-06-13 06:03:12.000000000 +0000
+++ new-portaCL/notes.org 1970-01-01 00:00:00.000000000 +0000
@@ -1,108 +0,0 @@
-PortaCL: Easing the Creation of CL Portability Libraries
-
-* Rationale
-
-Sometimes you want to do something based upon *features*. Often, that results
-in lots of reader conditionals, and a final reader conditional duplicating and
-negating all previous conditionals. Ew!
-
-* API
-
-** ASDF Components: port-file, port-module
-
-It's not uncommon for a portability library to include something like:
- :(:file
- : #+sbcl "port-sbcl"
- : #+clisp "port-clisp"
- : #-(or sbcl clisp) (error "not supported"))
-port-file and port-modules allow you to specify things more like so:
- :(:port-file "port-~A")
-or, less positionally,
- :(:port-file "port-~/implementation/")
-
-Whether such magical divinations are a good thing is left to you to decide.
-
-port-file and port-module both also support specification of an :alternate-file,
-which if specified will be used in place of throwing a not-implemented error.
-E.g., for use if only one or two implementations need special behavior.
-
-You can also specify :not-found-condition, the condition type which will be
-thrown if no applicable file is found. (e.g., you might prefer 'not-supported
-instead, or 'not-necessary if a missing component is okay).
-
-** Condition: not-implemented
-
-Useful for indicating a particular thing is not implemented.
-
-This is the default condition thrown when an implementation-specific ASDF
-component is not found.
-
-** Condition: not-supported
-
-A particular thing is not implemented and won't be. E.g., because the lisp
-implementation lacks the necessary features.
-
-** Condition: not-necessary
-
-If this thing is not implemented, it didn't need to be.
-
-When specified as the :not-found-condition in a defsystem form, will cause
-operations on the component to be considered successful even if the component
-could not be found.
-
-** Function: featurep feature-expression
-
-Given a feature expression, returns true if that expression is true.
-
-see [[http://www.lispworks.com/documentation/HyperSpec/Body/24_aba.htm][CLHS 24.1.2.1]] for details.
-
-** Macro: define-feature-test test-name-or-names lambda-list [documentation] &body
-
-Defines a feature test which shall return true if the given feature expressions apply.
-
-see feature-tests.lisp for usage examples.
-
-** Macro: feature-cond ([feature-conditional] [clause]+)*
-
-A macro version of #+foo (thing) #+bar (thing2) #-(or foo bar) (no-thing), with
-all the caveats and shortcomings that implies.
-
-** Macro: feature-ecase ([[feature-conditional] [clause]+]+)
-
-feature-case, except always includes a final (error 'not-implemented).
-
-* Future Ideas
-
-** ASDF component enhancements
-*** platform / operating system
-
-It might be useful to also offer up the operating system for interpolation into
-port-files. (e.g., via ~/platform/ or ~/operating-system/).
-
-*** shared-implementation support
-
-It might also be useful to offer a way to specify that certain implementations
-should be treated just like another implementation. E.g.,
- :(:port-file "port-~a" :treat-as (:ecl :sbcl))
-could be used by usocket, instead of futzing with :alternate-file.
-
-** Other porting styles?
-
-Per-file implementation is not the only possible or used porting approach.
-Perhaps some others should also be supported?
-
- * SLIME's defimplementation
- * Xach's CLOS-based approach
- * Any others?
-
-* Bugs
-
-ASDF systems sometimes try to recursively load themselves a couple hundred
-times. (Though I've seen that even without loading portaCL, so may not be
-entirely my bug...)
-
-* see also
-trivial-features -- smooths out the unnecessary differences between
- implementation *features*
-alexandria -- implements a #'featurep which exactly matches that used by the
- standard's #+/#- readmacros.
diff -rN -u old-portaCL/portacl.asd new-portaCL/portacl.asd
--- old-portaCL/portacl.asd 2013-06-13 06:03:12.000000000 +0000
+++ new-portaCL/portacl.asd 2013-06-13 06:03:12.000000000 +0000
@@ -1,6 +1,6 @@
(asdf:defsystem portaCL
- :version "0.1.2"
+ :version "0.1.3"
:description "Eases the creation of portability libraries."
:maintainer " <pix@kepibu.org>"
:author " <pixel@kepibu.org>"
diff -rN -u old-portaCL/reader.lisp new-portaCL/reader.lisp
--- old-portaCL/reader.lisp 2013-06-13 06:03:12.000000000 +0000
+++ new-portaCL/reader.lisp 2013-06-13 06:03:12.000000000 +0000
@@ -1,28 +1,55 @@
(in-package #:portaCL)
+(define-condition suppressed-error (warning)
+ ((original-error :initarg :error)
+ (feature-expression :initarg :feature))
+ (:report (lambda (c s)
+ (format s "Suppressed an error while testing feature expansion ~S: ~A"
+ (slot-value c 'feature-expression)
+ (slot-value c 'original-error)))))
+
;; see CLHS 2.4.8.17 <http://www.lispworks.com/documentation/HyperSpec/Body/02_dhq.htm>
-(defun feature-reader (stream subchar arg)
+(defun feature-reader (stream fn arg)
"Reader for enhanced #+/#- feature conditionals."
- (declare (ignore arg))
+ (when (and arg (not *read-suppress*))
+ (error 'simple-error
+ :format-control "Numeric arg (~D) specified on reader conditional where none allowed."
+ :format-arguments (list arg)))
(flet ((feature-truth (form)
- (ecase subchar
- (#\+ (featurep form))
- (#\- (not (featurep form)))))
+ ;; from CLHS *read-suppress* <http://www.lispworks.com/documentation/HyperSpec/Body/v_rd_sup.htm>:
+ ;; Any standardized reader macro that is defined to read a following
+ ;; object or token will do so, but not signal an error if the object
+ ;; read is not of an appropriate type or syntax.
+ ;;
+ ;; I take that to mean errors thrown during the course of #'featurep
+ ;; should be suppressed. Some implementations differ.
+ (handler-bind
+ ((error (lambda (c)
+ (when *read-suppress*
+ (warn 'suppressed-error :feature form :error c)
+ (return-from feature-truth nil)))))
+ (funcall fn form)))
(read-form (stream)
(read stream t nil t)))
(cond
- (*read-suppress*
- (read-form stream)
- (read-form stream)
- (values))
((feature-truth
- (let ((*package* (find-package :keyword)))
+ (let ((*package* (find-package :keyword))
+ ;; Incorrectly interns symbols, but we need more than just cl:nil :/
+ (*read-suppress* nil))
(read-form stream)))
(values (read-form stream)))
(t
(let ((*read-suppress* t)) (read-form stream))
(values)))))
+(defun |#+-reader| (stream subchar arg)
+ (declare (ignore subchar))
+ (feature-reader stream #'featurep arg))
+
+(defun |#--reader| (stream subchar arg)
+ (declare (ignore subchar))
+ (feature-reader stream (complement #'featurep) arg))
+
(defun install-feature-readers (&optional (*readtable* *readtable*))
- (set-dispatch-macro-character #\# #\+ #'feature-reader)
- (set-dispatch-macro-character #\# #\- #'feature-reader))
+ (set-dispatch-macro-character #\# #\+ #'|#+-reader|)
+ (set-dispatch-macro-character #\# #\- #'|#--reader|))