(def!type hash ()
`(integer 0 ,max-hash))
-;;; a type used for indexing into arrays, and for related quantities
-;;; like lengths of lists
+;;; a type used for indexing into sequences, and for related
+;;; quantities like lengths of lists and other sequences.
;;;
-;;; It's intentionally limited to one less than the
-;;; ARRAY-DIMENSION-LIMIT for efficiency reasons, because in SBCL
-;;; ARRAY-DIMENSION-LIMIT is MOST-POSITIVE-FIXNUM, and staying below
-;;; that lets the system know it can increment a value of this type
-;;; without having to worry about using a bignum to represent the
-;;; result.
+;;; A more correct value for the exclusive upper bound for indexing
+;;; would be (1- ARRAY-DIMENSION-LIMIT) since ARRAY-DIMENSION-LIMIT is
+;;; the exclusive maximum *size* of one array dimension (As specified
+;;; in CLHS entries for MAKE-ARRAY and "valid array dimensions"). The
+;;; current value is maintained to avoid breaking existing code that
+;;; also uses that type for upper bounds on indices (e.g. sequence
+;;; length).
;;;
-;;; (It should be safe to use ARRAY-DIMENSION-LIMIT as an exclusive
-;;; bound because ANSI specifies it as an exclusive bound.)
+;;; In SBCL, ARRAY-DIMENSION-LIMIT is arranged to be a little smaller
+;;; than MOST-POSITIVE-FIXNUM, for implementation (see comment above
+;;; ARRAY-DIMENSION-LIMIT) and efficiency reasons: staying below
+;;; MOST-POSITIVE-FIXNUM lets the system know it can increment a value
+;;; of type INDEX without having to worry about using a bignum to
+;;; represent the result.
(def!type index () `(integer 0 (,sb!xc:array-dimension-limit)))
;;; like INDEX, but only up to half the maximum. Used by hash-table
(eq (car clause) 'ignore))))
(cdr decl))))
decls))
-
;;; just like DOLIST, but with one-dimensional arrays
(defmacro dovector ((elt vector &optional result) &body body)
(multiple-value-bind (forms decls) (parse-body body :doc-string-allowed nil)
`(with-locked-system-table (,n-table)
,iter-form)
iter-form))))))
+
+;;; Executes BODY for all entries of PLIST with KEY and VALUE bound to
+;;; the respective keys and values.
+(defmacro doplist ((key val) plist &body body)
+ (with-unique-names (tail)
+ `(let ((,tail ,plist) ,key ,val)
+ (loop (when (null ,tail) (return nil))
+ (setq ,key (pop ,tail))
+ (when (null ,tail)
+ (error "malformed plist, odd number of elements"))
+ (setq ,val (pop ,tail))
+ (progn ,@body)))))
+
\f
;;;; hash cache utility
;;; our equality tests, because MEMBER and friends refer to EQLity.
;;; So:
(defun equal-but-no-car-recursion (x y)
- (cond
- ((eql x y) t)
- ((consp x)
- (and (consp y)
- (eql (car x) (car y))
- (equal-but-no-car-recursion (cdr x) (cdr y))))
- (t nil)))
+ (do () (())
+ (cond ((eql x y) (return t))
+ ((and (consp x)
+ (consp y)
+ (eql (pop x) (pop y))))
+ (t
+ (return)))))
\f
;;;; package idioms
(let* ((name (first spec))
(exp-temp (gensym "ONCE-ONLY")))
`(let ((,exp-temp ,(second spec))
- (,name (gensym ,(symbol-name name))))
+ (,name (sb!xc:gensym ,(symbol-name name))))
`(let ((,,name ,,exp-temp))
,,(frob (rest specs) body))))))))
\f
;;; deprecated.texinfo.
;;;
;;; EARLY:
+;;; - SB-THREAD::GET-MUTEX, since 1.0.37.33 (04/2010) -> Late: 01/2013
+;;; ^- initially deprecated without compile-time warning, hence the schedule
;;; - SB-THREAD::SPINLOCK (type), since 1.0.53.11 (08/2011) -> Late: 08/2012
;;; - SB-THREAD::MAKE-SPINLOCK, since 1.0.53.11 (08/2011) -> Late: 08/2012
;;; - SB-THREAD::WITH-SPINLOCK, since 1.0.53.11 (08/2011) -> Late: 08/2012
;;; - SB-C::MERGE-TAIL-CALLS (policy), since 1.0.53.74 (11/2011) -> Late: 11/2012
;;; - SB-EXT:QUIT, since 1.0.56.55 (05/2012) -> Late: 05/2013
;;; - SB-UNIX:UNIX-EXIT, since 1.0.56.55 (05/2012) -> Late: 05/2013
+;;; - SB-DEBUG:*SHOW-ENTRY-POINT-DETAILS*, since 1.1.4.9 (02/2013) -> Late: 02/2014
;;;
;;; LATE:
;;; - SB-SYS:OUTPUT-RAW-BYTES, since 1.0.8.16 (06/2007) -> Final: anytime
+;;; Note: make sure CLX doesn't use it anymore!
;;; - SB-C::STACK-ALLOCATE-DYNAMIC-EXTENT (policy), since 1.0.19.7 -> Final: anytime
;;; - SB-C::STACK-ALLOCATE-VECTOR (policy), since 1.0.19.7 -> Final: anytime
;;; - SB-C::STACK-ALLOCATE-VALUE-CELLS (policy), since 1.0.19.7 -> Final: anytime
(defmacro define-deprecated-function (state since name replacements lambda-list &body body)
(let* ((replacements (normalize-deprecation-replacements replacements))
- (doc (let ((*package* (find-package :keyword)))
- (apply #'format nil
- "~@<~S has been deprecated as of SBCL ~A.~
- ~#[~; Use ~S instead.~; ~
- Use ~S or ~S instead.~:; ~
- Use~@{~#[~; or~] ~S~^,~} instead.~]~@:>"
- name since replacements))))
+ (doc
+ (let ((*package* (find-package :keyword))
+ (*print-pretty* nil))
+ (apply #'format nil
+ "~S has been deprecated as of SBCL ~A.~
+ ~#[~;~2%Use ~S instead.~;~2%~
+ Use ~S or ~S instead.~:;~2%~
+ Use~@{~#[~; or~] ~S~^,~} instead.~]"
+ name since replacements))))
`(progn
,(ecase state
((:early :late)
- `(defun ,name ,lambda-list
- ,doc
- ,@body))
+ `(progn
+ (defun ,name ,lambda-list
+ ,doc
+ ,@body)))
((:final)
`(progn
(declaim (ftype (function * nil) ,name))
(setf (compiler-macro-function ',name)
(deprecation-compiler-macro ,state ,since ',name ',replacements)))))
+(defun check-deprecated-variable (name)
+ (let ((info (info :variable :deprecated name)))
+ (when info
+ (deprecation-warning (car info) (cdr info) name nil))))
+
+(defmacro define-deprecated-variable (state since name &key (value nil valuep) replacement)
+ `(progn
+ (setf (info :variable :deprecated ',name) (cons ,state ,since))
+ ,@(when (member state '(:early :late))
+ `((defvar ,name ,@(when valuep (list value))
+ ,(let ((*package* (find-package :keyword)))
+ (format nil
+ "~@<~S has been deprecated as of SBCL ~A~@[, use ~S instead~].~:>"
+ name since replacement)))))))
+
;;; Anaphoric macros
(defmacro awhen (test &body body)
`(let ((it ,test))
;;; Returns a list of members of LIST. Useful for dealing with circular lists.
;;; For a dotted list returns a secondary value of T -- in which case the
;;; primary return value does not include the dotted tail.
-(defun list-members (list)
+;;; If the maximum length is reached, return a secondary value of :MAYBE.
+(defun list-members (list &key max-length)
(when list
(do ((tail (cdr list) (cdr tail))
- (members (list (car list)) (cons (car tail) members)))
- ((or (not (consp tail)) (eq tail list))
- (values members (not (listp tail)))))))
+ (members (list (car list)) (cons (car tail) members))
+ (count 0 (1+ count)))
+ ((or (not (consp tail)) (eq tail list)
+ (and max-length (>= count max-length)))
+ (values members (or (not (listp tail))
+ (and (>= count max-length) :maybe)))))))
;;; Default evaluator mode (interpeter / compiler)
(if (eql x 0.0l0)
(make-unportable-float :long-float-negative-zero)
0.0l0))))
+
+;;; Signalling an error when trying to print an error condition is
+;;; generally a PITA, so whatever the failure encountered when
+;;; wondering about FILE-POSITION within a condition printer, 'tis
+;;; better silently to give up than to try to complain.
+(defun file-position-or-nil-for-error (stream &optional (pos nil posp))
+ ;; Arguably FILE-POSITION shouldn't be signalling errors at all; but
+ ;; "NIL if this cannot be determined" in the ANSI spec doesn't seem
+ ;; absolutely unambiguously to prohibit errors when, e.g., STREAM
+ ;; has been closed so that FILE-POSITION is a nonsense question. So
+ ;; my (WHN) impression is that the conservative approach is to
+ ;; IGNORE-ERRORS. (I encountered this failure from within a homebrew
+ ;; defsystemish operation where the ERROR-STREAM had been CL:CLOSEd,
+ ;; I think by nonlocally exiting through a WITH-OPEN-FILE, by the
+ ;; time an error was reported.)
+ (if posp
+ (ignore-errors (file-position stream pos))
+ (ignore-errors (file-position stream))))
+
+(defun stream-error-position-info (stream &optional position)
+ (unless (interactive-stream-p stream)
+ (let ((now (file-position-or-nil-for-error stream))
+ (pos position))
+ (when (and (not pos) now (plusp now))
+ ;; FILE-POSITION is the next character -- error is at the previous one.
+ (setf pos (1- now)))
+ (let (lineno colno)
+ (when (and pos
+ (< pos sb!xc:array-dimension-limit)
+ (file-position stream :start))
+ (let ((string
+ (make-string pos :element-type (stream-element-type stream))))
+ (when (= pos (read-sequence string stream))
+ ;; Lines count from 1, columns from 0. It's stupid and traditional.
+ (setq lineno (1+ (count #\Newline string))
+ colno (- pos (or (position #\Newline string :from-end t) 0)))))
+ (file-position-or-nil-for-error stream now))
+ (remove-if-not #'second
+ (list (list :line lineno)
+ (list :column colno)
+ (list :file-position pos)))))))
+
+(declaim (inline schwartzian-stable-sort-list))
+(defun schwartzian-stable-sort-list (list comparator &key key)
+ (if (null key)
+ (stable-sort (copy-list list) comparator)
+ (let* ((key (if (functionp key)
+ key
+ (symbol-function key)))
+ (wrapped (mapcar (lambda (x)
+ (cons x (funcall key x)))
+ list))
+ (sorted (stable-sort wrapped comparator :key #'cdr)))
+ (map-into sorted #'car sorted))))