Home | History | Annotate | Download | only in chicken
      1 ;; This file is no longer necessary with Chicken versions above 1.92
      2 ;;
      3 ;; This file overrides two functions inside TinyCLOS to provide support
      4 ;; for multi-argument generics.  There are many ways of linking this file
      5 ;; into your code... all that needs to happen is this file must be
      6 ;; executed after loading TinyCLOS but before any SWIG modules are loaded
      7 ;;
      8 ;; something like the following
      9 ;; (require 'tinyclos)
     10 ;; (load "multi-generic")
     11 ;; (declare (uses swigmod))
     12 ;;
     13 ;; An alternative to loading this scheme code directly is to add a
     14 ;; (declare (unit multi-generic)) to the top of this file, and then
     15 ;; compile this into the final executable or something.  Or compile
     16 ;; this into an extension.
     17 
     18 ;; Lastly, to override TinyCLOS method creation, two functions are
     19 ;; overridden: see the end of this file for which two are overridden.
     20 ;; You might want to remove those two lines and then exert more control over
     21 ;; which functions are used when.
     22 
     23 ;; Comments, bugs, suggestions: send either to chicken-users (at) nongnu.org or to
     24 ;; Most code copied from TinyCLOS
     25 
     26 (define  (make 
     27 			  'name "multi-generic"
     28 			  'direct-supers (list )
     29 			  'direct-slots '()))
     30 
     31 (letrec ([applicable?
     32           (lambda (c arg)
     33             (memq c (class-cpl (class-of arg))))]
     34 
     35          [more-specific?
     36           (lambda (c1 c2 arg)
     37             (memq c2 (memq c1 (class-cpl (class-of arg)))))]
     38 
     39          [filter-in
     40            (lambda (f l)
     41              (if (null? l)
     42                  '()
     43                  (let ([h (##sys#slot l 0)]
     44 	               [r (##sys#slot l 1)] )
     45 	           (if (f h)
     46 	               (cons h (filter-in f r))
     47 	               (filter-in f r) ) ) ) )])
     48 
     49 (add-method compute-apply-generic
     50   (make-method (list )
     51     (lambda (call-next-method generic)
     52       (lambda args
     53 		(let ([cam (let ([x (compute-apply-methods generic)]
     54 				 [y ((compute-methods generic) args)] )
     55 			     (lambda (args) (x y args)) ) ] )
     56 		  (cam args) ) ) ) ) )
     57 
     58 
     59 
     60 (add-method compute-methods
     61   (make-method (list )
     62     (lambda (call-next-method generic)
     63       (lambda (args)
     64 	(let ([applicable
     65 	       (filter-in (lambda (method)
     66                             (let check-applicable ([list1 (method-specializers method)]
     67                                                    [list2 args])
     68                               (cond ((null? list1) #t)
     69                                     ((null? list2) #f)
     70                                     (else
     71                                       (and (applicable? (##sys#slot list1 0) (##sys#slot list2 0))
     72                                            (check-applicable (##sys#slot list1 1) (##sys#slot list2 1)))))))
     73 			  (generic-methods generic) ) ] )
     74 	  (if (or (null? applicable) (null? (##sys#slot applicable 1)))
     75 	      applicable
     76 	      (let ([cmms (compute-method-more-specific? generic)])
     77 		(sort applicable (lambda (m1 m2) (cmms m1 m2 args))) ) ) ) ) ) ) )
     78 
     79 (add-method compute-method-more-specific?
     80   (make-method (list )
     81     (lambda (call-next-method generic)
     82       (lambda (m1 m2 args)
     83 	(let loop ((specls1 (method-specializers m1))
     84 		   (specls2 (method-specializers m2))
     85 		   (args args))
     86 	  (cond-expand
     87 	   [unsafe
     88 	    (let ((c1  (##sys#slot specls1 0))
     89 		  (c2  (##sys#slot specls2 0))
     90 		  (arg (##sys#slot args 0)))
     91 	      (if (eq? c1 c2)
     92 		  (loop (##sys#slot specls1 1)
     93 			(##sys#slot specls2 1)
     94 			(##sys#slot args 1))
     95 		  (more-specific? c1 c2 arg))) ]
     96 	   [else
     97 	    (cond ((and (null? specls1) (null? specls2))
     98 		   (##sys#error "two methods are equally specific" generic))
     99 		  ;((or (null? specls1) (null? specls2))
    100 		  ; (##sys#error "two methods have different number of specializers" generic))
    101                   ((null? specls1) #f)
    102                   ((null? specls2) #t)
    103 		  ((null? args)
    104 		   (##sys#error "fewer arguments than specializers" generic))
    105 		  (else
    106 		   (let ((c1  (##sys#slot specls1 0))
    107 			 (c2  (##sys#slot specls2 0))
    108 			 (arg (##sys#slot args 0)))
    109 		     (if (eq? c1 c2)
    110 			 (loop (##sys#slot specls1 1)
    111 			       (##sys#slot specls2 1)
    112 			       (##sys#slot args 1))
    113 			 (more-specific? c1 c2 arg)))) ) ] ) ) ) ) ) )
    114 
    115 ) ;; end of letrec
    116 
    117 (define multi-add-method
    118   (lambda (generic method)
    119     (slot-set!
    120      generic
    121      'methods
    122        (let filter-in-method ([methods (slot-ref generic 'methods)])
    123          (if (null? methods)
    124            (list method)
    125            (let ([l1 (length (method-specializers method))]
    126 		 [l2 (length (method-specializers (##sys#slot methods 0)))])
    127              (cond ((> l1 l2)
    128                     (cons (##sys#slot methods 0) (filter-in-method (##sys#slot methods 1))))
    129                    ((< l1 l2)
    130                     (cons method methods))
    131                    (else
    132                      (let check-method ([ms1 (method-specializers method)]
    133                                         [ms2 (method-specializers (##sys#slot methods 0))])
    134                        (cond ((and (null? ms1) (null? ms2))
    135                               (cons method (##sys#slot methods 1))) ;; skip the method already in the generic
    136                              ((eq? (##sys#slot ms1 0) (##sys#slot ms2 0))
    137                               (check-method (##sys#slot ms1 1) (##sys#slot ms2 1)))
    138                              (else
    139                                (cons (##sys#slot methods 0) (filter-in-method (##sys#slot methods 1))))))))))))
    140 
    141     (##sys#setslot (##sys#slot generic (- (##sys#size generic) 2)) 1 (compute-apply-generic generic)) ))
    142 
    143 (define (multi-add-global-method val sym specializers proc)
    144   (let ((generic (if (procedure? val) val (make  'name (##sys#symbol->string sym)))))
    145     (multi-add-method generic (make-method specializers proc))
    146     generic))
    147 
    148 ;; Might want to remove these, or perhaps do something like
    149 ;; (define old-add-method ##tinyclos#add-method)
    150 ;; and then you can switch between creating multi-generics and TinyCLOS generics.
    151 (set! ##tinyclos#add-method multi-add-method)
    152 (set! ##tinyclos#add-global-method multi-add-global-method)
    153