chiark / gitweb /
doc/concepts.tex: Typeset method rĂ´le names as identifiers.
[sod] / src / class-utilities.lisp
index 35c6d1781a6f23c8e0d85e858dd47ce7f3757ace..a26afd29b9695fbac1fd96293bbd2dfef7f7b2b1 100644 (file)
@@ -105,6 +105,51 @@ (defun report-inheritance-path (state super)
                             of `~A', defined here"
                            super sub)))))
 
+;;;--------------------------------------------------------------------------
+;;; Metaclass inference.
+
+(export 'select-minimal-class-property)
+(defun select-minimal-class-property (supers key order default what
+                                     &key (present (lambda (x)
+                                                     (format nil "`~A'" x)))
+                                          allow-empty)
+  "Return the minimal partially-ordered key from the SUPERS.
+
+   KEY is a function of one argument which returns some interesting property
+   of a class.  The keys are assumed to be partially ordered by ORDER, a
+   function of two arguments which returns non-nil if its first argument
+   precedes its second.  If there is a unique minimal key then return it;
+   otherwise report a useful error and pick some candidate in an arbitrary
+   way; the DEFAULT may be chosen if no better choices are available.  If
+   ALLOW-EMPTY is non-nil, then no error is reported if there are no SUPERS,
+   and the DEFAULT choice is returned immediately.
+
+   In an error message, the keys are described as WHAT, which should be a
+   noun phrase; keys are filtered through PRESENT, a function of one
+   argument, before presentation.
+
+   The function returns two values: the chosen value, and a flag which is
+   non-nil if it was chosen without errors."
+
+  (let ((candidates (partial-order-minima (mapcar key supers) order)))
+    (cond ((and (null candidates) allow-empty)
+          (values default t))
+         ((and candidates (null (cdr candidates)))
+          (values (car candidates) t))
+         (t
+          (cerror* "No obvious choice for implicit ~A: ~
+                    ~{~#[root classes must specify explicitly~:;~
+                         candidates are ~
+                         ~#[~;~A~;~A and ~A~:;~@{~A, ~#[~;and ~A~]~}~]~]~:}"
+                   what (mapcar present candidates))
+          (dolist (candidate candidates)
+            (let ((super (find candidate supers :key key)))
+              (info-with-location super
+                                  "Direct superclass `~A' defined here ~
+                                   has ~A ~A"
+                                  super what (funcall present candidate))))
+          (values (if candidates (car candidates) default) nil)))))
+
 ;;;--------------------------------------------------------------------------
 ;;; Miscellaneous useful functions.
 
@@ -128,11 +173,12 @@ (defun valid-name-p (name)
      * all of whose characters are alphanumeric or underscores
      * and which doesn't contain two consecutive underscores."
 
-  (and (stringp name)
-       (plusp (length name))
-       (alpha-char-p (char name 0))
-       (every (lambda (ch) (or (alphanumericp ch) (char= ch #\_))) name)
-       (not (search "__" name))))
+  (or (typep name 'temporary-variable)
+      (and (stringp name)
+          (plusp (length name))
+          (alpha-char-p (char name 0))
+          (every (lambda (ch) (or (alphanumericp ch) (char= ch #\_))) name)
+          (not (search "__" name)))))
 
 (export 'find-root-superclass)
 (defun find-root-superclass (class)
@@ -167,10 +213,15 @@ (defun find-root-superclass (class)
                                                   (sod-class-chains super)))
                                         supers)))
                    (list class))))
-    (cond ((null roots) (error "Class ~A has no root class!" class))
-         ((cdr roots) (error "Class ~A has multiple root classes ~
-                              ~{~A~#[~; and ~;, ~]~}"
-                             class roots))
+    (cond ((null roots)
+          (error "Class ~A has no root class!" class))
+         ((cdr roots)
+          (cerror* "Class ~A has multiple root classes ~
+                    ~{~#[~;~A~;~A and ~A~:; ~@{~A, ~#[~;and ~A~]~}~]~}"
+                   class roots)
+          (let ((state (make-inheritance-path-reporter-state class)))
+            (dolist (root roots)
+              (report-inheritance-path state root))))
          (t (car roots)))))
 
 (export 'find-root-metaclass)