r9709: update policy
[kmrcl.git] / equal.lisp
1 ;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
2 ;;;; *************************************************************************
3 ;;;; FILE IDENTIFICATION
4 ;;;;
5 ;;;; Name:          equal.lisp
6 ;;;; Purpose:       Generalized equal function for KMRCL package
7 ;;;; Programmer:    Kevin M. Rosenberg
8 ;;;; Date Started:  Apr 2000
9 ;;;;
10 ;;;; $Id$
11 ;;;;
12 ;;;; This file, part of KMRCL, is Copyright (c) 2002 by Kevin M. Rosenberg
13 ;;;;
14 ;;;; KMRCL users are granted the rights to distribute and use this software
15 ;;;; as governed by the terms of the Lisp Lesser GNU Public License
16 ;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL.
17 ;;;; *************************************************************************
18
19
20 (in-package :kmrcl)
21 (declaim (optimize (speed 3) (safety 1) (compilation-speed 0) (debug 3)))
22
23   
24 (defun generalized-equal (obj1 obj2)
25   (if (not (equal (type-of obj1) (type-of obj2)))
26       (progn
27         (terpri)
28         (describe obj1)
29         (describe obj2)
30         nil)
31     (typecase obj1
32       (double-float
33        (let ((diff (abs (/ (- obj1 obj2) obj1))))
34          (if (> diff (* 10 double-float-epsilon))
35              nil
36            t)))
37       (complex
38        (and (generalized-equal (realpart obj1) (realpart obj2))
39             (generalized-equal (imagpart obj1) (imagpart obj2))))
40       (structure-object
41        (generalized-equal-fielded-object obj1 obj2))
42       (standard-object
43        (generalized-equal-fielded-object obj1 obj2))
44       (hash-table
45        (generalized-equal-hash-table obj1 obj2)
46        )
47       (function
48        (generalized-equal-function obj1 obj2))
49       (string
50        (string= obj1 obj2))
51       (array
52        (generalized-equal-array obj1 obj2))
53       (t
54        (equal obj1 obj2)))))
55
56
57 (defun generalized-equal-function (obj1 obj2)
58   (string= (function-to-string obj1) (function-to-string obj2)))
59
60 (defun generalized-equal-array (obj1 obj2)
61   (block test
62     (when (not (= (array-total-size obj1) (array-total-size obj2)))
63       (return-from test nil))
64     (dotimes (i (array-total-size obj1))
65       (unless (generalized-equal (aref obj1 i) (aref obj2 i))
66         (return-from test nil)))
67     (return-from test t)))
68
69 (defun generalized-equal-hash-table (obj1 obj2)
70   (block test
71     (when (not (= (hash-table-count obj1) (hash-table-count obj2)))
72       (return-from test nil))
73     (maphash
74      #'(lambda (k v)
75          (multiple-value-bind (value found) (gethash k obj2)
76            (unless (and found (generalized-equal v value))
77              (return-from test nil))))
78      obj1)
79     (return-from test t)))
80
81 (defun generalized-equal-fielded-object (obj1 obj2)
82   (block test
83     (when (not (equal (class-of obj1) (class-of obj2)))
84       (return-from test nil))
85     (dolist (field (class-slot-names (class-name (class-of obj1))))
86       (unless (generalized-equal (slot-value obj1 field) (slot-value obj2 field))
87         (return-from test nil)))
88     (return-from test t)))
89
90 (defun class-slot-names (c-name)
91   "Given a CLASS-NAME, returns a list of the slots in the class."
92   #+(or allegro cmu lispworks sbcl scl)
93   (mapcar #'kmr-mop:slot-definition-name
94           (kmr-mop:class-slots (kmr-mop:find-class c-name)))
95   #+(and mcl (not openmcl))
96   (let* ((class (find-class c-name nil)))
97     (when (typep class 'standard-class)
98       (nconc (mapcar #'car (ccl:class-instance-slots class))
99              (mapcar #'car (ccl:class-class-slots class)))))
100   #-(or allegro lispworks cmu mcl sbcl scl openmcl)
101   (error "class-slot-names is not defined on this platform")
102   )
103
104 (defun structure-slot-names (s-name)
105   "Given a STRUCTURE-NAME, returns a list of the slots in the structure."
106   #+allegro (class-slot-names s-name)
107   #+lispworks (structure:structure-class-slot-names
108                (find-class s-name))
109   #+(or sbcl cmu) (mapcar #'kmr-mop:slot-definition-name
110                           (kmr-mop:class-slots (kmr-mop:find-class s-name)))
111   #+scl (mapcar #'kernel:dsd-name
112                 (kernel:dd-slots
113                  (kernel:layout-info
114                   (kernel:class-layout (find-class s-name)))))
115   #+(and mcl (not openmcl))
116   (let* ((sd (gethash s-name ccl::%defstructs%))
117                (slots (if sd (ccl::sd-slots sd))))
118           (mapcar #'car (if (symbolp (caar slots)) slots (cdr slots))))
119   #-(or allegro lispworks cmu sbcl scl mcl)
120   (error "structure-slot-names is not defined on this platform")
121   )
122
123 (defun function-to-string (obj)
124   "Returns the lambda code for a function. Relies on
125 Allegro implementation-dependent features."
126   (multiple-value-bind (lambda closurep name) (function-lambda-expression obj)
127     (declare (ignore closurep))
128     (if lambda
129           (format nil "#'~s" lambda)
130       (if name
131           (format nil "#'~s" name)
132         (progn
133           (print obj)
134           (break))))))
135