PageRenderTime 46ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/trunk/Lib/chicken/multi-generic.scm

#
Lisp | 152 lines | 109 code | 16 blank | 27 comment | 0 complexity | ad961c68564f1e6d367d96e39fac0a97 MD5 | raw file
Possible License(s): LGPL-2.1, Cube, GPL-3.0, 0BSD, GPL-2.0
  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. ;; Lastly, to override TinyCLOS method creation, two functions are
  18. ;; overridden: see the end of this file for which two are overridden.
  19. ;; You might want to remove those two lines and then exert more control over
  20. ;; which functions are used when.
  21. ;; Comments, bugs, suggestions: send either to chicken-users@nongnu.org or to
  22. ;; Most code copied from TinyCLOS
  23. (define <multi-generic> (make <entity-class>
  24. 'name "multi-generic"
  25. 'direct-supers (list <generic>)
  26. 'direct-slots '()))
  27. (letrec ([applicable?
  28. (lambda (c arg)
  29. (memq c (class-cpl (class-of arg))))]
  30. [more-specific?
  31. (lambda (c1 c2 arg)
  32. (memq c2 (memq c1 (class-cpl (class-of arg)))))]
  33. [filter-in
  34. (lambda (f l)
  35. (if (null? l)
  36. '()
  37. (let ([h (##sys#slot l 0)]
  38. [r (##sys#slot l 1)] )
  39. (if (f h)
  40. (cons h (filter-in f r))
  41. (filter-in f r) ) ) ) )])
  42. (add-method compute-apply-generic
  43. (make-method (list <multi-generic>)
  44. (lambda (call-next-method generic)
  45. (lambda args
  46. (let ([cam (let ([x (compute-apply-methods generic)]
  47. [y ((compute-methods generic) args)] )
  48. (lambda (args) (x y args)) ) ] )
  49. (cam args) ) ) ) ) )
  50. (add-method compute-methods
  51. (make-method (list <multi-generic>)
  52. (lambda (call-next-method generic)
  53. (lambda (args)
  54. (let ([applicable
  55. (filter-in (lambda (method)
  56. (let check-applicable ([list1 (method-specializers method)]
  57. [list2 args])
  58. (cond ((null? list1) #t)
  59. ((null? list2) #f)
  60. (else
  61. (and (applicable? (##sys#slot list1 0) (##sys#slot list2 0))
  62. (check-applicable (##sys#slot list1 1) (##sys#slot list2 1)))))))
  63. (generic-methods generic) ) ] )
  64. (if (or (null? applicable) (null? (##sys#slot applicable 1)))
  65. applicable
  66. (let ([cmms (compute-method-more-specific? generic)])
  67. (sort applicable (lambda (m1 m2) (cmms m1 m2 args))) ) ) ) ) ) ) )
  68. (add-method compute-method-more-specific?
  69. (make-method (list <multi-generic>)
  70. (lambda (call-next-method generic)
  71. (lambda (m1 m2 args)
  72. (let loop ((specls1 (method-specializers m1))
  73. (specls2 (method-specializers m2))
  74. (args args))
  75. (cond-expand
  76. [unsafe
  77. (let ((c1 (##sys#slot specls1 0))
  78. (c2 (##sys#slot specls2 0))
  79. (arg (##sys#slot args 0)))
  80. (if (eq? c1 c2)
  81. (loop (##sys#slot specls1 1)
  82. (##sys#slot specls2 1)
  83. (##sys#slot args 1))
  84. (more-specific? c1 c2 arg))) ]
  85. [else
  86. (cond ((and (null? specls1) (null? specls2))
  87. (##sys#error "two methods are equally specific" generic))
  88. ;((or (null? specls1) (null? specls2))
  89. ; (##sys#error "two methods have different number of specializers" generic))
  90. ((null? specls1) #f)
  91. ((null? specls2) #t)
  92. ((null? args)
  93. (##sys#error "fewer arguments than specializers" generic))
  94. (else
  95. (let ((c1 (##sys#slot specls1 0))
  96. (c2 (##sys#slot specls2 0))
  97. (arg (##sys#slot args 0)))
  98. (if (eq? c1 c2)
  99. (loop (##sys#slot specls1 1)
  100. (##sys#slot specls2 1)
  101. (##sys#slot args 1))
  102. (more-specific? c1 c2 arg)))) ) ] ) ) ) ) ) )
  103. ) ;; end of letrec
  104. (define multi-add-method
  105. (lambda (generic method)
  106. (slot-set!
  107. generic
  108. 'methods
  109. (let filter-in-method ([methods (slot-ref generic 'methods)])
  110. (if (null? methods)
  111. (list method)
  112. (let ([l1 (length (method-specializers method))]
  113. [l2 (length (method-specializers (##sys#slot methods 0)))])
  114. (cond ((> l1 l2)
  115. (cons (##sys#slot methods 0) (filter-in-method (##sys#slot methods 1))))
  116. ((< l1 l2)
  117. (cons method methods))
  118. (else
  119. (let check-method ([ms1 (method-specializers method)]
  120. [ms2 (method-specializers (##sys#slot methods 0))])
  121. (cond ((and (null? ms1) (null? ms2))
  122. (cons method (##sys#slot methods 1))) ;; skip the method already in the generic
  123. ((eq? (##sys#slot ms1 0) (##sys#slot ms2 0))
  124. (check-method (##sys#slot ms1 1) (##sys#slot ms2 1)))
  125. (else
  126. (cons (##sys#slot methods 0) (filter-in-method (##sys#slot methods 1))))))))))))
  127. (##sys#setslot (##sys#slot generic (- (##sys#size generic) 2)) 1 (compute-apply-generic generic)) ))
  128. (define (multi-add-global-method val sym specializers proc)
  129. (let ((generic (if (procedure? val) val (make <multi-generic> 'name (##sys#symbol->string sym)))))
  130. (multi-add-method generic (make-method specializers proc))
  131. generic))
  132. ;; Might want to remove these, or perhaps do something like
  133. ;; (define old-add-method ##tinyclos#add-method)
  134. ;; and then you can switch between creating multi-generics and TinyCLOS generics.
  135. (set! ##tinyclos#add-method multi-add-method)
  136. (set! ##tinyclos#add-global-method multi-add-global-method)