Ignoring non-repository paths: /feature-tests.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. Mon Jul 20 01:09:25 UTC 2009 pix@kepibu.org * Better reporting of undefined-feature-tests. 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-09-04 17:55:07.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/feature-tests.lisp new-portaCL/feature-tests.lisp --- old-portaCL/feature-tests.lisp 2013-09-04 17:55:07.000000000 +0000 +++ new-portaCL/feature-tests.lisp 2013-09-04 17:55:07.000000000 +0000 @@ -12,8 +12,8 @@ (declare (ignore _)) (restart-case (error 'undefined-feature-test - :format-control "Unknown feature test: ~a" - :format-arguments (list feature)) + :format-control "Unknown feature test: ~s" + :format-arguments (list (car feature))) (treat-as-true () :report "Pretend this feature-form were true." t) diff -rN -u old-portaCL/notes.org new-portaCL/notes.org --- old-portaCL/notes.org 2013-09-04 17:55:07.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-09-04 17:55:07.000000000 +0000 +++ new-portaCL/portacl.asd 2013-09-04 17:55:07.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 " " :author " " diff -rN -u old-portaCL/reader.lisp new-portaCL/reader.lisp --- old-portaCL/reader.lisp 2013-09-04 17:55:07.000000000 +0000 +++ new-portaCL/reader.lisp 2013-09-04 17:55:07.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 -(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* : + ;; 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|))