X-Git-Url: https://git.distorted.org.uk/~mdw/sod/blobdiff_plain/fc09e191754e82d26723b7c6cbf3bfc24fedbf44..e046c3f65a8f7241889fb9b6005aac21e2aad1a8:/src/utilities.lisp diff --git a/src/utilities.lisp b/src/utilities.lisp index dfe2454..72423fd 100644 --- a/src/utilities.lisp +++ b/src/utilities.lisp @@ -7,7 +7,7 @@ ;;;----- Licensing notice --------------------------------------------------- ;;; -;;; This file is part of the Sensble Object Design, an object system for C. +;;; This file is part of the Sensible Object Design, an object system for C. ;;; ;;; SOD is free software; you can redistribute it and/or modify ;;; it under the terms of the GNU General Public License as published by @@ -273,6 +273,22 @@ "If COND, evaluate BODY as a progn with `it' bound to the value of COND." `(let ((it ,cond)) (when it ,@body))) +(export 'aand) +(defmacro aand (&rest forms) + "Like `and', but anaphoric. + + Each FORM except the first is evaluated with `it' bound to the value of + the previous one. If there are no forms, then the result it `t'; if there + is exactly one, then wrapping it in `aand' is pointless." + (labels ((doit (first rest) + (if (null rest) + first + `(let ((it ,first)) + (if it ,(doit (car rest) (cdr rest)) nil))))) + (if (null forms) + 't + (doit (car forms) (cdr forms))))) + (export 'acond) (defmacro acond (&body clauses &environment env) "Like COND, but with `it' bound to the value of the condition. @@ -430,11 +446,13 @@ (:documentation "Reports an inconsistency in the arguments passed to `merge-lists'.") (:report (lambda (condition stream) - (format stream "Merge inconsistency: failed to decide among ~A." + (format stream "Merge inconsistency: failed to decide between ~ + ~{~#[~;~A~;~A and ~A~:;~ + ~@{~A, ~#[~;and ~A~]~}~]~}" (merge-error-candidates condition))))) (export 'merge-lists) -(defun merge-lists (lists &key pick (test #'eql)) +(defun merge-lists (lists &key pick (test #'eql) (present #'identity)) "Return a merge of the given LISTS. The resulting list contains the items of the given LISTS, with duplicates @@ -442,7 +460,10 @@ the input LISTS in the sense that if A precedes B in some input list then A will also precede B in the output list. If the lists aren't consistent (e.g., some list contains A followed by B, and another contains B followed - by A) then an error of type `inconsistent-merge-error' is signalled. + by A) then an error of type `inconsistent-merge-error' is signalled. The + offending items are filtered for presentation through the PRESENT function + before being attached to the condition, so as to produce a more useful + diagnostic message. Item equality is determined by TEST. @@ -484,7 +505,7 @@ candidates)) (winner (cond ((null leasts) (error 'inconsistent-merge-error - :candidates candidates)) + :candidates (mapcar present candidates))) ((null (cdr leasts)) (car leasts)) (pick @@ -714,6 +735,28 @@ (,print)) (,print))))) +(export 'print-ugly-stuff) +(defun print-ugly-stuff (stream func) + "Print not-pretty things to the stream underlying STREAM. + + The Lisp pretty-printing machinery, notably `pprint-logical-block', may + interpose additional streams between its body and the original target + stream. This makes it difficult to make use of the underlying stream's + special features, whatever they might be." + + ;; This is unpleasant. Hacky hacky. + #.(or #+sbcl '(if (typep stream 'sb-pretty:pretty-stream) + (let ((target (sb-pretty::pretty-stream-target stream))) + (pprint-newline :mandatory stream) + (funcall func target)) + (funcall func stream)) + #+cmu '(if (typep stream 'pp:pretty-stream) + (let ((target (pp::pretty-stream-target stream))) + (pprint-newline :mandatory stream) + (funcall func target)) + (funcall func stream)) + '(funcall func stream))) + ;;;-------------------------------------------------------------------------- ;;; Iteration macros. @@ -790,6 +833,30 @@ (setf (,to object) value)))))) ;;;-------------------------------------------------------------------------- +;;; Condition and error utilities. + +(export 'designated-condition) +(defun designated-condition (default-type datum arguments + &key allow-pointless-arguments) + "Return the condition designated by DATUM and ARGUMENTS. + + DATUM and ARGUMENTS together are a `condition designator' of (some + supertype of) DEFAULT-TYPE; return the condition so designated." + (typecase datum + (condition + (unless (or allow-pointless-arguments (null arguments)) + (error "Argument list provided with specific condition")) + datum) + (symbol + (apply #'make-condition datum arguments)) + ((or string function) + (make-condition default-type + :format-control datum + :format-arguments arguments)) + (t + (error "Unexpected condition designator datum ~S" datum)))) + +;;;-------------------------------------------------------------------------- ;;; CLOS hacking. (export 'default-slot)