| ;; This file is no longer necessary with Chicken versions above 1.92 |
| ;; |
| ;; This file overrides two functions inside TinyCLOS to provide support |
| ;; for multi-argument generics. There are many ways of linking this file |
| ;; into your code... all that needs to happen is this file must be |
| ;; executed after loading TinyCLOS but before any SWIG modules are loaded |
| ;; |
| ;; something like the following |
| ;; (require 'tinyclos) |
| ;; (load "multi-generic") |
| ;; (declare (uses swigmod)) |
| ;; |
| ;; An alternative to loading this scheme code directly is to add a |
| ;; (declare (unit multi-generic)) to the top of this file, and then |
| ;; compile this into the final executable or something. Or compile |
| ;; this into an extension. |
| |
| ;; Lastly, to override TinyCLOS method creation, two functions are |
| ;; overridden: see the end of this file for which two are overridden. |
| ;; You might want to remove those two lines and then exert more control over |
| ;; which functions are used when. |
| |
| ;; Comments, bugs, suggestions: send either to chicken-users@nongnu.org or to |
| ;; Most code copied from TinyCLOS |
| |
| (define <multi-generic> (make <entity-class> |
| 'name "multi-generic" |
| 'direct-supers (list <generic>) |
| 'direct-slots '())) |
| |
| (letrec ([applicable? |
| (lambda (c arg) |
| (memq c (class-cpl (class-of arg))))] |
| |
| [more-specific? |
| (lambda (c1 c2 arg) |
| (memq c2 (memq c1 (class-cpl (class-of arg)))))] |
| |
| [filter-in |
| (lambda (f l) |
| (if (null? l) |
| '() |
| (let ([h (##sys#slot l 0)] |
| [r (##sys#slot l 1)] ) |
| (if (f h) |
| (cons h (filter-in f r)) |
| (filter-in f r) ) ) ) )]) |
| |
| (add-method compute-apply-generic |
| (make-method (list <multi-generic>) |
| (lambda (call-next-method generic) |
| (lambda args |
| (let ([cam (let ([x (compute-apply-methods generic)] |
| [y ((compute-methods generic) args)] ) |
| (lambda (args) (x y args)) ) ] ) |
| (cam args) ) ) ) ) ) |
| |
| |
| |
| (add-method compute-methods |
| (make-method (list <multi-generic>) |
| (lambda (call-next-method generic) |
| (lambda (args) |
| (let ([applicable |
| (filter-in (lambda (method) |
| (let check-applicable ([list1 (method-specializers method)] |
| [list2 args]) |
| (cond ((null? list1) #t) |
| ((null? list2) #f) |
| (else |
| (and (applicable? (##sys#slot list1 0) (##sys#slot list2 0)) |
| (check-applicable (##sys#slot list1 1) (##sys#slot list2 1))))))) |
| (generic-methods generic) ) ] ) |
| (if (or (null? applicable) (null? (##sys#slot applicable 1))) |
| applicable |
| (let ([cmms (compute-method-more-specific? generic)]) |
| (sort applicable (lambda (m1 m2) (cmms m1 m2 args))) ) ) ) ) ) ) ) |
| |
| (add-method compute-method-more-specific? |
| (make-method (list <multi-generic>) |
| (lambda (call-next-method generic) |
| (lambda (m1 m2 args) |
| (let loop ((specls1 (method-specializers m1)) |
| (specls2 (method-specializers m2)) |
| (args args)) |
| (cond-expand |
| [unsafe |
| (let ((c1 (##sys#slot specls1 0)) |
| (c2 (##sys#slot specls2 0)) |
| (arg (##sys#slot args 0))) |
| (if (eq? c1 c2) |
| (loop (##sys#slot specls1 1) |
| (##sys#slot specls2 1) |
| (##sys#slot args 1)) |
| (more-specific? c1 c2 arg))) ] |
| [else |
| (cond ((and (null? specls1) (null? specls2)) |
| (##sys#error "two methods are equally specific" generic)) |
| ;((or (null? specls1) (null? specls2)) |
| ; (##sys#error "two methods have different number of specializers" generic)) |
| ((null? specls1) #f) |
| ((null? specls2) #t) |
| ((null? args) |
| (##sys#error "fewer arguments than specializers" generic)) |
| (else |
| (let ((c1 (##sys#slot specls1 0)) |
| (c2 (##sys#slot specls2 0)) |
| (arg (##sys#slot args 0))) |
| (if (eq? c1 c2) |
| (loop (##sys#slot specls1 1) |
| (##sys#slot specls2 1) |
| (##sys#slot args 1)) |
| (more-specific? c1 c2 arg)))) ) ] ) ) ) ) ) ) |
| |
| ) ;; end of letrec |
| |
| (define multi-add-method |
| (lambda (generic method) |
| (slot-set! |
| generic |
| 'methods |
| (let filter-in-method ([methods (slot-ref generic 'methods)]) |
| (if (null? methods) |
| (list method) |
| (let ([l1 (length (method-specializers method))] |
| [l2 (length (method-specializers (##sys#slot methods 0)))]) |
| (cond ((> l1 l2) |
| (cons (##sys#slot methods 0) (filter-in-method (##sys#slot methods 1)))) |
| ((< l1 l2) |
| (cons method methods)) |
| (else |
| (let check-method ([ms1 (method-specializers method)] |
| [ms2 (method-specializers (##sys#slot methods 0))]) |
| (cond ((and (null? ms1) (null? ms2)) |
| (cons method (##sys#slot methods 1))) ;; skip the method already in the generic |
| ((eq? (##sys#slot ms1 0) (##sys#slot ms2 0)) |
| (check-method (##sys#slot ms1 1) (##sys#slot ms2 1))) |
| (else |
| (cons (##sys#slot methods 0) (filter-in-method (##sys#slot methods 1)))))))))))) |
| |
| (##sys#setslot (##sys#slot generic (- (##sys#size generic) 2)) 1 (compute-apply-generic generic)) )) |
| |
| (define (multi-add-global-method val sym specializers proc) |
| (let ((generic (if (procedure? val) val (make <multi-generic> 'name (##sys#symbol->string sym))))) |
| (multi-add-method generic (make-method specializers proc)) |
| generic)) |
| |
| ;; Might want to remove these, or perhaps do something like |
| ;; (define old-add-method ##tinyclos#add-method) |
| ;; and then you can switch between creating multi-generics and TinyCLOS generics. |
| (set! ##tinyclos#add-method multi-add-method) |
| (set! ##tinyclos#add-global-method multi-add-global-method) |