Massive reorganization in progress.
[sod] / src / class-utilities.lisp
diff --git a/src/class-utilities.lisp b/src/class-utilities.lisp
new file mode 100644 (file)
index 0000000..bf02aa6
--- /dev/null
@@ -0,0 +1,199 @@
+;;; -*-lisp-*-
+;;;
+;;; A collection of utility functions for SOD classes
+;;;
+;;; (c) 2009 Straylight/Edgeware
+;;;
+
+;;;----- Licensing notice ---------------------------------------------------
+;;;
+;;; This file is part of the Sensble 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
+;;; the Free Software Foundation; either version 2 of the License, or
+;;; (at your option) any later version.
+;;;
+;;; SOD is distributed in the hope that it will be useful,
+;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with SOD; if not, write to the Free Software Foundation,
+;;; Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+(cl:in-package #:sod)
+
+;;;--------------------------------------------------------------------------
+;;; Finding things by name
+
+(export 'find-superclass-by-nick)
+(defun find-superclass-by-nick (class nick)
+  "Returns the superclass of CLASS with nickname NICK, or signals an error."
+
+  ;; Slightly tricky.  The class almost certainly hasn't been finalized, so
+  ;; trundle through its superclasses and hope for the best.
+  (if (string= nick (sod-class-nickname class))
+      class
+      (or (some (lambda (super)
+                 (find nick (sod-class-precedence-list super)
+                       :key #'sod-class-nickname
+                       :test #'string=))
+               (sod-class-direct-superclasses class))
+         (error "No superclass of `~A' with nickname `~A'" class nick))))
+
+(export '(find-instance-slot-by-name find-class-slot-by-name
+         find-message-by-name))
+(flet ((find-thing-by-name (what class list name key)
+        (or (find name list :key key :test #'string=)
+            (error "No ~A in class `~A' with name `~A'" what class name))))
+
+  (defun find-instance-slot-by-name (class super-nick slot-name)
+    (let ((super (find-superclass-by-nick class super-nick)))
+      (find-thing-by-name "slot" super (sod-class-slots super)
+                         slot-name #'sod-slot-name)))
+
+  (defun find-class-slot-by-name (class super-nick slot-name)
+    (let* ((meta (sod-class-metaclass class))
+          (super (find-superclass-by-nick meta super-nick)))
+      (find-thing-by-name "slot" super (sod-class-slots super)
+                         slot-name #'sod-slot-name)))
+
+  (defun find-message-by-name (class super-nick message-name)
+    (let ((super (find-superclass-by-nick class super-nick)))
+      (find-thing-by-name "message" super (sod-class-messages super)
+                         message-name #'sod-message-name))))
+
+;;;--------------------------------------------------------------------------
+;;; Miscellaneous useful functions.
+
+(export 'sod-subclass-p)
+(defun sod-subclass-p (class-a class-b)
+  "Return whether CLASS-A is a descendent of CLASS-B.
+
+   Careful!  Assumes that the class precedence list of CLASS-A has been
+   computed!"
+  (member class-b (sod-class-precedence-list class-a)))
+
+(export 'valid-name-p)
+(defun valid-name-p (name)
+  "Checks whether NAME is a valid name.
+
+   The rules are:
+
+     * the name must be a string
+     * which is nonempty
+     * whose first character is alphabetic
+     * 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))))
+
+(export 'find-root-superclass)
+(defun find-root-superclass (class)
+  "Returns the `root' superclass of CLASS.
+
+   The root superclass is the superclass which itself has no direct
+   superclasses.  In universes not based on the provided builtin module, the
+   root class may not be our beloved SodObject; however, there must be one
+   (otherwise the class graph is cyclic, which should be forbidden), and we
+   insist that it be unique."
+
+  ;; The root superclass must be a chain head since the chains partition the
+  ;; superclasses; the root has no superclasses so it can't have a link and
+  ;; must therefore be a head.  This narrows the field down quite a lot.
+  ;;
+  ;; Note!  This function gets called from `check-sod-class' before the
+  ;; class's chains have been computed.  Therefore we iterate over the direct
+  ;; superclass's chains rather than the class's own.  This misses a chain
+  ;; only in the case where the class is its own chain head.  There are two
+  ;; subcases: if there are no direct superclasses at all, then the class is
+  ;; its own root; otherwise, it clearly can't be the root and the omission
+  ;; is harmless.
+
+  (let* ((supers (sod-class-direct-superclasses class))
+        (roots (if supers
+                   (remove-duplicates
+                    (remove-if #'sod-class-direct-superclasses
+                               (mappend (lambda (super)
+                                          (mapcar (lambda (chain)
+                                                    (sod-class-chain-head
+                                                     (car chain)))
+                                                  (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))
+         (t (car roots)))))
+
+(export 'find-root-metaclass)
+(defun find-root-metaclass (class)
+  "Returns the `root' metaclass of CLASS.
+
+   The root metaclass is the metaclass of the root superclass -- see
+   `find-root-superclass'."
+  (sod-class-metaclass (find-root-superclass class)))
+
+;;;--------------------------------------------------------------------------
+;;; Type hacking.
+
+(export 'argument-lists-compatible-p)
+(defun argument-lists-compatible-p (message-args method-args)
+  "Compare argument lists for compatibility.
+
+   Return true if METHOD-ARGS is a suitable method argument list
+   corresponding to the message argument list MESSAGE-ARGS.  This is the case
+   if the lists are the same length, each message argument has a
+   corresponding method argument with the same type, and if the message
+   arguments end in an ellpisis, the method arguments must end with a
+   `va_list' argument.  (We can't pass actual variable argument lists around,
+   except as `va_list' objects, which are devilish inconvenient things and
+   require much hacking.  See the method combination machinery for details.)"
+
+  (and (= (length message-args) (length method-args))
+       (every (lambda (message-arg method-arg)
+               (if (eq message-arg :ellipsis)
+                   (eq method-arg (c-type va-list))
+                   (c-type-equal-p (argument-type message-arg)
+                                   (argument-type method-arg))))
+             message-args method-args)))
+
+;;;--------------------------------------------------------------------------
+;;; Names of things.
+
+(export 'islots-struct-tag)
+(defun islots-struct-tag (class)
+  (format nil "~A__islots" class))
+
+(export 'ichain-struct-tag)
+(defun ichain-struct-tag (class chain-head)
+  (format nil "~A__ichain_~A" class (sod-class-nickname chain-head)))
+
+(export 'ichain-union-tag)
+(defun ichain-union-tag (class chain-head)
+  (format nil "~A__ichainu_~A" class (sod-class-nickname chain-head)))
+
+(export 'ilayout-struct-tag)
+(defun ilayout-struct-tag (class)
+  (format nil "~A__ilayout" class))
+
+(export 'vtmsgs-struct-tag)
+(defun vtmsgs-struct-tag (class super)
+  (format nil "~A__vtmsgs_~A" class (sod-class-nickname super)))
+
+(export 'vtable-struct-tag)
+(defun vtable-struct-tag (class chain-head)
+  (format nil "~A__vt_~A" class (sod-class-nickname chain-head)))
+
+(export 'vtable-name)
+(defun vtable-name (class chain-head)
+  (format nil "~A__vtable_~A" class (sod-class-nickname chain-head)))
+
+;;;----- That's all, folks --------------------------------------------------