-;;;; Definitons and translations of fundamental types
-
-(defmacro def-type-method (name args &optional documentation)
- `(progn
- (defgeneric ,name (,@args type &rest args)
- ,@(when documentation `((:documentation ,documentation))))
- (defmethod ,name (,@args (type symbol) &rest args)
- (let ((class (find-class type nil)))
- (if class
- (apply #',name ,@args class args)
- (multiple-value-bind (super-type expanded-p)
- (type-expand-1 (cons type args))
- (if expanded-p
- (,name ,@args super-type)
- (call-next-method))))))
- (defmethod ,name (,@args (type cons) &rest args)
- (declare (ignore args))
- (apply #',name ,@args (first type) (rest type)))))
-
-
-(def-type-method alien-type ())
-(def-type-method size-of ())
-(def-type-method to-alien-form (form))
-(def-type-method from-alien-form (form))
-(def-type-method cleanup-form (form)
+;;;; The "type method" system
+
+(defun find-applicable-type-method (name type-spec &optional (error-p t))
+ (let ((type-methods (get name 'type-methods)))
+ (labels ((search-method-in-cpl-order (classes)
+ (when classes
+ (or
+ (gethash (class-name (first classes)) type-methods)
+ (search-method-in-cpl-order (rest classes)))))
+ (lookup-method (type-spec)
+ (if (and (symbolp type-spec) (find-class type-spec nil))
+ (search-method-in-cpl-order
+ (class-precedence-list (find-class type-spec)))
+ (or
+ (let ((specifier (etypecase type-spec
+ (symbol type-spec)
+ (list (first type-spec)))))
+ (gethash specifier type-methods))
+ (multiple-value-bind (expanded-type expanded-p)
+ (type-expand-1 type-spec)
+ (when expanded-p
+ (lookup-method expanded-type))))))
+ (search-built-in-type-hierarchy (sub-tree)
+ (when (subtypep type-spec (first sub-tree))
+ (or
+ (search-nodes (cddr sub-tree))
+ (second sub-tree))))
+ (search-nodes (nodes)
+ (loop
+ for node in nodes
+ as function = (search-built-in-type-hierarchy node)
+ until function
+ finally (return function))))
+ (or
+ (lookup-method type-spec)
+ ;; This is to handle unexpandable types whichs doesn't name a class
+ (unless (and (symbolp type-spec) (find-class type-spec nil))
+ (search-nodes (get name 'built-in-type-hierarchy)))
+ (and
+ error-p
+ (error "No applicable type method for ~A when call width type specifier ~A" name type-spec))))))
+
+
+(defun insert-type-in-hierarchy (specifier function nodes)
+ (cond
+ ((let ((node (find specifier nodes :key #'first)))
+ (when node
+ (setf (second node) function)
+ nodes)))
+ ((let ((node
+ (find-if
+ #'(lambda (node)
+ (subtypep specifier (first node)))
+ nodes)))
+ (when node
+ (setf (cddr node)
+ (insert-type-in-hierarchy specifier function (cddr node)))
+ nodes)))
+ ((let ((sub-nodes (remove-if-not
+ #'(lambda (node)
+ (subtypep (first node) specifier))
+ nodes)))
+ (cons
+ (list* specifier function sub-nodes)
+ (nset-difference nodes sub-nodes))))))
+
+
+(defun add-type-method (name specifier function)
+ (setf (gethash specifier (get name 'type-methods)) function)
+ (when (typep (find-class specifier nil) 'built-in-class)
+ (setf (get name 'built-in-type-hierarchy)
+ (insert-type-in-hierarchy specifier function
+ (get name 'built-in-type-hierarchy)))))
+
+
+;; TODO: handle optional, key and rest arguments
+(defmacro define-type-generic (name lambda-list &optional documentation)
+ (if (or
+ (not lambda-list)
+ (find (first lambda-list) '(&optional &key &rest &allow-other-keys)))
+ (error "A type generic needs at least one required argument")
+ `(progn
+ (unless (get ',name 'type-methods)
+ (setf (get ',name 'type-methods) (make-hash-table))
+ (setf (get ',name 'built-in-type-hierarchy) ()))
+ (defun ,name ,lambda-list
+ ,documentation
+ (funcall
+ (find-applicable-type-method ',name ,(first lambda-list))
+ ,@lambda-list)))))
+
+
+(defmacro define-type-method (name lambda-list &body body)
+ (let ((specifier (cadar lambda-list))
+ (args (cons (caar lambda-list) (rest lambda-list))))
+ `(progn
+ (add-type-method ',name ',specifier #'(lambda ,args ,@body))
+ ',name)))
+
+
+
+;;;; Definitons and translations of fundamental types
+
+(define-type-generic alien-type (type-spec))
+(define-type-generic size-of (type-spec))
+(define-type-generic to-alien-form (type-spec form))
+(define-type-generic from-alien-form (type-spec form))
+(define-type-generic cleanup-form (type-spec form)